logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

vidyard.py (18927B)


  1. import re
  2. from .common import InfoExtractor
  3. from ..utils import (
  4. extract_attributes,
  5. float_or_none,
  6. int_or_none,
  7. join_nonempty,
  8. mimetype2ext,
  9. parse_resolution,
  10. str_or_none,
  11. unescapeHTML,
  12. url_or_none,
  13. )
  14. from ..utils.traversal import traverse_obj
  15. class VidyardBaseIE(InfoExtractor):
  16. _HEADERS = {'Referer': 'https://play.vidyard.com/'}
  17. def _get_formats_and_subtitles(self, sources, video_id):
  18. formats, subtitles = [], {}
  19. def add_hls_fmts_and_subs(m3u8_url):
  20. fmts, subs = self._extract_m3u8_formats_and_subtitles(
  21. m3u8_url, video_id, 'mp4', m3u8_id='hls', headers=self._HEADERS, fatal=False)
  22. formats.extend(fmts)
  23. self._merge_subtitles(subs, target=subtitles)
  24. hls_list = isinstance(sources, dict) and sources.pop('hls', None)
  25. if master_m3u8_url := traverse_obj(
  26. hls_list, (lambda _, v: v['profile'] == 'auto', 'url', {url_or_none}, any)):
  27. add_hls_fmts_and_subs(master_m3u8_url)
  28. if not formats: # These are duplicate and unnecesary requests if we got 'auto' hls fmts
  29. for variant_m3u8_url in traverse_obj(hls_list, (..., 'url', {url_or_none})):
  30. add_hls_fmts_and_subs(variant_m3u8_url)
  31. for source_type, source_list in traverse_obj(sources, ({dict.items}, ...)):
  32. for source in traverse_obj(source_list, lambda _, v: url_or_none(v['url'])):
  33. profile = source.get('profile')
  34. formats.append({
  35. 'url': source['url'],
  36. 'ext': mimetype2ext(source.get('mimeType'), default=None),
  37. 'format_id': join_nonempty('http', source_type, profile),
  38. **parse_resolution(profile),
  39. })
  40. self._remove_duplicate_formats(formats)
  41. return formats, subtitles
  42. def _get_direct_subtitles(self, caption_json):
  43. subs = {}
  44. for caption in traverse_obj(caption_json, lambda _, v: url_or_none(v['vttUrl'])):
  45. subs.setdefault(caption.get('language') or 'und', []).append({
  46. 'url': caption['vttUrl'],
  47. 'name': caption.get('name'),
  48. })
  49. return subs
  50. def _fetch_video_json(self, video_id):
  51. return self._download_json(
  52. f'https://play.vidyard.com/player/{video_id}.json', video_id)['payload']
  53. def _process_video_json(self, json_data, video_id):
  54. formats, subtitles = self._get_formats_and_subtitles(json_data['sources'], video_id)
  55. self._merge_subtitles(self._get_direct_subtitles(json_data.get('captions')), target=subtitles)
  56. return {
  57. **traverse_obj(json_data, {
  58. 'id': ('facadeUuid', {str}),
  59. 'display_id': ('videoId', {int}, {str_or_none}),
  60. 'title': ('name', {str}),
  61. 'description': ('description', {str}, {unescapeHTML}, filter),
  62. 'duration': ((
  63. ('milliseconds', {float_or_none(scale=1000)}),
  64. ('seconds', {int_or_none})), any),
  65. 'thumbnails': ('thumbnailUrls', ('small', 'normal'), {'url': {url_or_none}}),
  66. 'tags': ('tags', ..., 'name', {str}),
  67. }),
  68. 'formats': formats,
  69. 'subtitles': subtitles,
  70. 'http_headers': self._HEADERS,
  71. }
  72. class VidyardIE(VidyardBaseIE):
  73. _VALID_URL = [
  74. r'https?://[\w-]+(?:\.hubs)?\.vidyard\.com/watch/(?P<id>[\w-]+)',
  75. r'https?://(?:embed|share)\.vidyard\.com/share/(?P<id>[\w-]+)',
  76. r'https?://play\.vidyard\.com/(?:player/)?(?P<id>[\w-]+)',
  77. ]
  78. _EMBED_REGEX = [r'<iframe[^>]* src=["\'](?P<url>(?:https?:)?//play\.vidyard\.com/[\w-]+)']
  79. _TESTS = [{
  80. 'url': 'https://vyexample03.hubs.vidyard.com/watch/oTDMPlUv--51Th455G5u7Q',
  81. 'info_dict': {
  82. 'id': 'oTDMPlUv--51Th455G5u7Q',
  83. 'display_id': '50347',
  84. 'ext': 'mp4',
  85. 'title': 'Homepage Video',
  86. 'description': 'Look I changed the description.',
  87. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/50347/OUPa5LTKV46849sLYngMqQ_small.jpg',
  88. 'duration': 99,
  89. 'tags': ['these', 'are', 'all', 'tags'],
  90. },
  91. }, {
  92. 'url': 'https://share.vidyard.com/watch/PaQzDAT1h8JqB8ivEu2j6Y?',
  93. 'info_dict': {
  94. 'id': 'PaQzDAT1h8JqB8ivEu2j6Y',
  95. 'display_id': '9281024',
  96. 'ext': 'mp4',
  97. 'title': 'Inline Embed',
  98. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/spacer.gif',
  99. 'duration': 41.186,
  100. },
  101. }, {
  102. 'url': 'https://embed.vidyard.com/share/oTDMPlUv--51Th455G5u7Q',
  103. 'info_dict': {
  104. 'id': 'oTDMPlUv--51Th455G5u7Q',
  105. 'display_id': '50347',
  106. 'ext': 'mp4',
  107. 'title': 'Homepage Video',
  108. 'description': 'Look I changed the description.',
  109. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/50347/OUPa5LTKV46849sLYngMqQ_small.jpg',
  110. 'duration': 99,
  111. 'tags': ['these', 'are', 'all', 'tags'],
  112. },
  113. }, {
  114. # First video from playlist below
  115. 'url': 'https://embed.vidyard.com/share/SyStyHtYujcBHe5PkZc5DL',
  116. 'info_dict': {
  117. 'id': 'SyStyHtYujcBHe5PkZc5DL',
  118. 'display_id': '41974005',
  119. 'ext': 'mp4',
  120. 'title': 'Prepare the Frame and Track for Palm Beach Polysatin Shutters With BiFold Track',
  121. 'description': r're:In this video, you will learn how to prepare the frame.+',
  122. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/41974005/IJw7oCaJcF1h7WWu3OVZ8A_small.png',
  123. 'duration': 258.666,
  124. },
  125. }, {
  126. # Playlist
  127. 'url': 'https://thelink.hubs.vidyard.com/watch/pwu7pCYWSwAnPxs8nDoFrE',
  128. 'info_dict': {
  129. 'id': 'pwu7pCYWSwAnPxs8nDoFrE',
  130. 'title': 'PLAYLIST - Palm Beach Shutters- Bi-Fold Track System Installation',
  131. 'entries': [{
  132. 'id': 'SyStyHtYujcBHe5PkZc5DL',
  133. 'display_id': '41974005',
  134. 'ext': 'mp4',
  135. 'title': 'Prepare the Frame and Track for Palm Beach Polysatin Shutters With BiFold Track',
  136. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/41974005/IJw7oCaJcF1h7WWu3OVZ8A_small.png',
  137. 'duration': 258.666,
  138. }, {
  139. 'id': '1Fw4B84jZTXLXWqkE71RiM',
  140. 'display_id': '5861113',
  141. 'ext': 'mp4',
  142. 'title': 'Palm Beach - Bi-Fold Track System "Frame Installation"',
  143. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/5861113/29CJ54s5g1_aP38zkKLHew_small.jpg',
  144. 'duration': 167.858,
  145. }, {
  146. 'id': 'DqP3wBvLXSpxrcqpT5kEeo',
  147. 'display_id': '41976334',
  148. 'ext': 'mp4',
  149. 'title': 'Install the Track for Palm Beach Polysatin Shutters With BiFold Track',
  150. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/5861090/RwG2VaTylUa6KhSTED1r1Q_small.png',
  151. 'duration': 94.229,
  152. }, {
  153. 'id': 'opfybfxpzQArxqtQYB6oBU',
  154. 'display_id': '41976364',
  155. 'ext': 'mp4',
  156. 'title': 'Install the Panel for Palm Beach Polysatin Shutters With BiFold Track',
  157. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/5860926/JIOaJR08dM4QgXi_iQ2zGA_small.png',
  158. 'duration': 191.467,
  159. }, {
  160. 'id': 'rWrXvkbTNNaNqD6189HJya',
  161. 'display_id': '41976382',
  162. 'ext': 'mp4',
  163. 'title': 'Adjust the Panels for Palm Beach Polysatin Shutters With BiFold Track',
  164. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/5860687/CwHxBv4UudAhOh43FVB4tw_small.png',
  165. 'duration': 138.155,
  166. }, {
  167. 'id': 'eYPTB521MZ9TPEArSethQ5',
  168. 'display_id': '41976409',
  169. 'ext': 'mp4',
  170. 'title': 'Assemble and Install the Valance for Palm Beach Polysatin Shutters With BiFold Track',
  171. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/5861425/0y68qlMU4O5VKU7bJ8i_AA_small.png',
  172. 'duration': 148.224,
  173. }],
  174. },
  175. 'playlist_count': 6,
  176. }, {
  177. # Non hubs.vidyard.com playlist
  178. 'url': 'https://salesforce.vidyard.com/watch/d4vqPjs7Q5EzVEis5QT3jd',
  179. 'info_dict': {
  180. 'id': 'd4vqPjs7Q5EzVEis5QT3jd',
  181. 'title': 'How To: Service Cloud: Import External Content in Lightning Knowledge',
  182. 'entries': [{
  183. 'id': 'mcjDpSZir2iSttbvFkx6Rv',
  184. 'display_id': '29479036',
  185. 'ext': 'mp4',
  186. 'title': 'Welcome to this Expert Coaching Series',
  187. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/ouyQi9WuwyiOupChUWNmjQ/7170d3485ba602e012df05_small.jpg',
  188. 'duration': 38.205,
  189. }, {
  190. 'id': '84bPYwpg243G6xYEfJdYw9',
  191. 'display_id': '21820704',
  192. 'ext': 'mp4',
  193. 'title': 'Chapter 1 - Title + Agenda',
  194. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/HFPN0ZgQq4Ow8BghGcQSow/bfaa30123c8f6601e7d7f2_small.jpg',
  195. 'duration': 98.016,
  196. }, {
  197. 'id': 'nP17fMuvA66buVHUrzqjTi',
  198. 'display_id': '21820707',
  199. 'ext': 'mp4',
  200. 'title': 'Chapter 2 - Import Options',
  201. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/rGRIF5nFjPI9OOA2qJ_Dbg/86a8d02bfec9a566845dd4_small.jpg',
  202. 'duration': 199.136,
  203. }, {
  204. 'id': 'm54EcwXdpA5gDBH5rgCYoV',
  205. 'display_id': '21820710',
  206. 'ext': 'mp4',
  207. 'title': 'Chapter 3 - Importing Article Translations',
  208. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/IVX4XR8zpSsiNIHx45kz-A/1ccbf8a29a33856d06b3ed_small.jpg',
  209. 'duration': 184.352,
  210. }, {
  211. 'id': 'j4nzS42oq4hE9oRV73w3eQ',
  212. 'display_id': '21820716',
  213. 'ext': 'mp4',
  214. 'title': 'Chapter 4 - Best Practices',
  215. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/BtrRrQpRDLbA4AT95YQyog/1f1e6b8e7fdc3fa95ec8d3_small.jpg',
  216. 'duration': 296.960,
  217. }, {
  218. 'id': 'y28PYfW5pftvers9PXzisC',
  219. 'display_id': '21820727',
  220. 'ext': 'mp4',
  221. 'title': 'Chapter 5 - Migration Steps',
  222. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/K2CdQOXDfLcrVTF60r0bdw/a09239ada28b6ffce12b1f_small.jpg',
  223. 'duration': 620.640,
  224. }, {
  225. 'id': 'YWU1eQxYvhj29SjYoPw5jH',
  226. 'display_id': '21820733',
  227. 'ext': 'mp4',
  228. 'title': 'Chapter 6 - Demo',
  229. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/rsmhP-cO8dAa8ilvFGCX0g/7911ef415167cd14032068_small.jpg',
  230. 'duration': 631.456,
  231. }, {
  232. 'id': 'nmEvVqpwdJUgb74zKsLGxn',
  233. 'display_id': '29479037',
  234. 'ext': 'mp4',
  235. 'title': 'Schedule Your Follow-Up',
  236. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/Rtwc7X4PEkF4Ae5kHi-Jvw/174ebed3f34227b1ffa1d0_small.jpg',
  237. 'duration': 33.608,
  238. }],
  239. },
  240. 'playlist_count': 8,
  241. }, {
  242. # URL of iframe embed src
  243. 'url': 'https://play.vidyard.com/iDqTwWGrd36vaLuaCY3nTs.html',
  244. 'info_dict': {
  245. 'id': 'iDqTwWGrd36vaLuaCY3nTs',
  246. 'display_id': '9281009',
  247. 'ext': 'mp4',
  248. 'title': 'Lightbox Embed',
  249. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/spacer.gif',
  250. 'duration': 39.035,
  251. },
  252. }, {
  253. # Player JSON URL
  254. 'url': 'https://play.vidyard.com/player/7GAApnNNbcZZ46k6JqJQSh.json?disable_analytics=0',
  255. 'info_dict': {
  256. 'id': '7GAApnNNbcZZ46k6JqJQSh',
  257. 'display_id': '820026',
  258. 'ext': 'mp4',
  259. 'title': 'The Art of Storytelling: How to Deliver Your Brand Story with Content & Social',
  260. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/MhbE-5sEFQu4x3fI6FkNlA/41eb5717c557cd19456910_small.jpg',
  261. 'duration': 2153.013,
  262. 'tags': ['Summit2017'],
  263. },
  264. }, {
  265. 'url': 'http://share.vidyard.com/share/diYeo6YR2yiGgL8odvS8Ri',
  266. 'only_matching': True,
  267. }, {
  268. 'url': 'https://play.vidyard.com/FFlz3ZpxhIfKQ1fd9DAryA',
  269. 'only_matching': True,
  270. }, {
  271. 'url': 'https://play.vidyard.com/qhMAu5A76GZVrFzOPgSf9A/type/standalone',
  272. 'only_matching': True,
  273. }]
  274. _WEBPAGE_TESTS = [{
  275. # URL containing inline/lightbox embedded video
  276. 'url': 'https://resources.altium.com/p/2-the-extreme-importance-of-pc-board-stack-up',
  277. 'info_dict': {
  278. 'id': 'GDx1oXrFWj4XHbipfoXaMn',
  279. 'display_id': '3225198',
  280. 'ext': 'mp4',
  281. 'title': 'The Extreme Importance of PC Board Stack Up',
  282. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/73_Q3_hBexWX7Og1sae6cg/9998fa4faec921439e2c04_small.jpg',
  283. 'duration': 3422.742,
  284. },
  285. }, {
  286. # <script ... id="vidyard_embed_code_DXx2sW4WaLA6hTdGFz7ja8" src="//play.vidyard.com/DXx2sW4WaLA6hTdGFz7ja8.js?
  287. 'url': 'http://videos.vivint.com/watch/DXx2sW4WaLA6hTdGFz7ja8',
  288. 'info_dict': {
  289. 'id': 'DXx2sW4WaLA6hTdGFz7ja8',
  290. 'display_id': '2746529',
  291. 'ext': 'mp4',
  292. 'title': 'How To Powercycle the Smart Hub Panel',
  293. 'duration': 30.613,
  294. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/_-6cw8xQUJ3qiCs_JENc_A/b21d7a5e47967f49399d30_small.jpg',
  295. },
  296. }, {
  297. # <script id="vidyard_embed_code_MIBHhiLVTxga7wqLsuoDjQ" src="//embed.vidyard.com/embed/MIBHhiLVTxga7wqLsuoDjQ/inline?v=2.1">
  298. 'url': 'https://www.babypips.com/learn/forex/introduction-to-metatrader4',
  299. 'info_dict': {
  300. 'id': 'MIBHhiLVTxga7wqLsuoDjQ',
  301. 'display_id': '20291',
  302. 'ext': 'mp4',
  303. 'title': 'Lesson 1 - Opening an MT4 Account',
  304. 'description': 'Never heard of MetaTrader4? Here\'s the 411 on the popular trading platform!',
  305. 'duration': 168,
  306. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/20291/IM-G2WXQR9VBLl2Cmzvftg_small.jpg',
  307. },
  308. }, {
  309. # <iframe ... src="//play.vidyard.com/d61w8EQoZv1LDuPxDkQP2Q/type/background?preview=1"
  310. 'url': 'https://www.avaya.com/en/',
  311. 'info_dict': {
  312. # These values come from the generic extractor and don't matter
  313. 'id': str,
  314. 'title': str,
  315. 'age_limit': 0,
  316. 'upload_date': str,
  317. 'description': str,
  318. 'thumbnail': str,
  319. 'timestamp': float,
  320. },
  321. 'playlist': [{
  322. 'info_dict': {
  323. 'id': 'd61w8EQoZv1LDuPxDkQP2Q',
  324. 'display_id': '42456529',
  325. 'ext': 'mp4',
  326. 'title': 'GettyImages-1027',
  327. 'duration': 6.0,
  328. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/42061563/p6bY08d2N4e4IDz-7J4_wkgsPq3-qgcx_small.jpg',
  329. },
  330. }, {
  331. 'info_dict': {
  332. 'id': 'VAsYDi7eiqZRbHodUA2meC',
  333. 'display_id': '42456569',
  334. 'ext': 'mp4',
  335. 'title': 'GettyImages-1325598833',
  336. 'duration': 6.083,
  337. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/42052358/y3qrbDpn_2quWr_5XBi7yzS3UvEI__ZM_small.jpg',
  338. },
  339. }],
  340. 'playlist_count': 2,
  341. }, {
  342. # <div class="vidyard-player-embed" data-uuid="vpCWTVHw3qrciLtVY94YkS"
  343. 'url': 'https://www.gogoair.com/',
  344. 'info_dict': {
  345. # These values come from the generic extractor and don't matter
  346. 'id': str,
  347. 'title': str,
  348. 'description': str,
  349. 'age_limit': 0,
  350. },
  351. 'playlist': [{
  352. 'info_dict': {
  353. 'id': 'vpCWTVHw3qrciLtVY94YkS',
  354. 'display_id': '40780699',
  355. 'ext': 'mp4',
  356. 'title': 'Upgrade to AVANCE 100% worth it - Jason Talley, Owner and Pilot, Testimonial',
  357. 'description': 'md5:f609824839439a51990cef55ffc472aa',
  358. 'duration': 70.737,
  359. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/40780699/KzjfYZz5MZl2gHF_e-4i2c6ib1cLDweQ_small.jpg',
  360. },
  361. }, {
  362. 'info_dict': {
  363. 'id': 'xAmV9AsLbnitCw35paLBD8',
  364. 'display_id': '31130867',
  365. 'ext': 'mp4',
  366. 'title': 'Brad Keselowski goes faster with Gogo AVANCE inflight Wi-Fi',
  367. 'duration': 132.565,
  368. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/31130867/HknyDtLdm2Eih9JZ4A5XLjhfBX_6HRw5_small.jpg',
  369. },
  370. }, {
  371. 'info_dict': {
  372. 'id': 'RkkrFRNxfP79nwCQavecpF',
  373. 'display_id': '39009815',
  374. 'ext': 'mp4',
  375. 'title': 'Live Demo of Gogo Galileo',
  376. 'description': 'md5:e2df497236f4e12c3fef8b392b5f23e0',
  377. 'duration': 112.128,
  378. 'thumbnail': 'https://cdn.vidyard.com/thumbnails/38144873/CWLlxfUbJ4Gh0ThbUum89IsEM4yupzMb_small.jpg',
  379. },
  380. }],
  381. 'playlist_count': 3,
  382. }]
  383. @classmethod
  384. def _extract_embed_urls(cls, url, webpage):
  385. # Handle protocol-less embed URLs
  386. for embed_url in super()._extract_embed_urls(url, webpage):
  387. if embed_url.startswith('//'):
  388. embed_url = f'https:{embed_url}'
  389. yield embed_url
  390. # Extract inline/lightbox embeds
  391. for embed_element in re.findall(
  392. r'(<(?:img|div)[^>]* class=(["\'])(?:[^>"\']* )?vidyard-player-embed(?: [^>"\']*)?\2[^>]+>)', webpage):
  393. if video_id := extract_attributes(embed_element[0]).get('data-uuid'):
  394. yield f'https://play.vidyard.com/{video_id}'
  395. for embed_id in re.findall(r'<script[^>]* id=["\']vidyard_embed_code_([\w-]+)["\']', webpage):
  396. yield f'https://play.vidyard.com/{embed_id}'
  397. def _real_extract(self, url):
  398. video_id = self._match_id(url)
  399. video_json = self._fetch_video_json(video_id)
  400. if len(video_json['chapters']) == 1:
  401. return self._process_video_json(video_json['chapters'][0], video_id)
  402. return self.playlist_result(
  403. (self._process_video_json(chapter, video_id) for chapter in video_json['chapters']),
  404. str(video_json['playerUuid']), video_json.get('name'))