Repository: yt-dlp/yt-dlp

Issue:
[TenPlay] downloading a series instead of an individual show from 10play.com.au
### 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 asking a question and **not** reporting a bug or requesting a feature
- [X] I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
- [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 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 questions **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)

### Please make sure the question is worded well enough to be understood

I want to download a season of a TV show using yt-dlp.  Currently I have it working on a show-by-show basis, but this is tedious, so I wondered if it is possible to point at a "parent" and have it download all the episodes in a series?  Specifically, I am looking to do this from 10play.

For example, the show Survivor (you will need to be in AU to access these links):  https://10play.com.au/australian-survivor
if I go to each episode's page & copy the masterManifest link from video downloadhelper, I can then use yt-dlp to download that episode.  However I have to do that for every episode.  

Does anyone know if there is a way to download all episodes at a time?


Thank you - I appreciate any help!

### Provide verbose output that clearly demonstrates the problem

- [ ] 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
- [ ] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

_No response_


Discussion:
> : [10play.com.au/australian-survivor](https://10play.com.au/australian-survivor)

Have you tried passing this URL? Show verbose
Thank you for the reply – here is what I got when trying that:
yt-dlp https://10play.com.au/australian-survivor --verbose
[debug] Command-line config: ['https://10play.com.au/australian-survivor', '--verbose']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version ***@***.******@***.***> [b532a3481] (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 N-111661-g567e78b283-20230731 (setts), ffprobe N-111661-g567e78b283-20230731
[debug] Optional libraries: Cryptodome-3.18.0, brotli-1.0.9, certifi-2023.05.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1855 extractors
[generic] Extracting URL: https://10play.com.au/australian-survivor
[generic] australian-survivor: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] australian-survivor: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://10play.com.au/australian-survivor
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1560, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1688, in __extract_info
  File "yt_dlp\extractor\common.py", line 710, in extract
  File "yt_dlp\extractor\generic.py", line 2568, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://10play.com.au/australian-survivor

So I also tried 1 level deeper on the url…
yt-dlp https://10play.com.au/australian-survivor/episodes --verbose
[debug] Command-line config: ['https://10play.com.au/australian-survivor/episodes', '--verbose']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version ***@***.******@***.***> [b532a3481] (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 N-111661-g567e78b283-20230731 (setts), ffprobe N-111661-g567e78b283-20230731
[debug] Optional libraries: Cryptodome-3.18.0, brotli-1.0.9, certifi-2023.05.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1855 extractors
[generic] Extracting URL: https://10play.com.au/australian-survivor/episodes
[generic] episodes: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] episodes: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://10play.com.au/australian-survivor/episodes
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1560, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1688, in __extract_info
  File "yt_dlp\extractor\common.py", line 710, in extract
  File "yt_dlp\extractor\generic.py", line 2568, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://10play.com.au/australian-survivor/episodes

and then one more level: (same result tho…)
yt-dlp https://10play.com.au/australian-survivor/episodes/season-5-all-stars --verbose
[debug] Command-line config: ['https://10play.com.au/australian-survivor/episodes/season-5-all-stars', '--verbose']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version ***@***.******@***.***> [b532a3481] (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 N-111661-g567e78b283-20230731 (setts), ffprobe N-111661-g567e78b283-20230731
[debug] Optional libraries: Cryptodome-3.18.0, brotli-1.0.9, certifi-2023.05.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1855 extractors
[generic] Extracting URL: https://10play.com.au/australian-survivor/episodes/season-5-all-stars
[generic] season-5-all-stars: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] season-5-all-stars: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://10play.com.au/australian-survivor/episodes/season-5-all-stars
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1560, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1688, in __extract_info
  File "yt_dlp\extractor\common.py", line 710, in extract
  File "yt_dlp\extractor\generic.py", line 2568, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://10play.com.au/australian-survivor/episodes/season-5-all-stars


are those helpful at all?  I can access those URL’s from a browser with no issues


-randy

From: pukkandan ***@***.***>
Sent: Tuesday, August 1, 2023 7:17 AM
To: yt-dlp/yt-dlp ***@***.***>
Cc: ***@***.***; Author ***@***.***>
Subject: Re: [yt-dlp/yt-dlp] downloading a series instead of an individual show (Issue #7744)


: 10play.com.au/australian-survivor<https://10play.com.au/australian-survivor>

Have you tried passing this URL? Show verbose

—
Reply to this email directly, view it on GitHub<https://github.com/yt-dlp/yt-dlp/issues/7744#issuecomment-1660424175>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AHERI7L2MNVEFEIIOI4V3X3XTEFWJANCNFSM6AAAAAA272VBCU>.
You are receiving this because you authored the thread.Message ID: ***@***.******@***.***>>

There's an upstream patch implementing this in youtube-dl, that should be trivial to bring over. It also allows grabbing auth cookies which is neat.

https://github.com/ytdl-org/youtube-dl/pull/31892/commits/8b11dbba0051da0c5f22576a2593b15abe6a2deb

Resolving patch:
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -1957,7 +1957,10 @@
     WeTvSeriesIE,
 )
 from .tennistv import TennisTVIE
-from .tenplay import TenPlayIE
+from .tenplay import (
+    TenPlayIE,
+    TenPlaySeasonIE,
+)
 from .testurl import TestURLIE
 from .tf1 import TF1IE
 from .tfo import TFOIE
diff --git a/yt_dlp/extractor/tenplay.py b/yt_dlp/extractor/tenplay.py
--- a/yt_dlp/extractor/tenplay.py
+++ b/yt_dlp/extractor/tenplay.py
@@ -1,9 +1,11 @@
-from datetime import datetime
 import base64
+import functools
+import itertools
+from datetime import datetime
 
 from .common import InfoExtractor
 from ..networking import HEADRequest
-from ..utils import int_or_none, urlencode_postdata
+from ..utils import int_or_none, traverse_obj, urlencode_postdata, urljoin
 
 
 class TenPlayIE(InfoExtractor):
@@ -113,3 +115,55 @@ def _real_extract(self, url):
             'uploader': 'Channel 10',
             'uploader_id': '2199827728001',
         }
+
+
+class TenPlaySeasonIE(InfoExtractor):
+    _VALID_URL = r'https?://(?:www\.)?10play\.com\.au/(?P<show>[^/?#]+)/episodes/(?P<season>[^/?#]+)/?(?:$|[?#])'
+    _TESTS = [{
+        'url': 'https://10play.com.au/masterchef/episodes/season-14',
+        'info_dict': {
+            'title': 'Season 14',
+            'id': 'MjMyOTIy',
+        },
+        'playlist_mincount': 64,
+    }, {
+        'url': 'https://10play.com.au/the-bold-and-the-beautiful-fast-tracked/episodes/season-2022',
+        'info_dict': {
+            'title': 'Season 2022',
+            'id': 'Mjc0OTIw',
+        },
+        'playlist_mincount': 256,
+    }]
+
+    def _entries(self, load_more_url, display_id=None):
+        skip_ids = []
+        for page in itertools.count(1):
+            episodes_carousel = self._download_json(
+                load_more_url, display_id, query={'skipIds[]': skip_ids},
+                note=f'Fetching episodes page {page}')
+
+            episodes_chunk = episodes_carousel['items']
+            skip_ids.extend(ep['id'] for ep in episodes_chunk)
+
+            for ep in episodes_chunk:
+                yield ep['cardLink']
+            if not episodes_carousel['hasMore']:
+                break
+
+    def _real_extract(self, url):
+        show, season = self._match_valid_url(url).group('show', 'season')
+        season_info = self._download_json(
+            f'https://10play.com.au/api/shows/{show}/episodes/{season}', f'{show}/{season}')
+
+        episodes_carousel = traverse_obj(season_info, (
+            'content', 0, 'components', (
+                lambda _, v: v['title'].lower() == 'episodes',
+                (..., {dict}),
+            )), get_all=False) or {}
+
+        playlist_id = episodes_carousel['tpId']
+
+        return self.playlist_from_matches(
+            self._entries(urljoin(url, episodes_carousel['loadMoreUrl']), playlist_id),
+            playlist_id, traverse_obj(season_info, ('content', 0, 'title', {str})),
+            getter=functools.partial(urljoin, url))


Test patch: