From 0c604ef4a88a918f2b9fcef11970c0469d6c7072 Mon Sep 17 00:00:00 2001 From: volcanic001 Date: Fri, 9 Oct 2026 09:58:47 -0600 Subject: [PATCH] fix(captions): don't ask youtube for a translation when none was asked for The timedtext request carries tlang whether or not a translation was requested, and youtube now turns down anything that asks for one: it answers with its block page instead of the subtitle. Auto-generated captions come out as a WebVTT file with no cues in it, every other track as a page of html. Auto-generated tracks also have to name format 3. Left to itself youtube answers in the older dialect these days, which the parser below doesn't read and quietly turns into an empty file. Co-Authored-By: Claude Opus 5 --- src/invidious/routes/api/v1/videos.cr | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/invidious/routes/api/v1/videos.cr b/src/invidious/routes/api/v1/videos.cr index 14221ed03..7d4a683c0 100644 --- a/src/invidious/routes/api/v1/videos.cr +++ b/src/invidious/routes/api/v1/videos.cr @@ -105,12 +105,21 @@ module Invidious::Routes::API::V1::Videos webvtt = transcript.to_vtt else # Timedtext API handling - url = URI.parse("#{caption.base_url}&tlang=#{tlang}").request_target + url = URI.parse(caption.base_url).request_target + + # Youtube turns down anything that asks for a translation, and an empty + # tlang still counts as asking: it answers with its block page instead + # of the subtitle, whatever the rest of the request says. So it only + # goes on when a translation was actually asked for. + url += "&tlang=#{tlang}" if tlang && !tlang.empty? # Auto-generated captions often have cues that aren't aligned properly with the video, # as well as some other markup that makes it cumbersome, so we try to fix that here if caption.name.includes? "auto-generated" - caption_xml = YT_POOL.client &.get(url).body + # The parser below reads timedtext format 3. Left to itself youtube + # now answers in the older dialect, which comes out as a + # WebVTT file with no cues in it. + caption_xml = YT_POOL.client &.get("#{url}&fmt=srv3").body settings_field = { "Kind" => "captions",