Repository: yt-dlp/yt-dlp

Issue:
RedGifs: not all playlist videos are extracted
### 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'm running yt-dlp version **2023.06.21** ([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)
- [X] 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

Austria

### Provide a description that is worded well enough to be understood

In this example, I use this user/playlist URL: https://www.redgifs.com/users/ignored52.

The page says that at the moment there are 74 posts. After scrolling down the page and counting items myself I can confirm that there are exactly 74 items.

Yt-dlp extracts and downloads only 30 of them. From my experience, I can say that the downloaded items number is often a multiple of 30 less than the available items count.

I ran the command with the `-f sd` option to decrease the downloading time. It doesn't affect the playlist itself and the extracted items count.

### 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', 'sd', 'https://www.redgifs.com/users/ignored52']
[debug] Encodings: locale cp1251, fs utf-8, pref cp1251, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version nightly@2023.06.20.042249 [5cc09c004] (zip)
[debug] Python 3.11.2 (CPython AMD64 64bit) - Windows-10-10.0.22621-SP0 (OpenSSL 1.1.1s  1 Nov 2022)
[debug] exe versions: none
[debug] Optional libraries: brotli-1.0.9, certifi-2022.12.07, sqlite3-2.6.0
[debug] Proxy map: {}
[debug] Loaded 1848 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp-nightly-builds/releases/latest
Available version: nightly@2023.06.20.042249, Current version: nightly@2023.06.20.042249
Current Build Hash: 27159a288227f6134b3ef8341d22becdbe9bf33caf4a40fc02f1b2b09236eb7f
yt-dlp is up to date (nightly@2023.06.20.042249)
[RedGifsUser] Extracting URL: https://www.redgifs.com/users/ignored52
[download] Downloading playlist: ignored52
[RedGifsUser] ignored52: Fetching temporary token
[RedGifsUser] ignored52: Downloading JSON metadata page 1
[RedGifsUser] ignored52: Downloading JSON metadata page 2
[RedGifsUser] Playlist ignored52: Downloading 30 items of 30
[download] Downloading item 1 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] rundownslatebluefirebelliedtoad: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/RundownSlateblueFirebelliedtoad-mobile.mp4?expires=1687369800&signature=v2:3bfb2648c37944288c7a9682a8f30f14096fe3c12a9db8b96e2e2523c4ee79ac&for=87.249.133&hash=7011125643"
[debug] File locking is not supported. Proceeding without locking
[download] Destination: Hotwife Kissing Sex [rundownslatebluefirebelliedtoad].mp4
[download] 100% of    4.84MiB in 00:00:06 at 758.96KiB/s
[download] Downloading item 2 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] shamelessdisguisedlovebird: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/ShamelessDisguisedLovebird-mobile.mp4?expires=1687369800&signature=v2:624cd1fc8996d7f2a43a1d497b4b720b5cfb844819f2e13e30f156f8901f081f&for=87.249.133&hash=7011125643"
[download] Destination: Boobs Cleavage Hotwife [shamelessdisguisedlovebird].mp4
[download] 100% of  216.02KiB in 00:00:02 at 90.93KiB/s
[download] Downloading item 3 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] violetimpracticalpeccary: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/VioletImpracticalPeccary-mobile.mp4?expires=1687369800&signature=v2:fc4b286d183134112a25ec8681f209e1e31c1a565efac8c16fe9565fca31d5b7&for=87.249.133&hash=7011125643"
[download] Destination: Boobs Cleavage Hotwife [violetimpracticalpeccary].mp4
[download] 100% of  324.42KiB in 00:00:02 at 132.66KiB/s
[download] Downloading item 4 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] cyanrespectfulpondskater: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/CyanRespectfulPondskater-mobile.mp4?expires=1687369800&signature=v2:77ee9be6fed0ce220e51fdca89b20e14e8e31a3544ea00e149ca818016636379&for=87.249.133&hash=7011125643"
[download] Destination: Boobs Cleavage Hotwife [cyanrespectfulpondskater].mp4
[download] 100% of  277.14KiB in 00:00:03 at 87.23KiB/s
[download] Downloading item 5 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] unusualrustyxinjiangovenator: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/UnusualRustyXinjiangovenator-mobile.mp4?expires=1687369800&signature=v2:cc4e6a8a58e9918e562fc35068db514fa1e6b8ea49e8066fd63da5643b756ef6&for=87.249.133&hash=7011125643"
[download] Destination: Hotwife Kissing Seduction Sex [unusualrustyxinjiangovenator].mp4
[download] 100% of    6.55MiB in 00:00:04 at 1.51MiB/s
[download] Downloading item 6 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] warmheartedlatelynx: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/WarmheartedLateLynx-mobile.mp4?expires=1687369800&signature=v2:6e8981f8420cf773fad6f1d86f07f04b1932dd3c7b7de497e388ed33eb67d3a7&for=87.249.133&hash=7011125643"
[download] Destination: Ass Boobs Seduction [warmheartedlatelynx].mp4
[download] 100% of    2.86MiB in 00:00:03 at 860.61KiB/s
[download] Downloading item 7 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] warpedsinfulgnatcatcher: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/WarpedSinfulGnatcatcher-mobile.mp4?expires=1687369800&signature=v2:ccc4f3e1a56cb163e2551043c3f64854820d06a82dd52420325a8cca1fa18ef0&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Jiggling MILF [warpedsinfulgnatcatcher].mp4
[download] 100% of  476.62KiB in 00:00:02 at 213.76KiB/s
[download] Downloading item 8 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] unknowndistinctbumblebee: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/UnknownDistinctBumblebee-mobile.mp4?expires=1687369800&signature=v2:44caec101ea99f0fc69968917f159483af9d979509801580fccf27e79fb5837a&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Hotwife Nude [unknowndistinctbumblebee].mp4
[download] 100% of    1.85MiB in 00:00:03 at 500.44KiB/s
[download] Downloading item 9 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] shorttermgrowlingasianlion: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/ShorttermGrowlingAsianlion-mobile.mp4?expires=1687369800&signature=v2:bdfd83785f2666822ead98dfc756830c3946b5264b7c2f149273e8d8be38983b&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Kissing Lesbian [shorttermgrowlingasianlion].mp4
[download] 100% of    3.14MiB in 00:00:03 at 977.53KiB/s
[download] Downloading item 10 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] everysevereweasel: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/EverySevereWeasel-mobile.mp4?expires=1687369800&signature=v2:c237ec0f1601bd71cc5d8fa1fb88bfa971e3633573601a4aa5e1fd8b00dd7d9f&for=87.249.133&hash=7011125643"
[download] Destination: Kissing Lesbian Saliva [everysevereweasel].mp4
[download] 100% of  871.57KiB in 00:00:19 at 45.39KiB/s
[download] Downloading item 11 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] knottyunwittingmammoth: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/KnottyUnwittingMammoth-mobile.mp4?expires=1687369800&signature=v2:f3ba301b58d67f1dc06c834fb6d5215c970e7f0f040765286aa8aa13fafd2fcc&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Cleavage [knottyunwittingmammoth].mp4
[download] 100% of  889.96KiB in 00:00:01 at 821.90KiB/s
[download] Downloading item 12 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] aggressiveimperfectjackal: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/AggressiveImperfectJackal-mobile.mp4?expires=1687369800&signature=v2:04b6377142fc501018af8d66933f7449067f2a65127c49c5f2d1861b6f278568&for=87.249.133&hash=7011125643"
[download] Destination: Ass Bollywood Shaking [aggressiveimperfectjackal].mp4
[download] 100% of  372.01KiB in 00:00:02 at 180.12KiB/s
[download] Downloading item 13 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] snappyhumiliatingadouri: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/SnappyHumiliatingAdouri-mobile.mp4?expires=1687369800&signature=v2:1e12cdd5d3527e63caf055a88972e45a8f54cda19e51929172a67259b910de62&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Kissing Sex [snappyhumiliatingadouri].mp4
[download] 100% of    2.69MiB in 00:00:03 at 795.98KiB/s
[download] Downloading item 14 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] wellmadehonorableghostshrimp: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/WellmadeHonorableGhostshrimp-mobile.mp4?expires=1687369800&signature=v2:611106f423a048512e5cf29b7b6730ba1ad23da83c259a34c55b6ffcc8a45a5b&for=87.249.133&hash=7011125643"
[download] Destination: Ass Bollywood Hotwife [wellmadehonorableghostshrimp].mp4
[download] 100% of   97.99KiB in 00:00:01 at 53.31KiB/s
[download] Downloading item 15 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] fatgenerousgreathornedowl: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/FatGenerousGreathornedowl-mobile.mp4?expires=1687369800&signature=v2:af8b7908b846d6da62b3551e18d62617ce5cc00fccceba77326c679e6ad2d469&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Kissing Sex [fatgenerousgreathornedowl].mp4
[download] 100% of    5.95MiB in 00:00:04 at 1.27MiB/s
[download] Downloading item 16 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] thinchubbyafricanclawedfrog: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/ThinChubbyAfricanclawedfrog-mobile.mp4?expires=1687369800&signature=v2:285b106cdec0369dbb03ce13e71f87b1f794ef7b60ded631c6e8ba4212aef263&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Kissing Seduction [thinchubbyafricanclawedfrog].mp4
[download] 100% of    4.17MiB in 00:00:06 at 634.39KiB/s
[download] Downloading item 17 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] swiftlowbetafish: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/SwiftLowBetafish-mobile.mp4?expires=1687369800&signature=v2:1ebe10901a7e076de4c6e6f99f1bc1b2fa0542c2a7a776dd030b9d18cb85d60d&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Dancing Sex [swiftlowbetafish].mp4
[download] 100% of  936.95KiB in 00:00:02 at 386.90KiB/s
[download] Downloading item 18 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] glossylimegreenbrownbear: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/GlossyLimegreenBrownbear-mobile.mp4?expires=1687369800&signature=v2:851cc51f18e2a9a0d69c1612d017c08023486a17375e322cf944d467afc02cda&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Sexy Susi [glossylimegreenbrownbear].mp4
[download] 100% of  486.10KiB in 00:00:03 at 149.12KiB/s
[download] Downloading item 19 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] meagerteeminggenet: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/MeagerTeemingGenet-mobile.mp4?expires=1687369800&signature=v2:a59f55a04bb1d75b800f008c346ae1e9e8af230f16fa27cddc59cfcce57fc511&for=87.249.133&hash=7011125643"
[download] Destination: Ass Bollywood Grabbing Groping Kissing [meagerteeminggenet].mp4
[download] 100% of  680.17KiB in 00:00:02 at 234.80KiB/s
[download] Downloading item 20 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] buzzingolivedrabbeaver: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/BuzzingOlivedrabBeaver-mobile.mp4?expires=1687369800&signature=v2:8064c00e1ebb79f6db6af2f81bf3f3439b007970047ac936ef4d80160afd5b5e&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Hotwife Sex [buzzingolivedrabbeaver].mp4
[download] 100% of  685.84KiB in 00:00:02 at 294.64KiB/s
[download] Downloading item 21 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] papayawhiphiddenmink: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/PapayawhipHiddenMink-mobile.mp4?expires=1687369800&signature=v2:ea90fbb54479cd790a4cf3037829c46cea30445ac16bb7ae814776fbbf42db32&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Kissing Teasing [papayawhiphiddenmink].mp4
[download] 100% of    4.52MiB in 00:00:04 at 1.12MiB/s
[download] Downloading item 22 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] intrepidsnivelinggrouse: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/IntrepidSnivelingGrouse-mobile.mp4?expires=1687369800&signature=v2:c9b2c10a74fba2a4b3dda85a5225b8164734d77e6e77f816fb09e9b7e5e0b833&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Jiggling [intrepidsnivelinggrouse].mp4
[download] 100% of  260.22KiB in 00:00:02 at 121.65KiB/s
[download] Downloading item 23 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] funnyillegalasianpiedstarling: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/FunnyIllegalAsianpiedstarling-mobile.mp4?expires=1687369800&signature=v2:29004cbad688b8e5487688fe5abc8f8c69b6664b01880a1b6aa4b4845be26f76&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Jiggling [funnyillegalasianpiedstarling].mp4
[download] 100% of  230.53KiB in 00:00:02 at 107.27KiB/s
[download] Downloading item 24 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] interestingunwieldyamericanwigeon: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/InterestingUnwieldyAmericanwigeon-mobile.mp4?expires=1687369800&signature=v2:265011fd0f6a20ea212cf25b624eb5d53717e1ad8ebb473ac4852bc44526f154&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Jiggling [interestingunwieldyamericanwigeon].mp4
[download] 100% of  847.57KiB in 00:00:02 at 326.75KiB/s
[download] Downloading item 25 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] hollowhandyizuthrush: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/HollowHandyIzuthrush-mobile.mp4?expires=1687369800&signature=v2:5f287680171fb20c267225132acfe69daec4bc9f272a95e9c2c142db49f7207e&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Jiggling [hollowhandyizuthrush].mp4
[download] 100% of  203.77KiB in 00:00:03 at 57.94KiB/s
[download] Downloading item 26 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] untruetatteredcockroach: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/UntrueTatteredCockroach-mobile.mp4?expires=1687369800&signature=v2:1445b834df7cd7e09d4db8bdd2a083e9f007943300eaef5ef79a9e12598e9226&for=87.249.133&hash=7011125643"
[download] Destination: Ass Bollywood Boobs Chubby [untruetatteredcockroach].mp4
[download] 100% of    2.22MiB in 00:00:02 at 776.81KiB/s
[download] Downloading item 27 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] grizzledphysicalemu: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/GrizzledPhysicalEmu-mobile.mp4?expires=1687369800&signature=v2:c392bb3db0f177bf3d80e089830ada0bb78c0199215bfbcfcc3d1700aa1d72b5&for=87.249.133&hash=7011125643"
[download] Destination: Boobs Hotwife Naked [grizzledphysicalemu].mp4
[download] 100% of    1.68MiB in 00:00:03 at 530.68KiB/s
[download] Downloading item 28 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] saddlebrownstarchyscallop: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/SaddlebrownStarchyScallop-mobile.mp4?expires=1687369800&signature=v2:817691363cc1f8488d67158fb5e4d0f426640a6b04be614f8b8e7420cf69e39e&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Hotwife Sex [saddlebrownstarchyscallop].mp4
[download] 100% of  473.12KiB in 00:00:02 at 202.77KiB/s
[download] Downloading item 29 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] kookyfinishedaplomadofalcon: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/KookyFinishedAplomadofalcon-mobile.mp4?expires=1687369800&signature=v2:6e822e93d6b80f6f9c02a7b3d978ecd0ea31b9c635acd8df8d6e92a820fecc61&for=87.249.133&hash=7011125643"
[download] Destination: Bollywood Boobs Kissing Sex [kookyfinishedaplomadofalcon].mp4
[download] 100% of    1.34MiB in 00:00:02 at 521.01KiB/s
[download] Downloading item 30 of 30
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] wellmadeimprobableseahorse: Downloading 1 format(s): sd
[debug] Invoking http downloader on "https://thumbs44.redgifs.com/WellmadeImprobableSeahorse-mobile.mp4?expires=1687369800&signature=v2:4b824244bed0edb4f7e3fcc0ec0cbd022f08b41151713bda5c90a26f0972a608&for=87.249.133&hash=7011125643"
[download] Destination: Kissing Lesbian Threesome [wellmadeimprobableseahorse].mp4
[download] 100% of    4.34MiB in 00:00:03 at 1.26MiB/s
[download] Finished downloading playlist: ignored52
```


Discussion:

Patch:
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index 3186a999d..3130deda3 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -4381,7 +4381,9 @@ def _write_thumbnails(self, label, info_dict, filename, thumb_filename_base=None
             return None
 
         for idx, t in list(enumerate(thumbnails))[::-1]:
-            thumb_ext = (f'{t["id"]}.' if multiple else '') + determine_ext(t['url'], 'jpg')
+            thumb_ext = t.get('ext') or determine_ext(t['url'], 'jpg')
+            if multiple:
+                thumb_ext = f'{t["id"]}.{thumb_ext}'
             thumb_display_id = f'{label} thumbnail {t["id"]}'
             thumb_filename = replace_extension(filename, thumb_ext, info_dict.get('ext'))
             thumb_filename_final = replace_extension(thumb_filename_base, thumb_ext, info_dict.get('ext'))
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
index 51caefd4d..25a233a2d 100644
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -208,6 +208,10 @@
     BandcampUserIE,
     BandcampWeeklyIE,
 )
+from .bandlab import (
+    BandlabIE,
+    BandlabPlaylistIE,
+)
 from .bannedvideo import BannedVideoIE
 from .bbc import (
     BBCIE,
@@ -1649,6 +1653,7 @@
     RadioKapitalIE,
     RadioKapitalShowIE,
 )
+from .radioradicale import RadioRadicaleIE
 from .radiozet import RadioZetPodcastIE
 from .radlive import (
     RadLiveChannelIE,
diff --git a/yt_dlp/extractor/afreecatv.py b/yt_dlp/extractor/afreecatv.py
index 6682a8981..572d1a389 100644
--- a/yt_dlp/extractor/afreecatv.py
+++ b/yt_dlp/extractor/afreecatv.py
@@ -66,6 +66,14 @@ def _call_api(self, endpoint, display_id, data=None, headers=None, query=None):
             extensions={'legacy_ssl': True}), display_id,
             'Downloading API JSON', 'Unable to download API JSON')
 
+    @staticmethod
+    def _fixup_thumb(thumb_url):
+        if not url_or_none(thumb_url):
+            return None
+        # Core would determine_ext as 'php' from the url, so we need to provide the real ext
+        # See: https://github.com/yt-dlp/yt-dlp/issues/11537
+        return [{'url': thumb_url, 'ext': 'jpg'}]
+
 
 class AfreecaTVIE(AfreecaTVBaseIE):
     IE_NAME = 'soop'
@@ -155,7 +163,7 @@ def _real_extract(self, url):
             'uploader': ('writer_nick', {str}),
             'uploader_id': ('bj_id', {str}),
             'duration': ('total_file_duration', {int_or_none(scale=1000)}),
-            'thumbnail': ('thumb', {url_or_none}),
+            'thumbnails': ('thumb', {self._fixup_thumb}),
         })
 
         entries = []
@@ -226,8 +234,7 @@ def _real_extract(self, url):
 
         return self.playlist_result(self._entries(data), video_id)
 
-    @staticmethod
-    def _entries(data):
+    def _entries(self, data):
         # 'files' is always a list with 1 element
         yield from traverse_obj(data, (
             'data', lambda _, v: v['story_type'] == 'catch',
@@ -238,7 +245,7 @@ def _entries(data):
                 'title': ('title', {str}),
                 'uploader': ('writer_nick', {str}),
                 'uploader_id': ('writer_id', {str}),
-                'thumbnail': ('thumb', {url_or_none}),
+                'thumbnails': ('thumb', {self._fixup_thumb}),
                 'timestamp': ('write_timestamp', {int_or_none}),
             }))
 
diff --git a/yt_dlp/extractor/bandlab.py b/yt_dlp/extractor/bandlab.py
new file mode 100644
index 000000000..e48d5d3f7
--- /dev/null
+++ b/yt_dlp/extractor/bandlab.py
@@ -0,0 +1,438 @@
+
+from .common import InfoExtractor
+from ..utils import (
+    ExtractorError,
+    float_or_none,
+    format_field,
+    int_or_none,
+    parse_iso8601,
+    parse_qs,
+    truncate_string,
+    url_or_none,
+)
+from ..utils.traversal import traverse_obj, value
+
+
+class BandlabBaseIE(InfoExtractor):
+    def _call_api(self, endpoint, asset_id, **kwargs):
+        headers = kwargs.pop('headers', None) or {}
+        return self._download_json(
+            f'https://www.bandlab.com/api/v1.3/{endpoint}/{asset_id}',
+            asset_id, headers={
+                'accept': 'application/json',
+                'referer': 'https://www.bandlab.com/',
+                'x-client-id': 'BandLab-Web',
+                'x-client-version': '10.1.124',
+                **headers,
+            }, **kwargs)
+
+    def _parse_revision(self, revision_data, url=None):
+        return {
+            'vcodec': 'none',
+            'media_type': 'revision',
+            'extractor_key': BandlabIE.ie_key(),
+            'extractor': BandlabIE.IE_NAME,
+            **traverse_obj(revision_data, {
+                'webpage_url': (
+                    'id', ({value(url)}, {format_field(template='https://www.bandlab.com/revision/%s')}), filter, any),
+                'id': (('revisionId', 'id'), {str}, any),
+                'title': ('song', 'name', {str}),
+                'track': ('song', 'name', {str}),
+                'url': ('mixdown', 'file', {url_or_none}),
+                'thumbnail': ('song', 'picture', 'url', {url_or_none}),
+                'description': ('description', {str}),
+                'uploader': ('creator', 'name', {str}),
+                'uploader_id': ('creator', 'username', {str}),
+                'timestamp': ('createdOn', {parse_iso8601}),
+                'duration': ('mixdown', 'duration', {float_or_none}),
+                'view_count': ('counters', 'plays', {int_or_none}),
+                'like_count': ('counters', 'likes', {int_or_none}),
+                'comment_count': ('counters', 'comments', {int_or_none}),
+                'genres': ('genres', ..., 'name', {str}),
+            }),
+        }
+
+    def _parse_track(self, track_data, url=None):
+        return {
+            'vcodec': 'none',
+            'media_type': 'track',
+            'extractor_key': BandlabIE.ie_key(),
+            'extractor': BandlabIE.IE_NAME,
+            **traverse_obj(track_data, {
+                'webpage_url': (
+                    'id', ({value(url)}, {format_field(template='https://www.bandlab.com/post/%s')}), filter, any),
+                'id': (('revisionId', 'id'), {str}, any),
+                'url': ('track', 'sample', 'audioUrl', {url_or_none}),
+                'title': ('track', 'name', {str}),
+                'track': ('track', 'name', {str}),
+                'description': ('caption', {str}),
+                'thumbnail': ('track', 'picture', ('original', 'url'), {url_or_none}, any),
+                'view_count': ('counters', 'plays', {int_or_none}),
+                'like_count': ('counters', 'likes', {int_or_none}),
+                'comment_count': ('counters', 'comments', {int_or_none}),
+                'duration': ('track', 'sample', 'duration', {float_or_none}),
+                'uploader': ('creator', 'name', {str}),
+                'uploader_id': ('creator', 'username', {str}),
+                'timestamp': ('createdOn', {parse_iso8601}),
+            }),
+        }
+
+    def _parse_video(self, video_data, url=None):
+        return {
+            'media_type': 'video',
+            'extractor_key': BandlabIE.ie_key(),
+            'extractor': BandlabIE.IE_NAME,
+            **traverse_obj(video_data, {
+                'id': ('id', {str}),
+                'webpage_url': (
+                    'id', ({value(url)}, {format_field(template='https://www.bandlab.com/post/%s')}), filter, any),
+                'url': ('video', 'url', {url_or_none}),
+                'title': ('caption', {lambda x: x.replace('\n', ' ')}, {truncate_string(left=50)}),
+                'description': ('caption', {str}),
+                'thumbnail': ('video', 'picture', 'url', {url_or_none}),
+                'view_count': ('video', 'counters', 'plays', {int_or_none}),
+                'like_count': ('video', 'counters', 'likes', {int_or_none}),
+                'comment_count': ('counters', 'comments', {int_or_none}),
+                'duration': ('video', 'duration', {float_or_none}),
+                'uploader': ('creator', 'name', {str}),
+                'uploader_id': ('creator', 'username', {str}),
+            }),
+        }
+
+
+class BandlabIE(BandlabBaseIE):
+    _VALID_URL = [
+        r'https?://(?:www\.)?bandlab.com/(?P<url_type>track|post|revision)/(?P<id>[\da-f_-]+)',
+        r'https?://(?:www\.)?bandlab.com/(?P<url_type>embed)/\?(?:[^#]*&)?id=(?P<id>[\da-f-]+)',
+    ]
+    _EMBED_REGEX = [rf'<iframe[^>]+src=[\'"](?P<url>{_VALID_URL[1]})[\'"]']
+    _TESTS = [{
+        'url': 'https://www.bandlab.com/track/04b37e88dba24967b9dac8eb8567ff39_07d7f906fc96ee11b75e000d3a428fff',
+        'md5': '46f7b43367dd268bbcf0bbe466753b2c',
+        'info_dict': {
+            'id': '02d7f906-fc96-ee11-b75e-000d3a428fff',
+            'ext': 'm4a',
+            'uploader_id': 'ender_milze',
+            'track': 'sweet black',
+            'description': 'composed by juanjn3737',
+            'timestamp': 1702171963,
+            'view_count': int,
+            'like_count': int,
+            'duration': 54.629999999999995,
+            'title': 'sweet black',
+            'upload_date': '20231210',
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/songs/fa082beb-b856-4730-9170-a57e4e32cc2c/',
+            'genres': ['Lofi'],
+            'uploader': 'ender milze',
+            'comment_count': int,
+            'media_type': 'revision',
+        },
+    }, {
+        # Same track as above but post URL
+        'url': 'https://www.bandlab.com/post/07d7f906-fc96-ee11-b75e-000d3a428fff',
+        'md5': '46f7b43367dd268bbcf0bbe466753b2c',
+        'info_dict': {
+            'id': '02d7f906-fc96-ee11-b75e-000d3a428fff',
+            'ext': 'm4a',
+            'uploader_id': 'ender_milze',
+            'track': 'sweet black',
+            'description': 'composed by juanjn3737',
+            'timestamp': 1702171973,
+            'view_count': int,
+            'like_count': int,
+            'duration': 54.629999999999995,
+            'title': 'sweet black',
+            'upload_date': '20231210',
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/songs/fa082beb-b856-4730-9170-a57e4e32cc2c/',
+            'genres': ['Lofi'],
+            'uploader': 'ender milze',
+            'comment_count': int,
+            'media_type': 'revision',
+        },
+    }, {
+        # SharedKey Example
+        'url': 'https://www.bandlab.com/track/048916c2-c6da-ee11-85f9-6045bd2e11f9?sharedKey=0NNWX8qYAEmI38lWAzCNDA',
+        'md5': '15174b57c44440e2a2008be9cae00250',
+        'info_dict': {
+            'id': '038916c2-c6da-ee11-85f9-6045bd2e11f9',
+            'ext': 'm4a',
+            'comment_count': int,
+            'genres': ['Other'],
+            'uploader_id': 'user8353034818103753',
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/songs/51b18363-da23-4b9b-a29c-2933a3e561ca/',
+            'timestamp': 1709625771,
+            'track': 'PodcastMaerchen4b',
+            'duration': 468.14,
+            'view_count': int,
+            'description': 'Podcast: Neues aus der Märchenwelt',
+            'like_count': int,
+            'upload_date': '20240305',
+            'uploader': 'Erna Wageneder',
+            'title': 'PodcastMaerchen4b',
+            'media_type': 'revision',
+        },
+    }, {
+        # Different Revision selected
+        'url': 'https://www.bandlab.com/track/130343fc-148b-ea11-96d2-0003ffd1fc09?revId=110343fc-148b-ea11-96d2-0003ffd1fc09',
+        'md5': '74e055ef9325d63f37088772fbfe4454',
+        'info_dict': {
+            'id': '110343fc-148b-ea11-96d2-0003ffd1fc09',
+            'ext': 'm4a',
+            'timestamp': 1588273294,
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/users/b612e533-e4f7-4542-9f50-3fcfd8dd822c/',
+            'description': 'Final Revision.',
+            'title': 'Replay ( Instrumental)',
+            'uploader': 'David R Sparks',
+            'uploader_id': 'davesnothome69',
+            'view_count': int,
+            'comment_count': int,
+            'track': 'Replay ( Instrumental)',
+            'genres': ['Rock'],
+            'upload_date': '20200430',
+            'like_count': int,
+            'duration': 279.43,
+            'media_type': 'revision',
+        },
+    }, {
+        # Video
+        'url': 'https://www.bandlab.com/post/5cdf9036-3857-ef11-991a-6045bd36e0d9',
+        'md5': '8caa2ef28e86c1dacf167293cfdbeba9',
+        'info_dict': {
+            'id': '5cdf9036-3857-ef11-991a-6045bd36e0d9',
+            'ext': 'mp4',
+            'duration': 44.705,
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/videos/67c6cef1-cef6-40d3-831e-a55bc1dcb972/',
+            'comment_count': int,
+            'title': 'backing vocals',
+            'uploader_id': 'marliashya',
+            'uploader': 'auraa',
+            'like_count': int,
+            'description': 'backing vocals',
+            'media_type': 'video',
+        },
+    }, {
+        # Embed Example
+        'url': 'https://www.bandlab.com/embed/?blur=false&id=014de0a4-7d82-ea11-a94c-0003ffd19c0f',
+        'md5': 'a4ad05cb68c54faaed9b0a8453a8cf4a',
+        'info_dict': {
+            'id': '014de0a4-7d82-ea11-a94c-0003ffd19c0f',
+            'ext': 'm4a',
+            'comment_count': int,
+            'genres': ['Electronic'],
+            'uploader': 'Charlie Henson',
+            'timestamp': 1587328674,
+            'upload_date': '20200419',
+            'view_count': int,
+            'track': 'Positronic Meltdown',
+            'duration': 318.55,
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/songs/87165bc3-5439-496e-b1f7-a9f13b541ff2/',
+            'description': 'Checkout my tracks at AOMX http://aomxsounds.com/',
+            'uploader_id': 'microfreaks',
+            'title': 'Positronic Meltdown',
+            'like_count': int,
+            'media_type': 'revision',
+        },
+    }, {
+        # Track without revisions available
+        'url': 'https://www.bandlab.com/track/55767ac51789ea11a94c0003ffd1fc09_2f007b0a37b94ec7a69bc25ae15108a5',
+        'md5': 'f05d68a3769952c2d9257c473e14c15f',
+        'info_dict': {
+            'id': '55767ac51789ea11a94c0003ffd1fc09_2f007b0a37b94ec7a69bc25ae15108a5',
+            'ext': 'm4a',
+            'track': 'insame',
+            'like_count': int,
+            'duration': 84.03,
+            'title': 'insame',
+            'view_count': int,
+            'comment_count': int,
+            'uploader': 'Sorakime',
+            'uploader_id': 'sorakime',
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/users/572a351a-0f3a-4c6a-ac39-1a5defdeeb1c/',
+            'timestamp': 1691162128,
+            'upload_date': '20230804',
+            'media_type': 'track',
+        },
+    }, {
+        'url': 'https://www.bandlab.com/revision/014de0a4-7d82-ea11-a94c-0003ffd19c0f',
+        'only_matching': True,
+    }]
+    _WEBPAGE_TESTS = [{
+        'url': 'https://phantomluigi.github.io/',
+        'info_dict': {
+            'id': 'e14223c3-7871-ef11-bdfd-000d3a980db3',
+            'ext': 'm4a',
+            'view_count': int,
+            'upload_date': '20240913',
+            'uploader_id': 'phantommusicofficial',
+            'timestamp': 1726194897,
+            'uploader': 'Phantom',
+            'comment_count': int,
+            'genres': ['Progresive Rock'],
+            'description': 'md5:a38cd668f7a2843295ef284114f18429',
+            'duration': 225.23,
+            'like_count': int,
+            'title': 'Vermilion Pt. 2 (Cover)',
+            'track': 'Vermilion Pt. 2 (Cover)',
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/songs/62b10750-7aef-4f42-ad08-1af52f577e97/',
+            'media_type': 'revision',
+        },
+    }]
+
+    def _real_extract(self, url):
+        display_id, url_type = self._match_valid_url(url).group('id', 'url_type')
+
+        qs = parse_qs(url)
+        revision_id = traverse_obj(qs, (('revId', 'id'), 0, any))
+        if url_type == 'revision':
+            revision_id = display_id
+
+        revision_data = None
+        if not revision_id:
+            post_data = self._call_api(
+                'posts', display_id, note='Downloading post data',
+                query=traverse_obj(qs, {'sharedKey': ('sharedKey', 0)}))
+
+            revision_id = traverse_obj(post_data, (('revisionId', ('revision', 'id')), {str}, any))
+            revision_data = traverse_obj(post_data, ('revision', {dict}))
+
+            if not revision_data and not revision_id:
+                post_type = post_data.get('type')
+                if post_type == 'Video':
+                    return self._parse_video(post_data, url=url)
+                if post_type == 'Track':
+                    return self._parse_track(post_data, url=url)
+                raise ExtractorError(f'Could not extract data for post type {post_type!r}')
+
+        if not revision_data:
+            revision_data = self._call_api(
+                'revisions', revision_id, note='Downloading revision data', query={'edit': 'false'})
+
+        return self._parse_revision(revision_data, url=url)
+
+
+class BandlabPlaylistIE(BandlabBaseIE):
+    _VALID_URL = [
+        r'https?://(?:www\.)?bandlab.com/(?:[\w]+/)?(?P<type>albums|collections)/(?P<id>[\da-f-]+)',
+        r'https?://(?:www\.)?bandlab.com/(?P<type>embed)/collection/\?(?:[^#]*&)?id=(?P<id>[\da-f-]+)',
+    ]
+    _EMBED_REGEX = [rf'<iframe[^>]+src=[\'"](?P<url>{_VALID_URL[1]})[\'"]']
+    _TESTS = [{
+        'url': 'https://www.bandlab.com/davesnothome69/albums/89b79ea6-de42-ed11-b495-00224845aac7',
+        'info_dict': {
+            'thumbnail': 'https://bl-prod-images.azureedge.net/v1.3/albums/69507ff3-579a-45be-afca-9e87eddec944/',
+            'release_date': '20221003',
+            'title': 'Remnants',
+            'album': 'Remnants',
+            'like_count': int,
+            'album_type': 'LP',
+            'description': 'A collection of some feel good, rock hits.',
+            'comment_count': int,
+            'view_count': int,
+            'id': '89b79ea6-de42-ed11-b495-00224845aac7',
+            'uploader': 'David R Sparks',
+            'uploader_id': 'davesnothome69',
+        },
+        'playlist_count': 10,
+    }, {
+        'url': 'https://www.bandlab.com/slytheband/collections/955102d4-1040-ef11-86c3-000d3a42581b',
+        'info_dict': {
+            'id': '955102d4-1040-ef11-86c3-000d3a42581b',
+            'timestamp': 1720762659,
+            'view_count': int,
+            'title': 'My Shit 🖤',
+            'uploader_id': 'slytheband',
+            'uploader': '𝓢𝓛𝓨',
+            'upload_date': '20240712',
+            'like_count': int,
+            'thumbnail': 'https://bandlabimages.azureedge.net/v1.0/collections/2c64ca12-b180-4b76-8587-7a8da76bddc8/',
+        },
+        'playlist_count': 15,
+    }, {
+        # Embeds can contain both albums and collections with the same URL pattern. This is an album
+        'url': 'https://www.bandlab.com/embed/collection/?id=12cc6f7f-951b-ee11-907c-00224844f303',
+        'info_dict': {
+            'id': '12cc6f7f-951b-ee11-907c-00224844f303',
+            'release_date': '20230706',
+            'description': 'This is a collection of songs I created when I had an Amiga computer.',
+            'view_count': int,
+            'title': 'Mark Salud The Amiga Collection',
+            'uploader_id': 'mssirmooth1962',
+            'comment_count': int,
+            'thumbnail': 'https://bl-prod-images.azureedge.net/v1.3/albums/d618bd7b-0537-40d5-bdd8-61b066e77d59/',
+            'like_count': int,
+            'uploader': 'Mark Salud',
+            'album': 'Mark Salud The Amiga Collection',
+            'album_type': 'LP',
+        },
+        'playlist_count': 24,
+    }, {
+        # Tracks without revision id
+        'url': 'https://www.bandlab.com/embed/collection/?id=e98aafb5-d932-ee11-b8f0-00224844c719',
+        'info_dict': {
+            'like_count': int,
+            'uploader_id': 'sorakime',
+            'comment_count': int,
+            'uploader': 'Sorakime',
+            'view_count': int,
+            'description': 'md5:4ec31c568a5f5a5a2b17572ea64c3825',
+            'release_date': '20230812',
+            'title': 'Art',
+            'album': 'Art',
+            'album_type': 'Album',
+            'id': 'e98aafb5-d932-ee11-b8f0-00224844c719',
+            'thumbnail': 'https://bl-prod-images.azureedge.net/v1.3/albums/20c890de-e94a-4422-828a-2da6377a13c8/',
+        },
+        'playlist_count': 13,
+    }, {
+        'url': 'https://www.bandlab.com/albums/89b79ea6-de42-ed11-b495-00224845aac7',
+        'only_matching': True,
+    }]
+
+    def _entries(self, album_data):
+        for post in traverse_obj(album_data, ('posts', lambda _, v: v['type'])):
+            post_type = post['type']
+            if post_type == 'Revision':
+                yield self._parse_revision(post.get('revision'))
+            elif post_type == 'Track':
+                yield self._parse_track(post)
+            elif post_type == 'Video':
+                yield self._parse_video(post)
+            else:
+                self.report_warning(f'Skipping unknown post type: "{post_type}"')
+
+    def _real_extract(self, url):
+        playlist_id, playlist_type = self._match_valid_url(url).group('id', 'type')
+
+        endpoints = {
+            'albums': ['albums'],
+            'collections': ['collections'],
+            'embed': ['collections', 'albums'],
+        }.get(playlist_type)
+        for endpoint in endpoints:
+            playlist_data = self._call_api(
+                endpoint, playlist_id, note=f'Downloading {endpoint[:-1]} data',
+                fatal=False, expected_status=404)
+            if not playlist_data.get('errorCode'):
+                playlist_type = endpoint
+                break
+        if error_code := playlist_data.get('errorCode'):
+            raise ExtractorError(f'Could not find playlist data. Error code: "{error_code}"')
+
+        return self.playlist_result(
+            self._entries(playlist_data), playlist_id,
+            **traverse_obj(playlist_data, {
+                'title': ('name', {str}),
+                'description': ('description', {str}),
+                'uploader': ('creator', 'name', {str}),
+                'uploader_id': ('creator', 'username', {str}),
+                'timestamp': ('createdOn', {parse_iso8601}),
+                'release_date': ('releaseDate', {lambda x: x.replace('-', '')}, filter),
+                'thumbnail': ('picture', ('original', 'url'), {url_or_none}, any),
+                'like_count': ('counters', 'likes', {int_or_none}),
+                'comment_count': ('counters', 'comments', {int_or_none}),
+                'view_count': ('counters', 'plays', {int_or_none}),
+            }),
+            **(traverse_obj(playlist_data, {
+                'album': ('name', {str}),
+                'album_type': ('type', {str}),
+            }) if playlist_type == 'albums' else {}))
diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py
index 01915acf2..23f6fc6c4 100644
--- a/yt_dlp/extractor/common.py
+++ b/yt_dlp/extractor/common.py
@@ -279,6 +279,7 @@ class InfoExtractor:
     thumbnails:     A list of dictionaries, with the following entries:
                         * "id" (optional, string) - Thumbnail format ID
                         * "url"
+                        * "ext" (optional, string) - actual image extension if not given in URL
                         * "preference" (optional, int) - quality of the image
                         * "width" (optional, int)
                         * "height" (optional, int)
diff --git a/yt_dlp/extractor/radioradicale.py b/yt_dlp/extractor/radioradicale.py
new file mode 100644
index 000000000..472e25c45
--- /dev/null
+++ b/yt_dlp/extractor/radioradicale.py
@@ -0,0 +1,105 @@
+from .common import InfoExtractor
+from ..utils import url_or_none
+from ..utils.traversal import traverse_obj
+
+
+class RadioRadicaleIE(InfoExtractor):
+    _VALID_URL = r'https?://(?:www\.)?radioradicale\.it/scheda/(?P<id>[0-9]+)'
+    _TESTS = [{
+        'url': 'https://www.radioradicale.it/scheda/471591',
+        'md5': 'eb0fbe43a601f1a361cbd00f3c45af4a',
+        'info_dict': {
+            'id': '471591',
+            'ext': 'mp4',
+            'title': 'md5:e8fbb8de57011a3255db0beca69af73d',
+            'description': 'md5:5e15a789a2fe4d67da8d1366996e89ef',
+            'location': 'Napoli',
+            'duration': 2852.0,
+            'timestamp': 1459987200,
+            'upload_date': '20160407',
+            'thumbnail': 'https://www.radioradicale.it/photo400/0/0/9/0/1/00901768.jpg',
+        },
+    }, {
+        'url': 'https://www.radioradicale.it/scheda/742783/parlamento-riunito-in-seduta-comune-11a-della-xix-legislatura',
+        'info_dict': {
+            'id': '742783',
+            'title': 'Parlamento riunito in seduta comune (11ª della XIX legislatura)',
+            'description': '-) Votazione per l\'elezione di un giudice della Corte Costituzionale (nono scrutinio)',
+            'location': 'CAMERA',
+            'duration': 5868.0,
+            'timestamp': 1730246400,
+            'upload_date': '20241030',
+        },
+        'playlist': [{
+            'md5': 'aa48de55dcc45478e4cd200f299aab7d',
+            'info_dict': {
+                'id': '742783-0',
+                'ext': 'mp4',
+                'title': 'Parlamento riunito in seduta comune (11ª della XIX legislatura)',
+            },
+        }, {
+            'md5': 'be915c189c70ad2920e5810f32260ff5',
+            'info_dict': {
+                'id': '742783-1',
+                'ext': 'mp4',
+                'title': 'Parlamento riunito in seduta comune (11ª della XIX legislatura)',
+            },
+        }, {
+            'md5': 'f0ee4047342baf8ed3128a8417ac5e0a',
+            'info_dict': {
+                'id': '742783-2',
+                'ext': 'mp4',
+                'title': 'Parlamento riunito in seduta comune (11ª della XIX legislatura)',
+            },
+        }],
+    }]
+
+    def _entries(self, videos_info, page_id):
+        for idx, video in enumerate(traverse_obj(
+                videos_info, ('playlist', lambda _, v: v['sources']))):
+            video_id = f'{page_id}-{idx}'
+            formats = []
+            subtitles = {}
+
+            for m3u8_url in traverse_obj(video, ('sources', ..., 'src', {url_or_none})):
+                fmts, subs = self._extract_m3u8_formats_and_subtitles(m3u8_url, video_id)
+                formats.extend(fmts)
+                self._merge_subtitles(subs, target=subtitles)
+            for sub in traverse_obj(video, ('subtitles', ..., lambda _, v: url_or_none(v['src']))):
+                self._merge_subtitles({sub.get('srclang') or 'und': [{
+                    'url': sub['src'],
+                    'name': sub.get('label'),
+                }]}, target=subtitles)
+
+            yield {
+                'id': video_id,
+                'title': video.get('title'),
+                'formats': formats,
+                'subtitles': subtitles,
+            }
+
+    def _real_extract(self, url):
+        page_id = self._match_id(url)
+        webpage = self._download_webpage(url, page_id)
+
+        videos_info = self._search_json(
+            r'jQuery\.extend\(Drupal\.settings\s*,',
+            webpage, 'videos_info', page_id)['RRscheda']
+
+        entries = list(self._entries(videos_info, page_id))
+
+        common_info = {
+            'id': page_id,
+            'title': self._og_search_title(webpage),
+            'description': self._og_search_description(webpage),
+            'location': videos_info.get('luogo'),
+            **self._search_json_ld(webpage, page_id),
+        }
+
+        if len(entries) == 1:
+            return {
+                **entries[0],
+                **common_info,
+            }
+
+        return self.playlist_result(entries, multi_video=True, **common_info)
diff --git a/yt_dlp/extractor/redgifs.py b/yt_dlp/extractor/redgifs.py
index 50138ab12..b11ea273d 100644
--- a/yt_dlp/extractor/redgifs.py
+++ b/yt_dlp/extractor/redgifs.py
@@ -213,7 +213,7 @@ def _real_extract(self, url):
 class RedGifsUserIE(RedGifsBaseInfoExtractor):
     IE_DESC = 'Redgifs user'
     _VALID_URL = r'https?://(?:www\.)?redgifs\.com/users/(?P<username>[^/?#]+)(?:\?(?P<query>[^#]+))?'
-    _PAGE_SIZE = 30
+    _PAGE_SIZE = 80
     _TESTS = [
         {
             'url': 'https://www.redgifs.com/users/lamsinka89',
@@ -222,7 +222,7 @@ class RedGifsUserIE(RedGifsBaseInfoExtractor):
                 'title': 'lamsinka89',
                 'description': 'RedGifs user lamsinka89, ordered by recent',
             },
-            'playlist_mincount': 100,
+            'playlist_mincount': 391,
         },
         {
             'url': 'https://www.redgifs.com/users/lamsinka89?page=3',
@@ -231,7 +231,7 @@ class RedGifsUserIE(RedGifsBaseInfoExtractor):
                 'title': 'lamsinka89',
                 'description': 'RedGifs user lamsinka89, ordered by recent',
             },
-            'playlist_count': 30,
+            'playlist_count': 80,
         },
         {
             'url': 'https://www.redgifs.com/users/lamsinka89?order=best&type=g',
@@ -240,7 +240,17 @@ class RedGifsUserIE(RedGifsBaseInfoExtractor):
                 'title': 'lamsinka89',
                 'description': 'RedGifs user lamsinka89, ordered by best',
             },
-            'playlist_mincount': 100,
+            'playlist_mincount': 391,
+        },
+        {
+            'url': 'https://www.redgifs.com/users/ignored52',
+            'note': 'https://github.com/yt-dlp/yt-dlp/issues/7382',
+            'info_dict': {
+                'id': 'ignored52',
+                'title': 'ignored52',
+                'description': 'RedGifs user ignored52, ordered by recent',
+            },
+            'playlist_mincount': 121,
         },
     ]
 
diff --git a/yt_dlp/extractor/spankbang.py b/yt_dlp/extractor/spankbang.py
index 6805a72de..05f0bb146 100644
--- a/yt_dlp/extractor/spankbang.py
+++ b/yt_dlp/extractor/spankbang.py
@@ -71,9 +71,11 @@ class SpankBangIE(InfoExtractor):
     def _real_extract(self, url):
         mobj = self._match_valid_url(url)
         video_id = mobj.group('id') or mobj.group('id_2')
+        country = self.get_param('geo_bypass_country') or 'US'
+        self._set_cookie('.spankbang.com', 'country', country.upper())
         webpage = self._download_webpage(
             url.replace(f'/{video_id}/embed', f'/{video_id}/video'),
-            video_id, headers={'Cookie': 'country=US'})
+            video_id, impersonate=True)
 
         if re.search(r'<[^>]+\b(?:id|class)=["\']video_removed', webpage):
             raise ExtractorError(
diff --git a/yt_dlp/utils/_utils.py b/yt_dlp/utils/_utils.py
index b28bb555e..89c53c39e 100644
--- a/yt_dlp/utils/_utils.py
+++ b/yt_dlp/utils/_utils.py
@@ -216,7 +216,7 @@ def partial_application(func):
     sig = inspect.signature(func)
     required_args = [
         param.name for param in sig.parameters.values()
-        if param.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.VAR_POSITIONAL)
+        if param.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)
         if param.default is inspect.Parameter.empty
     ]
 
@@ -4837,7 +4837,6 @@ def number_of_digits(number):
     return len('%d' % number)
 
 
-@partial_application
 def join_nonempty(*values, delim='-', from_dict=None):
     if from_dict is not None:
         values = (traversal.traverse_obj(from_dict, variadic(v)) for v in values)
diff --git a/yt_dlp/utils/traversal.py b/yt_dlp/utils/traversal.py
index 361f239ba..76b51f53d 100644
--- a/yt_dlp/utils/traversal.py
+++ b/yt_dlp/utils/traversal.py
@@ -332,14 +332,14 @@ class _RequiredError(ExtractorError):
 
 
 @typing.overload
-def subs_list_to_dict(*, ext: str | None = None) -> collections.abc.Callable[[list[dict]], dict[str, list[dict]]]: ...
+def subs_list_to_dict(*, lang: str | None = 'und', ext: str | None = None) -> collections.abc.Callable[[list[dict]], dict[str, list[dict]]]: ...
 
 
 @typing.overload
-def subs_list_to_dict(subs: list[dict] | None, /, *, ext: str | None = None) -> dict[str, list[dict]]: ...
+def subs_list_to_dict(subs: list[dict] | None, /, *, lang: str | None = 'und', ext: str | None = None) -> dict[str, list[dict]]: ...
 
 
-def subs_list_to_dict(subs: list[dict] | None = None, /, *, ext=None):
+def subs_list_to_dict(subs: list[dict] | None = None, /, *, lang='und', ext=None):
     """
     Convert subtitles from a traversal into a subtitle dict.
     The path should have an `all` immediately before this function.
@@ -352,7 +352,7 @@ def subs_list_to_dict(subs: list[dict] | None = None, /, *, ext=None):
     `quality`  The sort order for each subtitle
     """
     if subs is None:
-        return functools.partial(subs_list_to_dict, ext=ext)
+        return functools.partial(subs_list_to_dict, lang=lang, ext=ext)
 
     result = collections.defaultdict(list)
 
@@ -360,10 +360,16 @@ def subs_list_to_dict(subs: list[dict] | None = None, /, *, ext=None):
         if not url_or_none(sub.get('url')) and not sub.get('data'):
             continue
         sub_id = sub.pop('id', None)
-        if sub_id is None:
-            continue
-        if ext is not None and not sub.get('ext'):
-            sub['ext'] = ext
+        if not isinstance(sub_id, str):
+            if not lang:
+                continue
+            sub_id = lang
+        sub_ext = sub.get('ext')
+        if not isinstance(sub_ext, str):
+            if not ext:
+                sub.pop('ext', None)
+            else:
+                sub['ext'] = ext
         result[sub_id].append(sub)
     result = dict(result)
 
@@ -452,9 +458,9 @@ def trim(s):
     return trim
 
 
-def unpack(func):
+def unpack(func, **kwargs):
     @functools.wraps(func)
-    def inner(items, **kwargs):
+    def inner(items):
         return func(*items, **kwargs)
 
     return inner


Test patch:
diff --git a/test/test_traversal.py b/test/test_traversal.py
index d48606e99..bc433029d 100644
--- a/test/test_traversal.py
+++ b/test/test_traversal.py
@@ -481,7 +481,7 @@ def test_subs_list_to_dict(self):
             'id': 'name',
             'data': 'content',
             'url': 'url',
-        }, all, {subs_list_to_dict}]) == {
+        }, all, {subs_list_to_dict(lang=None)}]) == {
             'de': [{'url': 'https://example.com/subs/de.ass'}],
             'en': [{'data': 'content'}],
         }, 'subs with mandatory items missing should be filtered'
@@ -507,6 +507,54 @@ def test_subs_list_to_dict(self):
             {'url': 'https://example.com/subs/en1', 'ext': 'ext'},
             {'url': 'https://example.com/subs/en2', 'ext': 'ext'},
         ]}, '`quality` key should sort subtitle list accordingly'
+        assert traverse_obj([
+            {'name': 'de', 'url': 'https://example.com/subs/de.ass'},
+            {'name': 'de'},
+            {'name': 'en', 'content': 'content'},
+            {'url': 'https://example.com/subs/en'},
+        ], [..., {
+            'id': 'name',
+            'url': 'url',
+            'data': 'content',
+        }, all, {subs_list_to_dict(lang='en')}]) == {
+            'de': [{'url': 'https://example.com/subs/de.ass'}],
+            'en': [
+                {'data': 'content'},
+                {'url': 'https://example.com/subs/en'},
+            ],
+        }, 'optionally provided lang should be used if no id available'
+        assert traverse_obj([
+            {'name': 1, 'url': 'https://example.com/subs/de1'},
+            {'name': {}, 'url': 'https://example.com/subs/de2'},
+            {'name': 'de', 'ext': 1, 'url': 'https://example.com/subs/de3'},
+            {'name': 'de', 'ext': {}, 'url': 'https://example.com/subs/de4'},
+        ], [..., {
+            'id': 'name',
+            'url': 'url',
+            'ext': 'ext',
+        }, all, {subs_list_to_dict(lang=None)}]) == {
+            'de': [
+                {'url': 'https://example.com/subs/de3'},
+                {'url': 'https://example.com/subs/de4'},
+            ],
+        }, 'non str types should be ignored for id and ext'
+        assert traverse_obj([
+            {'name': 1, 'url': 'https://example.com/subs/de1'},
+            {'name': {}, 'url': 'https://example.com/subs/de2'},
+            {'name': 'de', 'ext': 1, 'url': 'https://example.com/subs/de3'},
+            {'name': 'de', 'ext': {}, 'url': 'https://example.com/subs/de4'},
+        ], [..., {
+            'id': 'name',
+            'url': 'url',
+            'ext': 'ext',
+        }, all, {subs_list_to_dict(lang='de')}]) == {
+            'de': [
+                {'url': 'https://example.com/subs/de1'},
+                {'url': 'https://example.com/subs/de2'},
+                {'url': 'https://example.com/subs/de3'},
+                {'url': 'https://example.com/subs/de4'},
+            ],
+        }, 'non str types should be replaced by default id'
 
     def test_trim_str(self):
         with pytest.raises(TypeError):
@@ -525,7 +573,7 @@ def test_trim_str(self):
     def test_unpack(self):
         assert unpack(lambda *x: ''.join(map(str, x)))([1, 2, 3]) == '123'
         assert unpack(join_nonempty)([1, 2, 3]) == '1-2-3'
-        assert unpack(join_nonempty(delim=' '))([1, 2, 3]) == '1 2 3'
+        assert unpack(join_nonempty, delim=' ')([1, 2, 3]) == '1 2 3'
         with pytest.raises(TypeError):
             unpack(join_nonempty)()
         with pytest.raises(TypeError):
diff --git a/test/test_utils.py b/test/test_utils.py
index b5f35736b..835774a91 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -72,7 +72,6 @@
     intlist_to_bytes,
     iri_to_uri,
     is_html,
-    join_nonempty,
     js_to_json,
     limit_length,
     locked_file,
@@ -2158,10 +2157,6 @@ def test_partial_application(self):
         assert int_or_none(v=10) == 10, 'keyword passed positional should call function'
         assert int_or_none(scale=0.1)(10) == 100, 'call after partial application should call the function'
 
-        assert callable(join_nonempty(delim=', ')), 'varargs positional should apply partially'
-        assert callable(join_nonempty()), 'varargs positional should apply partially'
-        assert join_nonempty(None, delim=', ') == '', 'passed varargs should call the function'
-
 
 if __name__ == '__main__':
     unittest.main()