Repository: yt-dlp/yt-dlp

Issue:
--load-info-json causes bestvideo to select differently
### 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 a bug unrelated to a specific site
- [X] I've verified that I'm running yt-dlp version **2023.07.06** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
- [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)

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

different formats are selected when --load-info-json is used compared to when the url is supplied.
testing with this video: https://www.youtube.com/watch?v=SQZJHC2AoSg

### Provide verbose output that clearly demonstrates the problem

- [X] Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
- [x] 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', '-f', 'bestvideo[height>=?360][height<=?1440][width<=?2560]', '-o', 'R:/ytdl/%(title)s-%(id)s.%(ext)s', 'https://www.youtube.com/watch?v=SQZJHC2AoSg']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version nightly@2023.08.26.221345 [59e92b1f1] (win_exe)
[debug] Python 3.8.10 (CPython AMD64 64bit) - Windows-10-10.0.22621-SP0 (OpenSSL 1.1.1k  25 Mar 2021)
[debug] exe versions: ffmpeg 6.0-full_build-www.gyan.dev (setts), ffprobe 6.0-full_build-www.gyan.dev
[debug] Optional libraries: Cryptodome-3.18.0, brotli-1.0.9, certifi-2023.07.22, mutagen-1.46.0, sqlite3-2.6.0, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1864 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp-nightly-builds/releases/latest
Available version: nightly@2023.08.26.221345, Current version: nightly@2023.08.26.221345
Current Build Hash: 160ae1891082169f72796f1dea667f192c7945167b1f1fdb9ccfe43f6798e61c
yt-dlp is up to date (nightly@2023.08.26.221345)
[youtube] Extracting URL: https://www.youtube.com/watch?v=SQZJHC2AoSg
[youtube] SQZJHC2AoSg: Downloading webpage
[youtube] SQZJHC2AoSg: Downloading ios player API JSON
[youtube] SQZJHC2AoSg: Downloading android player API JSON
[youtube] SQZJHC2AoSg: Downloading m3u8 information
[debug] Sort order given by extractor: quality, res, fps, hdr:12, source, vcodec:vp9.2, channels, acodec, lang, proto
[debug] Formats sorted by: hasvid, ie_pref, quality, res, fps, hdr:12(7), source, vcodec:vp9.2(10), channels, acodec, lang, proto, size, br, asr, vext, aext, hasaud, id
[info] SQZJHC2AoSg: Downloading 1 format(s): 616
[debug] Invoking hlsnative downloader on "https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1693193298/ei/8r_rZOaxOMaAkwaNtr1o/ip/181.214.153.179/id/4906491c2d80a128/itag/616/source/youtube/requiressl/yes/ratebypass/yes/pfa/1/wft/1/sgovp/clen%3D13951674%3Bdur%3D133.749%3Bgir%3Dyes%3Bitag%3D356%3Blmt%3D1693054993905428/hls_chunk_host/rr1---sn-q4fl6nde.googlevideo.com/mh/hr/mm/31,26/mn/sn-q4fl6nde,sn-a5msener/ms/au,onr/mv/m/mvi/1/pl/24/initcwndbps/1548750/vprv/1/playlist_type/DVR/dover/13/txp/5532434/mt/1693171517/fvip/2/short_key/1/keepalive/yes/fexp/24007246,51000024/beids/24350018/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,wft,sgovp,vprv,playlist_type/sig/AOq0QJ8wRAIgNtfFmhWvNNhyvqtxOgSLZJOQg4ImOQpEVlWrjBowzR0CIBgzHIYwWDUopWDnGXhNgrR0_KwML8iT00pSeZowS3_8/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/AG3C_xAwRQIgbqKXALRTjHx3SeQubM6qY1Wgiht78TmVv5dkjQ_XN_YCIQDhIAEBwXjnWEMmzYkp9imnTxt3v5xsnyws38Gr1P4GJw%3D%3D/playlist/index.m3u8"
[hlsnative] Downloading m3u8 manifest
[hlsnative] Total fragments: 25
[download] Destination: R:\ytdl\Ranni's Quest Part 2 ｜ Elden Ring #58-SQZJHC2AoSg.mp4
[debug] File locking is not supported. Proceeding without locking
[download] 100% of   13.32MiB in 00:00:10 at 1.33MiB/s
[debug] ffprobe command line: ffprobe -hide_banner -show_format -show_streams -print_format json "file:R:\ytdl\Ranni's Quest Part 2 ｜ Elden Ring #58-SQZJHC2AoSg.mp4"

--then, when using json--
yt-dlp --dump-single-json https://www.youtube.com/watch?v=SQZJHC2AoSg | yt-dlp -vU -f "bestvideo[height>=?360][height<=?1440][width<=?2560]" --load-info-json - -o "R:/ytdl/%(title)s-%(id)s.%(ext)s"
--


[debug] Command-line config: ['-vU', '-f', 'bestvideo[height>=?360][height<=?1440][width<=?2560]', '--load-info-json', '-', '-o', 'R:/ytdl/%(title)s-%(id)s.%(ext)s']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version nightly@2023.08.26.221345 [59e92b1f1] (win_exe)
[debug] Python 3.8.10 (CPython AMD64 64bit) - Windows-10-10.0.22621-SP0 (OpenSSL 1.1.1k  25 Mar 2021)
[debug] exe versions: ffmpeg 6.0-full_build-www.gyan.dev (setts), ffprobe 6.0-full_build-www.gyan.dev
[debug] Optional libraries: Cryptodome-3.18.0, brotli-1.0.9, certifi-2023.07.22, mutagen-1.46.0, sqlite3-2.6.0, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1864 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp-nightly-builds/releases/latest
Available version: nightly@2023.08.26.221345, Current version: nightly@2023.08.26.221345
Current Build Hash: 160ae1891082169f72796f1dea667f192c7945167b1f1fdb9ccfe43f6798e61c
yt-dlp is up to date (nightly@2023.08.26.221345)
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, size, br, asr, proto, vext, aext, hasaud, source, id
[info] SQZJHC2AoSg: Downloading 1 format(s): 248
[debug] Invoking http downloader on "https://rr2---sn-q4flrnld.googlevideo.com/videoplayback?expire=1693193425&ei=ccDrZLjEE_6UsfIPpfa90AY&ip=181.214.153.179&id=o-AEIrW_OBEnaE8jjV5aYDohKHIUGC5Bu8D8282hDurcO_&itag=248&source=youtube&requiressl=yes&mh=hr&mm=31%2C29&mn=sn-q4flrnld%2Csn-q4fl6nde&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1548750&vprv=1&svpuc=1&mime=video%2Fwebm&gir=yes&clen=7384177&dur=133.749&lmt=1693066494210518&mt=1693171517&fvip=1&keepalive=yes&fexp=24007246&beids=24350017&c=IOS&txp=5535434&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhALcobSROV1GdBQ4SNAGYU10eywAlnTXuDUaTSQlwzYfoAiA0lHUOuK_A13baz06bnhFtrNJRrdPrgQgxqJAgwjeCtw%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAM-Vu_Ibehe8quEmet9l7LWyYrQfvhS-lYgvSyHhEYLtAiBXTzVsBWjR2YKr51Ig0nYqi7siIcL_d0gsPmXaqhz52w%3D%3D"
[debug] File locking is not supported. Proceeding without locking
[download] Destination: R:\ytdl\Ranni's Quest Part 2 ｜ Elden Ring #58-SQZJHC2AoSg.webm
[download] 100% of    7.04MiB in 00:00:00 at 9.92MiB/s
```



Discussion:
looks like it is not applying the extractor's sort order because it doesn't actually invoke it
maybe a solution could be have the extractors sort order written to the json, though I don't know if that conflicts with the intent of the json
The extractor sort order field (`_format_sort_fields`) is considered a private field and is sanitized/ignored unless you run your command with `--no-clean-info-json` added. I defer to pukkandan for as to whether or not this is the intended behavior
100% sure this is not the intended behavior:

The `_format_sort_fields` field was added when `InfoExtractor._sort_formats()` was deprecated (in 784320c98c2a7e84d72636bc25f6f54c86f5e481, 9f14daf22b4080ae1531a772ee7574959af4e2fa), so that core code could handle format sorting instead of the individual extractors. And then the `_format_sort_fields` field was added to `sanitize_info()` in 08e29b9f1f0b6e5fe1c1e87bf8169bfd7ac91d57.

So this means that the previous behavior was that the formats would be sorted by the extractors, and that sorting would remain after info JSON is written and loaded again. The new behavior has core code sort the formats in `YoutubeDL.process_info()`, which is called *after* the info JSON is loaded, so because `_format_sort_fields` is being sanitized, the sorting is done without knowledge of the extractor's preferred sorting.

The fix is simple:

```diff
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index f10167826..d5c0a2422 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -3550,7 +3550,7 @@ def sanitize_info(info_dict, remove_private_keys=False):
             reject = lambda k, v: v is None or k.startswith('__') or k in {
                 'requested_downloads', 'requested_formats', 'requested_subtitles', 'requested_entries',
                 'entries', 'filepath', '_filename', 'filename', 'infojson_filename', 'original_url',
-                'playlist_autonumber', '_format_sort_fields',
+                'playlist_autonumber',
             }
         else:
             reject = lambda k, v: False
```

Resolving patch:
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -3550,7 +3550,7 @@ def sanitize_info(info_dict, remove_private_keys=False):
             reject = lambda k, v: v is None or k.startswith('__') or k in {
                 'requested_downloads', 'requested_formats', 'requested_subtitles', 'requested_entries',
                 'entries', 'filepath', '_filename', 'filename', 'infojson_filename', 'original_url',
-                'playlist_autonumber', '_format_sort_fields',
+                'playlist_autonumber',
             }
         else:
             reject = lambda k, v: False


Test patch: