Repository: yt-dlp/yt-dlp

Issue:
Eleven Sports Support
### 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 new site support request
- [X] I've verified that I'm running yt-dlp version **2023.03.04** ([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 none of provided URLs [violate any copyrights](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
- [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 am willing to share it if required

### Region

USA

### Example URLs

https://elevensports.com/view/event/clf46yr3kenn80jgrqsjmwefk
https://elevensports.com/view/event/clg194tweum9o0jgriaujon6e
https://elevensports.com/view/event/clg2gp2uouybs0jgro7zqf67q

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

Trying to download VODs of streams from the site elevensports.com .

Generic downloader does seems to find some kind of embedded video but once it gets to that page it fails from there. 

Any help as to where it is going wrong or if I'm missing anything would be greatly appreciated. Not really sure where it went wrong. If the generic downloader does work then great, but if anyone has time to make one specifically for this site that would be greatly appreciated.

All videos end the same way with the verbose output posted below.

As far as I know, you don't need an account to view any video on the site and it is a global site, but I am in USA. There is/are ad(s) shown on preroll if that helps.

### 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', 'https://elevensports.com/en/view/event/clfik0imy8sn90je14pxe0ypc']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version stable@2023.03.04 [392389b7d] (win_exe)
[debug] Python 3.8.10 (CPython AMD64 64bit) - Windows-10-10.0.19045-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.17, brotli-1.0.9, certifi-2022.12.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-10.4
[debug] Proxy map: {}
[debug] Loaded 1786 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Available version: stable@2023.03.04, Current version: stable@2023.03.04
Current Build Hash: 5590c57bd0433ed239a2deaaf92e2ad6f37fe50f53664c821575cafe106a9421
yt-dlp is up to date (stable@2023.03.04)
[generic] Extracting URL: https://elevensports.com/en/view/event/clfik0imy8sn90je14pxe0ypc
[generic] clfik0imy8sn90je14pxe0ypc: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] clfik0imy8sn90je14pxe0ypc: Extracting information
[debug] Looking for embeds
[debug] Identified a twitter:player iframe
[generic] Extracting URL: https://elevensports.com/embed/11484?id=clfik0imy8sn90je14pxe0ypc
[generic] 11484?id=clfik0imy8sn90je14pxe0ypc: Downloading webpage
[redirect] Following redirect to https://elevensports.com/en/embed/11484?id=clfik0imy8sn90je14pxe0ypc
[generic] Extracting URL: https://elevensports.com/en/embed/11484?id=clfik0imy8sn90je14pxe0ypc
[generic] 11484?id=clfik0imy8sn90je14pxe0ypc: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] 11484?id=clfik0imy8sn90je14pxe0ypc: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://elevensports.com/en/embed/11484?id=clfik0imy8sn90je14pxe0ypc
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1518, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1594, in __extract_info
  File "yt_dlp\extractor\common.py", line 694, in extract
  File "yt_dlp\extractor\generic.py", line 2510, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://elevensports.com/en/embed/11484?id=clfik0imy8sn90je14pxe0ypc
```



Discussion:
The first event has a webpage with Next.js hydration JSON whose `.props.pageProps.event.streamUrl` is a manifest URL. However the URL gives "401 Signature is invalid" even with a `--referer`.
Yep I see the .m3u8 playlist now. I get the same thing as you now.

Does that mean it's not likely something can be done about it?

Thanks for trying anyways if so!


> Does that mean it's not likely something can be done about it?

I think the m3u8 request requires additional http headers (maybe `Authorization`?). You can look at the network requests in your browser's devtools to if you want to try to recreate the browser's m3u8 request with yt-dlp. (e.g. `yt-dlp --add-header "Authorization: Bearer ..."`) A dedicated extractor would probably be able to handle this
Would you look at that! Found the entry for `master.m3u8` mentioned in the `streamUrl` in the chrome dev tools under network.

It uses a `sig=XXXXXXX` parameter when it accesses it. 

example URL that I passed in to yt-dlp. Worked perfectly!

https://europe-west-hls.mls.mycujoo.tv/esgp/clf46yr6w0w7k0107s1pc224t/master.m3u8?sig=uLMdx2kwLRKtL6Puu1_5lPG6xJI

I'm guessing that signature is generated in some way. Should be able to make a dedicated extractor with that, yeah?

I'm not a web guy, I do embedded C++ work mostly but I might take a crack at this unless someone else can do it way faster.
There are two possibilities: either the sig parameter is computed using some JS that is in or referenced from the page, or there is some request-response that provides the value. The former is more likely since the plain manifest is already in the page.

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
@@ -541,6 +541,7 @@
 from .eighttracks import EightTracksIE
 from .einthusan import EinthusanIE
 from .eitb import EitbIE
+from .elevensports import ElevenSportsIE
 from .ellentube import (
     EllenTubeIE,
     EllenTubeVideoIE,
diff --git a/yt_dlp/extractor/elevensports.py b/yt_dlp/extractor/elevensports.py
new file mode 100644
--- /dev/null
+++ b/yt_dlp/extractor/elevensports.py
@@ -0,0 +1,59 @@
+from .common import InfoExtractor
+from ..utils import (
+    parse_iso8601,
+    traverse_obj,
+    url_or_none,
+)
+
+
+class ElevenSportsIE(InfoExtractor):
+    _VALID_URL = r'https?://(?:www\.)?elevensports\.com/view/event/(?P<id>\w+)'
+    _TESTS = [{
+        'url': 'https://elevensports.com/view/event/clf46yr3kenn80jgrqsjmwefk',
+        'md5': 'c0958d9ff90e4503a75544358758921d',
+        'info_dict': {
+            'id': 'clf46yr3kenn80jgrqsjmwefk',
+            'title': 'Cleveland SC vs Lionsbridge FC',
+            'ext': 'mp4',
+            'description': 'md5:03b5238d6549f4ea1fddadf69b5e0b58',
+            'upload_date': '20230323',
+            'timestamp': 1679612400,
+            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+        },
+        'params': {'skip_download': 'm3u8'}
+    }, {
+        'url': 'https://elevensports.com/view/event/clhpyd53b06160jez74qhgkmf',
+        'md5': 'c0958d9ff90e4503a75544358758921d',
+        'info_dict': {
+            'id': 'clhpyd53b06160jez74qhgkmf',
+            'title': 'AJNLF vs ARRAF',
+            'ext': 'mp4',
+            'description': 'md5:c8c5e75c78f37c6d15cd6c475e43a8c1',
+            'upload_date': '20230521',
+            'timestamp': 1684684800,
+            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+        },
+        'params': {'skip_download': 'm3u8'}
+    }]
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+        webpage = self._download_webpage(url, video_id)
+        event_id = self._search_nextjs_data(webpage, video_id)['props']['pageProps']['event']['mclsEventId']
+        event_data = self._download_json(
+            f'https://mcls-api.mycujoo.tv/bff/events/v1beta1/{event_id}', video_id,
+            headers={'Authorization': 'Bearer FBVKACGN37JQC5SFA0OVK8KKSIOP153G'})
+        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
+            event_data['streams'][0]['full_url'], video_id, 'mp4', m3u8_id='hls')
+
+        return {
+            'id': video_id,
+            'formats': formats,
+            'subtitles': subtitles,
+            **traverse_obj(event_data, {
+                'title': ('title', {str}),
+                'description': ('description', {str}),
+                'timestamp': ('start_time', {parse_iso8601}),
+                'thumbnail': ('thumbnail_url', {url_or_none}),
+            }),
+        }


Test patch: