A refused Facebook cookie on a group post is no longer "no video in this post":
measured on dev, Facebook answers with its login page (no post id at all), so the
adapter now raises its own POST_NOT_SHOWN verdict and the catalog explains it as
the refused cookie. The adapter owns both post verdicts and the "cannot parse
data" wall wording, so the Chrome retry and the catalog read one constant. The
group-post "no formats" entry is cookie-scoped (with a cookie yt-dlp only gets the
post's video link) and uses the adapter's own group-post test instead of a
"/groups/" substring.
Job transitions that move an asset's ref count (resume, cancel, delete,
redownload) re-read the job under a row lock, asset first, so two overlapping
requests can no longer release the same hold. get_or_create_asset loses an insert
race inside a SAVEPOINT instead of rolling back the caller's pending writes (Retry
lost its cleared error). Every worker-side job failure goes through
finalize_if_running and writes error_detail, so a cancel that landed first is kept
and a failure never shows an earlier one's copy. The worker asks the download
child's own cookie gate (credentials.usable_cookie) when a job inherits a failed
asset.
UI: the Facebook copy no longer points at a Retry button the failure window does
not have, says "paste" like Settings, and the HU wording is fixed
("Újraletöltés" on the re-download confirm). A category this client has no copy
for falls back to the generic sentence instead of a raw key.
Tests: route serialization of error_detail, the probed duration through a real
_download, the page-read cap and a failed read, a Retry off a pending asset, the
confirm-button labels, the lock and savepoint races.
35 lines
1.8 KiB
Plaintext
35 lines
1.8 KiB
Plaintext
fastapi>=0.115,<1.0
|
||
# Multipart form parsing — required by Starlette/FastAPI to accept file uploads (UploadFile/Form).
|
||
# The download center's local-file Upload (E7) is the only multipart endpoint in the app.
|
||
python-multipart>=0.0.9,<1.0
|
||
uvicorn[standard]>=0.30,<1.0
|
||
sqlalchemy>=2.0,<2.1
|
||
psycopg[binary]>=3.2,<4.0
|
||
alembic>=1.13,<2.0
|
||
pydantic>=2.7,<3.0
|
||
pydantic-settings>=2.3,<3.0
|
||
authlib>=1.3,<2.0
|
||
httpx>=0.27,<1.0
|
||
feedparser>=6.0,<7.0
|
||
apscheduler>=3.10,<4.0
|
||
py3langid>=0.3,<1.0
|
||
itsdangerous>=2.1,<3.0
|
||
cryptography>=50.0.0,<51
|
||
argon2-cffi>=23.1,<26
|
||
# Download center: yt-dlp does the extraction/download; ffmpeg (OS-level in the Dockerfile)
|
||
# does the merge + postprocessing. The [default] extra pulls yt-dlp-ejs (the challenge-solver
|
||
# scripts Deno runs for YouTube's n-signature). yt-dlp is release-often (YouTube changes), so
|
||
# keep the floor loose and bump it when extraction breaks.
|
||
yt-dlp[default]>=2025.5.22
|
||
# Browser impersonation (`impersonate`) for Facebook downloads made with the user's cookie: some
|
||
# links answer a signed-in plain request with "Cannot parse data" and only parse as Chrome (E11.5,
|
||
# measured). The range is YT-DLP's, not ours — yt-dlp 2026.07.04 imports curl_cffi only in
|
||
# 0.10.x–0.15.x and silently reports every target "unavailable" outside it. Move it when bumping
|
||
# yt-dlp; `tests/test_facebook_impersonation.py::test_the_locked_curl_cffi_can_impersonate_chrome`
|
||
# fails the gate if the locked pair can no longer impersonate.
|
||
curl_cffi>=0.10,<0.16
|
||
# PO Token provider plugin: talks to the bgutil sidecar (brainicism/bgutil-ytdlp-pot-provider)
|
||
# to mint YouTube Proof-of-Origin tokens on demand, bypassing "confirm you're not a bot" and
|
||
# unlocking high-quality formats. Pin to match the sidecar image tag (versions must align).
|
||
bgutil-ytdlp-pot-provider==1.3.1
|