1.0.0.7 - Matching improves, added library page. Removed schedule scans support
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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 aren’t just bad — they’re offensive.",
|
||||
"This is taking forever because you live like this.",
|
||||
"I’ve 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 won’t.",
|
||||
],
|
||||
nice: [
|
||||
"Taking a moment to find your perfect subtitles.",
|
||||
|
||||
@@ -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 ============
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user