From 90ac59d38107f71c3e0fadaf8dbc2a54c08ef0d2 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 3 Jun 2026 00:04:40 -0500 Subject: [PATCH] Fix listen mode audio quality labels --- spec/invidious/utils_spec.cr | 23 +++++++++++++++++++++++ src/invidious/helpers/utils.cr | 18 ++++++++++++++++++ src/invidious/routes/api/manifest.cr | 6 +++--- src/invidious/routes/watch.cr | 4 ++-- src/invidious/views/components/player.ecr | 4 ++-- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/spec/invidious/utils_spec.cr b/spec/invidious/utils_spec.cr index 7c2c2711..299a9860 100644 --- a/spec/invidious/utils_spec.cr +++ b/spec/invidious/utils_spec.cr @@ -1,6 +1,29 @@ require "../spec_helper" Spectator.describe "Utils" do + describe "audio_bitrate_label" do + it "formats audio bitrates as kbps labels" do + expect(audio_bitrate_label(128000)).to eq("128 kbps") + expect(audio_bitrate_label(129003)).to eq("129 kbps") + end + end + + describe "audio_quality_label_to_bitrate" do + it "parses the current kbps label format" do + expect(audio_quality_label_to_bitrate("128 kbps")).to eq(128000) + end + + it "keeps legacy k-suffixed labels compatible" do + expect(audio_quality_label_to_bitrate("128k")).to eq(128000) + expect(audio_quality_label_to_bitrate("128000k")).to eq(128000) + end + + it "ignores unrelated quality labels" do + expect(audio_quality_label_to_bitrate("medium")).to be_nil + expect(audio_quality_label_to_bitrate("dash")).to be_nil + end + end + describe "decode_date" do it "parses short dates (en-US)" do expect(decode_date("1s ago")).to be_close(Time.utc - 1.second, 500.milliseconds) diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 7a262f84..f8e0c6f0 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -20,6 +20,24 @@ def elapsed_text(elapsed) "#{(millis * 1000).round(2)}µs" end +def audio_bitrate_kbps(bitrate : Int) : Int32 + (bitrate.to_f / 1000).round.to_i +end + +def audio_bitrate_label(bitrate : Int) : String + "#{audio_bitrate_kbps(bitrate)} kbps" +end + +def audio_quality_label_to_bitrate(quality : String) : Int32? + match = quality.strip.downcase.match(/^(\d+)\s*(?:k|kbps)$/) + return nil unless match + + bitrate = match[1].to_i + bitrate *= 1000 if bitrate < 1000 + + bitrate +end + def decode_length_seconds(string) length_seconds = string.gsub(/[^0-9:]/, "") return 0_i32 if length_seconds.empty? diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index c27caad7..04fb8372 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -72,15 +72,15 @@ module Invidious::Routes::API::Manifest lang = audio_track["id"]?.try &.as_s.split('.')[0] || "und" is_default = audio_track.has_key?("audioIsDefault") ? audio_track["audioIsDefault"].as_bool : i == 0 displayname = audio_track["displayName"]?.try &.as_s || "Unknown" - bitrate = fmt["bitrate"] + bitrate = fmt["bitrate"].as_i # Different representations of the same audio should be groupped into one AdaptationSet. # However, most players don't support auto quality switching, so we have to trick them # into providing a quality selector. # See https://github.com/iv-org/invidious/issues/3074 for more details. - xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: "#{displayname} [#{bitrate}k]", lang: lang) do + xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: "#{displayname} [#{audio_bitrate_label(bitrate)}]", lang: lang) do codecs = fmt["mimeType"].as_s.split("codecs=")[1].strip('"') - bandwidth = fmt["bitrate"].as_i + bandwidth = bitrate itag = fmt["itag"].as_i url = fmt["url"].as_s diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr index 7a68a145..de51e118 100644 --- a/src/invidious/routes/watch.cr +++ b/src/invidious/routes/watch.cr @@ -166,9 +166,9 @@ module Invidious::Routes::Watch if params.listen url = audio_streams[0]["url"].as_s - if params.quality.ends_with? "k" + if bitrate = audio_quality_label_to_bitrate(params.quality) audio_streams.each do |fmt| - if fmt["bitrate"].as_i == params.quality.rchop("k").to_i + if fmt["bitrate"].as_i == bitrate url = fmt["url"].as_s end end diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index fbd472e0..8f1f545b 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -28,12 +28,12 @@ src_url = invidious_companion.public_url.to_s + src_url + "&check=#{invidious_companion_check_id}" if (invidious_companion) - bitrate = fmt["bitrate"] + bitrate = fmt["bitrate"].as_i mimetype = HTML.escape(fmt["mimeType"].as_s) selected = (i == best_m4a_stream_index) %> - + <% if !params.local && !CONFIG.disabled?("local") %> <% end %>