1.1.1 - OCR garbage removal detection frontend changes
This commit is contained in:
@@ -172,7 +172,7 @@
|
||||
class={`flex items-center px-3 py-2 text-xs ${collapsed ? "justify-center" : "justify-between"}`}
|
||||
>
|
||||
{#if !collapsed}
|
||||
<Badge className="text-text-secondary">Version: v1.1.0</Badge>
|
||||
<Badge className="text-text-secondary">Version: v1.1.1</Badge>
|
||||
{:else}
|
||||
<Badge className="text-text-secondary">v</Badge>
|
||||
{/if}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
searchTitle,
|
||||
saveSuggestedMatches,
|
||||
processBatch,
|
||||
previewClean,
|
||||
} from "../../lib/api.js";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
@@ -68,6 +69,11 @@
|
||||
|
||||
let showPreview = false;
|
||||
let previewFile = null;
|
||||
let showCleanPreview = false;
|
||||
let cleanPreviewFile = null;
|
||||
let cleanPreviewData = null;
|
||||
let cleanPreviewLoading = false;
|
||||
let cleanPreviewError = null;
|
||||
let showTitleSelector = false;
|
||||
let titleSelectorFile = null;
|
||||
let searchResults = [];
|
||||
@@ -759,6 +765,30 @@
|
||||
previewFile = null;
|
||||
}
|
||||
|
||||
async function openCleanPreview(file) {
|
||||
cleanPreviewFile = file;
|
||||
showCleanPreview = true;
|
||||
cleanPreviewLoading = true;
|
||||
cleanPreviewError = null;
|
||||
cleanPreviewData = null;
|
||||
|
||||
try {
|
||||
const response = await previewClean(file.path);
|
||||
cleanPreviewData = response.preview;
|
||||
} catch (err) {
|
||||
cleanPreviewError = err?.message || "Failed to load preview";
|
||||
} finally {
|
||||
cleanPreviewLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function closeCleanPreview() {
|
||||
showCleanPreview = false;
|
||||
cleanPreviewFile = null;
|
||||
cleanPreviewData = null;
|
||||
cleanPreviewError = null;
|
||||
}
|
||||
|
||||
function getMediaType(file) {
|
||||
return file.media_type || "N/A";
|
||||
}
|
||||
@@ -1261,6 +1291,13 @@
|
||||
</TableCell>
|
||||
<TableCell className="w-64">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
on:click={() => openCleanPreview(file)}
|
||||
size="sm"
|
||||
className="bg-bg-secondary text-text-secondary border border-border hover:text-white"
|
||||
>
|
||||
Preview
|
||||
</Button>
|
||||
<span class="text-[11px] text-text-tertiary">
|
||||
Clean only
|
||||
</span>
|
||||
@@ -1361,6 +1398,13 @@
|
||||
</TableCell>
|
||||
<TableCell className="w-64">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
on:click={() => openCleanPreview(file)}
|
||||
size="sm"
|
||||
className="bg-bg-secondary text-text-secondary border border-border hover:text-white"
|
||||
>
|
||||
Preview
|
||||
</Button>
|
||||
<span class="text-[11px] text-text-tertiary">
|
||||
Clean only
|
||||
</span>
|
||||
@@ -2312,6 +2356,104 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Clean Preview Modal -->
|
||||
{#if showCleanPreview && cleanPreviewFile}
|
||||
<div
|
||||
class="fixed inset-0 bg-black/95 flex items-center justify-center z-50 p-4"
|
||||
on:click={closeCleanPreview}
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
on:keydown={(e) => e.key === "Escape" && closeCleanPreview()}
|
||||
>
|
||||
<div
|
||||
class="bg-bg-card border border-border rounded-2xl p-8 max-w-4xl w-full max-h-[90vh] overflow-y-auto"
|
||||
on:click|stopPropagation
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
on:keydown
|
||||
>
|
||||
<div class="flex items-start justify-between mb-6">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-medium mb-1">Clean Preview</h3>
|
||||
<p class="text-sm text-text-secondary font-mono">
|
||||
{cleanPreviewFile.name}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
on:click={closeCleanPreview}
|
||||
class="text-text-secondary hover:text-white transition-colors"
|
||||
>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if cleanPreviewLoading}
|
||||
<div class="py-16 text-center text-text-tertiary text-sm">
|
||||
Building preview…
|
||||
</div>
|
||||
{:else if cleanPreviewError}
|
||||
<div class="py-12 text-center text-red-400 text-sm">
|
||||
{cleanPreviewError}
|
||||
</div>
|
||||
{:else if cleanPreviewData}
|
||||
<div class="space-y-5">
|
||||
<div class="flex items-center justify-between text-xs text-text-tertiary uppercase tracking-wide">
|
||||
<span>Summary</span>
|
||||
<span>
|
||||
{cleanPreviewData.removed_blocks} removed ·
|
||||
{cleanPreviewData.modified_blocks} modified
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{#if cleanPreviewData.total_changed_blocks === 0}
|
||||
<div class="text-sm text-text-tertiary">
|
||||
No changes detected.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-3">
|
||||
{#each cleanPreviewData.changes as change}
|
||||
<div class="border border-border rounded-xl p-4 bg-bg-primary/40">
|
||||
<div class="text-[11px] text-text-tertiary uppercase tracking-wide mb-3">
|
||||
{change.type} · {change.timecode}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3 text-[11px]">
|
||||
<div class="text-text-secondary">Before</div>
|
||||
<div class="text-text-secondary">After</div>
|
||||
<pre
|
||||
class="whitespace-pre-wrap text-text-tertiary bg-bg-secondary/40 border border-border rounded-lg p-2 min-h-[48px]"
|
||||
>{change.before || "(removed)"}</pre>
|
||||
<pre
|
||||
class="whitespace-pre-wrap text-text-secondary bg-bg-secondary/40 border border-border rounded-lg p-2 min-h-[48px]"
|
||||
>{change.after || "(removed)"}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if cleanPreviewData.changes_truncated}
|
||||
<div class="text-[11px] text-text-tertiary">
|
||||
Preview truncated. Run clean to apply all changes.
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Title Selector Modal -->
|
||||
{#if showTitleSelector && titleSelectorFile}
|
||||
<div
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
let stripKeywords = settings.strip_keywords !== false;
|
||||
let cleanSubtitleContent = settings.clean_subtitle_content !== false;
|
||||
let cleanSubtitleGarbage = settings.clean_subtitle_garbage === true;
|
||||
let forceRemoveKeywords = Array.isArray(settings.clean_subtitle_force_remove)
|
||||
? settings.clean_subtitle_force_remove.join(", ")
|
||||
: settings.clean_subtitle_force_remove || "YTS, OpenSubtitles";
|
||||
@@ -28,6 +29,12 @@
|
||||
{ before: "Downloaded from RARBG", after: "(removed)" },
|
||||
];
|
||||
|
||||
const garbageExamples = [
|
||||
{ before: "♪♪", after: "(removed)" },
|
||||
{ before: "00:01:23,456 --> 00:01:25,000", after: "(removed)" },
|
||||
{ before: "WE HAVE TO GO!\nWE HAVE TO GO!", after: "WE HAVE TO GO!" },
|
||||
];
|
||||
|
||||
// Keywords that get stripped from filenames
|
||||
const filenameKeywords = [
|
||||
{ name: "Quality", examples: ["480p", "720p", "1080p", "4K", "HDR"] },
|
||||
@@ -56,6 +63,7 @@
|
||||
onSave({
|
||||
strip_keywords: stripKeywords,
|
||||
clean_subtitle_content: cleanSubtitleContent,
|
||||
clean_subtitle_garbage: cleanSubtitleGarbage,
|
||||
clean_subtitle_force_remove: forceRemoveKeywords
|
||||
.split(/[\n,]+/)
|
||||
.map((entry) => entry.trim())
|
||||
@@ -169,6 +177,53 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="p-5 bg-bg-secondary border border-border rounded-xl mb-5">
|
||||
<label class="flex items-start justify-between gap-4 rounded-xl border border-border bg-bg-secondary/40 px-4 py-3">
|
||||
<div class="flex-1">
|
||||
<div class="text-[14px] font-medium mb-1">Remove OCR Garbage</div>
|
||||
<div class="text-[12px] text-text-tertiary leading-relaxed">
|
||||
Strip music-only lines (♪♪), embedded timecodes, and duplicate OCR
|
||||
lines inside a block. Can be enabled without ad removal.
|
||||
</div>
|
||||
</div>
|
||||
<span class="relative mt-0.5 inline-flex items-center">
|
||||
<input type="checkbox" bind:checked={cleanSubtitleGarbage} class="sr-only peer" />
|
||||
<span class="h-6 w-11 rounded-full border border-border bg-bg-card transition-colors peer-checked:bg-accent peer-checked:border-accent/60"></span>
|
||||
<span class="absolute left-0.5 h-5 w-5 rounded-full bg-text-tertiary transition-transform peer-checked:translate-x-5 peer-checked:bg-bg-primary"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{#if cleanSubtitleGarbage}
|
||||
<div class="bg-bg-card border border-border rounded-xl overflow-hidden mb-5">
|
||||
<div class="px-4 py-2.5 border-b border-border bg-bg-secondary">
|
||||
<span
|
||||
class="text-[11px] font-medium text-text-secondary uppercase tracking-wide"
|
||||
>OCR Garbage Examples</span
|
||||
>
|
||||
</div>
|
||||
<div class="divide-y divide-border">
|
||||
{#each garbageExamples as example}
|
||||
<div class="px-4 py-2.5 flex items-center gap-3">
|
||||
<div class="flex-1 min-w-0">
|
||||
<code class="text-[11px] text-text-tertiary break-all"
|
||||
>{example.before}</code
|
||||
>
|
||||
</div>
|
||||
<ArrowRight class="w-3.5 h-3.5 text-text-tertiary flex-shrink-0" />
|
||||
<div class="flex-shrink-0">
|
||||
<code
|
||||
class="text-[11px] {example.after === '(removed)'
|
||||
? 'text-red-400'
|
||||
: 'text-green-400'} font-medium">{example.after}</code
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Collapsible Details -->
|
||||
{#if cleanSubtitleContent}
|
||||
<div class="space-y-4 pl-2 border-l-2 border-border ml-2">
|
||||
|
||||
@@ -485,6 +485,18 @@ export async function clearAllSuggestedMatches() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/clean/preview - Preview cleaning changes
|
||||
* Body: { file: string }
|
||||
* Returns: { success, preview }
|
||||
*/
|
||||
export async function previewClean(file) {
|
||||
return apiFetch('/clean/preview', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file })
|
||||
})
|
||||
}
|
||||
|
||||
// ============ MAINTENANCE API ============
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user