Repository: yt-dlp/yt-dlp

Issue:
twitter: not best audio
### DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

- [X] I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field

### Checklist

- [X] I'm reporting that yt-dlp is broken on a **supported** site
- [X] I've verified that I have **updated yt-dlp to nightly or master** ([update instructions](https://github.com/yt-dlp/yt-dlp#update-channels))
- [X] I've checked that all provided URLs are playable in a browser with the same IP and same login details
- [X] I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#video-url-contains-an-ampersand--and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
- [X] I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
- [ ] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required

### Region

Switzerland

### Provide a description that is worded well enough to be understood

Twitter: yt-dlp doesn't download the audio with the highest bitrate by default.

```
yt-dlp --list-formats "https://twitter.com/TuckerCarlson/status/1754939251257475555"
[twitter] Extracting URL: https://twitter.com/TuckerCarlson/status/1754939251257475555
[twitter] 1754939251257475555: Downloading guest token
[twitter] 1754939251257475555: Downloading GraphQL JSON
[twitter] 1754939251257475555: Downloading m3u8 information
[info] Available formats for 1754840391583494144:
ID                     EXT RESOLUTION │   FILESIZE    TBR PROTO │ VCODEC       VBR ACODEC     MORE INFO
───────────────────────────────────────────────────────────────────────────────────────────────────────
hls-audio-128000-Audio mp4 audio only │                   m3u8  │ audio only       unknown    Audio
hls-audio-32000-Audio  mp4 audio only │                   m3u8  │ audio only       unknown    Audio
hls-audio-64000-Audio  mp4 audio only │                   m3u8  │ audio only       unknown    Audio
http-288               mp4 480x270    │ ≈  9.44MiB   288k https │ unknown          unknown
hls-173                mp4 480x270    │ ~  5.68MiB   173k m3u8  │ avc1.4D4015 173k video only
http-832               mp4 640x360    │ ≈ 27.26MiB   832k https │ unknown          unknown
hls-261                mp4 640x360    │ ~  8.58MiB   262k m3u8  │ avc1.4D401E 262k video only
http-2176              mp4 1280x720   │ ≈ 71.29MiB  2176k https │ unknown          unknown
hls-507                mp4 1280x720   │ ~ 16.64MiB   508k m3u8  │ avc1.64001F 508k video only
http-10368             mp4 1920x1080  │ ≈339.70MiB 10368k https │ unknown          unknown
hls-900                mp4 1920x1080  │ ~ 29.51MiB   901k m3u8  │ avc1.640032 901k video only
```

```
yt-dlp --skip-download "https://twitter.com/TuckerCarlson/status/1754939251257475555"
[twitter] Extracting URL: https://twitter.com/TuckerCarlson/status/1754939251257475555
[twitter] 1754939251257475555: Downloading guest token
[twitter] 1754939251257475555: Downloading GraphQL JSON
[twitter] 1754939251257475555: Downloading m3u8 information
[info] 1754840391583494144: Downloading 1 format(s): hls-900+hls-audio-64000-Audio

```


### Provide verbose output that clearly demonstrates the problem

- [X] Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
- [ ] If using API, add `'verbose': True` to `YoutubeDL` params instead
- [X] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

```shell
[debug] Command-line config: ['-vU']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version stable@2023.12.30 from yt-dlp/yt-dlp [f10589e34]
[debug] Python 3.11.6 (CPython x86_64 64bit) - Linux-6.6.9-1-default-x86_64-with-glibc2.38 (OpenSSL 3.1.4 24 Oct 2023, glibc 2.38)
[debug] exe versions: ffmpeg 5.1.4 (fdk,setts), ffprobe 5.1.4
[debug] Optional libraries: brotli-1.1.0, certifi-2023.11.17, requests-2.31.0, sqlite3-3.44.2, urllib3-2.1.0
[debug] Proxy map: {}
[debug] Request Handlers: urllib, requests
[debug] Loaded 1798 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: stable@2023.12.30 from yt-dlp/yt-dlp
yt-dlp is up to date (stable@2023.12.30 from yt-dlp/yt-dlp)
```



Discussion:
this is what twitter is serving for HLS now
```
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:NAME="Audio",TYPE=AUDIO,GROUP-ID="audio-32000",AUTOSELECT=YES,URI="/amplify_video/1754840391583494144/pl/mp4a/32000/KEDS4gVuUtFCTVG6.m3u8?container=cmaf"
#EXT-X-MEDIA:NAME="Audio",TYPE=AUDIO,GROUP-ID="audio-64000",AUTOSELECT=YES,URI="/amplify_video/1754840391583494144/pl/mp4a/64000/8sBYvnMjSzmlCDC0.m3u8?container=cmaf"
#EXT-X-MEDIA:NAME="Audio",TYPE=AUDIO,GROUP-ID="audio-128000",AUTOSELECT=YES,URI="/amplify_video/1754840391583494144/pl/mp4a/128000/zQPJkAye2dD_7B51.m3u8?container=cmaf"

#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=173477,BANDWIDTH=187931,RESOLUTION=480x270,CODECS="mp4a.40.2,avc1.4D4015",AUDIO="audio-32000"
/amplify_video/1754840391583494144/pl/avc1/480x270/mp4a/32000/Bu3l3eup0SaY4mXG.m3u8?container=cmaf
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=261905,BANDWIDTH=306980,RESOLUTION=640x360,CODECS="mp4a.40.2,avc1.4D401E",AUDIO="audio-64000"
/amplify_video/1754840391583494144/pl/avc1/640x360/mp4a/64000/iq9PXFpXz3QFUuOV.m3u8?container=cmaf
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=507946,BANDWIDTH=642234,RESOLUTION=1280x720,CODECS="mp4a.40.2,avc1.64001F",AUDIO="audio-128000"
/amplify_video/1754840391583494144/pl/avc1/1280x720/mp4a/128000/e1kcntl3sJ03MO6f.m3u8?container=cmaf
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=900602,BANDWIDTH=1146489,RESOLUTION=1920x1080,CODECS="mp4a.40.2,avc1.640032",AUDIO="audio-128000"
/amplify_video/1754840391583494144/pl/avc1/1920x1080/mp4a/128000/wEsbJzleHFj1e_QA.m3u8?container=cmaf
```

we could do this
```diff
diff --git a/yt_dlp/extractor/twitter.py b/yt_dlp/extractor/twitter.py
index c3a6e406c..786b95468 100644
--- a/yt_dlp/extractor/twitter.py
+++ b/yt_dlp/extractor/twitter.py
@@ -100,9 +100,13 @@ def _extract_variant_formats(self, variant, video_id):
         if not variant_url:
             return [], {}
         elif '.m3u8' in variant_url:
-            return self._extract_m3u8_formats_and_subtitles(
+            fmts, subs = self._extract_m3u8_formats_and_subtitles(
                 variant_url, video_id, 'mp4', 'm3u8_native',
                 m3u8_id='hls', fatal=False)
+            for f in traverse_obj(fmts, lambda _, v: v['vcodec'] == 'none' and v.get('tbr') is None):
+                if mobj := re.match(r'hls-audio-(?P<bitrate>\d+)', f['format_id']):
+                    f['tbr'] = int_or_none(mobj.group('bitrate'), 1000)
+            return fmts, subs
         else:
             tbr = int_or_none(dict_get(variant, ('bitrate', 'bit_rate')), 1000) or None
             f = {
```

Resolving patch:
diff --git a/yt_dlp/extractor/twitter.py b/yt_dlp/extractor/twitter.py
--- a/yt_dlp/extractor/twitter.py
+++ b/yt_dlp/extractor/twitter.py
@@ -100,9 +100,13 @@ def _extract_variant_formats(self, variant, video_id):
         if not variant_url:
             return [], {}
         elif '.m3u8' in variant_url:
-            return self._extract_m3u8_formats_and_subtitles(
+            fmts, subs = self._extract_m3u8_formats_and_subtitles(
                 variant_url, video_id, 'mp4', 'm3u8_native',
                 m3u8_id='hls', fatal=False)
+            for f in traverse_obj(fmts, lambda _, v: v['vcodec'] == 'none' and v.get('tbr') is None):
+                if mobj := re.match(r'hls-[Aa]udio-(?P<bitrate>\d{4,})', f['format_id']):
+                    f['tbr'] = int_or_none(mobj.group('bitrate'), 1000)
+            return fmts, subs
         else:
             tbr = int_or_none(dict_get(variant, ('bitrate', 'bit_rate')), 1000) or None
             f = {


Test patch: