fix(downloads): E11.5 epic-end review round 1

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.
This commit is contained in:
2026-09-14 00:50:05 +02:00
parent b17d92d7c2
commit e887233210
20 changed files with 610 additions and 112 deletions
+21 -5
View File
@@ -1,5 +1,6 @@
"""Category classification of raw yt-dlp / ffmpeg download-failure strings (S2)."""
from app.downloads import adapters
from app.downloads import errors as dlerrors
from app.downloads.errors import classify
@@ -102,21 +103,36 @@ def test_facebook_cannot_parse_data_with_a_cookie_is_the_cookie_refused():
def test_a_facebook_group_post_without_formats_names_the_link():
assert dlerrors.diagnose(_FB_NO_FORMATS, url=_GROUP_POST, cookie_attached=True) == (
# Without a cookie yt-dlp is given the post link itself (measured, prod jobs 66 and 145).
assert dlerrors.diagnose(_FB_NO_FORMATS, url=_GROUP_POST) == (
"format_unavailable",
"facebook.group_post_video",
)
# A reel with no formats is another failure: only the category.
assert dlerrors.diagnose(_FB_NO_FORMATS, url=_REEL, cookie_attached=True) == (
# With one it is only ever given the post's VIDEO link, so the text is about that video: the adapter
# found it, and "this post has no video" would be false.
assert dlerrors.diagnose(_FB_NO_FORMATS, url=_GROUP_POST, cookie_attached=True) == (
"format_unavailable",
None,
)
# A reel, or a group link that is not a post, is another failure: only the category.
for url in (_REEL, "https://www.facebook.com/groups/983554479435596/"):
assert dlerrors.diagnose(_FB_NO_FORMATS, url=url) == ("format_unavailable", None), url
def test_our_post_not_shown_verdict_is_the_refused_cookie():
# The adapter's text when a signed-in read of the post got a page without the post (the login page).
msg = adapters.POST_NOT_SHOWN_MESSAGE
assert classify(msg) == "login_required"
assert dlerrors.diagnose(msg, url=_GROUP_POST, cookie_attached=True) == (
"login_required",
"facebook.cookie_rejected",
)
def test_our_post_without_a_video_verdict_names_the_group_link():
# The worker's own text when the post page holds no single video (S4): the message and its rule and
# The adapter's own text when the post page holds no single video (S4): the message and its rule and
# catalog entry are pinned together, or an edit to the wording orphans the copy.
msg = dlerrors.POST_WITHOUT_VIDEO_MESSAGE
msg = adapters.POST_WITHOUT_VIDEO_MESSAGE
assert classify(msg) == "format_unavailable"
assert dlerrors.diagnose(msg, url=_GROUP_POST, cookie_attached=True) == (
"format_unavailable",