Two visual gaps for non-catalog downloads: - Channel link: YouTube already exposes channel_url; Facebook exposes none but a numeric uploader_id that resolves at facebook.com/<id>. `_uploader_url` derives it so the auto-detected channel renders as a real clickable link. - Poster: a source with no thumbnail (e.g. a direct reddit HLS URL) showed a blank image box. The worker now cuts a representative frame with ffmpeg (`ensure_poster`) into the `<base>.jpg` sidecar and records `poster_path` (migration 0050). The card, the public watch page (<video poster> + og:image), and link previews fall back to it via new authed + public poster endpoints. Adds `app.downloads.backfill` (one-off, re-run-safe) to fill uploader_url (re-extract YouTube/Facebook metadata) and posters for pre-existing downloads.
28 lines
1,013 B
Python
28 lines
1,013 B
Python
"""local poster path for thumbnail-less downloads
|
|
|
|
Revision ID: 0050_asset_poster
|
|
Revises: 0049_download_display_meta
|
|
Create Date: 2026-07-07
|
|
|
|
Some sources (a direct media URL like a reddit v.redd.it HLS manifest) carry no thumbnail, so the
|
|
download shows a blank image box. The worker now extracts a representative frame with ffmpeg and
|
|
saves it as a poster sidecar; `poster_path` (relative to download_root) records it so the card /
|
|
watch page / link preview can fall back to it. Nullable — only set when there was no thumbnail.
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0050_asset_poster"
|
|
down_revision: Union[str, None] = "0049_download_display_meta"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("media_assets", sa.Column("poster_path", sa.Text(), nullable=True))
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("media_assets", "poster_path")
|