paramountplus.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import itertools
  2. from .cbs import CBSBaseIE
  3. from .common import InfoExtractor
  4. from ..utils import (
  5. ExtractorError,
  6. int_or_none,
  7. url_or_none,
  8. )
  9. class ParamountPlusIE(CBSBaseIE):
  10. _VALID_URL = r'''(?x)
  11. (?:
  12. paramountplus:|
  13. https?://(?:www\.)?(?:
  14. paramountplus\.com/(?:shows|movies)/(?:video|[^/]+/video|[^/]+)/
  15. )(?P<id>[\w-]+))'''
  16. # All tests are blocked outside US
  17. _TESTS = [{
  18. 'url': 'https://www.paramountplus.com/shows/video/Oe44g5_NrlgiZE3aQVONleD6vXc8kP0k/',
  19. 'info_dict': {
  20. 'id': 'Oe44g5_NrlgiZE3aQVONleD6vXc8kP0k',
  21. 'ext': 'mp4',
  22. 'title': 'CatDog - Climb Every CatDog/The Canine Mutiny',
  23. 'description': 'md5:7ac835000645a69933df226940e3c859',
  24. 'duration': 1426,
  25. 'timestamp': 920264400,
  26. 'upload_date': '19990301',
  27. 'uploader': 'CBSI-NEW',
  28. 'episode_number': 5,
  29. 'thumbnail': r're:https?://.+\.jpg$',
  30. 'season': 'Season 2',
  31. 'chapters': 'count:3',
  32. 'episode': 'Episode 5',
  33. 'season_number': 2,
  34. 'series': 'CatDog',
  35. },
  36. 'params': {
  37. 'skip_download': 'm3u8',
  38. },
  39. }, {
  40. 'url': 'https://www.paramountplus.com/shows/video/6hSWYWRrR9EUTz7IEe5fJKBhYvSUfexd/',
  41. 'info_dict': {
  42. 'id': '6hSWYWRrR9EUTz7IEe5fJKBhYvSUfexd',
  43. 'ext': 'mp4',
  44. 'title': '7/23/21 WEEK IN REVIEW (Rep. Jahana Hayes/Howard Fineman/Sen. Michael Bennet/Sheera Frenkel & Cecilia Kang)',
  45. 'description': 'md5:f4adcea3e8b106192022e121f1565bae',
  46. 'duration': 2506,
  47. 'timestamp': 1627063200,
  48. 'upload_date': '20210723',
  49. 'uploader': 'CBSI-NEW',
  50. 'episode_number': 81,
  51. 'thumbnail': r're:https?://.+\.jpg$',
  52. 'season': 'Season 2',
  53. 'chapters': 'count:4',
  54. 'episode': 'Episode 81',
  55. 'season_number': 2,
  56. 'series': 'Tooning Out The News',
  57. },
  58. 'params': {
  59. 'skip_download': 'm3u8',
  60. },
  61. }, {
  62. 'url': 'https://www.paramountplus.com/movies/video/vM2vm0kE6vsS2U41VhMRKTOVHyQAr6pC/',
  63. 'info_dict': {
  64. 'id': 'vM2vm0kE6vsS2U41VhMRKTOVHyQAr6pC',
  65. 'ext': 'mp4',
  66. 'title': 'Daddy\'s Home',
  67. 'upload_date': '20151225',
  68. 'description': 'md5:9a6300c504d5e12000e8707f20c54745',
  69. 'uploader': 'CBSI-NEW',
  70. 'timestamp': 1451030400,
  71. 'thumbnail': r're:https?://.+\.jpg$',
  72. 'chapters': 'count:0',
  73. 'duration': 5761,
  74. 'series': 'Paramount+ Movies',
  75. },
  76. 'params': {
  77. 'skip_download': 'm3u8',
  78. },
  79. 'skip': 'DRM',
  80. }, {
  81. 'url': 'https://www.paramountplus.com/movies/video/5EKDXPOzdVf9voUqW6oRuocyAEeJGbEc/',
  82. 'info_dict': {
  83. 'id': '5EKDXPOzdVf9voUqW6oRuocyAEeJGbEc',
  84. 'ext': 'mp4',
  85. 'uploader': 'CBSI-NEW',
  86. 'description': 'md5:bc7b6fea84ba631ef77a9bda9f2ff911',
  87. 'timestamp': 1577865600,
  88. 'title': 'Sonic the Hedgehog',
  89. 'upload_date': '20200101',
  90. 'thumbnail': r're:https?://.+\.jpg$',
  91. 'chapters': 'count:0',
  92. 'duration': 5932,
  93. 'series': 'Paramount+ Movies',
  94. },
  95. 'params': {
  96. 'skip_download': 'm3u8',
  97. },
  98. 'skip': 'DRM',
  99. }, {
  100. 'url': 'https://www.paramountplus.com/shows/the-real-world/video/mOVeHeL9ub9yWdyzSZFYz8Uj4ZBkVzQg/the-real-world-reunion/',
  101. 'only_matching': True,
  102. }, {
  103. 'url': 'https://www.paramountplus.com/shows/video/mOVeHeL9ub9yWdyzSZFYz8Uj4ZBkVzQg/',
  104. 'only_matching': True,
  105. }, {
  106. 'url': 'https://www.paramountplus.com/movies/video/W0VyStQqUnqKzJkrpSAIARuCc9YuYGNy/',
  107. 'only_matching': True,
  108. }, {
  109. 'url': 'https://www.paramountplus.com/movies/paw-patrol-the-movie/W0VyStQqUnqKzJkrpSAIARuCc9YuYGNy/',
  110. 'only_matching': True,
  111. }]
  112. def _extract_video_info(self, content_id, mpx_acc=2198311517):
  113. items_data = self._download_json(
  114. f'https://www.paramountplus.com/apps-api/v2.0/androidtv/video/cid/{content_id}.json',
  115. content_id, query={
  116. 'locale': 'en-us',
  117. 'at': 'ABCXgPuoStiPipsK0OHVXIVh68zNys+G4f7nW9R6qH68GDOcneW6Kg89cJXGfiQCsj0=',
  118. }, headers=self.geo_verification_headers())
  119. asset_types = {
  120. item.get('assetType'): {
  121. 'format': 'SMIL',
  122. 'formats': 'M3U+none,MPEG4', # '+none' specifies ProtectionScheme (no DRM)
  123. } for item in items_data['itemList']
  124. }
  125. item = items_data['itemList'][-1]
  126. info, error = {}, None
  127. metadata = {
  128. 'title': item.get('title'),
  129. 'series': item.get('seriesTitle'),
  130. 'season_number': int_or_none(item.get('seasonNum')),
  131. 'episode_number': int_or_none(item.get('episodeNum')),
  132. 'duration': int_or_none(item.get('duration')),
  133. 'thumbnail': url_or_none(item.get('thumbnail')),
  134. }
  135. try:
  136. info = self._extract_common_video_info(content_id, asset_types, mpx_acc, extra_info=metadata)
  137. except ExtractorError as e:
  138. error = e
  139. # Check for DRM formats to give appropriate error
  140. if not info.get('formats'):
  141. for query in asset_types.values():
  142. query['formats'] = 'MPEG-DASH,M3U,MPEG4' # allows DRM formats
  143. try:
  144. drm_info = self._extract_common_video_info(content_id, asset_types, mpx_acc, extra_info=metadata)
  145. except ExtractorError:
  146. if error:
  147. raise error from None
  148. raise
  149. if drm_info['formats']:
  150. self.report_drm(content_id)
  151. elif error:
  152. raise error
  153. return info
  154. class ParamountPlusSeriesIE(InfoExtractor):
  155. _VALID_URL = r'https?://(?:www\.)?paramountplus\.com/shows/(?P<id>[a-zA-Z0-9-_]+)/?(?:[#?]|$)'
  156. _TESTS = [{
  157. 'url': 'https://www.paramountplus.com/shows/drake-josh',
  158. 'playlist_mincount': 50,
  159. 'info_dict': {
  160. 'id': 'drake-josh',
  161. },
  162. }, {
  163. 'url': 'https://www.paramountplus.com/shows/hawaii_five_0/',
  164. 'playlist_mincount': 240,
  165. 'info_dict': {
  166. 'id': 'hawaii_five_0',
  167. },
  168. }, {
  169. 'url': 'https://www.paramountplus.com/shows/spongebob-squarepants/',
  170. 'playlist_mincount': 248,
  171. 'info_dict': {
  172. 'id': 'spongebob-squarepants',
  173. },
  174. }]
  175. def _entries(self, show_name):
  176. for page in itertools.count():
  177. show_json = self._download_json(
  178. f'https://www.paramountplus.com/shows/{show_name}/xhr/episodes/page/{page}/size/50/xs/0/season/0', show_name)
  179. if not show_json.get('success'):
  180. return
  181. for episode in show_json['result']['data']:
  182. yield self.url_result(
  183. 'https://www.paramountplus.com{}'.format(episode['url']),
  184. ie=ParamountPlusIE.ie_key(), video_id=episode['content_id'])
  185. def _real_extract(self, url):
  186. show_name = self._match_id(url)
  187. return self.playlist_result(self._entries(show_name), playlist_id=show_name)