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).
59 lines
1.4 KiB
Plaintext
59 lines
1.4 KiB
Plaintext
# Pinned dependency lock for reproducible builds (SEC-5). GENERATED — do not hand-edit.
|
|
# Source of truth is requirements.txt (the floor ranges); regenerate after editing it with:
|
|
# docker run --rm -v "$PWD":/b python:3.12-slim sh -c 'pip install -q -r /b/requirements.txt && pip freeze --exclude pip --exclude setuptools --exclude wheel' | sort
|
|
# The prod Dockerfile + Dockerfile.test + CI install THIS file; pip-audit scans it in CI.
|
|
APScheduler==3.11.3
|
|
Authlib==1.7.2
|
|
Mako==1.4.1
|
|
MarkupSafe==3.0.3
|
|
PyYAML==6.0.3
|
|
SQLAlchemy==2.0.52
|
|
alembic==1.19.1
|
|
annotated-doc==0.0.5
|
|
annotated-types==0.8.0
|
|
anyio==4.14.2
|
|
argon2-cffi-bindings==25.1.0
|
|
argon2-cffi==25.1.0
|
|
bgutil-ytdlp-pot-provider==1.3.1
|
|
brotli==1.2.0
|
|
certifi==2026.7.22
|
|
cffi==2.1.1
|
|
charset-normalizer==3.5.1
|
|
click==8.4.2
|
|
cryptography==50.0.0
|
|
fastapi==0.141.1
|
|
feedparser-sgmllib==2.1.0
|
|
feedparser==6.0.14
|
|
greenlet==3.5.5
|
|
h11==0.16.0
|
|
httpcore==1.0.9
|
|
httptools==0.8.0
|
|
httpx==0.28.1
|
|
idna==3.18
|
|
itsdangerous==2.2.0
|
|
joserfc==1.7.4
|
|
mutagen==1.48.1
|
|
numpy==2.5.2
|
|
psycopg-binary==3.3.4
|
|
psycopg==3.3.4
|
|
py3langid==0.3.0
|
|
pycparser==3.0
|
|
pycryptodomex==3.23.0
|
|
pydantic-settings==2.15.0
|
|
pydantic==2.13.4
|
|
pydantic_core==2.46.4
|
|
python-dotenv==1.2.3
|
|
python-multipart==0.0.32
|
|
requests==2.34.2
|
|
starlette==1.6.0
|
|
typing-inspection==0.4.4
|
|
typing_extensions==4.16.0
|
|
tzlocal==5.4.4
|
|
urllib3==2.7.0
|
|
uvicorn==0.52.3
|
|
uvloop==0.22.1
|
|
watchfiles==1.2.0
|
|
websockets==17.0.1
|
|
yt-dlp-ejs==0.8.0
|
|
yt-dlp==2026.7.4
|