siftlode/backend/alembic/versions/0037_download_builtin_profiles.py
npeter83 317750e491 feat(downloads): M1 — schema, config, worker container, deps
- models: MediaAsset (shared file cache, unique on source+format_sig), DownloadJob
  (per-user request), DownloadProfile (presets), DownloadShare (ACL), DownloadQuota
- migrations 0036 (5 tables) + 0037 (6 builtin presets)
- sysconfig 'downloads' group (per-user defaults, retention/GC, layout, worker concurrency)
- config: DOWNLOAD_ROOT / WORKER_ENABLED env + download defaults
- deps: yt-dlp in requirements, ffmpeg in Dockerfile runtime stage
- worker: dedicated container (python -m app.worker), thin idle entry (loop lands in M2)
- localdev compose: worker service + downloads bind-mount; gitignore downloads/
2026-07-02 23:57:40 +02:00

67 lines
2.1 KiB
Python

"""seed builtin download profiles (presets)
Revision ID: 0037_dl_builtin_profiles
Revises: 0036_download_center
Create Date: 2026-07-02
Seeds the shipped download presets (user_id NULL, is_builtin=true). `spec` is the format
definition consumed by app.downloads.formats: mode ("av" audio+video / "v" video-only /
"a" audio-only), max_height (null = best), container, audio_format, vcodec preference, and
the embed_*/sponsorblock postprocessor flags. Names are English (the UI default); the frontend
localizes builtin labels by a stable key derived from the name.
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "0037_dl_builtin_profiles"
down_revision: Union[str, None] = "0036_download_center"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def _spec(mode, height=None, container=None, audio_format=None, vcodec=None):
return {
"mode": mode,
"max_height": height,
"container": container,
"audio_format": audio_format,
"vcodec": vcodec,
"embed_subs": False,
"embed_chapters": True,
"embed_thumbnail": True,
"sponsorblock": False,
}
_BUILTINS = [
("Best (MP4)", _spec("av", None, "mp4"), 10),
("1080p (MP4)", _spec("av", 1080, "mp4"), 20),
("720p (MP4)", _spec("av", 720, "mp4"), 30),
("Audio (M4A)", _spec("a", None, None, "m4a"), 40),
("Audio (MP3)", _spec("a", None, None, "mp3"), 50),
("Video only (MP4)", _spec("v", None, "mp4"), 60),
]
def upgrade() -> None:
profiles = sa.table(
"download_profiles",
sa.column("user_id", sa.Integer),
sa.column("name", sa.String),
sa.column("spec", sa.JSON),
sa.column("is_builtin", sa.Boolean),
sa.column("sort_order", sa.Integer),
)
op.bulk_insert(
profiles,
[
{"user_id": None, "name": name, "spec": spec, "is_builtin": True, "sort_order": order}
for name, spec, order in _BUILTINS
],
)
def downgrade() -> None:
op.execute("DELETE FROM download_profiles WHERE is_builtin = true")