Fix unreceivable captions/transcript (iv-org/invidious#5571)#259
Fix unreceivable captions/transcript (iv-org/invidious#5571)#259shiny-comic wants to merge 4 commits intoiv-org:masterfrom
Conversation
This fixes subtitles and transcript not receivable due to updated Youtube API (iv-org/invidious#5571). > NOTE: > This may eat up the po tokens on public instance and leads to 429 error or similar problem. Sorry for my poor English.
There was a problem hiding this comment.
Pull request overview
This PR fixes an issue where captions and transcripts were not being retrieved due to changes in YouTube's API. The fix is based on FreeTube's solution and implements support for PO (Proof of Origin) tokens when fetching captions.
- Adds PO token and client name parameters to caption fetching logic
- Implements new caption retrieval path using YouTube's updated API with token authentication
- Adds VTT positioning data cleanup function to process fetched captions
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| src/routes/invidious_routes/captions.ts | Retrieves PO token and client name when available and passes them to the transcript handler |
| src/lib/helpers/youtubeTranscriptsHandling.ts | Implements new token-based caption fetching logic with fallback to existing method, adds VTT processing function |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -24,11 +25,56 @@ const ESCAPE_SUBSTITUTIONS = { | |||
| "\u00A0": " ", | |||
| }; | |||
|
|
|||
There was a problem hiding this comment.
The newly added "shiftVttToCenter" function lacks documentation. Please add a JSDoc comment explaining what the function does, its parameters, and return value. This is especially important since the function name doesn't clearly convey its purpose of stripping positioning data from VTT timing lines.
| /** | |
| * Normalizes WebVTT timing lines by stripping any additional cue settings | |
| * (such as position, alignment, or line information) after the timing range. | |
| * | |
| * For example, a line like: | |
| * `00:00:01.000 --> 00:00:04.000 line:0% position:50% align:middle` | |
| * becomes: | |
| * `00:00:01.000 --> 00:00:04.000` | |
| * | |
| * This is useful when you want subtitles to render with default (typically | |
| * centered) positioning by removing any explicit positioning data. | |
| * | |
| * @param vtt - Full WebVTT file contents as a string. | |
| * @returns The VTT string with timing lines cleaned of trailing cue settings. | |
| */ |
| function shiftVttToCenter(vtt: string): string { | ||
| const lines = vtt.split('\n'); | ||
| const updatedLines: string[] = []; | ||
| const timingRegex = /^((?:\d{1,2}:)?\d{2}:\d{2}\.\d{3} --> (?:\d{1,2}:)?\d{2}:\d{2}\.\d{3})(.*)$/; | ||
|
|
||
| for (const line of lines) { | ||
| const match = line.match(timingRegex); | ||
| if (match) { | ||
| updatedLines.push(match[1]); | ||
| } else { | ||
| updatedLines.push(line); | ||
| } | ||
| } | ||
|
|
||
| return updatedLines.join('\n'); | ||
| } | ||
|
|
There was a problem hiding this comment.
The function name "shiftVttToCenter" is misleading. The function doesn't shift captions to center position - it strips positioning information from VTT timing lines. According to the regex pattern, it removes any positioning data after the timestamp (the second capture group that's discarded). Consider renaming to something more descriptive like "stripVttPositioning" or "removeVttPositionData".
| function shiftVttToCenter(vtt: string): string { | |
| const lines = vtt.split('\n'); | |
| const updatedLines: string[] = []; | |
| const timingRegex = /^((?:\d{1,2}:)?\d{2}:\d{2}\.\d{3} --> (?:\d{1,2}:)?\d{2}:\d{2}\.\d{3})(.*)$/; | |
| for (const line of lines) { | |
| const match = line.match(timingRegex); | |
| if (match) { | |
| updatedLines.push(match[1]); | |
| } else { | |
| updatedLines.push(line); | |
| } | |
| } | |
| return updatedLines.join('\n'); | |
| } | |
| /** | |
| * Strips positioning and additional cue settings from VTT timing lines, | |
| * leaving only the raw timestamp range (e.g. "00:00:01.000 --> 00:00:02.000"). | |
| */ | |
| function stripVttPositioning(vtt: string): string { | |
| const lines = vtt.split('\n'); | |
| const updatedLines: string[] = []; | |
| const timingRegex = | |
| /^((?:\d{1,2}:)?\d{2}:\d{2}\.\d{3} --> (?:\d{1,2}:)?\d{2}:\d{2}\.\d{3})(.*)$/; | |
| for (const line of lines) { | |
| const match = line.match(timingRegex); | |
| if (match) { | |
| updatedLines.push(match[1]); | |
| } else { | |
| updatedLines.push(line); | |
| } | |
| } | |
| return updatedLines.join('\n'); | |
| } | |
| /** | |
| * @deprecated This function does not actually "shift" captions to the center. | |
| * It strips positioning information from VTT timing lines. | |
| * Use {@link stripVttPositioning} instead. | |
| */ | |
| function shiftVttToCenter(vtt: string): string { | |
| return stripVttPositioning(vtt); | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Tested this on a private instance and can confirm it fixes captions. Built from this branch (commit f8e1699) and deployed as the companion for a private Invidious instance. Captions are fully functional again — tested with dQw4w9WgXcQ and a few more. Before this patch the VTT response was header-only with no cues. After, full subtitle cues are returned. Thanks for the fix! |
Are you sure? I just build the image with the fix right now, but still no subtitles displayed :( |
@fl0wfr |
uGWZzn_GPQs, no english or french subs. I just checked the logs, I can see an error: |
|
For anyone who don't like karaoke-like captions from auto-generated one, you can elimilate word by word time syncing with this tweak on top of this PR, diff --git a/src/lib/helpers/youtubeTranscriptsHandling.ts b/src/lib/helpers/youtubeTranscriptsHandling.ts
index 7dec9d2..6f18409 100644
--- a/src/lib/helpers/youtubeTranscriptsHandling.ts
+++ b/src/lib/helpers/youtubeTranscriptsHandling.ts
@@ -25,6 +25,10 @@ const ESCAPE_SUBSTITUTIONS = {
"\u00A0": " ",
};
+function cleanTimedTextCue(vtt: string): string {
+ return vtt.replace(/<\d{2}:\d{2}:\d{2}.\d{3}><c>([^<]+)<\/c>/g, "$1");
+}
+
function shiftVttToCenter(vtt: string): string {
const lines = vtt.split("\n");
const updatedLines: string[] = [];
@@ -74,6 +78,7 @@ export async function handleTranscripts(
});
}
+ vttText = cleanTimedTextCue(vttText);
vttText = shiftVttToCenter(vttText);
return vttText;Somehow I couldn't see any captions from original YT sites (maybe I'm blocked?) so I don't know if this new behavior is as same as YT ones. Please let me know I should include this in PR. |
Well, I don't understand. I built the image from @shiny-comic fork (branch fix-caption), and now it works. |

This fixes subtitles and transcript not receivable due to updated Youtube API (iv-org/invidious#5571).
This is based on FreeTube's fix for subtitles (FreeTubeApp/FreeTube#7484).
Sorry for my poor English.