diff --git a/CHANGELOG.md b/CHANGELOG.md index 40c02ee2..8b6f900b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ edit the release notes and re-run the generator. Upgrading, pinning a version and rolling back: [docs/self-hosting.md](docs/self-hosting.md#upgrading). +## 0.107.1 — 2026-10-16 + +### Fixed + +- Downloads: a failed download's explanation now says to press Retry, instead of pointing to a download list that no longer exists. + ## 0.107.0 — 2026-10-15 ### New diff --git a/VERSION b/VERSION index a9a7f3fe..44e1fdfa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.107.0 +0.107.1 diff --git a/frontend/src/content/release-notes/en.json b/frontend/src/content/release-notes/en.json index 34f2ab8e..20b23fd1 100644 --- a/frontend/src/content/release-notes/en.json +++ b/frontend/src/content/release-notes/en.json @@ -1,4 +1,12 @@ [ + { + "version": "0.107.1", + "date": "2026-10-16", + "new": [], + "fixed": [ + "Downloads: a failed download's explanation now says to press Retry, instead of pointing to a download list that no longer exists." + ] + }, { "version": "0.107.0", "date": "2026-10-15", diff --git a/frontend/src/content/release-notes/hu.json b/frontend/src/content/release-notes/hu.json index 31388b1b..a97111bc 100644 --- a/frontend/src/content/release-notes/hu.json +++ b/frontend/src/content/release-notes/hu.json @@ -1,4 +1,12 @@ [ + { + "version": "0.107.1", + "date": "2026-10-16", + "new": [], + "fixed": [ + "Letöltés: egy sikertelen letöltés magyarázata most az Újra gombot mondja, nem egy már nem létező letöltési listát." + ] + }, { "version": "0.107.0", "date": "2026-10-15", diff --git a/frontend/src/i18n/locales/en/downloads.json b/frontend/src/i18n/locales/en/downloads.json index 66d00946..2549e42c 100644 --- a/frontend/src/i18n/locales/en/downloads.json +++ b/frontend/src/i18n/locales/en/downloads.json @@ -316,11 +316,11 @@ "facebook": { "login_wall": { "short": "Facebook sign-in required", - "body": "Facebook only shows this to signed-in users (e.g. 18+ content or a closed group). Add your Facebook cookies under Settings → Account → Download authentication, then retry the download from the list." + "body": "Facebook only shows this to signed-in users (e.g. 18+ content or a closed group). Add your Facebook cookies under Settings → Account → Download authentication, then press Retry." }, "cookie_rejected": { "short": "Facebook didn't accept the cookies", - "body": "The cookies have probably expired, or you signed out in that browser. Export them again, paste them in under Settings → Account → Download authentication, then retry the download from the list. If it still fails, the content has been removed or isn't visible to you." + "body": "The cookies have probably expired, or you signed out in that browser. Export them again, paste them in under Settings → Account → Download authentication, then press Retry. If it still fails, the content has been removed or isn't visible to you." }, "group_post_video": { "short": "No video found in this post", @@ -330,15 +330,15 @@ "youtube": { "bot_wall": { "short": "YouTube sign-in required", - "body": "YouTube only serves this video to a signed-in viewer — it asks the downloader to confirm it isn't a bot. Other YouTube videos are usually unaffected. Add your YouTube cookies under Settings → Account → Download authentication, then retry the download from the list." + "body": "YouTube only serves this video to a signed-in viewer — it asks the downloader to confirm it isn't a bot. Other YouTube videos are usually unaffected. Add your YouTube cookies under Settings → Account → Download authentication, then press Retry." }, "cookie_rejected": { "short": "YouTube didn't accept the cookies", - "body": "YouTube rotates exported cookies as a security measure, so yours have probably expired. Export them again from a private browsing window, close that window without browsing on, paste them in under Settings → Account → Download authentication, then retry the download from the list." + "body": "YouTube rotates exported cookies as a security measure, so yours have probably expired. Export them again from a private browsing window, close that window without browsing on, paste them in under Settings → Account → Download authentication, then press Retry." }, "media_forbidden": { "short": "YouTube refused the video stream", - "body": "YouTube found the video but refused to send the file to a signed-out download. It comes and goes, so a retry from the list is worth one try — and adding your YouTube cookies under Settings → Account → Download authentication is what gets a stubborn one through." + "body": "YouTube found the video but refused to send the file to a signed-out download. It comes and goes, so pressing Retry once is worth it — and adding your YouTube cookies under Settings → Account → Download authentication is what gets a stubborn one through." } } } diff --git a/frontend/src/i18n/locales/hu/downloads.json b/frontend/src/i18n/locales/hu/downloads.json index 3dcccbe0..59f90d89 100644 --- a/frontend/src/i18n/locales/hu/downloads.json +++ b/frontend/src/i18n/locales/hu/downloads.json @@ -316,11 +316,11 @@ "facebook": { "login_wall": { "short": "Facebook-bejelentkezés szükséges", - "body": "A Facebook ezt csak bejelentkezve mutatja (pl. 18+ tartalom vagy zárt csoport). Illeszd be a Facebook-cookie-kat a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd a listában indítsd újra a letöltést." + "body": "A Facebook ezt csak bejelentkezve mutatja (pl. 18+ tartalom vagy zárt csoport). Illeszd be a Facebook-cookie-kat a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd nyomd meg az Újra gombot." }, "cookie_rejected": { "short": "A Facebook nem fogadta el a cookie-kat", - "body": "A cookie-k valószínűleg lejártak, vagy kijelentkeztél abban a böngészőben. Exportáld újra őket, illeszd be a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd a listában indítsd újra a letöltést. Ha így sem megy, a tartalmat törölték, vagy nem látható számodra." + "body": "A cookie-k valószínűleg lejártak, vagy kijelentkeztél abban a böngészőben. Exportáld újra őket, illeszd be a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd nyomd meg az Újra gombot. Ha így sem megy, a tartalmat törölték, vagy nem látható számodra." }, "group_post_video": { "short": "Ebben a posztban nem található videó", @@ -330,15 +330,15 @@ "youtube": { "bot_wall": { "short": "YouTube-bejelentkezés szükséges", - "body": "A YouTube ezt a videót csak bejelentkezett nézőnek adja ki — a letöltőprogramtól azt kéri, igazolja, hogy nem robot. A többi YouTube-videót ez általában nem érinti. Illeszd be a YouTube-cookie-kat a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd a listában indítsd újra a letöltést." + "body": "A YouTube ezt a videót csak bejelentkezett nézőnek adja ki — a letöltőprogramtól azt kéri, igazolja, hogy nem robot. A többi YouTube-videót ez általában nem érinti. Illeszd be a YouTube-cookie-kat a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd nyomd meg az Újra gombot." }, "cookie_rejected": { "short": "A YouTube nem fogadta el a cookie-kat", - "body": "A YouTube biztonsági okból lecseréli az exportált cookie-kat, ezért a tiéid valószínűleg lejártak. Exportáld őket újra egy privát böngészőablakból, majd zárd be azt az ablakot további böngészés nélkül, illeszd be a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd a listában indítsd újra a letöltést." + "body": "A YouTube biztonsági okból lecseréli az exportált cookie-kat, ezért a tiéid valószínűleg lejártak. Exportáld őket újra egy privát böngészőablakból, majd zárd be azt az ablakot további böngészés nélkül, illeszd be a Beállítások → Fiók → Letöltés-hitelesítés alatt, majd nyomd meg az Újra gombot." }, "media_forbidden": { "short": "A YouTube nem adta ki a videófájlt", - "body": "A YouTube visszaadta a videó adatait, de a fájlt már nem adta ki bejelentkezés nélkül. Ez hol van, hol nincs, ezért érdemes újrapróbálni a listából — a makacs eseteken pedig a YouTube-cookie-k segítenek, a Beállítások → Fiók → Letöltés-hitelesítés alatt." + "body": "A YouTube visszaadta a videó adatait, de a fájlt már nem adta ki bejelentkezés nélkül. Ez hol van, hol nincs, ezért érdemes egyszer megnyomni az Újra gombot — a makacs eseteken pedig a YouTube-cookie-k segítenek, a Beállítások → Fiók → Letöltés-hitelesítés alatt." } } } diff --git a/frontend/src/lib/gates.snapshot.json b/frontend/src/lib/gates.snapshot.json index 44a7ff48..a478db2d 100644 --- a/frontend/src/lib/gates.snapshot.json +++ b/frontend/src/lib/gates.snapshot.json @@ -1307,21 +1307,39 @@ "export MSYS_NO_PATHCONV=1 # Git Bash: do not rewrite the in-container paths below", "TESTCOMPOSE=backend/docker-compose.test.yml", "failed=()", + "timings=()", + "ms() { date +%s%3N; }", + "secs() { awk -v t=\"$1\" 'BEGIN { printf \"%.1f\", t / 1000 }'; }", + "took=\"\"", + "timed() { # timed — sets $took (no subshell: it must add to $timings)", + " took=\"$(secs $(($(ms) - $3)))\"", + " timings+=(\"$(printf '%8s s %s' \"$took\" \"$1\")$([ \"$2\" = red ] && echo ' (red)')\")", + " if [ -n \"${GATE_TIMES_FILE:-}\" ]; then", + " printf '%s\\t%s\\t%s\\t%s\\t%s\\t%s\\n' \"$(date -u +%FT%TZ)\" \"$mode\" \"$1\" \"$took\" \"$2\" \\", + " \"$(git rev-parse --short 'HEAD^{tree}' 2>/dev/null)\" >>\"$GATE_TIMES_FILE\"", + " fi", + " return 0", + "}", "lane() { # lane — run it, remember a failure, never stop", - " local name=\"$1\"", + " local name=\"$1\" t0 result=ok", " shift", + " t0=\"$(ms)\"", " if [ \"$brief\" = 0 ]; then", " echo \">> $name…\"", - " \"$@\" || failed+=(\"$name\")", + " \"$@\" || { failed+=(\"$name\"); result=red; }", + " timed \"$name\" \"$result\" \"$t0\"", + " echo \"<< $name: $took s\"", " return 0", " fi", " local out", " out=\"$(mktemp)\"", " if \"$@\" >\"$out\" 2>&1; then", - " echo \">> $name: ok\"", + " timed \"$name\" ok \"$t0\"", + " echo \">> $name: ok ($took s)\"", " else", " failed+=(\"$name\")", - " echo \">> $name: FAILED\"", + " timed \"$name\" red \"$t0\"", + " echo \">> $name: FAILED ($took s)\"", " brief_excerpt \"$out\"", " fi", " rm -f \"$out\"", @@ -1361,6 +1379,7 @@ " return 1", "}", "frontend() { (cd frontend && \"$@\"); }", + "gate_t0=\"$(ms)\"", "lane \"ruff\" ruff_check", "lane \"ruff format --check\" ruff_format", "lane \"basedpyright (new errors only)\" basedpyright", @@ -1387,6 +1406,9 @@ " lane \"pip-audit (backend lock)\" pip_audit", " lane \"npm audit (production dependencies)\" frontend npm audit --omit=dev --audit-level=high", "fi", + "echo \">> lane times:\"", + "printf '%s\\n' \"${timings[@]}\"", + "echo \"$(printf '%8s' \"$(secs $(($(ms) - gate_t0)))\") s total\"", "if [ ${#failed[@]} -eq 0 ]; then", " echo \">> gate ($mode): green ✓\"", " exit 0", diff --git a/scripts/check.sh b/scripts/check.sh index 1b66cf98..41e1217e 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -9,6 +9,8 @@ # bash scripts/check.sh lint the static lanes only: no tests, no build, no audits # … --brief (either mode) a lane's output only when it fails, and only its first # error lines — for an agent, whose context the full log would flood +# GATE_TIMES_FILE= … also append each lane's time as a TSV row (UTC, mode, lane, seconds, +# ok|red, HEAD's tree) — to compare runs; every run prints the times anyway # # Each lane runs even when an earlier one failed, and the summary names every red one: a gate that # stops at the first failure hides the second until the first is fixed. The backend lanes run in the @@ -29,21 +31,42 @@ done export MSYS_NO_PATHCONV=1 # Git Bash: do not rewrite the in-container paths below TESTCOMPOSE=backend/docker-compose.test.yml failed=() +timings=() +ms() { date +%s%3N; } +secs() { awk -v t="$1" 'BEGIN { printf "%.1f", t / 1000 }'; } +# How long each lane took: a line after the lane, a table at the end, and — when GATE_TIMES_FILE names a +# file — one tab-separated row per lane appended to it (UTC time, mode, lane, seconds, ok|red, tree), so +# runs can be compared. The script writes no file unless asked to. +took="" +timed() { # timed — sets $took (no subshell: it must add to $timings) + took="$(secs $(($(ms) - $3)))" + timings+=("$(printf '%8s s %s' "$took" "$1")$([ "$2" = red ] && echo ' (red)')") + if [ -n "${GATE_TIMES_FILE:-}" ]; then + printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$(date -u +%FT%TZ)" "$mode" "$1" "$took" "$2" \ + "$(git rev-parse --short 'HEAD^{tree}' 2>/dev/null)" >>"$GATE_TIMES_FILE" + fi + return 0 +} lane() { # lane — run it, remember a failure, never stop - local name="$1" + local name="$1" t0 result=ok shift + t0="$(ms)" if [ "$brief" = 0 ]; then echo ">> $name…" - "$@" || failed+=("$name") + "$@" || { failed+=("$name"); result=red; } + timed "$name" "$result" "$t0" + echo "<< $name: $took s" return 0 fi local out out="$(mktemp)" if "$@" >"$out" 2>&1; then - echo ">> $name: ok" + timed "$name" ok "$t0" + echo ">> $name: ok ($took s)" else failed+=("$name") - echo ">> $name: FAILED" + timed "$name" red "$t0" + echo ">> $name: FAILED ($took s)" brief_excerpt "$out" fi rm -f "$out" @@ -102,6 +125,7 @@ basedpyright() { frontend() { (cd frontend && "$@"); } +gate_t0="$(ms)" lane "ruff" ruff_check lane "ruff format --check" ruff_format lane "basedpyright (new errors only)" basedpyright @@ -139,6 +163,9 @@ if [ "$mode" = all ]; then lane "npm audit (production dependencies)" frontend npm audit --omit=dev --audit-level=high fi +echo ">> lane times:" +printf '%s\n' "${timings[@]}" +echo "$(printf '%8s' "$(secs $(($(ms) - gate_t0)))") s total" if [ ${#failed[@]} -eq 0 ]; then echo ">> gate ($mode): green ✓" exit 0