feat(api): expose audioTrack on adaptiveFormats

Videos with multi-language audio carry an `audioTrack` object on each audio entry
of `streamingData.adaptiveFormats`, holding the language id (e.g. "en-US.4"), a
human-readable `displayName`, and whether the track is the original audio.

Invidious already parses and uses all three: the DASH manifest route reads
`audioTrack["id"]`, `audioTrack["audioIsDefault"]` and
`audioTrack["displayName"]` to label and order its audio AdaptationSets. But
`/api/v1/videos` drops it, so an API consumer has no way to tell one audio track
from another, and is left scraping it back out of the stream URL query string
(`xtags=acont%3Ddubbed%3Alang%3Dfr`) — undocumented, and silent when it changes.

Emit the same three fields the manifest route already relies on, each only when
present, in the same passthrough style as the neighbouring `colorInfo` and
`captionTrack` fields. Additive and optional throughout: a single-audio video
carries no `audioTrack` at all and so gains no field, which is correct — there is
no second track to distinguish.

AI disclosure, per AI_POLICY.md: this patch was written with AI assistance.
Exact model: Claude Opus 5, model ID `claude-opus-5[1m]`. Tool used to interact
with it: Claude Code, Anthropic's agentic CLI. The change was verified against a
live patched instance (see the PR for the measurements) and is running in
production on the submitter's instance. Submitted by and the responsibility of
@mdbraber.
This commit is contained in:
Maarten den Braber 2026-07-30 15:09:46 +02:00
parent 9d1291a0b8
commit 09fe7557d6

View File

@ -150,6 +150,23 @@ module Invidious::JSONify::APIv1
json.field "audioSampleRate", fmt["audioSampleRate"].as_s.to_i if fmt.has_key?("audioSampleRate")
json.field "audioChannels", fmt["audioChannels"] if fmt.has_key?("audioChannels")
# Multi-language audio. Only present when a video carries more than
# one audio track. The same data already drives the DASH manifest
# (see `Invidious::Routes::API::Manifest`), but was never exposed on
# the API, leaving clients to scrape `xtags` out of the stream URL.
if audio_track = fmt["audioTrack"]?
json.field "audioTrack" do
json.object do
# Language tag with a track discriminator, e.g. "en-US.4".
json.field "id", audio_track["id"] if audio_track["id"]?
# Human-readable label, e.g. "English (original)".
json.field "displayName", audio_track["displayName"] if audio_track["displayName"]?
# True for the video's original (undubbed) audio.
json.field "audioIsDefault", audio_track["audioIsDefault"] if audio_track["audioIsDefault"]?
end
end
end
# Extra misc stuff
json.field "colorInfo", fmt["colorInfo"] if fmt.has_key?("colorInfo")
json.field "captionTrack", fmt["captionTrack"] if fmt.has_key?("captionTrack")