feat(downloads): store + show a canonical "downloaded from" source URL

Every download now records the clean source page URL and shows it on the
Downloads page (open in a new tab, or copy to clipboard). The worker stores
yt-dlp's canonical webpage_url on the asset (migration 0043 adds
media_assets.source_webpage_url); the serializer prefers it and falls back to a
URL derived from source_kind+source_ref, so YouTube, external YouTube links and
external URLs (e.g. Facebook reels) all get a correct reference, and queued/older
rows work before the worker fills it. Edit clips return null (a clip's source is
the user's own earlier download, not a web page). i18n en/hu/de.
This commit is contained in:
npeter83 2026-07-04 21:25:37 +02:00
parent 0e1b24a93c
commit 42d465d760
9 changed files with 115 additions and 0 deletions

View file

@ -68,6 +68,20 @@ def resolve_source(raw: str) -> tuple[str, str]:
raise HTTPException(status_code=400, detail="That doesn't look like a valid video link.")
def _reference_url(job: DownloadJob, asset: MediaAsset | None) -> str | None:
"""The clean "downloaded from" URL for the Downloads page: the canonical page URL yt-dlp
recorded, else one derived from source_kind+source_ref (covers queued/older rows before the
worker fills it). Returns None for edit derivatives a clip's source is the user's own earlier
download, not a web page, so there's no meaningful external URL to show."""
if job.job_kind == "edit":
return None
if asset is not None and asset.source_webpage_url:
return asset.source_webpage_url
if job.source_kind in ("youtube", "url"):
return service.source_url(job.source_kind, job.source_ref)
return None
def _serialize(job: DownloadJob, asset: MediaAsset | None) -> dict:
ready = asset is not None and asset.status == "ready" and bool(asset.rel_path)
return {
@ -82,6 +96,7 @@ def _serialize(job: DownloadJob, asset: MediaAsset | None) -> dict:
"created_at": job.created_at.isoformat() if job.created_at else None,
"source_kind": job.source_kind,
"source_ref": job.source_ref,
"source_url": _reference_url(job, asset),
"profile_id": job.profile_id,
"spec": job.profile_snapshot,
"display_name": job.display_name,