Add the non-yt-dlp ingest path so a user-uploaded file joins the same download
library and inherits its function set. Any file type is accepted: media
(video/audio) gets the full set (metadata, share, play/edit), a generic file
(pdf/zip/docx/…) is download-only.
- MediaAsset.media_kind ("video"|"audio"|"file") — the stored discriminator that
gates library actions. Set at every finalize (download probe, editor carries the
source's kind, upload probe); consolidates the ad-hoc audio-detect that lived only
in links.py into one invariant (downloads.mediakind.derive). Migration 0076 adds
the column + backfills existing rows.
- downloads/upload.py: ingest_upload probes the file (ffprobe → codecs/dims/duration),
places it in a per-user .uploads tree, and creates an already-ready asset + done job
(the same cache-hit shortcut enqueue uses). Uploads never expire (irreplaceable).
- POST /api/downloads/upload: streaming multipart with the per-file cap
(download_upload_max_bytes, default 500 MiB) enforced on actual bytes read; quota
charges the known size up front (quota.check_upload).
- Security: a generic file is always served force-download (attachment +
application/octet-stream) on both the in-app and public endpoints, so uploaded
HTML/SVG can't execute inline. Edit + redownload reject non-media / upload sources.
- python-multipart added (first multipart endpoint in the app).
24 lines
1021 B
Python
24 lines
1021 B
Python
"""The media-kind discriminator for a library asset — the single home of one invariant.
|
|
|
|
`MediaAsset.media_kind` is `"video" | "audio" | "file"`. It decides which library actions apply:
|
|
media (video/audio) gets play/edit/share; a generic uploaded file gets download + share only, and
|
|
is never rendered inline (see the force-download serving in the file/watch routes). Every finalize
|
|
path computes it HERE — the download worker + the upload ingest via `derive`, the editor by
|
|
carrying the source's kind over — so the rule lives in code, not in a comment that can drift (an
|
|
earlier ad-hoc `vcodec is None and acodec is not None` audio check lived only in links.py).
|
|
"""
|
|
|
|
VIDEO = "video"
|
|
AUDIO = "audio"
|
|
FILE = "file"
|
|
|
|
|
|
def derive(vcodec: str | None, acodec: str | None) -> str:
|
|
"""Media kind from the probed codecs: a video stream -> video, audio-only -> audio, neither
|
|
(a non-media upload: pdf/zip/docx/…) -> file."""
|
|
if vcodec:
|
|
return VIDEO
|
|
if acodec:
|
|
return AUDIO
|
|
return FILE
|