rtnews.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import re
  2. from .common import InfoExtractor
  3. from ..utils import js_to_json
  4. class RTNewsIE(InfoExtractor):
  5. _VALID_URL = r'https?://(?:www\.)?rt\.com/[^/]+/(?:[^/]+/)?(?P<id>\d+)'
  6. _TESTS = [{
  7. 'url': 'https://www.rt.com/sport/546301-djokovic-arrives-belgrade-crowds/',
  8. 'playlist_mincount': 2,
  9. 'info_dict': {
  10. 'id': '546301',
  11. 'title': 'Crowds gather to greet deported Djokovic as he returns to Serbia (VIDEO)',
  12. 'description': 'md5:1d5bfe1a988d81fd74227cfdf93d314d',
  13. 'thumbnail': 'https://cdni.rt.com/files/2022.01/article/61e587a085f540102c3386c1.png',
  14. },
  15. }, {
  16. 'url': 'https://www.rt.com/shows/in-question/535980-plot-to-assassinate-julian-assange/',
  17. 'playlist_mincount': 1,
  18. 'info_dict': {
  19. 'id': '535980',
  20. 'title': 'The plot to assassinate Julian Assange',
  21. 'description': 'md5:55279ce5e4441dc1d16e2e4a730152cd',
  22. 'thumbnail': 'https://cdni.rt.com/files/2021.09/article/615226f42030274e8879b53d.png',
  23. },
  24. 'playlist': [{
  25. 'info_dict': {
  26. 'id': '6152271d85f5400464496162',
  27. 'ext': 'mp4',
  28. 'title': '6152271d85f5400464496162',
  29. },
  30. }],
  31. }]
  32. def _entries(self, webpage):
  33. video_urls = set(re.findall(r'https://cdnv\.rt\.com/.*[a-f0-9]+\.mp4', webpage))
  34. for v_url in video_urls:
  35. v_id = re.search(r'([a-f0-9]+)\.mp4', v_url).group(1)
  36. if v_id:
  37. yield {
  38. 'id': v_id,
  39. 'title': v_id,
  40. 'url': v_url,
  41. }
  42. def _real_extract(self, url):
  43. playlist_id = self._match_id(url)
  44. webpage = self._download_webpage(url, playlist_id)
  45. return {
  46. '_type': 'playlist',
  47. 'id': playlist_id,
  48. 'entries': self._entries(webpage),
  49. 'title': self._og_search_title(webpage),
  50. 'description': self._og_search_description(webpage),
  51. 'thumbnail': self._og_search_thumbnail(webpage),
  52. }
  53. class RTDocumentryIE(InfoExtractor):
  54. _VALID_URL = r'https?://rtd\.rt\.com/(?:(?:series|shows)/[^/]+|films)/(?P<id>[^/?$&#]+)'
  55. _TESTS = [{
  56. 'url': 'https://rtd.rt.com/films/escobars-hitman/',
  57. 'info_dict': {
  58. 'id': 'escobars-hitman',
  59. 'ext': 'mp4',
  60. 'title': "Escobar's Hitman. Former drug-gang killer, now loved and loathed in Colombia",
  61. 'description': 'md5:647c76984b7cb9a8b52a567e87448d88',
  62. 'thumbnail': 'https://cdni.rt.com/rtd-files/films/escobars-hitman/escobars-hitman_11.jpg',
  63. 'average_rating': 8.53,
  64. 'duration': 3134.0,
  65. },
  66. 'params': {'skip_download': True},
  67. }, {
  68. 'url': 'https://rtd.rt.com/shows/the-kalashnikova-show-military-secrets-anna-knishenko/iskander-tactical-system-natos-headache/',
  69. 'info_dict': {
  70. 'id': 'iskander-tactical-system-natos-headache',
  71. 'ext': 'mp4',
  72. 'title': "Iskander tactical system. NATO's headache | The Kalashnikova Show. Episode 10",
  73. 'description': 'md5:da7c24a0aa67bc2bb88c86658508ca87',
  74. 'thumbnail': 'md5:89de8ce38c710b7c501ff02d47e2aa89',
  75. 'average_rating': 9.27,
  76. 'duration': 274.0,
  77. 'timestamp': 1605726000,
  78. 'view_count': int,
  79. 'upload_date': '20201118',
  80. },
  81. 'params': {'skip_download': True},
  82. }, {
  83. 'url': 'https://rtd.rt.com/series/i-am-hacked-trailer/introduction-to-safe-digital-life-ep2/',
  84. 'info_dict': {
  85. 'id': 'introduction-to-safe-digital-life-ep2',
  86. 'ext': 'mp4',
  87. 'title': 'How to Keep your Money away from Hackers | I am Hacked. Episode 2',
  88. 'description': 'md5:c46fa9a5af86c0008c45a3940a8cce87',
  89. 'thumbnail': 'md5:a5e81b9bf5aed8f5e23d9c053601b825',
  90. 'average_rating': 10.0,
  91. 'duration': 1524.0,
  92. 'timestamp': 1636977600,
  93. 'view_count': int,
  94. 'upload_date': '20211115',
  95. },
  96. 'params': {'skip_download': True},
  97. }]
  98. def _real_extract(self, url):
  99. video_id = self._match_id(url)
  100. webpage = self._download_webpage(url, video_id)
  101. ld_json = self._search_json_ld(webpage, None, fatal=False)
  102. if not ld_json:
  103. self.raise_no_formats('No video/audio found at the provided url.', expected=True)
  104. media_json = self._parse_json(
  105. self._search_regex(r'(?s)\'Med\'\s*:\s*\[\s*({.+})\s*\]\s*};', webpage, 'media info'),
  106. video_id, transform_source=js_to_json)
  107. if 'title' not in ld_json and 'title' in media_json:
  108. ld_json['title'] = media_json['title']
  109. formats = [{'url': src['file']} for src in media_json.get('sources') or [] if src.get('file')]
  110. return {
  111. 'id': video_id,
  112. 'thumbnail': media_json.get('image'),
  113. 'formats': formats,
  114. **ld_json,
  115. }
  116. class RTDocumentryPlaylistIE(InfoExtractor):
  117. _VALID_URL = r'https?://rtd\.rt\.com/(?:series|shows)/(?P<id>[^/]+)/$'
  118. _TESTS = [{
  119. 'url': 'https://rtd.rt.com/series/i-am-hacked-trailer/',
  120. 'playlist_mincount': 6,
  121. 'info_dict': {
  122. 'id': 'i-am-hacked-trailer',
  123. },
  124. }, {
  125. 'url': 'https://rtd.rt.com/shows/the-kalashnikova-show-military-secrets-anna-knishenko/',
  126. 'playlist_mincount': 34,
  127. 'info_dict': {
  128. 'id': 'the-kalashnikova-show-military-secrets-anna-knishenko',
  129. },
  130. }]
  131. def _entries(self, webpage, playlist_id):
  132. video_urls = set(re.findall(r'list-2__link\s*"\s*href="([^"]+)"', webpage))
  133. for v_url in video_urls:
  134. if playlist_id not in v_url:
  135. continue
  136. yield self.url_result(
  137. f'https://rtd.rt.com{v_url}',
  138. ie=RTDocumentryIE.ie_key())
  139. def _real_extract(self, url):
  140. playlist_id = self._match_id(url)
  141. webpage = self._download_webpage(url, playlist_id)
  142. return {
  143. '_type': 'playlist',
  144. 'id': playlist_id,
  145. 'entries': self._entries(webpage, playlist_id),
  146. }
  147. class RuptlyIE(InfoExtractor):
  148. _VALID_URL = r'https?://(?:www\.)?ruptly\.tv/[a-z]{2}/videos/(?P<id>\d+-\d+)'
  149. _TESTS = [{
  150. 'url': 'https://www.ruptly.tv/en/videos/20220112-020-Japan-Double-trouble-Tokyo-zoo-presents-adorable-panda-twins',
  151. 'info_dict': {
  152. 'id': '20220112-020',
  153. 'ext': 'mp4',
  154. 'title': 'Japan: Double trouble! Tokyo zoo presents adorable panda twins | Video Ruptly',
  155. 'description': 'md5:85a8da5fdb31486f0562daf4360ce75a',
  156. 'thumbnail': 'https://storage.ruptly.tv/thumbnails/20220112-020/i6JQKnTNpYuqaXsR/i6JQKnTNpYuqaXsR.jpg',
  157. },
  158. 'params': {'skip_download': True},
  159. }]
  160. def _real_extract(self, url):
  161. video_id = self._match_id(url)
  162. webpage = self._download_webpage(url, video_id)
  163. m3u8_url = self._search_regex(r'preview_url"\s?:\s?"(https?://storage\.ruptly\.tv/video_projects/.+\.m3u8)"', webpage, 'm3u8 url', fatal=False)
  164. if not m3u8_url:
  165. self.raise_no_formats('No video/audio found at the provided url.', expected=True)
  166. formats, subs = self._extract_m3u8_formats_and_subtitles(m3u8_url, video_id, ext='mp4')
  167. return {
  168. 'id': video_id,
  169. 'formats': formats,
  170. 'subtitles': subs,
  171. 'title': self._og_search_title(webpage),
  172. 'description': self._og_search_description(webpage),
  173. 'thumbnail': self._og_search_thumbnail(webpage),
  174. }