diff --git a/src/main/services/LibraryService.ts b/src/main/services/LibraryService.ts index 6040995..2527925 100644 --- a/src/main/services/LibraryService.ts +++ b/src/main/services/LibraryService.ts @@ -29,7 +29,7 @@ export class LibraryService { private readonly metadataFailures = new Set(); private readonly checksumFailures = new Set(); private readonly organization: OrganizationService; - private metadataSuggestionCache: { authors: Set; copyrights: Set } | null = null; + private metadataSuggestionCache: { authors: Set } | null = null; private readonly waveformPreviewCache = new Map(); public constructor( @@ -378,7 +378,6 @@ export class LibraryService { return this.organizeFile(payload.fileId, { customName: updated.customName ?? undefined, author: metadataSnapshot.author?.trim() || undefined, - copyright: metadataSnapshot.copyright?.trim() || undefined, rating: metadataSnapshot.rating ?? undefined }); } @@ -435,7 +434,7 @@ export class LibraryService { */ public async organizeFile( fileId: number, - metadata: { customName?: string | null; author?: string | null; copyright?: string | null; rating?: number } + metadata: { customName?: string | null; author?: string | null; rating?: number } ): Promise { const record = this.database.getFileById(fileId); const category = this.organization.getPrimaryCategory(record); @@ -503,9 +502,7 @@ export class LibraryService { // Merge with new metadata (only update fields that are provided) const requestedAuthor = this.normaliseMetadataInput(metadata.author); - const requestedCopyright = this.normaliseMetadataInput(metadata.copyright); const mergedAuthor = requestedAuthor !== undefined ? requestedAuthor : existing.author ?? null; - const mergedCopyright = requestedCopyright !== undefined ? requestedCopyright : existing.copyright ?? null; const mergedRating = metadata.rating !== undefined ? metadata.rating : existing.rating; this.tagService.writeMetadataOnly(updatedRecord.absolutePath, { @@ -513,16 +510,12 @@ export class LibraryService { categories: updatedRecord.categories, title: effectiveCustomName, author: mergedAuthor, - copyright: mergedCopyright, rating: mergedRating }); // Update suggestions cache for any metadata that was provided if (typeof mergedAuthor === 'string' && mergedAuthor.length > 0) { - this.updateMetadataSuggestionsCache(mergedAuthor, undefined); - } - if (typeof mergedCopyright === 'string' && mergedCopyright.length > 0) { - this.updateMetadataSuggestionsCache(undefined, mergedCopyright); + this.updateMetadataSuggestionsCache(mergedAuthor); } this.resetMetadataSuggestionsCache(); @@ -602,9 +595,7 @@ export class LibraryService { // Merge with new metadata (only update fields that are provided) const movedAuthor = this.normaliseMetadataInput(metadata.author); - const movedCopyright = this.normaliseMetadataInput(metadata.copyright); const mergedAuthor = movedAuthor !== undefined ? movedAuthor : existing.author ?? null; - const mergedCopyright = movedCopyright !== undefined ? movedCopyright : existing.copyright ?? null; const mergedRating = metadata.rating !== undefined ? metadata.rating : existing.rating; // Write tags, categories, and additional metadata to the organized file @@ -613,16 +604,12 @@ export class LibraryService { categories: updated.categories, title: effectiveCustomName, author: mergedAuthor, - copyright: mergedCopyright, rating: mergedRating }); // Update suggestions cache for any metadata that was provided if (typeof mergedAuthor === 'string' && mergedAuthor.length > 0) { - this.updateMetadataSuggestionsCache(mergedAuthor, undefined); - } - if (typeof mergedCopyright === 'string' && mergedCopyright.length > 0) { - this.updateMetadataSuggestionsCache(undefined, mergedCopyright); + this.updateMetadataSuggestionsCache(mergedAuthor); } this.resetMetadataSuggestionsCache(); @@ -666,20 +653,20 @@ export class LibraryService { } /** - * Reads embedded metadata from a WAV file and returns author, copyright, title, and rating. + * Reads embedded metadata from a WAV file and returns author, title, and rating. */ - public async readFileMetadata(fileId: number): Promise<{ author?: string; copyright?: string; title?: string; rating?: number }> { + public async readFileMetadata(fileId: number): Promise<{ author?: string; title?: string; rating?: number }> { const record = this.database.getFileById(fileId); const metadata = this.tagService.readMetadata(record.absolutePath); - this.updateMetadataSuggestionsCache(metadata.author, metadata.copyright); + this.updateMetadataSuggestionsCache(metadata.author); return metadata; } /** - * Updates metadata (author, copyright, rating) without organizing the file. + * Updates metadata (author, rating) without organizing the file. * Only writes to the WAV INFO chunk, doesn't move or rename the file. */ - public async updateFileMetadata(fileId: number, metadata: { author?: string | null; copyright?: string | null; rating?: number }): Promise { + public async updateFileMetadata(fileId: number, metadata: { author?: string | null; rating?: number }): Promise { const record = this.database.getFileById(fileId); // Read existing metadata from the WAV file @@ -687,13 +674,11 @@ export class LibraryService { // Merge with new metadata (only update fields that are provided) const requestedAuthor = this.normaliseMetadataInput(metadata.author); - const requestedCopyright = this.normaliseMetadataInput(metadata.copyright); const mergedMetadata = { tags: record.tags, categories: record.categories, title: record.customName ?? existing.title, author: requestedAuthor !== undefined ? requestedAuthor : existing.author ?? null, - copyright: requestedCopyright !== undefined ? requestedCopyright : existing.copyright ?? null, rating: metadata.rating !== undefined ? metadata.rating : existing.rating }; @@ -702,10 +687,7 @@ export class LibraryService { // Update suggestions cache if (typeof mergedMetadata.author === 'string' && mergedMetadata.author.length > 0) { - this.updateMetadataSuggestionsCache(mergedMetadata.author, undefined); - } - if (typeof mergedMetadata.copyright === 'string' && mergedMetadata.copyright.length > 0) { - this.updateMetadataSuggestionsCache(undefined, mergedMetadata.copyright); + this.updateMetadataSuggestionsCache(mergedMetadata.author); } this.waveformPreviewCache.delete(fileId); @@ -715,10 +697,9 @@ export class LibraryService { /** * Returns distinct metadata suggestions aggregated from the library. */ - public async listMetadataSuggestions(): Promise<{ authors: string[]; copyrights: string[] }> { + public async listMetadataSuggestions(): Promise<{ authors: string[] }> { if (!this.metadataSuggestionCache) { const authors = new Set(); - const copyrights = new Set(); const files = this.database.listFiles(); for (const file of files) { @@ -727,9 +708,6 @@ export class LibraryService { if (metadata.author) { this.addSuggestionValue(authors, metadata.author); } - if (metadata.copyright) { - this.addSuggestionValue(copyrights, metadata.copyright); - } } catch (error) { if (!this.metadataFailures.has(file.absolutePath)) { this.metadataFailures.add(file.absolutePath); @@ -739,12 +717,11 @@ export class LibraryService { } } - this.metadataSuggestionCache = { authors, copyrights }; + this.metadataSuggestionCache = { authors }; } return { - authors: Array.from(this.metadataSuggestionCache.authors).sort((a, b) => a.localeCompare(b)), - copyrights: Array.from(this.metadataSuggestionCache.copyrights).sort((a, b) => a.localeCompare(b)) + authors: Array.from(this.metadataSuggestionCache.authors).sort((a, b) => a.localeCompare(b)) }; } @@ -769,16 +746,13 @@ export class LibraryService { return trimmed.length > 0 ? trimmed : null; } - private updateMetadataSuggestionsCache(author?: string | null, copyright?: string | null): void { + private updateMetadataSuggestionsCache(author?: string | null): void { if (!this.metadataSuggestionCache) { return; } if (author) { this.addSuggestionValue(this.metadataSuggestionCache.authors, author); } - if (copyright) { - this.addSuggestionValue(this.metadataSuggestionCache.copyrights, copyright); - } } /** diff --git a/src/main/services/TagService.ts b/src/main/services/TagService.ts index 4e7b5e5..cbe7795 100644 --- a/src/main/services/TagService.ts +++ b/src/main/services/TagService.ts @@ -42,7 +42,6 @@ export class TagService { */ public readMetadata(filePath: string): { author?: string; - copyright?: string; title?: string; rating?: number; } { @@ -58,7 +57,6 @@ export class TagService { return { author: tags.IART?.trim() || undefined, - copyright: tags.ICOP?.trim() || undefined, title: tags.INAM?.trim() || undefined, rating }; @@ -79,7 +77,6 @@ export class TagService { categories: string[]; title?: string | null; author?: string | null; - copyright?: string | null; rating?: number; } ): void { @@ -96,7 +93,6 @@ export class TagService { categories: string[]; title?: string | null; author?: string | null; - copyright?: string | null; rating?: number; } ): void { @@ -123,7 +119,6 @@ export class TagService { IGNR: null, INAM: metadata.title?.trim()?.length ? metadata.title.trim() : null, IART: metadata.author?.trim()?.length ? metadata.author.trim() : null, - ICOP: metadata.copyright?.trim()?.length ? metadata.copyright.trim() : null, IRTD: metadata.rating && metadata.rating > 0 ? String(metadata.rating * 2) : null, ISFT: 'AudioSort' }; @@ -138,7 +133,6 @@ export class TagService { categories: metadata.categories.join(', '), title: metadata.title, author: metadata.author, - copyright: metadata.copyright, rating: metadata.rating }); } catch (error) { diff --git a/src/renderer/src/components/FileDetailPanel.tsx b/src/renderer/src/components/FileDetailPanel.tsx index d1a0cea..00419c1 100644 --- a/src/renderer/src/components/FileDetailPanel.tsx +++ b/src/renderer/src/components/FileDetailPanel.tsx @@ -10,8 +10,8 @@ export interface FileDetailPanelProps { categories: CategoryRecord[]; onRename(newName: string): Promise; onMove(targetRelativeDirectory: string): Promise; - /** Organizes a file with optional metadata fields (customName, author, copyright, rating 1-5). */ - onOrganize(metadata: { customName?: string | null; author?: string | null; copyright?: string | null; rating?: number }): Promise; + /** Organizes a file with optional metadata fields (customName, author, rating 1-5). */ + onOrganize(metadata: { customName?: string | null; author?: string | null; rating?: number }): Promise; onUpdateTags(data: { tags: string[]; categories: string[] }): Promise; onUpdateCustomName(customName: string | null): Promise; metadataSuggestionsVersion: number; @@ -29,9 +29,8 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize const [busy, setBusy] = useState(false); const [isTagSectionExpanded, setIsTagSectionExpanded] = useState(true); const [suggestions, setSuggestions] = useState<{ authors: string[] }>({ authors: [] }); - const initialMetadataRef = useRef<{ author: string; copyright: string; rating: number; customName: string }>({ + const initialMetadataRef = useRef<{ author: string; rating: number; customName: string }>({ author: '', - copyright: '', rating: 0, customName: '' }); @@ -94,7 +93,7 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize setAuthorDraft(''); setRatingDraft(0); setIsEditingCustomName(false); - initialMetadataRef.current = { author: '', copyright: '', rating: 0, customName: '' }; + initialMetadataRef.current = { author: '', rating: 0, customName: '' }; return; } @@ -102,7 +101,7 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize setCustomNameDraft(file.customName ?? ''); setIsEditingCustomName(false); const baseCustomName = (file.customName ?? '').trim(); - initialMetadataRef.current = { author: '', copyright: '', rating: 0, customName: baseCustomName }; + initialMetadataRef.current = { author: '', rating: 0, customName: baseCustomName }; void (async () => { try { @@ -118,7 +117,6 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize setRatingDraft(ratingValue); initialMetadataRef.current = { author: authorValue, - copyright: '', rating: ratingValue, customName: customNameValue }; @@ -129,7 +127,7 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize console.error('Failed to read file metadata:', error); setAuthorDraft(''); setRatingDraft(0); - initialMetadataRef.current = { author: '', copyright: '', rating: 0, customName: baseCustomName }; + initialMetadataRef.current = { author: '', rating: 0, customName: baseCustomName }; } })(); }, [appendSuggestion, file?.id]); @@ -206,7 +204,6 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize const trimmedCustomName = (nextCustomNameRaw ?? '').toString().trim(); const comparisonState = { author: trimmedAuthor, - copyright: '', rating: nextRatingValue, customName: trimmedCustomName }; @@ -215,7 +212,6 @@ export function FileDetailPanel({ file, categories, onRename, onMove, onOrganize if ( previous && previous.author === comparisonState.author && - previous.copyright === comparisonState.copyright && previous.rating === comparisonState.rating && previous.customName === comparisonState.customName ) { diff --git a/src/renderer/src/components/MultiFileEditor.tsx b/src/renderer/src/components/MultiFileEditor.tsx index 1b8943a..e9b7f97 100644 --- a/src/renderer/src/components/MultiFileEditor.tsx +++ b/src/renderer/src/components/MultiFileEditor.tsx @@ -8,8 +8,8 @@ export interface MultiFileEditorProps { categories: CategoryRecord[]; onUpdateTags(fileId: number, data: { tags: string[]; categories: string[] }): Promise; onUpdateCustomName(fileId: number, customName: string | null): Promise; - onOrganize(fileId: number, metadata: { customName?: string | null; author?: string | null; copyright?: string | null; rating?: number }): Promise; - onUpdateMetadata(fileId: number, metadata: { author?: string | null; copyright?: string | null; rating?: number }): Promise; + onOrganize(fileId: number, metadata: { customName?: string | null; author?: string | null; rating?: number }): Promise; + onUpdateMetadata(fileId: number, metadata: { author?: string | null; rating?: number }): Promise; metadataSuggestionsVersion: number; } @@ -19,9 +19,8 @@ export interface MultiFileEditorProps { export function MultiFileEditor({ files, categories, onUpdateTags, onUpdateCustomName, onOrganize, onUpdateMetadata, metadataSuggestionsVersion }: MultiFileEditorProps): JSX.Element { const [sharedCustomName, setSharedCustomName] = useState(''); const [sharedAuthor, setSharedAuthor] = useState(''); - const [sharedCopyright, setSharedCopyright] = useState(''); const [sharedRating, setSharedRating] = useState(0); - const [suggestions, setSuggestions] = useState<{ authors: string[]; copyrights: string[] }>({ authors: [], copyrights: [] }); + const [suggestions, setSuggestions] = useState<{ authors: string[] }>({ authors: [] }); const [isTagSectionExpanded, setIsTagSectionExpanded] = useState(true); // Compute aggregated tags and categories for TagEditor @@ -53,7 +52,7 @@ export function MultiFileEditor({ files, categories, onUpdateTags, onUpdateCusto const listRef = useRef(null); - type SuggestionKey = 'authors' | 'copyrights'; + type SuggestionKey = 'authors'; const appendSuggestion = useCallback((value: string, key: SuggestionKey) => { const trimmed = value.trim(); @@ -84,21 +83,9 @@ export function MultiFileEditor({ files, categories, onUpdateTags, onUpdateCusto return base.filter((entry) => entry.toLowerCase().includes(query)).slice(0, 5); }, [sharedAuthor, suggestions.authors]); - const filteredCopyrightSuggestions = useMemo(() => { - if (suggestions.copyrights.length === 0) { - return [] as string[]; - } - const query = sharedCopyright.trim().toLowerCase(); - const base = suggestions.copyrights.filter((entry) => entry.toLowerCase() !== query); - if (query.length === 0) { - return base.slice(0, 5); - } - return base.filter((entry) => entry.toLowerCase().includes(query)).slice(0, 5); - }, [sharedCopyright, suggestions.copyrights]); - useEffect(() => { let cancelled = false; - setSuggestions({ authors: [], copyrights: [] }); + setSuggestions({ authors: [] }); void (async () => { try { const result = await window.api.listMetadataSuggestions(); @@ -106,8 +93,7 @@ export function MultiFileEditor({ files, categories, onUpdateTags, onUpdateCusto return; } setSuggestions({ - authors: result.authors, - copyrights: result.copyrights + authors: result.authors }); } catch (error) { if (!cancelled) { @@ -188,37 +174,6 @@ export function MultiFileEditor({ files, categories, onUpdateTags, onUpdateCusto } }; - const handleSharedCopyrightBlur = async () => { - const normalized = sharedCopyright.trim(); - - if (normalized.length === 0) return; - - try { - let failureCount = 0; - for (const file of files) { - try { - if (file.categories.length > 0) { - await onOrganize(file.id, { copyright: normalized }); - } else { - await onUpdateMetadata(file.id, { copyright: normalized }); - } - } catch (error) { - failureCount += 1; - console.error(`Failed to update copyright for file ${file.id} (${file.fileName}):`, error); - } - } - if (failureCount > 0) { - console.error(`${failureCount} file(s) failed to update copyright`); - } - - if (normalized.length > 0) { - appendSuggestion(normalized, 'copyrights'); - } - } catch (error) { - console.error('Failed to update copyright:', error); - } - }; - const handleSharedRatingChange = async (rating: number) => { setSharedRating(rating); @@ -325,39 +280,6 @@ export function MultiFileEditor({ files, categories, onUpdateTags, onUpdateCusto )} -