diff --git a/app.tsx b/app.tsx index 6a6035d..f1ebca7 100644 --- a/app.tsx +++ b/app.tsx @@ -36,7 +36,7 @@ interface QueueItem { downloadName: string | null; report: { removedCount: number; removedTags: string[]; timestamp: string } | null; error: string | null; - analysis: { format: string; title: string; artist: string; genre: string; provenanceRisk: RiskLevel; detectedMarkers: string[] } | null; + analysis: { format: string; title: string; artist: string; genre: string; provenanceRisk: RiskLevel; detectedMarkers: string[]; parseError?: string | null } | null; logs: string[]; } @@ -590,7 +590,7 @@ export default function App() { const parsed = await readFileMetadata(item.file); return { seo: { title: parsed.title, description: '', tags: parsed.genre || '' }, - analysis: { format: parsed.format, title: parsed.title, artist: parsed.artist, genre: parsed.genre, provenanceRisk: parsed.provenanceRisk, detectedMarkers: parsed.detectedMarkers }, + analysis: { format: parsed.format, title: parsed.title, artist: parsed.artist, genre: parsed.genre, provenanceRisk: parsed.provenanceRisk, detectedMarkers: parsed.detectedMarkers, parseError: parsed.parseError || null }, }; } catch { return {}; } }; @@ -691,6 +691,11 @@ export default function App() { const doneCount = queue.filter(i => i.status === 'done').length; const progress = queue.length > 0 ? Math.round((doneCount / queue.length) * 100) : 0; + const isMp3 = activeItem ? activeItem.file.name.toLowerCase().endsWith('.mp3') : false; + const quickDisabledReason = !activeItem ? 'Select a file first.' : !isMp3 ? 'Quick Cleanse supports MP3 files only.' : ''; + const seoDisabledReason = !activeItem ? 'Select a file to provide context first.' : ''; + const serverDisabledReason = isBatching ? 'Server cleanse already running.' : queue.length === 0 ? 'Add at least one file first.' : queue.every(i => i.status === 'done') ? 'All files are already completed.' : ''; + const resultSource = activeItem?.downloadName?.startsWith('quick_cleansed_') ? 'Browser Quick Cleanse' : 'Full Server Cleanse'; // ── Render ─────────────────────────────────────────────────────────────────── return ( @@ -967,7 +972,8 @@ export default function App() { updateItem(activeItem.id, { error: errorMessage }); addLog(activeItem.id, `SEO generation failed: ${errorMessage}`); } - }} className="px-3 py-1.5 text-xs bg-violet-700 hover:bg-violet-600 rounded-lg">Generate AI SEO Payload + }} disabled={!activeItem} className="px-3 py-1.5 text-xs bg-violet-700 hover:bg-violet-600 disabled:bg-slate-700 disabled:text-slate-400 disabled:cursor-not-allowed rounded-lg">Generate AI SEO Payload + {seoDisabledReason &&

{seoDisabledReason}

}
-
- - +

MP3-only • local metadata rewrite • no server usage counted.

+ {quickDisabledReason &&

{quickDisabledReason}

} +
+
+ +

Deeper forensic server pipeline • all supported formats • usage-counted.

+ {serverDisabledReason &&

{serverDisabledReason}

} +
{activeItem.downloadUrl && ( - Manual Download Link +
+

Result Source: {resultSource}

+ Manual Download Link +
)}

Analysis

-
-
Format: {activeItem.analysis?.format || '—'}
Title: {activeItem.analysis?.title || '—'}
-
Artist: {activeItem.analysis?.artist || '—'}
Genre: {activeItem.analysis?.genre || '—'}
-
Provenance Risk: {activeItem.analysis?.provenanceRisk || 'Low'}
-
Markers: {(activeItem.analysis?.detectedMarkers || []).join(', ') || 'none'}
+ {activeItem.analysis?.parseError && ( +
+ Metadata parser used fallback values for some fields. +
+ )} +
+
Format

{activeItem.analysis?.format || '—'}

+
Title

{activeItem.analysis?.title || '—'}

+
Artist

{activeItem.analysis?.artist || '—'}

+
Genre

{activeItem.analysis?.genre || '—'}

+
Provenance Risk

{activeItem.analysis?.provenanceRisk || 'Low'}{activeItem.analysis?.provenanceRisk === 'Low' ? ' (lower risk, not guaranteed)' : ''}

+
Detected Markers

{(activeItem.analysis?.detectedMarkers || []).join(', ') || 'None detected'}

-

System Log

{activeItem.logs.map((l, i) =>
{l}
)}
+

System Log

{activeItem.logs.map((l, i) => { const isErr = /failed|error/i.test(l); const isSuccess = /complete|generated|starting server cleanse/i.test(l); const m = l.match(/^\[(.*?)\]\s*(.*)$/); return
{m ? m[1] : '--:--:--'}{m ? m[2] : l}
; })}
{/* Forensic report */} {activeItem.report && (