You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.9 KiB

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