fix: a failed subtitle no longer destroys the download

Two faults behind the 429 in the report.

The subtitle languages were a wildcard: 'en.*' also matches every
machine-translated variant YouTube offers — en-en-US and dozens more —
so each download fired a burst of subtitle requests and earned an HTTP
429. Languages are now named exactly, as '<lang>,<lang>-orig'. Turning
subtitles off now really does request none; it previously still asked
for English.

Worse, yt-dlp exits non-zero if anything at all failed, and a caption it
could not fetch was enough to mark a fully downloaded video as failed
and leave the file orphaned. yt-dlp now runs with --ignore-errors, and
success is judged by whether the media file actually landed rather than
by the exit code.

429 also gets its own message pointing at the sign-in setting, which
raises the limit.
This commit is contained in:
vincent
2026-08-29 14:29:14 +02:00
parent 22c7a3a5ec
commit 923fd3273f
3 changed files with 89 additions and 29 deletions
+14 -9
View File
@@ -121,6 +121,11 @@ fn explain_yt_dlp_error(stderr: &str, signed_in: bool) -> String {
.into()
};
}
if stderr.contains("429") || stderr.contains("Too Many Requests") {
return "YouTube is rate-limiting this machine. Wait a few minutes, or sign in \
under Settings → Sign in to YouTube, which raises the limit."
.into();
}
if stderr.contains("Operation not permitted") && stderr.contains("Safari") {
return "macOS blocked access to Safari's cookies. Give FlightTube Full Disk Access in System Settings → Privacy & Security, or pick a different browser."
.into();
@@ -1038,15 +1043,15 @@ pub async fn download_video(
let stderr_lines = stderr_task.await.unwrap_or_default();
drop(permit);
if status.success() {
// yt-dlp normally reports the path via `--print after_move:`; if that
// line went missing, find the file it wrote by its embedded video id.
let path = match final_path {
Some(p) => p,
None => find_by_video_id(&library, &video_id)
.await
.ok_or("Download finished but the file could not be located.")?,
};
// yt-dlp exits non-zero if *anything* failed, including a subtitle it could
// not fetch. If the video itself landed, the download succeeded — throwing
// away a finished file over a missing caption would be absurd.
let landed = match final_path {
Some(p) if tokio::fs::metadata(&p).await.is_ok() => Some(p),
_ => find_by_video_id(&library, &video_id).await,
};
if let Some(path) = landed {
// YouTube's captions arrive pinned to the left edge and full of
// karaoke timing tags; clean them before they reach the player.
tidy_subtitles(&library, &video_id).await;