logo

youtube-dl

[mirror] Download/Watch videos from video hostersgit clone https://hacktivis.me/git/mirror/youtube-dl.git

tiktok.py (5002B)


  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. from .common import InfoExtractor
  4. from ..utils import (
  5. compat_str,
  6. ExtractorError,
  7. float_or_none,
  8. int_or_none,
  9. str_or_none,
  10. try_get,
  11. url_or_none,
  12. )
  13. class TikTokBaseIE(InfoExtractor):
  14. def _extract_video(self, data, video_id=None):
  15. video = data['video']
  16. description = str_or_none(try_get(data, lambda x: x['desc']))
  17. width = int_or_none(try_get(data, lambda x: video['width']))
  18. height = int_or_none(try_get(data, lambda x: video['height']))
  19. format_urls = set()
  20. formats = []
  21. for format_id in ('download', 'play'):
  22. format_url = url_or_none(video.get('%sAddr' % format_id))
  23. if not format_url:
  24. continue
  25. if format_url in format_urls:
  26. continue
  27. format_urls.add(format_url)
  28. formats.append({
  29. 'url': format_url,
  30. 'ext': 'mp4',
  31. 'height': height,
  32. 'width': width,
  33. 'http_headers': {
  34. 'Referer': 'https://www.tiktok.com/',
  35. }
  36. })
  37. self._sort_formats(formats)
  38. thumbnail = url_or_none(video.get('cover'))
  39. duration = float_or_none(video.get('duration'))
  40. uploader = try_get(data, lambda x: x['author']['nickname'], compat_str)
  41. uploader_id = try_get(data, lambda x: x['author']['id'], compat_str)
  42. timestamp = int_or_none(data.get('createTime'))
  43. def stats(key):
  44. return int_or_none(try_get(
  45. data, lambda x: x['stats']['%sCount' % key]))
  46. view_count = stats('play')
  47. like_count = stats('digg')
  48. comment_count = stats('comment')
  49. repost_count = stats('share')
  50. aweme_id = data.get('id') or video_id
  51. return {
  52. 'id': aweme_id,
  53. 'title': uploader or aweme_id,
  54. 'description': description,
  55. 'thumbnail': thumbnail,
  56. 'duration': duration,
  57. 'uploader': uploader,
  58. 'uploader_id': uploader_id,
  59. 'timestamp': timestamp,
  60. 'view_count': view_count,
  61. 'like_count': like_count,
  62. 'comment_count': comment_count,
  63. 'repost_count': repost_count,
  64. 'formats': formats,
  65. }
  66. class TikTokIE(TikTokBaseIE):
  67. _VALID_URL = r'https?://(?:www\.)?tiktok\.com/@[^/]+/video/(?P<id>\d+)'
  68. _TESTS = [{
  69. 'url': 'https://www.tiktok.com/@zureeal/video/6606727368545406213',
  70. 'md5': '163ceff303bb52de60e6887fe399e6cd',
  71. 'info_dict': {
  72. 'id': '6606727368545406213',
  73. 'ext': 'mp4',
  74. 'title': 'Zureeal',
  75. 'description': '#bowsette#mario#cosplay#uk#lgbt#gaming#asian#bowsettecosplay',
  76. 'thumbnail': r're:^https?://.*',
  77. 'duration': 15,
  78. 'uploader': 'Zureeal',
  79. 'uploader_id': '188294915489964032',
  80. 'timestamp': 1538248586,
  81. 'upload_date': '20180929',
  82. 'view_count': int,
  83. 'like_count': int,
  84. 'comment_count': int,
  85. 'repost_count': int,
  86. }
  87. }]
  88. def _real_initialize(self):
  89. # Setup session (will set necessary cookies)
  90. self._request_webpage(
  91. 'https://www.tiktok.com/', None, note='Setting up session')
  92. def _real_extract(self, url):
  93. video_id = self._match_id(url)
  94. webpage = self._download_webpage(url, video_id)
  95. page_props = self._parse_json(self._search_regex(
  96. r'<script[^>]+\bid=["\']__NEXT_DATA__[^>]+>\s*({.+?})\s*</script',
  97. webpage, 'data'), video_id)['props']['pageProps']
  98. data = try_get(page_props, lambda x: x['itemInfo']['itemStruct'], dict)
  99. if not data and page_props.get('statusCode') == 10216:
  100. raise ExtractorError('This video is private', expected=True)
  101. return self._extract_video(data, video_id)
  102. class TikTokUserIE(TikTokBaseIE):
  103. _VALID_URL = r'https://(?:www\.)?tiktok\.com/@(?P<id>[^/?#&]+)'
  104. _TESTS = [{
  105. 'url': 'https://www.tiktok.com/@zureeal',
  106. 'info_dict': {
  107. 'id': '188294915489964032',
  108. },
  109. 'playlist_mincount': 24,
  110. }]
  111. _WORKING = False
  112. @classmethod
  113. def suitable(cls, url):
  114. return False if TikTokIE.suitable(url) else super(TikTokUserIE, cls).suitable(url)
  115. def _real_extract(self, url):
  116. user_id = self._match_id(url)
  117. data = self._download_json(
  118. 'https://m.tiktok.com/h5/share/usr/list/%s/' % user_id, user_id,
  119. query={'_signature': '_'})
  120. entries = []
  121. for aweme in data['aweme_list']:
  122. try:
  123. entry = self._extract_video(aweme)
  124. except ExtractorError:
  125. continue
  126. entry['extractor_key'] = TikTokIE.ie_key()
  127. entries.append(entry)
  128. return self.playlist_result(entries, user_id)