Skip to content

fix(audio): prioritize highest quality audio codec over numerical bitrate#3809

Open
kairosci wants to merge 4 commits into
MetrolistGroup:mainfrom
kairosci:feat/fix-highest-audio-quality
Open

fix(audio): prioritize highest quality audio codec over numerical bitrate#3809
kairosci wants to merge 4 commits into
MetrolistGroup:mainfrom
kairosci:feat/fix-highest-audio-quality

Conversation

@kairosci
Copy link
Copy Markdown
Contributor

@kairosci kairosci commented May 25, 2026

Problem

"High" or "Highest possible" audio quality selection didn't always choose the true highest quality audio codec (e.g. itag 774 for premium users), and sometimes downgraded due to slight VBR bitrate fluctuations crossing the hardcoded target bitrate limits.

Cause

  1. The previous itagRanking list completely omitted the recently introduced itag 774 (Opus 256kbps), falling back to lower quality codecs.
  2. The selection algorithm used strict bitrate limits (e.g., <= 256000). Since YouTube streams can have slightly variable bitrates (like 256008), this caused valid high-quality formats to be incorrectly filtered out, forcing a fallback to 160kbps (itag 251) on "High" settings.

Solution

  • Added itag 774 (Opus 256kbps) as the highest priority format in the itagRanking list.
  • Added a tolerance margin to the targetBitrate calculation (e.g., 260000.0 instead of 256000.0) to safely catch VBR streams that slightly exceed the nominal bitrate, preventing unintended quality downgrades.

Testing

  • Successfully compiled assembleFossDebug.
  • Verified that itag 774 is correctly prioritized and selected when using "Highest possible" and "High" qualities.

Related Issues

Summary by CodeRabbit

  • Refactor
    • Improved audio format selection to use a ranked preference list, yielding more consistent choices based on requested quality and network metering.
    • Retains previous fallback logic to pick the closest available format when preferred choices aren’t available, improving playback reliability across networks.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 127f5fd7-61d9-47fa-a081-b2e135c23d74

📥 Commits

Reviewing files that changed from the base of the PR and between 2835bee and e0aa857.

📒 Files selected for processing (1)
  • app/src/main/kotlin/com/metrolist/music/utils/YTPlayerUtils.kt

📝 Walkthrough

Walkthrough

YTPlayerUtils.findFormat now prefers a ranked itag list (scanned from a startItag derived from AudioQuality and network metered state) to pick an exact audio format; if none match, it falls back to the existing bitrate/target-based selection.

Changes

Audio format selection strategy

Layer / File(s) Summary
Ranked itag selection and fallback logic
app/src/main/kotlin/com/metrolist/music/utils/YTPlayerUtils.kt
Adds a private itagRanking and reworks findFormat to compute available audio itags, derive a startItag from AudioQuality and metered-network state (with AUTO varying by metered), scan itagRanking from that start to select the first matching itag and return/log that format; if no ranked itag matches, continues into the existing bitrate/targetBitrate-based fallback logic.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • MetrolistGroup/Metrolist#3549: Both PRs modify YTPlayerUtils.findFormat's audio format selection logic—this PR adds ordered itagRanking/startItag scanning while #3549 adjusts deterministic itag/bitrate choices.
  • MetrolistGroup/Metrolist#3543: Related changes to how AudioQuality is interpreted (enum/ordering) which can affect startItag derivation used by this PR.

Suggested reviewers

  • nyxiereal
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: prioritizing highest quality audio codec over numeric bitrate in the audio format selection logic.
Description check ✅ Passed The description comprehensively covers the problem, cause, solution, testing, and related issues following the template structure.
Linked Issues check ✅ Passed The changes address all coding requirements from #3808: adding itag 774 as highest priority and implementing bitrate tolerance to prevent quality downgrades.
Out of Scope Changes check ✅ Passed All changes are scoped to YTPlayerUtils.findFormat audio selection logic, directly addressing the linked issue requirements with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kairosci kairosci marked this pull request as draft May 25, 2026 21:16
@kairosci kairosci force-pushed the feat/fix-highest-audio-quality branch 5 times, most recently from 54d4658 to 7ac88b8 Compare May 25, 2026 21:33
@kairosci kairosci force-pushed the feat/fix-highest-audio-quality branch from 7ac88b8 to 933a3f1 Compare May 25, 2026 21:34
@Bec-de-Xorbin
Copy link
Copy Markdown

Bec-de-Xorbin commented May 26, 2026

I tried the PR 3809 build, and VERY_HIGH ("Highest possible") uses itag 140 on every track on this album (https://music.youtube.com/playlist?list=OLAK5uy_mqKanBXpCEbjgm8W3MU8ZCtPQzYn_LaVs) but one (https://music.youtube.com/watch?v=Ikmdr_TNrbQ) which uses itag 251.

With HIGH it's completely opposite, it uses itag 251 on every track on this album (https://music.youtube.com/playlist?list=OLAK5uy_mqKanBXpCEbjgm8W3MU8ZCtPQzYn_LaVs) but one (https://music.youtube.com/watch?v=Ikmdr_TNrbQ) which uses itag 140.

So I'm struggling to see any logic here...

I'd still sort them all by itag and forget the bitrate sort completely so the logic would be predictable and clean. Something like this:

ITAGS_FROM_BEST_TO_WORST = [
    774, // WebM | Opus     | ~256 Kbps (Premium)
    141, // MP4  | AAC (LC) | 256 Kbps (Premium)
    251, // WebM | Opus     | ~128 Kbps
    140, // MP4  | AAC (LC) | 128 Kbps
    250, // WebM | Opus     | ~70 Kbps
    249, // WebM | Opus     | ~50 Kbps
    139, // MP4  | AAC (HE) | 48 Kbps
    600, // WebM | Opus     | ~35 Kbps
    599  // MP4  | AAC (HE) | 30 Kbps
]

VERY_HIGH: start the search from 774 and select the highest available itag from the list
HIGH: from 251 ...
LOW: from 250 ...
AUTO: if metered start from 250, otherwise 774 ...

…ection

Replace the bitrate-sorting approach with a fixed itag priority ranking
(774, 141, 251, 140, 250, 249, 139, 600, 599) that selects the highest
available codec from the appropriate starting point per quality level.

- VERY_HIGH: start from 774
- HIGH: start from 251
- LOW: start from 250
- AUTO (metered): start from 250, else 774

This ensures predictable codec-aware selection unaffected by VBR
bitrate fluctuations.
@kairosci
Copy link
Copy Markdown
Contributor Author

@Bec-de-Xorbin thx for test; I believe I fixed, could you pls retest?

@Bec-de-Xorbin
Copy link
Copy Markdown

@Bec-de-Xorbin thx for test; I believe I fixed, could you pls retest?

Works as expected, thank you!

@kairosci kairosci marked this pull request as ready for review May 26, 2026 18:32
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/kotlin/com/metrolist/music/utils/YTPlayerUtils.kt (1)

437-449: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reintroduce a non-ranked fallback when no ranked itag matches.

If selectedItag is null, Line 449 returns null immediately, even when audioCapableFormats is non-empty. That can unnecessarily fail selection for valid streams whose itags are not in itagRanking (and this propagates to playback/download format choice).

Suggested patch
-        val format = selectedItag?.let { tag -> audioCapableFormats.first { it.itag == tag } }
+        val format = selectedItag?.let { tag -> audioCapableFormats.first { it.itag == tag } }
 
-        if (format != null) {
+        if (format != null) {
             Timber.tag(logTag).d("Selected format: itag=${format.itag}, mimeType=${format.mimeType}, bitrate=${format.bitrate}, audioQuality label: ${format.audioQuality}")
-        } else {
-            Timber.tag(logTag).d("No suitable audio format found")
+            return format
         }
 
-        return format
+        // Fallback: preserve resiliency when ranking does not cover available itags.
+        val fallback = when (audioQuality) {
+            AudioQuality.VERY_HIGH -> audioCapableFormats.maxByOrNull { it.bitrate }
+            AudioQuality.HIGH -> audioCapableFormats.maxByOrNull { it.bitrate }
+            AudioQuality.LOW -> audioCapableFormats.minByOrNull { it.bitrate }
+            AudioQuality.AUTO -> {
+                if (connectivityManager.isActiveNetworkMetered) {
+                    audioCapableFormats.minByOrNull { it.bitrate }
+                } else {
+                    audioCapableFormats.maxByOrNull { it.bitrate }
+                }
+            }
+        }
+        if (fallback != null) {
+            Timber.tag(logTag).d("Selected fallback format: itag=${fallback.itag}, bitrate=${fallback.bitrate}")
+        } else {
+            Timber.tag(logTag).d("No suitable audio format found")
+        }
+        return fallback
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/kotlin/com/metrolist/music/utils/YTPlayerUtils.kt` around lines
437 - 449, The code currently returns null if selectedItag is not found in
itagRanking, causing missed valid formats; modify the selection logic around
selectedItag and format so that when selectedItag is null but
audioCapableFormats is non-empty you fall back to a non-ranked choice (e.g.,
pick audioCapableFormats.maxByOrNull { it.bitrate } or the first element)
instead of returning null—update the block that computes format (and the
following Timber logging) to use this fallback so a valid Format is returned
when available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/src/main/kotlin/com/metrolist/music/utils/YTPlayerUtils.kt`:
- Around line 437-449: The code currently returns null if selectedItag is not
found in itagRanking, causing missed valid formats; modify the selection logic
around selectedItag and format so that when selectedItag is null but
audioCapableFormats is non-empty you fall back to a non-ranked choice (e.g.,
pick audioCapableFormats.maxByOrNull { it.bitrate } or the first element)
instead of returning null—update the block that computes format (and the
following Timber logging) to use this fallback so a valid Format is returned
when available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6673eb88-bcfe-4e89-83fd-3c0cfceaa71c

📥 Commits

Reviewing files that changed from the base of the PR and between 8ed9c56 and 2835bee.

📒 Files selected for processing (1)
  • app/src/main/kotlin/com/metrolist/music/utils/YTPlayerUtils.kt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Highest Quality Preference Not Respected

2 participants