feat(downloads): editable details with clickable channel and extra links

The edit (pencil) action now edits a download's full display metadata — title,
channel name, channel link and any number of extra reference URLs — instead of
just the name. The channel and links render as clickable links on the library
card, and the channel link is auto-filled from the source (yt-dlp channel_url)
when available. Shared watch pages resolve the same per-download overrides, so a
rename/channel/link edit is reflected on the public /watch page too, with every
link clickable.

Adds migration 0049 (media_assets.uploader_url; download_jobs.display_uploader,
display_uploader_url, extra_links) and generalizes the rename endpoint into a
metadata update with URL validation. EN/HU/DE strings included.
This commit is contained in:
npeter83 2026-07-07 20:19:18 +02:00
parent 04d35375ed
commit 8c86c6b4a8
12 changed files with 327 additions and 41 deletions

View file

@ -851,6 +851,7 @@ export interface DownloadAsset {
status: string;
title: string | null;
uploader: string | null;
uploader_url: string | null; // yt-dlp channel/uploader page URL (auto; null for e.g. reddit)
upload_date: string | null;
duration_s: number | null;
size_bytes: number | null;
@ -903,6 +904,9 @@ export interface DownloadJob {
profile_id: number | null;
spec: DownloadSpec;
display_name: string | null;
display_uploader: string | null; // user override of the channel name
display_uploader_url: string | null; // user override of the channel link
extra_links: string[]; // extra reference URLs (beyond source_url)
job_kind?: string; // "download" (default) | "edit"
source_job_id?: number | null; // parent download an edit was cut from
edit_spec?: EditSpec | null;
@ -1410,8 +1414,17 @@ export const api = {
cancelDownload: (id: number): Promise<DownloadJob> =>
req(`/api/downloads/${id}/cancel`, { method: "POST" }, { idempotent: true }),
deleteDownload: (id: number) => req(`/api/downloads/${id}`, { method: "DELETE" }),
renameDownload: (id: number, display_name: string): Promise<DownloadJob> =>
req(`/api/downloads/${id}`, { method: "PATCH", body: JSON.stringify({ display_name }) }),
// Update a download's display metadata (title / channel name+link / extra reference URLs).
updateDownloadMeta: (
id: number,
meta: {
display_name?: string;
display_uploader?: string;
display_uploader_url?: string;
extra_links?: string[];
}
): Promise<DownloadJob> =>
req(`/api/downloads/${id}`, { method: "PATCH", body: JSON.stringify(meta) }),
shareDownload: (id: number, email: string): Promise<{ shared_with: string }> =>
req(`/api/downloads/${id}/share`, { method: "POST", body: JSON.stringify({ email }) }),
unshareDownload: (id: number, email: string) =>