/** * Component: Paths Settings Tab * Documentation: documentation/settings-pages.md */ 'use client'; import React, { useState, useEffect } from 'react'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { usePathsSettings } from './usePathsSettings'; import type { PathsSettings } from '../../lib/types'; import { validateTemplate, generateMockPreviews } from '@/lib/utils/path-template.util'; interface PathsTabProps { paths: PathsSettings; onChange: (paths: PathsSettings) => void; onValidationChange: (isValid: boolean) => void; } interface TemplatePreview { isValid: boolean; error?: string; previewPaths?: string[]; } export function PathsTab({ paths, onChange, onValidationChange }: PathsTabProps) { const { testing, testResult, updatePath, testPaths } = usePathsSettings({ paths, onChange, onValidationChange, }); // Live preview state for audiobook template const [audiobookPreview, setAudiobookPreview] = useState(null); // Live preview state for ebook template const [ebookPreview, setEbookPreview] = useState(null); // Update audiobook live preview whenever template changes useEffect(() => { const template = paths.audiobookPathTemplate || '{author}/{title} {asin}'; const validation = validateTemplate(template); if (validation.valid) { setAudiobookPreview({ isValid: true, previewPaths: generateMockPreviews(template), }); } else { setAudiobookPreview({ isValid: false, error: validation.error, }); } }, [paths.audiobookPathTemplate]); // Update ebook live preview whenever template changes useEffect(() => { const template = paths.ebookPathTemplate || '{author}/{title} {asin}'; const validation = validateTemplate(template); if (validation.valid) { setEbookPreview({ isValid: true, previewPaths: generateMockPreviews(template), }); } else { setEbookPreview({ isValid: false, error: validation.error, }); } }, [paths.ebookPathTemplate]); const audiobookTemplate = paths.audiobookPathTemplate || '{author}/{title} {asin}'; const ebookTemplate = paths.ebookPathTemplate || '{author}/{title} {asin}'; const ebookMatchesAudiobook = ebookTemplate === audiobookTemplate; return (

Directory Paths

Configure download and media directory paths.

{/* Download Directory */}
updatePath('downloadDir', e.target.value)} placeholder="/downloads" className="font-mono" />

Temporary location for downloads before they are organized into the media library

{/* Media Directory */}
updatePath('mediaDir', e.target.value)} placeholder="/media/audiobooks" className="font-mono" />

Final location for organized audiobook library (Your backend scans this directory)

{/* Audiobook Organization Template */}
updatePath('audiobookPathTemplate', e.target.value)} placeholder="{author}/{title} {asin}" className="font-mono" />

Customize how audiobooks are organized within the media directory

{/* Audiobook Validation Error */} {audiobookPreview && !audiobookPreview.isValid && (
{audiobookPreview.error || 'Invalid template format'}
)} {/* Audiobook Preview Examples */} {audiobookPreview && audiobookPreview.isValid && audiobookPreview.previewPaths && (

Preview Examples

{audiobookPreview.previewPaths.map((preview, index) => (
{paths.mediaDir || '/media/audiobooks'}/{preview}
))}
)}
{/* Ebook Organization Template */}
updatePath('ebookPathTemplate', e.target.value)} placeholder="{author}/{title} {asin}" className="font-mono flex-1" />

Customize how ebooks are organized within the media directory

{/* Ebook Validation Error */} {ebookPreview && !ebookPreview.isValid && (
{ebookPreview.error || 'Invalid template format'}
)} {/* Ebook Preview Examples */} {ebookPreview && ebookPreview.isValid && ebookPreview.previewPaths && (

Preview Examples

{ebookPreview.previewPaths.map((preview, index) => (
{paths.mediaDir || '/media/audiobooks'}/{preview}
))}
)}
{/* Variable Reference Panel (shared for both templates) */}

Available Variables

{'{author}'} - Book author
{'{title}'} - Book title
{'{narrator}'} - Narrator name
{'{year}'} - Release year
{'{asin}'} - Audible ASIN
{'{series}'} - Book series name
{'{seriesPart}'} - Series part/position
{/* Metadata Tagging Toggle */}
updatePath('metadataTaggingEnabled', e.target.checked)} className="mt-1 h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500" />

Automatically write correct title, author, and narrator metadata to m4b and mp3 files during file organization. This significantly improves Plex matching accuracy for audiobooks with missing or incorrect metadata.

{/* Chapter Merging Toggle */}
updatePath('chapterMergingEnabled', e.target.checked)} className="mt-1 h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500" />

Automatically merge multi-file chapter downloads into a single M4B audiobook with chapter markers. Improves playback experience and library organization.

{/* Test Paths Button */}
{testResult && (
{testResult.message}
)}
); }