]> jfr.im git - yt-dlp.git/commitdiff
[whowatch] Add extractor #292
authorThe Hatsune Daishi <redacted>
Sun, 2 May 2021 14:13:37 +0000 (23:13 +0900)
committerGitHub <redacted>
Sun, 2 May 2021 14:13:37 +0000 (19:43 +0530)
closes #223

Authored by: nao20010128nao
Modified from: https://github.com/nao20010128nao/ytdl-patched/blob/9e4a0e061a558cdb05a618e27f47ca0ac56ece94/youtube_dl/extractor/whowatch.py

yt_dlp/downloader/__init__.py
yt_dlp/downloader/external.py
yt_dlp/extractor/common.py
yt_dlp/extractor/extractors.py
yt_dlp/extractor/whowatch.py [new file with mode: 0644]

index 510c7b60140054619464d0bee70eacc68431d0e3..c7ba918627a763b74cc65c86387298a07ec4377b 100644 (file)
@@ -31,6 +31,7 @@ def _get_real_downloader(info_dict, protocol=None, *args, **kwargs):
 
 PROTOCOL_MAP = {
     'rtmp': RtmpFD,
+    'rtmp_ffmpeg': FFmpegFD,
     'm3u8_native': HlsFD,
     'm3u8': FFmpegFD,
     'mms': RtspFD,
@@ -46,6 +47,7 @@ def _get_real_downloader(info_dict, protocol=None, *args, **kwargs):
 def shorten_protocol_name(proto, simplify=False):
     short_protocol_names = {
         'm3u8_native': 'm3u8_n',
+        'rtmp_ffmpeg': 'rtmp_f',
         'http_dash_segments': 'dash',
         'niconico_dmc': 'dmc',
     }
@@ -54,6 +56,7 @@ def shorten_protocol_name(proto, simplify=False):
             'https': 'http',
             'ftps': 'ftp',
             'm3u8_native': 'm3u8',
+            'rtmp_ffmpeg': 'rtmp',
             'm3u8_frag_urls': 'm3u8',
             'dash_frag_urls': 'dash',
         })
index d879bc66d49ab26e26c67f313569127978319c92..89f3ef28de8c5e5caf5e17a05e775494e754d410 100644 (file)
@@ -338,7 +338,7 @@ def _make_cmd(self, tmpfilename, info_dict):
 
 
 class FFmpegFD(ExternalFD):
-    SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps', 'm3u8', 'm3u8_native', 'rtsp', 'rtmp', 'mms')
+    SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps', 'm3u8', 'm3u8_native', 'rtsp', 'rtmp', 'rtmp_ffmpeg', 'mms')
 
     @classmethod
     def available(cls, path=None):
index 2ca25951b2a761aa3cd79303c985a9d38ee42a3d..642c949301628b4fbd83ac70f617fe632d640c5e 100644 (file)
@@ -157,7 +157,7 @@ class InfoExtractor(object):
                     * player_url SWF Player URL (used for rtmpdump).
                     * protocol   The protocol that will be used for the actual
                                  download, lower-case.
-                                 "http", "https", "rtsp", "rtmp", "rtmpe",
+                                 "http", "https", "rtsp", "rtmp", "rtmp_ffmpeg", "rtmpe",
                                  "m3u8", "m3u8_native" or "http_dash_segments".
                     * fragment_base_url
                                  Base URL for fragments. Each fragment's path
index 46a401e89acb690e7d48b53b7b590e7fbf7c566d..79f9c74a32b77b0170aa04914e86b2565e9946a4 100644 (file)
 )
 from .weiqitv import WeiqiTVIE
 from .wimtv import WimTVIE
+from .whowatch import WhoWatchIE
 from .wistia import (
     WistiaIE,
     WistiaPlaylistIE,
diff --git a/yt_dlp/extractor/whowatch.py b/yt_dlp/extractor/whowatch.py
new file mode 100644 (file)
index 0000000..8080f28
--- /dev/null
@@ -0,0 +1,101 @@
+# coding: utf-8
+from __future__ import unicode_literals
+
+from .common import InfoExtractor
+from ..utils import (
+    int_or_none,
+    qualities,
+    try_get,
+    ExtractorError,
+)
+from ..compat import compat_str
+
+
+class WhoWatchIE(InfoExtractor):
+    IE_NAME = 'whowatch'
+    _VALID_URL = r'https?://whowatch\.tv/viewer/(?P<id>\d+)'
+
+    _TESTS = [{
+        'url': 'https://whowatch.tv/viewer/21450171',
+        'only_matching': True,
+    }]
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+        self._download_webpage(url, video_id)
+        metadata = self._download_json('https://api.whowatch.tv/lives/%s' % video_id, video_id)
+        live_data = self._download_json('https://api.whowatch.tv/lives/%s/play' % video_id, video_id)
+
+        title = try_get(None, (
+            lambda x: live_data['share_info']['live_title'][1:-1],
+            lambda x: metadata['live']['title'],
+        ), compat_str)
+
+        hls_url = live_data.get('hls_url')
+        if not hls_url:
+            raise ExtractorError(live_data.get('error_message') or 'The user is offline.', expected=True)
+
+        QUALITIES = qualities(['low', 'medium', 'high', 'veryhigh'])
+        formats = []
+
+        for i, fmt in enumerate(live_data.get('streams') or []):
+            name = fmt.get('quality') or fmt.get('name') or compat_str(i)
+            hls_url = fmt.get('hls_url')
+            rtmp_url = fmt.get('rtmp_url')
+            audio_only = fmt.get('audio_only')
+            quality = QUALITIES(fmt.get('quality'))
+
+            if hls_url:
+                hls_fmts = self._extract_m3u8_formats(
+                    hls_url, video_id, ext='mp4', entry_protocol='m3u8',
+                    m3u8_id='hls-%s' % name, quality=quality)
+                formats.extend(hls_fmts)
+            else:
+                hls_fmts = []
+
+            # RTMP url for audio_only is same as high format, so skip it
+            if rtmp_url and not audio_only:
+                formats.append({
+                    'url': rtmp_url,
+                    'format_id': 'rtmp-%s' % name,
+                    'ext': 'mp4',
+                    'protocol': 'rtmp_ffmpeg',  # ffmpeg can, while rtmpdump can't
+                    'vcodec': 'h264',
+                    'acodec': 'aac',
+                    'quality': quality,
+                    'format_note': fmt.get('label'),
+                    # note: HLS and RTMP have same resolution for now, so it's acceptable
+                    'width': try_get(hls_fmts, lambda x: x[0]['width'], int),
+                    'height': try_get(hls_fmts, lambda x: x[0]['height'], int),
+                })
+
+        # This contains the same formats as the above manifests and is used only as a fallback
+        formats.extend(self._extract_m3u8_formats(
+            hls_url, video_id, ext='mp4', entry_protocol='m3u8',
+            m3u8_id='hls'))
+        self._remove_duplicate_formats(formats)
+        self._sort_formats(formats)
+
+        uploader_url = try_get(metadata, lambda x: x['live']['user']['user_path'], compat_str)
+        if uploader_url:
+            uploader_url = 'https://whowatch.tv/profile/%s' % uploader_url
+        uploader_id = compat_str(try_get(metadata, lambda x: x['live']['user']['id'], int))
+        uploader = try_get(metadata, lambda x: x['live']['user']['name'], compat_str)
+        thumbnail = try_get(metadata, lambda x: x['live']['latest_thumbnail_url'], compat_str)
+        timestamp = int_or_none(try_get(metadata, lambda x: x['live']['started_at'], int), scale=1000)
+        view_count = try_get(metadata, lambda x: x['live']['total_view_count'], int)
+        comment_count = try_get(metadata, lambda x: x['live']['comment_count'], int)
+
+        return {
+            'id': video_id,
+            'title': title,
+            'uploader_id': uploader_id,
+            'uploader_url': uploader_url,
+            'uploader': uploader,
+            'formats': formats,
+            'thumbnail': thumbnail,
+            'timestamp': timestamp,
+            'view_count': view_count,
+            'comment_count': comment_count,
+            'is_live': True,
+        }