younow.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import itertools
  2. from .common import InfoExtractor
  3. from ..utils import (
  4. ExtractorError,
  5. format_field,
  6. int_or_none,
  7. str_or_none,
  8. try_get,
  9. )
  10. CDN_API_BASE = 'https://cdn.younow.com/php/api'
  11. MOMENT_URL_FORMAT = f'{CDN_API_BASE}/moment/fetch/id=%s'
  12. class YouNowLiveIE(InfoExtractor):
  13. _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P<id>[^/?#&]+)'
  14. _TEST = {
  15. 'url': 'https://www.younow.com/AmandaPadeezy',
  16. 'info_dict': {
  17. 'id': 'AmandaPadeezy',
  18. 'ext': 'mp4',
  19. 'is_live': True,
  20. 'title': 'March 26, 2017',
  21. 'thumbnail': r're:^https?://.*\.jpg$',
  22. 'tags': ['girls'],
  23. 'categories': ['girls'],
  24. 'uploader': 'AmandaPadeezy',
  25. 'uploader_id': '6716501',
  26. 'uploader_url': 'https://www.younow.com/AmandaPadeezy',
  27. 'creator': 'AmandaPadeezy',
  28. },
  29. 'skip': True,
  30. }
  31. @classmethod
  32. def suitable(cls, url):
  33. return (False
  34. if YouNowChannelIE.suitable(url) or YouNowMomentIE.suitable(url)
  35. else super().suitable(url))
  36. def _real_extract(self, url):
  37. username = self._match_id(url)
  38. data = self._download_json(
  39. f'https://api.younow.com/php/api/broadcast/info/curId=0/user={username}', username)
  40. if data.get('errorCode') != 0:
  41. raise ExtractorError(data['errorMsg'], expected=True)
  42. uploader = try_get(
  43. data, lambda x: x['user']['profileUrlString'],
  44. str) or username
  45. return {
  46. 'id': uploader,
  47. 'is_live': True,
  48. 'title': uploader,
  49. 'thumbnail': data.get('awsUrl'),
  50. 'tags': data.get('tags'),
  51. 'categories': data.get('tags'),
  52. 'uploader': uploader,
  53. 'uploader_id': data.get('userId'),
  54. 'uploader_url': f'https://www.younow.com/{username}',
  55. 'creator': uploader,
  56. 'view_count': int_or_none(data.get('viewers')),
  57. 'like_count': int_or_none(data.get('likes')),
  58. 'formats': [{
  59. 'url': '{}/broadcast/videoPath/hls=1/broadcastId={}/channelId={}'.format(CDN_API_BASE, data['broadcastId'], data['userId']),
  60. 'ext': 'mp4',
  61. 'protocol': 'm3u8',
  62. }],
  63. }
  64. def _extract_moment(item, fatal=True):
  65. moment_id = item.get('momentId')
  66. if not moment_id:
  67. if not fatal:
  68. return
  69. raise ExtractorError('Unable to extract moment id')
  70. moment_id = str(moment_id)
  71. title = item.get('text')
  72. if not title:
  73. title = 'YouNow %s' % (
  74. item.get('momentType') or item.get('titleType') or 'moment')
  75. uploader = try_get(item, lambda x: x['owner']['name'], str)
  76. uploader_id = try_get(item, lambda x: x['owner']['userId'])
  77. uploader_url = format_field(uploader, None, 'https://www.younow.com/%s')
  78. return {
  79. 'extractor_key': 'YouNowMoment',
  80. 'id': moment_id,
  81. 'title': title,
  82. 'view_count': int_or_none(item.get('views')),
  83. 'like_count': int_or_none(item.get('likes')),
  84. 'timestamp': int_or_none(item.get('created')),
  85. 'creator': uploader,
  86. 'uploader': uploader,
  87. 'uploader_id': str_or_none(uploader_id),
  88. 'uploader_url': uploader_url,
  89. 'formats': [{
  90. 'url': f'https://hls.younow.com/momentsplaylists/live/{moment_id}/{moment_id}.m3u8',
  91. 'ext': 'mp4',
  92. 'protocol': 'm3u8_native',
  93. }],
  94. }
  95. class YouNowChannelIE(InfoExtractor):
  96. _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P<id>[^/]+)/channel'
  97. _TEST = {
  98. 'url': 'https://www.younow.com/its_Kateee_/channel',
  99. 'info_dict': {
  100. 'id': '14629760',
  101. 'title': 'its_Kateee_ moments',
  102. },
  103. 'playlist_mincount': 8,
  104. }
  105. def _entries(self, username, channel_id):
  106. created_before = 0
  107. for page_num in itertools.count(1):
  108. if created_before is None:
  109. break
  110. info = self._download_json(
  111. f'{CDN_API_BASE}/moment/profile/channelId={channel_id}/createdBefore={created_before}/records=20',
  112. username, note=f'Downloading moments page {page_num}')
  113. items = info.get('items')
  114. if not items or not isinstance(items, list):
  115. break
  116. for item in items:
  117. if not isinstance(item, dict):
  118. continue
  119. item_type = item.get('type')
  120. if item_type == 'moment':
  121. entry = _extract_moment(item, fatal=False)
  122. if entry:
  123. yield entry
  124. elif item_type == 'collection':
  125. moments = item.get('momentsIds')
  126. if isinstance(moments, list):
  127. for moment_id in moments:
  128. m = self._download_json(
  129. MOMENT_URL_FORMAT % moment_id, username,
  130. note=f'Downloading {moment_id} moment JSON',
  131. fatal=False)
  132. if m and isinstance(m, dict) and m.get('item'):
  133. entry = _extract_moment(m['item'])
  134. if entry:
  135. yield entry
  136. created_before = int_or_none(item.get('created'))
  137. def _real_extract(self, url):
  138. username = self._match_id(url)
  139. channel_id = str(self._download_json(
  140. f'https://api.younow.com/php/api/broadcast/info/curId=0/user={username}',
  141. username, note='Downloading user information')['userId'])
  142. return self.playlist_result(
  143. self._entries(username, channel_id), channel_id,
  144. f'{username} moments')
  145. class YouNowMomentIE(InfoExtractor):
  146. _VALID_URL = r'https?://(?:www\.)?younow\.com/[^/]+/(?P<id>[^/?#&]+)'
  147. _TEST = {
  148. 'url': 'https://www.younow.com/GABO.../20712117/36319236/3b316doc/m',
  149. 'md5': 'a30c70eadb9fb39a1aa3c8c0d22a0807',
  150. 'info_dict': {
  151. 'id': '20712117',
  152. 'ext': 'mp4',
  153. 'title': 'YouNow capture',
  154. 'view_count': int,
  155. 'like_count': int,
  156. 'timestamp': 1490432040,
  157. 'upload_date': '20170325',
  158. 'uploader': 'GABO...',
  159. 'uploader_id': '35917228',
  160. },
  161. }
  162. @classmethod
  163. def suitable(cls, url):
  164. return (False
  165. if YouNowChannelIE.suitable(url)
  166. else super().suitable(url))
  167. def _real_extract(self, url):
  168. video_id = self._match_id(url)
  169. item = self._download_json(MOMENT_URL_FORMAT % video_id, video_id)
  170. return _extract_moment(item['item'])