1.0.0.7 - Matching improves, added library page. Removed schedule scans support

This commit is contained in:
ponzischeme89
2026-01-18 22:29:51 +13:00
parent 170694bc28
commit 9251e6e837
10 changed files with 502 additions and 52 deletions
+3 -3
View File
@@ -7,7 +7,7 @@
import SettingsPanel from './components/SettingsPanel.svelte'
import ScanPanel from './components/ScanPanel.svelte'
import HistoryPanel from './components/HistoryPanel.svelte'
import ScheduledScansPanel from './components/ScheduledScansPanel.svelte'
import LibraryPanel from './components/LibraryPanel.svelte'
import { Menu } from 'lucide-svelte'
import ToastHost from './components/ToastHost.svelte'
import { healthCheck } from './lib/api.js'
@@ -164,8 +164,8 @@
onOpenHistory={() => navigateTo('history')}
/>
{/key}
{:else if currentView === 'scheduled'}
<ScheduledScansPanel />
{:else if currentView === 'library'}
<LibraryPanel />
{/if}
</div>
</main>
+7 -10
View File
@@ -3,16 +3,13 @@
import { Separator } from "../lib/components/ui/separator";
import { Badge } from "../lib/components/ui/badge";
import {
Calendar,
Download,
ChevronLeft,
ChevronRight,
Github,
Heart,
Package,
Scan,
Settings,
History,
Library,
} from "lucide-svelte";
import ThemeSelector from "./ThemeSelector.svelte";
import sublogueLogo from "../assets/sublogue_v2.png";
@@ -115,16 +112,16 @@
className={`w-full rounded-md py-1.5 text-[13px] font-semibold leading-none ${
collapsed ? "justify-center px-0" : "justify-start px-2 gap-2"
} ${
currentView === "scheduled"
currentView === "library"
? "bg-[color:var(--bg-hover)] text-white font-bold"
: "text-text-secondary hover:text-white hover:bg-[color:var(--bg-hover)]"
}`}
on:click={() => onNavigate("scheduled")}
aria-current={currentView === "scheduled" ? "page" : undefined}
on:click={() => onNavigate("library")}
aria-current={currentView === "library" ? "page" : undefined}
>
<Calendar class="h-4 w-4" />
<Library class="h-4 w-4" />
{#if !collapsed}
Scheduled Scans
Library
{/if}
</Button>
@@ -157,7 +154,7 @@
>
{#if !collapsed}
<Badge className="bg-white/10 text-text-secondary"
>v1.0.6 Release Candiate</Badge
>v1.0.7 Release Candiate</Badge
>
{:else}
<Badge className="bg-white/10 text-text-secondary">v</Badge>
+153
View File
@@ -0,0 +1,153 @@
<script>
import { onMount } from "svelte";
import { getLibraryReport } from "../lib/api.js";
import { Button } from "../lib/components/ui/button";
import { ChevronDown, ChevronUp, RefreshCcw, FileText } from "lucide-svelte";
let items = [];
let loading = false;
let error = null;
let expanded = {};
async function loadLibrary() {
loading = true;
error = null;
try {
const response = await getLibraryReport();
items = response.items || [];
} catch (err) {
error = `Failed to load library report: ${err.message}`;
} finally {
loading = false;
}
}
function toggleScan(key) {
expanded = { ...expanded, [key]: !expanded[key] };
}
onMount(loadLibrary);
</script>
<div class="space-y-6">
<div class="flex items-start justify-between gap-4">
<div>
<h2 class="text-xl font-bold text-text-primary">Library Health</h2>
<p class="text-[13px] text-text-secondary">
Review subtitles from each scan and spot missing plots, duplicates, and insufficient gaps.
</p>
</div>
<Button
variant="outline"
size="sm"
className="border-white/15 text-text-secondary hover:bg-white/10"
on:click={loadLibrary}
disabled={loading}
>
<RefreshCcw class="h-4 w-4" />
Refresh
</Button>
</div>
{#if error}
<div class="px-5 py-4 bg-red-500/5 border border-red-500/20 rounded-xl">
<p class="text-[13px] text-red-300">{error}</p>
</div>
{/if}
{#if loading}
<div class="text-[13px] text-text-secondary">Loading library report...</div>
{:else if items.length === 0}
<div class="border border-border rounded-2xl p-12 text-center">
<div class="flex flex-col items-center gap-4">
<FileText class="w-12 h-12 text-text-tertiary" />
<div>
<p class="text-[13px] text-text-secondary mb-1">No scan data yet</p>
<p class="text-[11px] text-text-tertiary">Run a scan to populate the library report.</p>
</div>
</div>
</div>
{:else}
<div class="space-y-4">
{#each items as item}
<div class="rounded-2xl border border-border bg-card/60 overflow-hidden">
<div class="px-6 py-4 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div class="space-y-1">
<div class="text-[13px] font-semibold text-text-primary">
{item.title}{item.year ? ` (${item.year})` : ""}
</div>
<div class="text-[11px] text-text-tertiary">
{item.files.length} subtitle file{item.files.length === 1 ? "" : "s"}
</div>
</div>
<div class="flex items-center gap-3">
<span class="text-[11px] text-yellow-200 bg-yellow-500/10 border border-yellow-500/30 px-3 py-1 rounded-full">
Missing: {item.health.missing_plot}
</span>
<span class="text-[11px] text-orange-200 bg-orange-500/10 border border-orange-500/30 px-3 py-1 rounded-full">
Duplicates: {item.health.duplicate_plot}
</span>
<span class="text-[11px] text-red-200 bg-red-500/10 border border-red-500/30 px-3 py-1 rounded-full">
Gap issues: {item.health.insufficient_gap}
</span>
<button
class="ml-2 text-text-secondary hover:text-white transition-colors"
on:click={() => toggleScan(item.title)}
aria-label="Toggle scan details"
>
{#if expanded[item.title]}
<ChevronUp class="h-4 w-4" />
{:else}
<ChevronDown class="h-4 w-4" />
{/if}
</button>
</div>
</div>
{#if expanded[item.title]}
<div class="border-t border-border bg-bg-secondary/40">
<div class="px-6 py-4 overflow-x-auto">
<table class="min-w-full text-[12px] text-text-secondary">
<thead>
<tr class="text-text-tertiary text-[11px] uppercase tracking-wide">
<th class="text-left py-2 pr-4">File</th>
<th class="text-left py-2 pr-4">Status</th>
<th class="text-left py-2 pr-4">Plot</th>
<th class="text-left py-2">Issues</th>
</tr>
</thead>
<tbody class="divide-y divide-border">
{#each item.files as file}
<tr>
<td class="py-3 pr-4 text-text-primary">
{file.display_name || file.name}
</td>
<td class="py-3 pr-4">{file.status || "Not Loaded"}</td>
<td class="py-3 pr-4">
{file.has_plot ? "Present" : "Missing"}
</td>
<td class="py-3">
{#if file.issues.length === 0}
<span class="text-green-300">Healthy</span>
{:else}
<div class="space-y-1">
{#each file.issues as issue}
<div class="text-[11px] text-red-200">
{issue.type.replace("_", " ")}{issue.reason}
</div>
{/each}
</div>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
{/if}
</div>
{/each}
</div>
{/if}
</div>
+12 -12
View File
@@ -19,18 +19,18 @@
"This scan is sponsored by existential dread.",
],
rude: [
"Ugh, more files? Seriously?",
"You could've organized these better, you know.",
"Why are there so many files? Get a hobby.",
"I don't get paid enough for this.",
"Your naming conventions are a crime.",
"This is taking forever because of YOUR mess.",
"I've seen better file structures in a dumpster.",
"Oh great, another scan. My favorite.",
"Do you even know what you're looking for?",
"These files are judging you. So am I.",
"Scanning your questionable life choices.",
"I hope you appreciate this. You won't.",
"Ugh, more files? What did you do, collect them competitively?",
"You could've organized these better. You actively chose not to.",
"Why are there so many files? Therapy is cheaper.",
"I don't get paid enough for this. Actually, I don't get paid at all.",
"Your naming conventions arent just bad — theyre offensive.",
"This is taking forever because you live like this.",
"Ive seen better file structures in a crime scene.",
"Oh great, another scan. Thrilling. Electrifying. Life-changing.",
"Do you even know what you're looking for, or are we just clicking things now?",
"These files are judging you. Loudly.",
"Scanning your deeply questionable life choices.",
"I hope you appreciate this. Statistically, you wont.",
],
nice: [
"Taking a moment to find your perfect subtitles.",
+10
View File
@@ -332,6 +332,16 @@ export async function getStatistics() {
return apiFetch('/statistics')
}
// ============ LIBRARY API ============
/**
* GET /api/library - Get library health report
* Returns: { success, scans: [...] }
*/
export async function getLibraryReport(limit = 25) {
return apiFetch(`/library?limit=${limit}`)
}
// ============ SCHEDULED SCANS API ============
/**