library: rename Scan nav/page to Library, show audio codecs per row
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m4s
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m4s
Per-row audio codec summary (distinct lowercased codecs across an item's audio streams) via scalar subquery on media_streams, rendered as "ac3 · aac" in a new monospace Audio column. v2026.04.15.9 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "netfelix-audio-fix",
|
"name": "netfelix-audio-fix",
|
||||||
"version": "2026.04.15.8",
|
"version": "2026.04.15.9",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
|
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
|
||||||
"dev:client": "vite",
|
"dev:client": "vite",
|
||||||
|
|||||||
@@ -163,7 +163,11 @@ app.get("/items", (c) => {
|
|||||||
`
|
`
|
||||||
SELECT id, jellyfin_id, name, type, series_name, season_number, episode_number,
|
SELECT id, jellyfin_id, name, type, series_name, season_number, episode_number,
|
||||||
scan_status, original_language, orig_lang_source, container, file_size, file_path,
|
scan_status, original_language, orig_lang_source, container, file_size, file_path,
|
||||||
last_scanned_at, ingest_source
|
last_scanned_at, ingest_source,
|
||||||
|
(SELECT GROUP_CONCAT(DISTINCT LOWER(codec))
|
||||||
|
FROM media_streams
|
||||||
|
WHERE item_id = media_items.id AND type = 'Audio' AND codec IS NOT NULL
|
||||||
|
) AS audio_codecs
|
||||||
FROM media_items
|
FROM media_items
|
||||||
${where.sql}
|
${where.sql}
|
||||||
ORDER BY COALESCE(last_scanned_at, created_at) DESC, id DESC
|
ORDER BY COALESCE(last_scanned_at, created_at) DESC, id DESC
|
||||||
@@ -186,6 +190,7 @@ app.get("/items", (c) => {
|
|||||||
file_path: string;
|
file_path: string;
|
||||||
last_scanned_at: string | null;
|
last_scanned_at: string | null;
|
||||||
ingest_source: string | null;
|
ingest_source: string | null;
|
||||||
|
audio_codecs: string | null;
|
||||||
}>;
|
}>;
|
||||||
const total = (db.prepare(`SELECT COUNT(*) as n FROM media_items ${where.sql}`).get(...where.args) as { n: number }).n;
|
const total = (db.prepare(`SELECT COUNT(*) as n FROM media_items ${where.sql}`).get(...where.args) as { n: number }).n;
|
||||||
return c.json({ rows, total, hasMore: query.offset + rows.length < total, query });
|
return c.json({ rows, total, hasMore: query.offset + rows.length < total, query });
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ interface ScanItemsRow {
|
|||||||
file_path: string;
|
file_path: string;
|
||||||
last_scanned_at: string | null;
|
last_scanned_at: string | null;
|
||||||
ingest_source: "scan" | "webhook" | null;
|
ingest_source: "scan" | "webhook" | null;
|
||||||
|
audio_codecs: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScanItemsResponse {
|
interface ScanItemsResponse {
|
||||||
@@ -397,7 +398,7 @@ export function ScanPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h1 className="text-xl font-bold m-0">Scan</h1>
|
<h1 className="text-xl font-bold m-0">Library</h1>
|
||||||
<MqttBadge />
|
<MqttBadge />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -581,22 +582,32 @@ export function ScanPage() {
|
|||||||
<table className="w-full border-collapse text-[0.8rem]">
|
<table className="w-full border-collapse text-[0.8rem]">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{["Scanned", "Name", "Type", "Series / Ep", "Language", "Container", "Size", "Source", "Status", "Path"].map(
|
{[
|
||||||
(h) => (
|
"Scanned",
|
||||||
|
"Name",
|
||||||
|
"Type",
|
||||||
|
"Series / Ep",
|
||||||
|
"Language",
|
||||||
|
"Audio",
|
||||||
|
"Container",
|
||||||
|
"Size",
|
||||||
|
"Source",
|
||||||
|
"Status",
|
||||||
|
"Path",
|
||||||
|
].map((h) => (
|
||||||
<th
|
<th
|
||||||
key={h}
|
key={h}
|
||||||
className="text-left text-[0.66rem] font-bold uppercase tracking-[0.05em] text-gray-500 py-1 px-2 border-b border-gray-200 whitespace-nowrap"
|
className="text-left text-[0.66rem] font-bold uppercase tracking-[0.05em] text-gray-500 py-1 px-2 border-b border-gray-200 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{h}
|
{h}
|
||||||
</th>
|
</th>
|
||||||
),
|
))}
|
||||||
)}
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{itemsRows.length === 0 && !itemsLoading && (
|
{itemsRows.length === 0 && !itemsLoading && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={10} className="py-3 px-2 text-gray-400">
|
<td colSpan={11} className="py-3 px-2 text-gray-400">
|
||||||
No items match the current filters.
|
No items match the current filters.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -616,6 +627,9 @@ export function ScanPage() {
|
|||||||
<div>{row.original_language ?? "—"}</div>
|
<div>{row.original_language ?? "—"}</div>
|
||||||
<div className="text-[0.68rem] text-gray-500">{row.orig_lang_source ?? "—"}</div>
|
<div className="text-[0.68rem] text-gray-500">{row.orig_lang_source ?? "—"}</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td className="py-1.5 px-2 border-b border-gray-100 font-mono text-[0.72rem]">
|
||||||
|
{row.audio_codecs ? row.audio_codecs.split(",").join(" · ") : "—"}
|
||||||
|
</td>
|
||||||
<td className="py-1.5 px-2 border-b border-gray-100">{row.container ?? "—"}</td>
|
<td className="py-1.5 px-2 border-b border-gray-100">{row.container ?? "—"}</td>
|
||||||
<td className="py-1.5 px-2 border-b border-gray-100 whitespace-nowrap">{formatFileSize(row.file_size)}</td>
|
<td className="py-1.5 px-2 border-b border-gray-100 whitespace-nowrap">{formatFileSize(row.file_size)}</td>
|
||||||
<td className="py-1.5 px-2 border-b border-gray-100">
|
<td className="py-1.5 px-2 border-b border-gray-100">
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ function RootLayout() {
|
|||||||
<VersionBadge />
|
<VersionBadge />
|
||||||
<div className="flex flex-wrap items-center gap-0.5">
|
<div className="flex flex-wrap items-center gap-0.5">
|
||||||
<NavLink to="/" exact>
|
<NavLink to="/" exact>
|
||||||
Scan
|
Library
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink to="/pipeline">Pipeline</NavLink>
|
<NavLink to="/pipeline">Pipeline</NavLink>
|
||||||
<NavLink to="/review/subtitles">Subtitles</NavLink>
|
<NavLink to="/review/subtitles">Subtitles</NavLink>
|
||||||
|
|||||||
Reference in New Issue
Block a user