Fix listen mode audio quality labels

This commit is contained in:
Leonardo 2026-06-03 00:04:40 -05:00
parent 0e0ee40cb6
commit 90ac59d381
5 changed files with 48 additions and 7 deletions

View File

@ -1,6 +1,29 @@
require "../spec_helper" require "../spec_helper"
Spectator.describe "Utils" do 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 describe "decode_date" do
it "parses short dates (en-US)" do it "parses short dates (en-US)" do
expect(decode_date("1s ago")).to be_close(Time.utc - 1.second, 500.milliseconds) expect(decode_date("1s ago")).to be_close(Time.utc - 1.second, 500.milliseconds)

View File

@ -20,6 +20,24 @@ def elapsed_text(elapsed)
"#{(millis * 1000).round(2)}µs" "#{(millis * 1000).round(2)}µs"
end 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) def decode_length_seconds(string)
length_seconds = string.gsub(/[^0-9:]/, "") length_seconds = string.gsub(/[^0-9:]/, "")
return 0_i32 if length_seconds.empty? return 0_i32 if length_seconds.empty?

View File

@ -72,15 +72,15 @@ module Invidious::Routes::API::Manifest
lang = audio_track["id"]?.try &.as_s.split('.')[0] || "und" lang = audio_track["id"]?.try &.as_s.split('.')[0] || "und"
is_default = audio_track.has_key?("audioIsDefault") ? audio_track["audioIsDefault"].as_bool : i == 0 is_default = audio_track.has_key?("audioIsDefault") ? audio_track["audioIsDefault"].as_bool : i == 0
displayname = audio_track["displayName"]?.try &.as_s || "Unknown" 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. # 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 # However, most players don't support auto quality switching, so we have to trick them
# into providing a quality selector. # into providing a quality selector.
# See https://github.com/iv-org/invidious/issues/3074 for more details. # 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('"') codecs = fmt["mimeType"].as_s.split("codecs=")[1].strip('"')
bandwidth = fmt["bitrate"].as_i bandwidth = bitrate
itag = fmt["itag"].as_i itag = fmt["itag"].as_i
url = fmt["url"].as_s url = fmt["url"].as_s

View File

@ -166,9 +166,9 @@ module Invidious::Routes::Watch
if params.listen if params.listen
url = audio_streams[0]["url"].as_s 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| 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 url = fmt["url"].as_s
end end
end end

View File

@ -28,12 +28,12 @@
src_url = invidious_companion.public_url.to_s + src_url + src_url = invidious_companion.public_url.to_s + src_url +
"&check=#{invidious_companion_check_id}" if (invidious_companion) "&check=#{invidious_companion_check_id}" if (invidious_companion)
bitrate = fmt["bitrate"] bitrate = fmt["bitrate"].as_i
mimetype = HTML.escape(fmt["mimeType"].as_s) mimetype = HTML.escape(fmt["mimeType"].as_s)
selected = (i == best_m4a_stream_index) selected = (i == best_m4a_stream_index)
%> %>
<source src="<%= src_url %>" type='<%= mimetype %>' label="<%= bitrate %>k" selected="<%= selected %>"> <source src="<%= src_url %>" type='<%= mimetype %>' label="<%= audio_bitrate_label(bitrate) %>" selected="<%= selected %>">
<% if !params.local && !CONFIG.disabled?("local") %> <% if !params.local && !CONFIG.disabled?("local") %>
<source src="<%= src_url %>&local=true" type='<%= mimetype %>' hidequalityoption="true"> <source src="<%= src_url %>&local=true" type='<%= mimetype %>' hidequalityoption="true">
<% end %> <% end %>