logo

youtube-dl

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

vice.py (12414B)


  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import functools
  4. import hashlib
  5. import json
  6. import random
  7. import re
  8. import time
  9. from .adobepass import AdobePassIE
  10. from .common import InfoExtractor
  11. from .youtube import YoutubeIE
  12. from ..compat import (
  13. compat_HTTPError,
  14. compat_str,
  15. )
  16. from ..utils import (
  17. clean_html,
  18. ExtractorError,
  19. int_or_none,
  20. OnDemandPagedList,
  21. parse_age_limit,
  22. str_or_none,
  23. try_get,
  24. )
  25. class ViceBaseIE(InfoExtractor):
  26. def _call_api(self, resource, resource_key, resource_id, locale, fields, args=''):
  27. return self._download_json(
  28. 'https://video.vice.com/api/v1/graphql', resource_id, query={
  29. 'query': '''{
  30. %s(locale: "%s", %s: "%s"%s) {
  31. %s
  32. }
  33. }''' % (resource, locale, resource_key, resource_id, args, fields),
  34. })['data'][resource]
  35. class ViceIE(ViceBaseIE, AdobePassIE):
  36. IE_NAME = 'vice'
  37. _VALID_URL = r'https?://(?:(?:video|vms)\.vice|(?:www\.)?vice(?:land|tv))\.com/(?P<locale>[^/]+)/(?:video/[^/]+|embed)/(?P<id>[\da-f]{24})'
  38. _TESTS = [{
  39. 'url': 'https://video.vice.com/en_us/video/pet-cremator/58c69e38a55424f1227dc3f7',
  40. 'info_dict': {
  41. 'id': '58c69e38a55424f1227dc3f7',
  42. 'ext': 'mp4',
  43. 'title': '10 Questions You Always Wanted To Ask: Pet Cremator',
  44. 'description': 'md5:fe856caacf61fe0e74fab15ce2b07ca5',
  45. 'uploader': 'vice',
  46. 'uploader_id': '57a204088cb727dec794c67b',
  47. 'timestamp': 1489664942,
  48. 'upload_date': '20170316',
  49. 'age_limit': 14,
  50. },
  51. 'params': {
  52. # m3u8 download
  53. 'skip_download': True,
  54. },
  55. }, {
  56. # geo restricted to US
  57. 'url': 'https://video.vice.com/en_us/video/the-signal-from-tolva/5816510690b70e6c5fd39a56',
  58. 'info_dict': {
  59. 'id': '5816510690b70e6c5fd39a56',
  60. 'ext': 'mp4',
  61. 'uploader': 'vice',
  62. 'title': 'The Signal From Tölva',
  63. 'description': 'md5:3927e3c79f9e8094606a2b3c5b5e55d5',
  64. 'uploader_id': '57a204088cb727dec794c67b',
  65. 'timestamp': 1477941983,
  66. 'upload_date': '20161031',
  67. },
  68. 'params': {
  69. # m3u8 download
  70. 'skip_download': True,
  71. },
  72. }, {
  73. 'url': 'https://video.vice.com/alps/video/ulfs-wien-beruchtigste-grafitti-crew-part-1/581b12b60a0e1f4c0fb6ea2f',
  74. 'info_dict': {
  75. 'id': '581b12b60a0e1f4c0fb6ea2f',
  76. 'ext': 'mp4',
  77. 'title': 'ULFs - Wien berüchtigste Grafitti Crew - Part 1',
  78. 'description': 'Zwischen Hinterzimmer-Tattoos und U-Bahnschächten erzählen uns die Ulfs, wie es ist, "süchtig nach Sachbeschädigung" zu sein.',
  79. 'uploader': 'vice',
  80. 'uploader_id': '57a204088cb727dec794c67b',
  81. 'timestamp': 1485368119,
  82. 'upload_date': '20170125',
  83. 'age_limit': 14,
  84. },
  85. 'params': {
  86. # AES-encrypted m3u8
  87. 'skip_download': True,
  88. },
  89. }, {
  90. 'url': 'https://video.vice.com/en_us/video/pizza-show-trailer/56d8c9a54d286ed92f7f30e4',
  91. 'only_matching': True,
  92. }, {
  93. 'url': 'https://video.vice.com/en_us/embed/57f41d3556a0a80f54726060',
  94. 'only_matching': True,
  95. }, {
  96. 'url': 'https://vms.vice.com/en_us/video/preplay/58c69e38a55424f1227dc3f7',
  97. 'only_matching': True,
  98. }, {
  99. 'url': 'https://www.viceland.com/en_us/video/thursday-march-1-2018/5a8f2d7ff1cdb332dd446ec1',
  100. 'only_matching': True,
  101. }]
  102. @staticmethod
  103. def _extract_urls(webpage):
  104. return re.findall(
  105. r'<iframe\b[^>]+\bsrc=["\']((?:https?:)?//video\.vice\.com/[^/]+/embed/[\da-f]{24})',
  106. webpage)
  107. @staticmethod
  108. def _extract_url(webpage):
  109. urls = ViceIE._extract_urls(webpage)
  110. return urls[0] if urls else None
  111. def _real_extract(self, url):
  112. locale, video_id = re.match(self._VALID_URL, url).groups()
  113. video = self._call_api('videos', 'id', video_id, locale, '''body
  114. locked
  115. rating
  116. thumbnail_url
  117. title''')[0]
  118. title = video['title'].strip()
  119. rating = video.get('rating')
  120. query = {}
  121. if video.get('locked'):
  122. resource = self._get_mvpd_resource(
  123. 'VICELAND', title, video_id, rating)
  124. query['tvetoken'] = self._extract_mvpd_auth(
  125. url, video_id, 'VICELAND', resource)
  126. # signature generation algorithm is reverse engineered from signatureGenerator in
  127. # webpack:///../shared/~/vice-player/dist/js/vice-player.js in
  128. # https://www.viceland.com/assets/common/js/web.vendor.bundle.js
  129. # new JS is located here https://vice-web-statics-cdn.vice.com/vice-player/player-embed.js
  130. exp = int(time.time()) + 1440
  131. query.update({
  132. 'exp': exp,
  133. 'sign': hashlib.sha512(('%s:GET:%d' % (video_id, exp)).encode()).hexdigest(),
  134. 'skipadstitching': 1,
  135. 'platform': 'desktop',
  136. 'rn': random.randint(10000, 100000),
  137. })
  138. try:
  139. preplay = self._download_json(
  140. 'https://vms.vice.com/%s/video/preplay/%s' % (locale, video_id),
  141. video_id, query=query)
  142. except ExtractorError as e:
  143. if isinstance(e.cause, compat_HTTPError) and e.cause.code in (400, 401):
  144. error = json.loads(e.cause.read().decode())
  145. error_message = error.get('error_description') or error['details']
  146. raise ExtractorError('%s said: %s' % (
  147. self.IE_NAME, error_message), expected=True)
  148. raise
  149. video_data = preplay['video']
  150. formats = self._extract_m3u8_formats(
  151. preplay['playURL'], video_id, 'mp4', 'm3u8_native')
  152. self._sort_formats(formats)
  153. episode = video_data.get('episode') or {}
  154. channel = video_data.get('channel') or {}
  155. season = video_data.get('season') or {}
  156. subtitles = {}
  157. for subtitle in preplay.get('subtitleURLs', []):
  158. cc_url = subtitle.get('url')
  159. if not cc_url:
  160. continue
  161. language_code = try_get(subtitle, lambda x: x['languages'][0]['language_code'], compat_str) or 'en'
  162. subtitles.setdefault(language_code, []).append({
  163. 'url': cc_url,
  164. })
  165. return {
  166. 'formats': formats,
  167. 'id': video_id,
  168. 'title': title,
  169. 'description': clean_html(video.get('body')),
  170. 'thumbnail': video.get('thumbnail_url'),
  171. 'duration': int_or_none(video_data.get('video_duration')),
  172. 'timestamp': int_or_none(video_data.get('created_at'), 1000),
  173. 'age_limit': parse_age_limit(video_data.get('video_rating') or rating),
  174. 'series': try_get(video_data, lambda x: x['show']['base']['display_title'], compat_str),
  175. 'episode_number': int_or_none(episode.get('episode_number')),
  176. 'episode_id': str_or_none(episode.get('id') or video_data.get('episode_id')),
  177. 'season_number': int_or_none(season.get('season_number')),
  178. 'season_id': str_or_none(season.get('id') or video_data.get('season_id')),
  179. 'uploader': channel.get('name'),
  180. 'uploader_id': str_or_none(channel.get('id')),
  181. 'subtitles': subtitles,
  182. }
  183. class ViceShowIE(ViceBaseIE):
  184. IE_NAME = 'vice:show'
  185. _VALID_URL = r'https?://(?:video\.vice|(?:www\.)?vice(?:land|tv))\.com/(?P<locale>[^/]+)/show/(?P<id>[^/?#&]+)'
  186. _PAGE_SIZE = 25
  187. _TESTS = [{
  188. 'url': 'https://video.vice.com/en_us/show/fck-thats-delicious',
  189. 'info_dict': {
  190. 'id': '57a2040c8cb727dec794c901',
  191. 'title': 'F*ck, That’s Delicious',
  192. 'description': 'The life and eating habits of rap’s greatest bon vivant, Action Bronson.',
  193. },
  194. 'playlist_mincount': 64,
  195. }, {
  196. 'url': 'https://www.vicetv.com/en_us/show/fck-thats-delicious',
  197. 'only_matching': True,
  198. }]
  199. def _fetch_page(self, locale, show_id, page):
  200. videos = self._call_api('videos', 'show_id', show_id, locale, '''body
  201. id
  202. url''', ', page: %d, per_page: %d' % (page + 1, self._PAGE_SIZE))
  203. for video in videos:
  204. yield self.url_result(
  205. video['url'], ViceIE.ie_key(), video.get('id'))
  206. def _real_extract(self, url):
  207. locale, display_id = re.match(self._VALID_URL, url).groups()
  208. show = self._call_api('shows', 'slug', display_id, locale, '''dek
  209. id
  210. title''')[0]
  211. show_id = show['id']
  212. entries = OnDemandPagedList(
  213. functools.partial(self._fetch_page, locale, show_id),
  214. self._PAGE_SIZE)
  215. return self.playlist_result(
  216. entries, show_id, show.get('title'), show.get('dek'))
  217. class ViceArticleIE(ViceBaseIE):
  218. IE_NAME = 'vice:article'
  219. _VALID_URL = r'https://(?:www\.)?vice\.com/(?P<locale>[^/]+)/article/(?:[0-9a-z]{6}/)?(?P<id>[^?#]+)'
  220. _TESTS = [{
  221. 'url': 'https://www.vice.com/en_us/article/on-set-with-the-woman-making-mormon-porn-in-utah',
  222. 'info_dict': {
  223. 'id': '58dc0a3dee202d2a0ccfcbd8',
  224. 'ext': 'mp4',
  225. 'title': 'Mormon War on Porn',
  226. 'description': 'md5:1c5d91fe25fa8aa304f9def118b92dbf',
  227. 'uploader': 'vice',
  228. 'uploader_id': '57a204088cb727dec794c67b',
  229. 'timestamp': 1491883129,
  230. 'upload_date': '20170411',
  231. 'age_limit': 17,
  232. },
  233. 'params': {
  234. # AES-encrypted m3u8
  235. 'skip_download': True,
  236. },
  237. 'add_ie': [ViceIE.ie_key()],
  238. }, {
  239. 'url': 'https://www.vice.com/en_us/article/how-to-hack-a-car',
  240. 'md5': '13010ee0bc694ea87ec40724397c2349',
  241. 'info_dict': {
  242. 'id': '3jstaBeXgAs',
  243. 'ext': 'mp4',
  244. 'title': 'How to Hack a Car: Phreaked Out (Episode 2)',
  245. 'description': 'md5:ee95453f7ff495db8efe14ae8bf56f30',
  246. 'uploader': 'Motherboard',
  247. 'uploader_id': 'MotherboardTV',
  248. 'upload_date': '20140529',
  249. },
  250. 'add_ie': [YoutubeIE.ie_key()],
  251. }, {
  252. 'url': 'https://www.vice.com/en_us/article/znm9dx/karley-sciortino-slutever-reloaded',
  253. 'md5': 'a7ecf64ee4fa19b916c16f4b56184ae2',
  254. 'info_dict': {
  255. 'id': '57f41d3556a0a80f54726060',
  256. 'ext': 'mp4',
  257. 'title': "Making The World's First Male Sex Doll",
  258. 'description': 'md5:19b00b215b99961cf869c40fbe9df755',
  259. 'uploader': 'vice',
  260. 'uploader_id': '57a204088cb727dec794c67b',
  261. 'timestamp': 1476919911,
  262. 'upload_date': '20161019',
  263. 'age_limit': 17,
  264. },
  265. 'params': {
  266. 'skip_download': True,
  267. 'format': 'bestvideo',
  268. },
  269. 'add_ie': [ViceIE.ie_key()],
  270. }, {
  271. 'url': 'https://www.vice.com/en_us/article/cowboy-capitalists-part-1',
  272. 'only_matching': True,
  273. }, {
  274. 'url': 'https://www.vice.com/ru/article/big-night-out-ibiza-clive-martin-229',
  275. 'only_matching': True,
  276. }]
  277. def _real_extract(self, url):
  278. locale, display_id = re.match(self._VALID_URL, url).groups()
  279. article = self._call_api('articles', 'slug', display_id, locale, '''body
  280. embed_code''')[0]
  281. body = article['body']
  282. def _url_res(video_url, ie_key):
  283. return {
  284. '_type': 'url_transparent',
  285. 'url': video_url,
  286. 'display_id': display_id,
  287. 'ie_key': ie_key,
  288. }
  289. vice_url = ViceIE._extract_url(body)
  290. if vice_url:
  291. return _url_res(vice_url, ViceIE.ie_key())
  292. embed_code = self._search_regex(
  293. r'embedCode=([^&\'"]+)', body,
  294. 'ooyala embed code', default=None)
  295. if embed_code:
  296. return _url_res('ooyala:%s' % embed_code, 'Ooyala')
  297. youtube_url = YoutubeIE._extract_url(body)
  298. if youtube_url:
  299. return _url_res(youtube_url, YoutubeIE.ie_key())
  300. video_url = self._html_search_regex(
  301. r'data-video-url="([^"]+)"',
  302. article['embed_code'], 'video URL')
  303. return _url_res(video_url, ViceIE.ie_key())