]> jfr.im git - yt-dlp.git/commitdiff
[17live] Add 17.live extractor (#866)
authorThe Hatsune Daishi <redacted>
Sat, 4 Sep 2021 22:37:28 +0000 (07:37 +0900)
committerGitHub <redacted>
Sat, 4 Sep 2021 22:37:28 +0000 (04:07 +0530)
Authored by: nao20010128nao

yt_dlp/extractor/extractors.py
yt_dlp/extractor/ichinanalive.py [new file with mode: 0644]

index 1a3093c150ac67fc7335e315f3c99aeb54e64e4b..4ef581b0767685e8aebe969ddc25409477da91b5 100644 (file)
     HungamaAlbumPlaylistIE,
 )
 from .hypem import HypemIE
+from .ichinanalive import (
+    IchinanaLiveIE,
+    IchinanaLiveClipIE,
+)
 from .ign import (
     IGNIE,
     IGNVideoIE,
diff --git a/yt_dlp/extractor/ichinanalive.py b/yt_dlp/extractor/ichinanalive.py
new file mode 100644 (file)
index 0000000..cb39f82
--- /dev/null
@@ -0,0 +1,167 @@
+# coding: utf-8
+from __future__ import unicode_literals
+
+from .common import InfoExtractor
+from ..utils import ExtractorError, str_or_none, traverse_obj, unified_strdate
+from ..compat import compat_str
+
+
+class IchinanaLiveIE(InfoExtractor):
+    IE_NAME = '17live'
+    _VALID_URL = r'https?://(?:www\.)?17\.live/(?:[^/]+/)*(?:live|profile/r)/(?P<id>\d+)'
+    _TESTS = [{
+        'url': 'https://17.live/live/3773096',
+        'info_dict': {
+            'id': '3773096',
+            'title': '萠珈☕🤡🍫moka',
+            'is_live': True,
+            'uploader': '萠珈☕🤡🍫moka',
+            'uploader_id': '3773096',
+            'like_count': 366,
+            'view_count': 18121,
+            'timestamp': 1630569012,
+        },
+        'skip': 'running as of writing, but may be ended as of testing',
+    }, {
+        'note': 'nothing except language differs',
+        'url': 'https://17.live/ja/live/3773096',
+        'only_matching': True,
+    }]
+
+    @classmethod
+    def suitable(cls, url):
+        return not IchinanaLiveClipIE.suitable(url) and super(IchinanaLiveIE, cls).suitable(url)
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+        url = 'https://17.live/live/%s' % video_id
+
+        enter = self._download_json(
+            'https://api-dsa.17app.co/api/v1/lives/%s/enter' % video_id, video_id,
+            headers={'Referer': url}, fatal=False, expected_status=420,
+            data=b'\0')
+        if enter and enter.get('message') == 'ended':
+            raise ExtractorError('This live has ended.', expected=True)
+
+        view_data = self._download_json(
+            'https://api-dsa.17app.co/api/v1/lives/%s' % video_id, video_id,
+            headers={'Referer': url})
+
+        uploader = traverse_obj(
+            view_data, ('userInfo', 'displayName'), ('userInfo', 'openID'))
+
+        video_urls = view_data.get('rtmpUrls')
+        if not video_urls:
+            raise ExtractorError('unable to extract live URL information')
+        formats = []
+        for (name, value) in video_urls[0].items():
+            if not isinstance(value, compat_str):
+                continue
+            if not value.startswith('http'):
+                continue
+            quality = -1
+            if 'web' in name:
+                quality -= 1
+            if 'High' in name:
+                quality += 4
+            if 'Low' in name:
+                quality -= 2
+            formats.append({
+                'format_id': name,
+                'url': value,
+                'quality': quality,
+                'http_headers': {'Referer': url},
+                'ext': 'flv',
+                'vcodec': 'h264',
+                'acodec': 'aac',
+            })
+
+        self._sort_formats(formats)
+
+        return {
+            'id': video_id,
+            'title': uploader or video_id,
+            'formats': formats,
+            'is_live': True,
+            'uploader': uploader,
+            'uploader_id': video_id,
+            'like_count': view_data.get('receivedLikeCount'),
+            'view_count': view_data.get('viewerCount'),
+            'thumbnail': view_data.get('coverPhoto'),
+            'description': view_data.get('caption'),
+            'timestamp': view_data.get('beginTime'),
+        }
+
+
+class IchinanaLiveClipIE(InfoExtractor):
+    IE_NAME = '17live:clip'
+    _VALID_URL = r'https?://(?:www\.)?17\.live/(?:[^/]+/)*profile/r/(?P<uploader_id>\d+)/clip/(?P<id>[^/]+)'
+    _TESTS = [{
+        'url': 'https://17.live/profile/r/1789280/clip/1bHQSK8KUieruFXaCH4A4upCzlN',
+        'info_dict': {
+            'id': '1bHQSK8KUieruFXaCH4A4upCzlN',
+            'title': 'マチコ先生🦋Class💋',
+            'description': 'マチ戦隊 第一次 バスターコール\n総額200万coin!\n動画制作@うぉーかー🌱Walker🎫',
+            'uploader_id': '1789280',
+        },
+    }, {
+        'url': 'https://17.live/ja/profile/r/1789280/clip/1bHQSK8KUieruFXaCH4A4upCzlN',
+        'only_matching': True,
+    }]
+
+    def _real_extract(self, url):
+        uploader_id, video_id = self._match_valid_url(url).groups()
+        url = 'https://17.live/profile/r/%s/clip/%s' % (uploader_id, video_id)
+
+        view_data = self._download_json(
+            'https://api-dsa.17app.co/api/v1/clips/%s' % video_id, video_id,
+            headers={'Referer': url})
+
+        uploader = traverse_obj(
+            view_data, ('userInfo', 'displayName'), ('userInfo', 'name'))
+
+        formats = []
+        if view_data.get('videoURL'):
+            formats.append({
+                'id': 'video',
+                'url': view_data['videoURL'],
+                'quality': -1,
+            })
+        if view_data.get('transcodeURL'):
+            formats.append({
+                'id': 'transcode',
+                'url': view_data['transcodeURL'],
+                'quality': -1,
+            })
+        if view_data.get('srcVideoURL'):
+            # highest quality
+            formats.append({
+                'id': 'srcVideo',
+                'url': view_data['srcVideoURL'],
+                'quality': 1,
+            })
+
+        for fmt in formats:
+            fmt.update({
+                'ext': 'mp4',
+                'protocol': 'https',
+                'vcodec': 'h264',
+                'acodec': 'aac',
+                'http_headers': {'Referer': url},
+            })
+
+        self._sort_formats(formats)
+
+        return {
+            'id': video_id,
+            'title': uploader or video_id,
+            'formats': formats,
+            'uploader': uploader,
+            'uploader_id': uploader_id,
+            'like_count': view_data.get('likeCount'),
+            'view_count': view_data.get('viewCount'),
+            'thumbnail': view_data.get('imageURL'),
+            'duration': view_data.get('duration'),
+            'description': view_data.get('caption'),
+            'upload_date': unified_strdate(str_or_none(view_data.get('createdAt'))),
+        }