Repository: yt-dlp/yt-dlp

Issue:
CNBCVideo error
### 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.10.13** ([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

USA

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

CNBCVideo not working

### Provide verbose output that clearly demonstrates the problem

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

### Complete Verbose Output

```shell
[debug] Command-line config: ['https://www.cnbc.com/video/2023/10/18/the-palestinians-are-not-hamas-says-yeshiva-university-president-rabbi-ari-berman.html', '-vU']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version stable@2023.10.13 [b634ba742] (pip)
[debug] Python 3.12.0 (CPython arm64 64bit) - macOS-14.0-arm64-arm-64bit (OpenSSL 3.1.3 19 Sep 2023)
[debug] exe versions: ffmpeg 6.0 (setts), ffprobe 6.0, rtmpdump 2.4
[debug] Optional libraries: Cryptodome-3.19.0, brotli-1.1.0, certifi-2023.07.22, mutagen-1.47.0, sqlite3-3.43.2, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1890 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Available version: stable@2023.10.13, Current version: stable@2023.10.13
yt-dlp is up to date (stable@2023.10.13)
[CNBCVideo] Extracting URL: https://www.cnbc.com/video/2023/10/18/the-palestinians-are-not-hamas-says-yeshiva-university-president-rabbi-ari-berman.html
[CNBCVideo] the-palestinians-are-not-hamas-says-yeshiva-university-president-rabbi-ari-berman: Downloading JSON metadata
[CNBC] Extracting URL: http://video.cnbc.com/gallery/?video=7000319414
[ThePlatform] Extracting URL: http://link.theplatform.com/s/gZWlPC/media/guid/2408950221/7000319414?mbr=true&manifest=m3u#__youtubedl_smuggle=%7B%22force_smil_url%22%3A+true%7D
[ThePlatform] 7000319414: Downloading SMIL data
ERROR: [ThePlatform] 7000319414: Unable to download XML: HTTP Error 404: Not Found (caused by <HTTPError 404: Not Found>); please report this issue on  https://github.com/yt-dlp/yt-dlp/issues?q= , filling out the appropriate issue template. Confirm you are on the latest version using  yt-dlp -U
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/common.py", line 715, in extract
    ie_result = self._real_extract(url)
                ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/theplatform.py", line 303, in _real_extract
    formats, subtitles = self._extract_theplatform_smil(smil_url, video_id)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/theplatform.py", line 36, in _extract_theplatform_smil
    meta = self._download_xml(
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/common.py", line 1069, in download_content
    res = getattr(self, download_handle.__name__)(url_or_request, video_id, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/common.py", line 1033, in download_handle
    res = self._download_webpage_handle(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/adobepass.py", line 1366, in _download_webpage_handle
    return super(AdobePassIE, self)._download_webpage_handle(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/common.py", line 903, in _download_webpage_handle
    urlh = self._request_webpage(url_or_request, video_id, note, errnote, fatal, data=data, headers=headers, query=query, expected_status=expected_status)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/common.py", line 860, in _request_webpage
    raise ExtractorError(errmsg, cause=err)

  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/networking/_urllib.py", line 410, in _send
    res = opener.open(urllib_req, timeout=float(request.extensions.get('timeout') or self.timeout))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.0/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 521, in open
    response = meth(req, response)
               ^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.0/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 630, in http_response
    response = self.parent.error(
               ^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.0/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 559, in error
    return self._call_chain(*args)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.0/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 492, in _call_chain
    result = func(*args)
             ^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.0/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 639, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 404: Not Found

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/YoutubeDL.py", line 4051, in urlopen
    return self._request_director.send(req)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/networking/common.py", line 114, in send
    response = handler.send(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/networking/_helper.py", line 204, in wrapper
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/networking/common.py", line 325, in send
    return self._send(request)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/networking/_urllib.py", line 415, in _send
    raise HTTPError(UrllibResponseAdapter(e.fp), redirect_loop='redirect error' in str(e)) from e
yt_dlp.networking.exceptions.HTTPError: HTTP Error 404: Not Found

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/extractor/common.py", line 847, in _request_webpage
    return self._downloader.urlopen(self._create_request(url_or_request, data, headers, query))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/yt-dlp/2023.10.13/libexec/lib/python3.12/site-packages/yt_dlp/YoutubeDL.py", line 4070, in urlopen
    raise _CompatHTTPError(e) from e
yt_dlp.networking.exceptions._CompatHTTPError: HTTP Error 404: Not Found
```

Bitchute - output template "%(uploader)s" no longer works.
### 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.10.13** ([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

USA

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

"%(uploader)s" used to work on Bitchute.  Now it outputs "NA".
The following templates also don't work on Bitchute (but I don't know if they ever did work):
creator
uploader_id
channel
channel_id

### Provide verbose output that clearly demonstrates the problem

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

### Complete Verbose Output

```shell
[debug] Command-line config: ['-vU', '-f', 'worst', '-o', 'r:\\%(uploader)s-%(id)s.%(ext)s', 'https://www.bitchute.com/video/lCLo8CUZXSeH/']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version stable@2023.10.13 [b634ba742] (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: phantomjs 2.1.1
[debug] Optional libraries: Cryptodome-3.19.0, brotli-1.1.0, certifi-2023.07.22, mutagen-1.47.0, sqlite3-3.35.5, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1890 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Available version: stable@2023.10.13, Current version: stable@2023.10.13
Current Build Hash: 13d6a04ef6678dc61cb6e2d42eb53e69486bd2f52b8e5d778db029ccd4c600b4
yt-dlp is up to date (stable@2023.10.13)
[BitChute] Extracting URL: https://www.bitchute.com/video/lCLo8CUZXSeH/
[BitChute] lCLo8CUZXSeH: Downloading webpage
[BitChute] lCLo8CUZXSeH: Checking https://zbbb278hfll091.bitchute.com/tpjhtmChu3TK/lCLo8CUZXSeH.mp4
[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] lCLo8CUZXSeH: Downloading 1 format(s): 0
[debug] Invoking http downloader on "https://zbbb278hfll091.bitchute.com/tpjhtmChu3TK/lCLo8CUZXSeH.mp4"
[debug] File locking is not supported. Proceeding without locking
[download] Destination: r:\NA-lCLo8CUZXSeH.mp4
[download] 100% of   10.96MiB in 00:00:01 at 7.10MiB/s
```
```



Discussion:
Related: #5871

But this seems like a different issue
My group and I are in a Software Engineering class in college. We will implement an extractor specifically for CNBC to tackle this issue.
> "%(uploader)s" used to work on Bitchute. Now it outputs "NA".
> The following templates also don't work on Bitchute (but I don't know if they ever did work):
> creator
> uploader_id
> channel
> channel_id

Out of these, only the `uploader` field was ever extracted for this site. Any of the others should be tracked in a separate "site feature request" issue (if there is any appropriate metadata to extract for those fields).
I created a pull request to submit the fix for the ``uploader`` information retrieval.
uploader is in class `video-card-channel`
alternatively class `details` -> class `name`

edit: `video-card-channel` is easier but `details` -> `name` is more reliable
> uploader is in class `video-card-channel` alternatively class `details` -> class `name`
> 
> edit: `video-card-channel` is easier but `details` -> `name` is more reliable

I noticed that ``video-card-channel`` doesn't exist in all URLs.
My proposed PR uses regex to get the value. Based on my tests, it seems accurate.

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
@@ -378,7 +378,6 @@
 from .clyp import ClypIE
 from .cmt import CMTIE
 from .cnbc import (
-    CNBCIE,
     CNBCVideoIE,
 )
 from .cnn import (
diff --git a/yt_dlp/extractor/cnbc.py b/yt_dlp/extractor/cnbc.py
--- a/yt_dlp/extractor/cnbc.py
+++ b/yt_dlp/extractor/cnbc.py
@@ -1,68 +1,97 @@
 from .common import InfoExtractor
-from ..utils import smuggle_url
+from ..utils import int_or_none, parse_iso8601, str_or_none, url_or_none
+from ..utils.traversal import traverse_obj
 
 
-class CNBCIE(InfoExtractor):
-    _VALID_URL = r'https?://video\.cnbc\.com/gallery/\?video=(?P<id>[0-9]+)'
-    _TEST = {
-        'url': 'http://video.cnbc.com/gallery/?video=3000503714',
+class CNBCVideoIE(InfoExtractor):
+    _VALID_URL = r'https?://(?:www\.)?cnbc\.com/video/(?:[^/?#]+/)+(?P<id>[^./?#&]+)\.html'
+
+    _TESTS = [{
+        'url': 'https://www.cnbc.com/video/2023/12/07/mcdonalds-just-unveiled-cosmcsits-new-spinoff-brand.html',
         'info_dict': {
-            'id': '3000503714',
             'ext': 'mp4',
-            'title': 'Fighting zombies is big business',
-            'description': 'md5:0c100d8e1a7947bd2feec9a5550e519e',
-            'timestamp': 1459332000,
-            'upload_date': '20160330',
-            'uploader': 'NBCU-CNBC',
+            'id': '107344774',
+            'display_id': 'mcdonalds-just-unveiled-cosmcsits-new-spinoff-brand',
+            'modified_timestamp': 1702053483,
+            'timestamp': 1701977810,
+            'channel': 'News Videos',
+            'upload_date': '20231207',
+            'description': 'md5:882c001d85cb43d7579b514307b3e78b',
+            'release_timestamp': 1701977375,
+            'modified_date': '20231208',
+            'release_date': '20231207',
+            'duration': 65,
+            'author': 'Sean Conlon',
+            'title': 'Here\'s a first look at McDonald\'s new spinoff brand, CosMc\'s',
+            'thumbnail': 'https://image.cnbcfm.com/api/v1/image/107344192-1701894812493-CosMcsskyHero_2336x1040_hero-desktop.jpg?v=1701894855',
         },
-        'params': {
-            # m3u8 download
-            'skip_download': True,
-        },
-        'skip': 'Dead link',
-    }
-
-    def _real_extract(self, url):
-        video_id = self._match_id(url)
-        return {
-            '_type': 'url_transparent',
-            'ie_key': 'ThePlatform',
-            'url': smuggle_url(
-                'http://link.theplatform.com/s/gZWlPC/media/guid/2408950221/%s?mbr=true&manifest=m3u' % video_id,
-                {'force_smil_url': True}),
-            'id': video_id,
-        }
-
-
-class CNBCVideoIE(InfoExtractor):
-    _VALID_URL = r'https?://(?:www\.)?cnbc\.com(?P<path>/video/(?:[^/]+/)+(?P<id>[^./?#&]+)\.html)'
-    _TEST = {
-        'url': 'https://www.cnbc.com/video/2018/07/19/trump-i-dont-necessarily-agree-with-raising-rates.html',
+        'expected_warnings': ['Unable to download f4m manifest'],
+    }, {
+        'url': 'https://www.cnbc.com/video/2023/12/08/jim-cramer-shares-his-take-on-seattles-tech-scene.html',
         'info_dict': {
-            'id': '7000031301',
+            'author': 'Jim Cramer',
+            'channel': 'Mad Money with Jim Cramer',
+            'description': 'md5:72925be21b952e95eba51178dddf4e3e',
+            'duration': 299.0,
             'ext': 'mp4',
-            'title': "Trump: I don't necessarily agree with raising rates",
-            'description': 'md5:878d8f0b4ebb5bb1dda3514b91b49de3',
-            'timestamp': 1531958400,
-            'upload_date': '20180719',
-            'uploader': 'NBCU-CNBC',
+            'id': '107345451',
+            'display_id': 'jim-cramer-shares-his-take-on-seattles-tech-scene',
+            'thumbnail': 'https://image.cnbcfm.com/api/v1/image/107345481-1702079431MM-B-120823.jpg?v=1702079430',
+            'timestamp': 1702080139,
+            'title': 'Jim Cramer shares his take on Seattle\'s tech scene',
+            'release_date': '20231208',
+            'upload_date': '20231209',
+            'modified_timestamp': 1702080139,
+            'modified_date': '20231209',
+            'release_timestamp': 1702073551,
         },
-        'params': {
-            'skip_download': True,
+        'expected_warnings': ['Unable to download f4m manifest'],
+    }, {
+        'url': 'https://www.cnbc.com/video/2023/12/08/the-epicenter-of-ai-is-in-seattle-says-jim-cramer.html',
+        'info_dict': {
+            'author': 'Jim Cramer',
+            'channel': 'Mad Money with Jim Cramer',
+            'description': 'md5:72925be21b952e95eba51178dddf4e3e',
+            'duration': 113.0,
+            'ext': 'mp4',
+            'id': '107345474',
+            'display_id': 'the-epicenter-of-ai-is-in-seattle-says-jim-cramer',
+            'thumbnail': 'https://image.cnbcfm.com/api/v1/image/107345486-Screenshot_2023-12-08_at_70339_PM.png?v=1702080248',
+            'timestamp': 1702080535,
+            'title': 'The epicenter of AI is in Seattle, says Jim Cramer',
+            'release_timestamp': 1702077347,
+            'modified_timestamp': 1702080535,
+            'release_date': '20231208',
+            'upload_date': '20231209',
+            'modified_date': '20231209',
         },
-        'skip': 'Dead link',
-    }
+        'expected_warnings': ['Unable to download f4m manifest'],
+    }]
 
     def _real_extract(self, url):
-        path, display_id = self._match_valid_url(url).groups()
-        video_id = self._download_json(
-            'https://webql-redesign.cnbcfm.com/graphql', display_id, query={
-                'query': '''{
-  page(path: "%s") {
-    vcpsId
-  }
-}''' % path,
-            })['data']['page']['vcpsId']
-        return self.url_result(
-            'http://video.cnbc.com/gallery/?video=%d' % video_id,
-            CNBCIE.ie_key())
+        display_id = self._match_id(url)
+        webpage = self._download_webpage(url, display_id)
+        data = self._search_json(r'window\.__s_data=', webpage, 'video data', display_id)
+
+        player_data = traverse_obj(data, (
+            'page', 'page', 'layout', ..., 'columns', ..., 'modules',
+            lambda _, v: v['name'] == 'clipPlayer', 'data', {dict}), get_all=False)
+
+        return {
+            'id': display_id,
+            'display_id': display_id,
+            'formats': self._extract_akamai_formats(player_data['playbackURL'], display_id),
+            **self._search_json_ld(webpage, display_id, fatal=False),
+            **traverse_obj(player_data, {
+                'id': ('id', {str_or_none}),
+                'title': ('title', {str}),
+                'description': ('description', {str}),
+                'author': ('author', ..., 'name', {str}),
+                'timestamp': ('datePublished', {parse_iso8601}),
+                'release_timestamp': ('uploadDate', {parse_iso8601}),
+                'modified_timestamp': ('dateLastPublished', {parse_iso8601}),
+                'thumbnail': ('thumbnail', {url_or_none}),
+                'duration': ('duration', {int_or_none}),
+                'channel': ('section', 'title', {str}),
+            }, get_all=False),
+        }


Test patch: