aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJC-Chung <52159296+JC-Chung@users.noreply.github.com>2023-01-05 19:23:34 +0800
committerGitHub <noreply@github.com>2023-01-05 11:23:34 +0000
commit933ed882e94ebfacc5e407dbd74fa25e672092c4 (patch)
tree5ae909e4d6ebee15fbc3be840a9f030a50339153
parenta1d9aca3382a83e61d5069a140664a112e6c54e4 (diff)
downloadhypervideo-pre-933ed882e94ebfacc5e407dbd74fa25e672092c4.tar.lz
hypervideo-pre-933ed882e94ebfacc5e407dbd74fa25e672092c4.tar.xz
hypervideo-pre-933ed882e94ebfacc5e407dbd74fa25e672092c4.zip
[extractor/tiktok] Add `TikTokLive` extractor (#5637)
Closes #3698 Authored by: JC-Chung
-rw-r--r--yt_dlp/extractor/_extractors.py1
-rw-r--r--yt_dlp/extractor/tiktok.py40
2 files changed, 41 insertions, 0 deletions
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
index 53ec29364..7a390a8d2 100644
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -1890,6 +1890,7 @@ from .tiktok import (
TikTokEffectIE,
TikTokTagIE,
TikTokVMIE,
+ TikTokLiveIE,
DouyinIE,
)
from .tinypic import TinyPicIE
diff --git a/yt_dlp/extractor/tiktok.py b/yt_dlp/extractor/tiktok.py
index 709d944dc..cc96de364 100644
--- a/yt_dlp/extractor/tiktok.py
+++ b/yt_dlp/extractor/tiktok.py
@@ -11,6 +11,7 @@ from ..utils import (
HEADRequest,
LazyList,
UnsupportedError,
+ UserNotLive,
get_element_by_id,
get_first,
int_or_none,
@@ -980,3 +981,42 @@ class TikTokVMIE(InfoExtractor):
if self.suitable(new_url): # Prevent infinite loop in case redirect fails
raise UnsupportedError(new_url)
return self.url_result(new_url)
+
+
+class TikTokLiveIE(InfoExtractor):
+ _VALID_URL = r'https?://(?:www\.)?tiktok\.com/@(?P<id>[\w\.-]+)/live'
+ IE_NAME = 'tiktok:live'
+
+ _TESTS = [{
+ 'url': 'https://www.tiktok.com/@iris04201/live',
+ 'only_matching': True,
+ }]
+
+ def _real_extract(self, url):
+ uploader = self._match_id(url)
+ webpage = self._download_webpage(url, uploader, headers={'User-Agent': 'User-Agent:Mozilla/5.0'})
+ room_id = self._html_search_regex(r'snssdk\d*://live\?room_id=(\d+)', webpage, 'room ID', default=None)
+ if not room_id:
+ raise UserNotLive(video_id=uploader)
+ live_info = traverse_obj(self._download_json(
+ 'https://www.tiktok.com/api/live/detail/', room_id, query={
+ 'aid': '1988',
+ 'roomID': room_id,
+ }), 'LiveRoomInfo', expected_type=dict, default={})
+
+ if 'status' not in live_info:
+ raise ExtractorError('Unexpected response from TikTok API')
+ # status = 2 if live else 4
+ if not int_or_none(live_info['status']) == 2:
+ raise UserNotLive(video_id=uploader)
+
+ return {
+ 'id': room_id,
+ 'title': live_info.get('title') or self._html_search_meta(['og:title', 'twitter:title'], webpage, default=''),
+ 'uploader': uploader,
+ 'uploader_id': traverse_obj(live_info, ('ownerInfo', 'id')),
+ 'creator': traverse_obj(live_info, ('ownerInfo', 'nickname')),
+ 'concurrent_view_count': traverse_obj(live_info, ('liveRoomStats', 'userCount'), expected_type=int),
+ 'formats': self._extract_m3u8_formats(live_info['liveUrl'], room_id, 'mp4', live=True),
+ 'is_live': True,
+ }