Rework the media grid with a viewer, metadata banners, and load gate

This commit is contained in:
Fisto
2026-09-16 12:29:45 +02:00
parent fae754fba0
commit f9b4041157
8 changed files with 604 additions and 52 deletions
+52 -17
View File
@@ -102,6 +102,18 @@ export function uniqueScrapeId(root: string, channelId: string, now = new Date()
return id;
}
export interface MediaFilterOptions {
kind?: MediaKind;
source?: string;
authorId?: string;
aspect?: string;
resolution?: string;
after?: string;
before?: string;
limit?: number;
offset?: number;
}
export interface MediaRow {
id: number;
kind: MediaKind;
@@ -369,22 +381,7 @@ export class ScrapeStore {
return row ?? null;
}
queryMedia(
options: {
kind?: MediaKind;
source?: string;
authorId?: string;
aspect?: string;
resolution?: string;
after?: string;
before?: string;
limit?: number;
offset?: number;
} = {}
): MediaRow[] {
const limit = Math.max(1, Math.min(options.limit ?? 50, 500));
const offset = Math.max(0, options.offset ?? 0);
private mediaWhere(options: MediaFilterOptions): { where: string; values: Array<string | number> } {
const conditions: string[] = [];
const values: Array<string | number> = [];
@@ -415,11 +412,49 @@ export class ScrapeStore {
const resolution = options.resolution ? resolutionCondition(options.resolution) : null;
if (resolution) conditions.push(resolution);
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
return { where: conditions.length ? `WHERE ${conditions.join(" AND ")}` : "", values };
}
queryMedia(options: MediaFilterOptions = {}): MediaRow[] {
const limit = Math.max(1, Math.min(options.limit ?? 50, 500));
const offset = Math.max(0, options.offset ?? 0);
const { where, values } = this.mediaWhere(options);
const rows = this.db
.prepare(`SELECT * FROM media ${where} ORDER BY id DESC LIMIT ? OFFSET ?`)
.all(...values, limit, offset);
return rows as unknown as MediaRow[];
}
/** Counts for the status row, under the same filters as queryMedia. */
stats(options: MediaFilterOptions = {}): {
total: number;
downloaded: number;
cached: number;
notDownloaded: number;
} {
const { where, values } = this.mediaWhere(options);
const row = this.db
.prepare(
`SELECT COUNT(*) AS total,
COALESCE(SUM(downloaded), 0) AS downloaded,
COALESCE(SUM(CASE WHEN downloaded = 0 AND preview_hash IS NOT NULL THEN 1 ELSE 0 END), 0) AS cached,
COALESCE(SUM(CASE WHEN downloaded = 0 THEN 1 ELSE 0 END), 0) AS not_downloaded
FROM media ${where}`
)
.get(...values) as unknown as {
total: number;
downloaded: number;
cached: number;
not_downloaded: number;
};
return {
total: row?.total ?? 0,
downloaded: row?.downloaded ?? 0,
cached: row?.cached ?? 0,
notDownloaded: row?.not_downloaded ?? 0
};
}
}