_find_jwplayer_data() returns dict or None
[youtube-dl] / youtube_dl / extractor / generic.py
1 # coding: utf-8
2
3 from __future__ import unicode_literals
4
5 import os
6 import re
7 import sys
8
9 from .common import InfoExtractor
10 from .youtube import YoutubeIE
11 from ..compat import (
12     compat_etree_fromstring,
13     compat_urllib_parse_unquote,
14     compat_urlparse,
15     compat_xml_parse_error,
16 )
17 from ..utils import (
18     determine_ext,
19     ExtractorError,
20     float_or_none,
21     HEADRequest,
22     is_html,
23     js_to_json,
24     orderedSet,
25     sanitized_Request,
26     smuggle_url,
27     unescapeHTML,
28     unified_strdate,
29     unsmuggle_url,
30     UnsupportedError,
31     xpath_text,
32 )
33 from .commonprotocols import RtmpIE
34 from .brightcove import (
35     BrightcoveLegacyIE,
36     BrightcoveNewIE,
37 )
38 from .nbc import NBCSportsVPlayerIE
39 from .ooyala import OoyalaIE
40 from .rutv import RUTVIE
41 from .tvc import TVCIE
42 from .sportbox import SportBoxEmbedIE
43 from .smotri import SmotriIE
44 from .myvi import MyviIE
45 from .condenast import CondeNastIE
46 from .udn import UDNEmbedIE
47 from .senateisvp import SenateISVPIE
48 from .svt import SVTIE
49 from .pornhub import PornHubIE
50 from .xhamster import XHamsterEmbedIE
51 from .tnaflix import TNAFlixNetworkEmbedIE
52 from .drtuber import DrTuberIE
53 from .redtube import RedTubeIE
54 from .vimeo import VimeoIE
55 from .dailymotion import (
56     DailymotionIE,
57     DailymotionCloudIE,
58 )
59 from .onionstudios import OnionStudiosIE
60 from .viewlift import ViewLiftEmbedIE
61 from .mtv import MTVServicesEmbeddedIE
62 from .pladform import PladformIE
63 from .videomore import VideomoreIE
64 from .webcaster import WebcasterFeedIE
65 from .googledrive import GoogleDriveIE
66 from .jwplatform import JWPlatformIE
67 from .digiteka import DigitekaIE
68 from .arkena import ArkenaIE
69 from .instagram import InstagramIE
70 from .liveleak import LiveLeakIE
71 from .threeqsdn import ThreeQSDNIE
72 from .theplatform import ThePlatformIE
73 from .vessel import VesselIE
74 from .kaltura import KalturaIE
75 from .eagleplatform import EaglePlatformIE
76 from .facebook import FacebookIE
77 from .soundcloud import SoundcloudIE
78 from .tunein import TuneInBaseIE
79 from .vbox7 import Vbox7IE
80 from .dbtv import DBTVIE
81 from .piksel import PikselIE
82 from .videa import VideaIE
83 from .twentymin import TwentyMinutenIE
84 from .ustream import UstreamIE
85 from .openload import OpenloadIE
86 from .videopress import VideoPressIE
87
88
89 class GenericIE(InfoExtractor):
90     IE_DESC = 'Generic downloader that works on some sites'
91     _VALID_URL = r'.*'
92     IE_NAME = 'generic'
93     _TESTS = [
94         # Direct link to a video
95         {
96             'url': 'http://media.w3.org/2010/05/sintel/trailer.mp4',
97             'md5': '67d406c2bcb6af27fa886f31aa934bbe',
98             'info_dict': {
99                 'id': 'trailer',
100                 'ext': 'mp4',
101                 'title': 'trailer',
102                 'upload_date': '20100513',
103             }
104         },
105         # Direct link to media delivered compressed (until Accept-Encoding is *)
106         {
107             'url': 'http://calimero.tk/muzik/FictionJunction-Parallel_Hearts.flac',
108             'md5': '128c42e68b13950268b648275386fc74',
109             'info_dict': {
110                 'id': 'FictionJunction-Parallel_Hearts',
111                 'ext': 'flac',
112                 'title': 'FictionJunction-Parallel_Hearts',
113                 'upload_date': '20140522',
114             },
115             'expected_warnings': [
116                 'URL could be a direct video link, returning it as such.'
117             ],
118             'skip': 'URL invalid',
119         },
120         # Direct download with broken HEAD
121         {
122             'url': 'http://ai-radio.org:8000/radio.opus',
123             'info_dict': {
124                 'id': 'radio',
125                 'ext': 'opus',
126                 'title': 'radio',
127             },
128             'params': {
129                 'skip_download': True,  # infinite live stream
130             },
131             'expected_warnings': [
132                 r'501.*Not Implemented',
133                 r'400.*Bad Request',
134             ],
135         },
136         # Direct link with incorrect MIME type
137         {
138             'url': 'http://ftp.nluug.nl/video/nluug/2014-11-20_nj14/zaal-2/5_Lennart_Poettering_-_Systemd.webm',
139             'md5': '4ccbebe5f36706d85221f204d7eb5913',
140             'info_dict': {
141                 'url': 'http://ftp.nluug.nl/video/nluug/2014-11-20_nj14/zaal-2/5_Lennart_Poettering_-_Systemd.webm',
142                 'id': '5_Lennart_Poettering_-_Systemd',
143                 'ext': 'webm',
144                 'title': '5_Lennart_Poettering_-_Systemd',
145                 'upload_date': '20141120',
146             },
147             'expected_warnings': [
148                 'URL could be a direct video link, returning it as such.'
149             ]
150         },
151         # RSS feed
152         {
153             'url': 'http://phihag.de/2014/youtube-dl/rss2.xml',
154             'info_dict': {
155                 'id': 'http://phihag.de/2014/youtube-dl/rss2.xml',
156                 'title': 'Zero Punctuation',
157                 'description': 're:.*groundbreaking video review series.*'
158             },
159             'playlist_mincount': 11,
160         },
161         # RSS feed with enclosure
162         {
163             'url': 'http://podcastfeeds.nbcnews.com/audio/podcast/MSNBC-MADDOW-NETCAST-M4V.xml',
164             'info_dict': {
165                 'id': 'pdv_maddow_netcast_m4v-02-27-2015-201624',
166                 'ext': 'm4v',
167                 'upload_date': '20150228',
168                 'title': 'pdv_maddow_netcast_m4v-02-27-2015-201624',
169             }
170         },
171         # SMIL from http://videolectures.net/promogram_igor_mekjavic_eng
172         {
173             'url': 'http://videolectures.net/promogram_igor_mekjavic_eng/video/1/smil.xml',
174             'info_dict': {
175                 'id': 'smil',
176                 'ext': 'mp4',
177                 'title': 'Automatics, robotics and biocybernetics',
178                 'description': 'md5:815fc1deb6b3a2bff99de2d5325be482',
179                 'upload_date': '20130627',
180                 'formats': 'mincount:16',
181                 'subtitles': 'mincount:1',
182             },
183             'params': {
184                 'force_generic_extractor': True,
185                 'skip_download': True,
186             },
187         },
188         # SMIL from http://www1.wdr.de/mediathek/video/livestream/index.html
189         {
190             'url': 'http://metafilegenerator.de/WDR/WDR_FS/hds/hds.smil',
191             'info_dict': {
192                 'id': 'hds',
193                 'ext': 'flv',
194                 'title': 'hds',
195                 'formats': 'mincount:1',
196             },
197             'params': {
198                 'skip_download': True,
199             },
200         },
201         # SMIL from https://www.restudy.dk/video/play/id/1637
202         {
203             'url': 'https://www.restudy.dk/awsmedia/SmilDirectory/video_1637.xml',
204             'info_dict': {
205                 'id': 'video_1637',
206                 'ext': 'flv',
207                 'title': 'video_1637',
208                 'formats': 'mincount:3',
209             },
210             'params': {
211                 'skip_download': True,
212             },
213         },
214         # SMIL from http://adventure.howstuffworks.com/5266-cool-jobs-iditarod-musher-video.htm
215         {
216             'url': 'http://services.media.howstuffworks.com/videos/450221/smil-service.smil',
217             'info_dict': {
218                 'id': 'smil-service',
219                 'ext': 'flv',
220                 'title': 'smil-service',
221                 'formats': 'mincount:1',
222             },
223             'params': {
224                 'skip_download': True,
225             },
226         },
227         # SMIL from http://new.livestream.com/CoheedandCambria/WebsterHall/videos/4719370
228         {
229             'url': 'http://api.new.livestream.com/accounts/1570303/events/1585861/videos/4719370.smil',
230             'info_dict': {
231                 'id': '4719370',
232                 'ext': 'mp4',
233                 'title': '571de1fd-47bc-48db-abf9-238872a58d1f',
234                 'formats': 'mincount:3',
235             },
236             'params': {
237                 'skip_download': True,
238             },
239         },
240         # XSPF playlist from http://www.telegraaf.nl/tv/nieuws/binnenland/24353229/__Tikibad_ontruimd_wegens_brand__.html
241         {
242             'url': 'http://www.telegraaf.nl/xml/playlist/2015/8/7/mZlp2ctYIUEB.xspf',
243             'info_dict': {
244                 'id': 'mZlp2ctYIUEB',
245                 'ext': 'mp4',
246                 'title': 'Tikibad ontruimd wegens brand',
247                 'description': 'md5:05ca046ff47b931f9b04855015e163a4',
248                 'thumbnail': r're:^https?://.*\.jpg$',
249                 'duration': 33,
250             },
251             'params': {
252                 'skip_download': True,
253             },
254         },
255         # MPD from http://dash-mse-test.appspot.com/media.html
256         {
257             'url': 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd',
258             'md5': '4b57baab2e30d6eb3a6a09f0ba57ef53',
259             'info_dict': {
260                 'id': 'car-20120827-manifest',
261                 'ext': 'mp4',
262                 'title': 'car-20120827-manifest',
263                 'formats': 'mincount:9',
264                 'upload_date': '20130904',
265             },
266             'params': {
267                 'format': 'bestvideo',
268             },
269         },
270         # m3u8 served with Content-Type: audio/x-mpegURL; charset=utf-8
271         {
272             'url': 'http://once.unicornmedia.com/now/master/playlist/bb0b18ba-64f5-4b1b-a29f-0ac252f06b68/77a785f3-5188-4806-b788-0893a61634ed/93677179-2d99-4ef4-9e17-fe70d49abfbf/content.m3u8',
273             'info_dict': {
274                 'id': 'content',
275                 'ext': 'mp4',
276                 'title': 'content',
277                 'formats': 'mincount:8',
278             },
279             'params': {
280                 # m3u8 downloads
281                 'skip_download': True,
282             },
283             'skip': 'video gone',
284         },
285         # m3u8 served with Content-Type: text/plain
286         {
287             'url': 'http://www.nacentapps.com/m3u8/index.m3u8',
288             'info_dict': {
289                 'id': 'index',
290                 'ext': 'mp4',
291                 'title': 'index',
292                 'upload_date': '20140720',
293                 'formats': 'mincount:11',
294             },
295             'params': {
296                 # m3u8 downloads
297                 'skip_download': True,
298             },
299             'skip': 'video gone',
300         },
301         # google redirect
302         {
303             'url': 'http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CCUQtwIwAA&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DcmQHVoWB5FY&ei=F-sNU-LLCaXk4QT52ICQBQ&usg=AFQjCNEw4hL29zgOohLXvpJ-Bdh2bils1Q&bvm=bv.61965928,d.bGE',
304             'info_dict': {
305                 'id': 'cmQHVoWB5FY',
306                 'ext': 'mp4',
307                 'upload_date': '20130224',
308                 'uploader_id': 'TheVerge',
309                 'description': r're:^Chris Ziegler takes a look at the\.*',
310                 'uploader': 'The Verge',
311                 'title': 'First Firefox OS phones side-by-side',
312             },
313             'params': {
314                 'skip_download': False,
315             }
316         },
317         {
318             # redirect in Refresh HTTP header
319             'url': 'https://www.facebook.com/l.php?u=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DpO8h3EaFRdo&h=TAQHsoToz&enc=AZN16h-b6o4Zq9pZkCCdOLNKMN96BbGMNtcFwHSaazus4JHT_MFYkAA-WARTX2kvsCIdlAIyHZjl6d33ILIJU7Jzwk_K3mcenAXoAzBNoZDI_Q7EXGDJnIhrGkLXo_LJ_pAa2Jzbx17UHMd3jAs--6j2zaeto5w9RTn8T_1kKg3fdC5WPX9Dbb18vzH7YFX0eSJmoa6SP114rvlkw6pkS1-T&s=1',
320             'info_dict': {
321                 'id': 'pO8h3EaFRdo',
322                 'ext': 'mp4',
323                 'title': 'Tripeo Boiler Room x Dekmantel Festival DJ Set',
324                 'description': 'md5:6294cc1af09c4049e0652b51a2df10d5',
325                 'upload_date': '20150917',
326                 'uploader_id': 'brtvofficial',
327                 'uploader': 'Boiler Room',
328             },
329             'params': {
330                 'skip_download': False,
331             },
332         },
333         {
334             'url': 'http://www.hodiho.fr/2013/02/regis-plante-sa-jeep.html',
335             'md5': '85b90ccc9d73b4acd9138d3af4c27f89',
336             'info_dict': {
337                 'id': '13601338388002',
338                 'ext': 'mp4',
339                 'uploader': 'www.hodiho.fr',
340                 'title': 'R\u00e9gis plante sa Jeep',
341             }
342         },
343         # bandcamp page with custom domain
344         {
345             'add_ie': ['Bandcamp'],
346             'url': 'http://bronyrock.com/track/the-pony-mash',
347             'info_dict': {
348                 'id': '3235767654',
349                 'ext': 'mp3',
350                 'title': 'The Pony Mash',
351                 'uploader': 'M_Pallante',
352             },
353             'skip': 'There is a limit of 200 free downloads / month for the test song',
354         },
355         {
356             # embedded brightcove video
357             # it also tests brightcove videos that need to set the 'Referer'
358             # in the http requests
359             'add_ie': ['BrightcoveLegacy'],
360             'url': 'http://www.bfmtv.com/video/bfmbusiness/cours-bourse/cours-bourse-l-analyse-technique-154522/',
361             'info_dict': {
362                 'id': '2765128793001',
363                 'ext': 'mp4',
364                 'title': 'Le cours de bourse : l’analyse technique',
365                 'description': 'md5:7e9ad046e968cb2d1114004aba466fd9',
366                 'uploader': 'BFM BUSINESS',
367             },
368             'params': {
369                 'skip_download': True,
370             },
371         },
372         {
373             # embedded with itemprop embedURL and video id spelled as `idVideo`
374             'add_id': ['BrightcoveLegacy'],
375             'url': 'http://bfmbusiness.bfmtv.com/mediaplayer/chroniques/olivier-delamarche/',
376             'info_dict': {
377                 'id': '5255628253001',
378                 'ext': 'mp4',
379                 'title': 'md5:37c519b1128915607601e75a87995fc0',
380                 'description': 'md5:37f7f888b434bb8f8cc8dbd4f7a4cf26',
381                 'uploader': 'BFM BUSINESS',
382                 'uploader_id': '876450612001',
383                 'timestamp': 1482255315,
384                 'upload_date': '20161220',
385             },
386             'params': {
387                 'skip_download': True,
388             },
389         },
390         {
391             # https://github.com/rg3/youtube-dl/issues/2253
392             'url': 'http://bcove.me/i6nfkrc3',
393             'md5': '0ba9446db037002366bab3b3eb30c88c',
394             'info_dict': {
395                 'id': '3101154703001',
396                 'ext': 'mp4',
397                 'title': 'Still no power',
398                 'uploader': 'thestar.com',
399                 'description': 'Mississauga resident David Farmer is still out of power as a result of the ice storm a month ago. To keep the house warm, Farmer cuts wood from his property for a wood burning stove downstairs.',
400             },
401             'add_ie': ['BrightcoveLegacy'],
402             'skip': 'video gone',
403         },
404         {
405             'url': 'http://www.championat.com/video/football/v/87/87499.html',
406             'md5': 'fb973ecf6e4a78a67453647444222983',
407             'info_dict': {
408                 'id': '3414141473001',
409                 'ext': 'mp4',
410                 'title': 'Видео. Удаление Дзагоева (ЦСКА)',
411                 'description': 'Онлайн-трансляция матча ЦСКА - "Волга"',
412                 'uploader': 'Championat',
413             },
414         },
415         {
416             # https://github.com/rg3/youtube-dl/issues/3541
417             'add_ie': ['BrightcoveLegacy'],
418             'url': 'http://www.kijk.nl/sbs6/leermijvrouwenkennen/videos/jqMiXKAYan2S/aflevering-1',
419             'info_dict': {
420                 'id': '3866516442001',
421                 'ext': 'mp4',
422                 'title': 'Leer mij vrouwen kennen: Aflevering 1',
423                 'description': 'Leer mij vrouwen kennen: Aflevering 1',
424                 'uploader': 'SBS Broadcasting',
425             },
426             'skip': 'Restricted to Netherlands',
427             'params': {
428                 'skip_download': True,  # m3u8 download
429             },
430         },
431         {
432             # Brightcove with alternative playerID key
433             'url': 'http://www.nature.com/nmeth/journal/v9/n7/fig_tab/nmeth.2062_SV1.html',
434             'info_dict': {
435                 'id': 'nmeth.2062_SV1',
436                 'title': 'Simultaneous multiview imaging of the Drosophila syncytial blastoderm : Quantitative high-speed imaging of entire developing embryos with simultaneous multiview light-sheet microscopy : Nature Methods : Nature Research',
437             },
438             'playlist': [{
439                 'info_dict': {
440                     'id': '2228375078001',
441                     'ext': 'mp4',
442                     'title': 'nmeth.2062-sv1',
443                     'description': 'nmeth.2062-sv1',
444                     'timestamp': 1363357591,
445                     'upload_date': '20130315',
446                     'uploader': 'Nature Publishing Group',
447                     'uploader_id': '1964492299001',
448                 },
449             }],
450         },
451         # ooyala video
452         {
453             'url': 'http://www.rollingstone.com/music/videos/norwegian-dj-cashmere-cat-goes-spartan-on-with-me-premiere-20131219',
454             'md5': '166dd577b433b4d4ebfee10b0824d8ff',
455             'info_dict': {
456                 'id': 'BwY2RxaTrTkslxOfcan0UCf0YqyvWysJ',
457                 'ext': 'mp4',
458                 'title': '2cc213299525360.mov',  # that's what we get
459                 'duration': 238.231,
460             },
461             'add_ie': ['Ooyala'],
462         },
463         {
464             # ooyala video embedded with http://player.ooyala.com/iframe.js
465             'url': 'http://www.macrumors.com/2015/07/24/steve-jobs-the-man-in-the-machine-first-trailer/',
466             'info_dict': {
467                 'id': 'p0MGJndjoG5SOKqO_hZJuZFPB-Tr5VgB',
468                 'ext': 'mp4',
469                 'title': '"Steve Jobs: Man in the Machine" trailer',
470                 'description': 'The first trailer for the Alex Gibney documentary "Steve Jobs: Man in the Machine."',
471                 'duration': 135.427,
472             },
473             'params': {
474                 'skip_download': True,
475             },
476             'skip': 'movie expired',
477         },
478         # embed.ly video
479         {
480             'url': 'http://www.tested.com/science/weird/460206-tested-grinding-coffee-2000-frames-second/',
481             'info_dict': {
482                 'id': '9ODmcdjQcHQ',
483                 'ext': 'mp4',
484                 'title': 'Tested: Grinding Coffee at 2000 Frames Per Second',
485                 'upload_date': '20140225',
486                 'description': 'md5:06a40fbf30b220468f1e0957c0f558ff',
487                 'uploader': 'Tested',
488                 'uploader_id': 'testedcom',
489             },
490             # No need to test YoutubeIE here
491             'params': {
492                 'skip_download': True,
493             },
494         },
495         # funnyordie embed
496         {
497             'url': 'http://www.theguardian.com/world/2014/mar/11/obama-zach-galifianakis-between-two-ferns',
498             'info_dict': {
499                 'id': '18e820ec3f',
500                 'ext': 'mp4',
501                 'title': 'Between Two Ferns with Zach Galifianakis: President Barack Obama',
502                 'description': 'Episode 18: President Barack Obama sits down with Zach Galifianakis for his most memorable interview yet.',
503             },
504             # HEAD requests lead to endless 301, while GET is OK
505             'expected_warnings': ['301'],
506         },
507         # RUTV embed
508         {
509             'url': 'http://www.rg.ru/2014/03/15/reg-dfo/anklav-anons.html',
510             'info_dict': {
511                 'id': '776940',
512                 'ext': 'mp4',
513                 'title': 'Охотское море стало целиком российским',
514                 'description': 'md5:5ed62483b14663e2a95ebbe115eb8f43',
515             },
516             'params': {
517                 # m3u8 download
518                 'skip_download': True,
519             },
520         },
521         # TVC embed
522         {
523             'url': 'http://sch1298sz.mskobr.ru/dou_edu/karamel_ki/filial_galleries/video/iframe_src_http_tvc_ru_video_iframe_id_55304_isplay_false_acc_video_id_channel_brand_id_11_show_episodes_episode_id_32307_frameb/',
524             'info_dict': {
525                 'id': '55304',
526                 'ext': 'mp4',
527                 'title': 'Дошкольное воспитание',
528             },
529         },
530         # SportBox embed
531         {
532             'url': 'http://www.vestifinance.ru/articles/25753',
533             'info_dict': {
534                 'id': '25753',
535                 'title': 'Прямые трансляции с Форума-выставки "Госзаказ-2013"',
536             },
537             'playlist': [{
538                 'info_dict': {
539                     'id': '370908',
540                     'title': 'Госзаказ. День 3',
541                     'ext': 'mp4',
542                 }
543             }, {
544                 'info_dict': {
545                     'id': '370905',
546                     'title': 'Госзаказ. День 2',
547                     'ext': 'mp4',
548                 }
549             }, {
550                 'info_dict': {
551                     'id': '370902',
552                     'title': 'Госзаказ. День 1',
553                     'ext': 'mp4',
554                 }
555             }],
556             'params': {
557                 # m3u8 download
558                 'skip_download': True,
559             },
560         },
561         # Myvi.ru embed
562         {
563             'url': 'http://www.kinomyvi.tv/news/detail/Pervij-dublirovannij-trejler--Uzhastikov-_nOw1',
564             'info_dict': {
565                 'id': 'f4dafcad-ff21-423d-89b5-146cfd89fa1e',
566                 'ext': 'mp4',
567                 'title': 'Ужастики, русский трейлер (2015)',
568                 'thumbnail': r're:^https?://.*\.jpg$',
569                 'duration': 153,
570             }
571         },
572         # XHamster embed
573         {
574             'url': 'http://www.numisc.com/forum/showthread.php?11696-FM15-which-pumiscer-was-this-%28-vid-%29-%28-alfa-as-fuck-srx-%29&s=711f5db534502e22260dec8c5e2d66d8',
575             'info_dict': {
576                 'id': 'showthread',
577                 'title': '[NSFL] [FM15] which pumiscer was this ( vid ) ( alfa as fuck srx )',
578             },
579             'playlist_mincount': 7,
580             # This forum does not allow <iframe> syntaxes anymore
581             # Now HTML tags are displayed as-is
582             'skip': 'No videos on this page',
583         },
584         # Embedded TED video
585         {
586             'url': 'http://en.support.wordpress.com/videos/ted-talks/',
587             'md5': '65fdff94098e4a607385a60c5177c638',
588             'info_dict': {
589                 'id': '1969',
590                 'ext': 'mp4',
591                 'title': 'Hidden miracles of the natural world',
592                 'uploader': 'Louie Schwartzberg',
593                 'description': 'md5:8145d19d320ff3e52f28401f4c4283b9',
594             }
595         },
596         # nowvideo embed hidden behind percent encoding
597         {
598             'url': 'http://www.waoanime.tv/the-super-dimension-fortress-macross-episode-1/',
599             'md5': '2baf4ddd70f697d94b1c18cf796d5107',
600             'info_dict': {
601                 'id': '06e53103ca9aa',
602                 'ext': 'flv',
603                 'title': 'Macross Episode 001  Watch Macross Episode 001 onl',
604                 'description': 'No description',
605             },
606         },
607         # arte embed
608         {
609             'url': 'http://www.tv-replay.fr/redirection/20-03-14/x-enius-arte-10753389.html',
610             'md5': '7653032cbb25bf6c80d80f217055fa43',
611             'info_dict': {
612                 'id': '048195-004_PLUS7-F',
613                 'ext': 'flv',
614                 'title': 'X:enius',
615                 'description': 'md5:d5fdf32ef6613cdbfd516ae658abf168',
616                 'upload_date': '20140320',
617             },
618             'params': {
619                 'skip_download': 'Requires rtmpdump'
620             },
621             'skip': 'video gone',
622         },
623         # francetv embed
624         {
625             'url': 'http://www.tsprod.com/replay-du-concert-alcaline-de-calogero',
626             'info_dict': {
627                 'id': 'EV_30231',
628                 'ext': 'mp4',
629                 'title': 'Alcaline, le concert avec Calogero',
630                 'description': 'md5:61f08036dcc8f47e9cfc33aed08ffaff',
631                 'upload_date': '20150226',
632                 'timestamp': 1424989860,
633                 'duration': 5400,
634             },
635             'params': {
636                 # m3u8 downloads
637                 'skip_download': True,
638             },
639             'expected_warnings': [
640                 'Forbidden'
641             ]
642         },
643         # Condé Nast embed
644         {
645             'url': 'http://www.wired.com/2014/04/honda-asimo/',
646             'md5': 'ba0dfe966fa007657bd1443ee672db0f',
647             'info_dict': {
648                 'id': '53501be369702d3275860000',
649                 'ext': 'mp4',
650                 'title': 'Honda’s  New Asimo Robot Is More Human Than Ever',
651             }
652         },
653         # Dailymotion embed
654         {
655             'url': 'http://www.spi0n.com/zap-spi0n-com-n216/',
656             'md5': '441aeeb82eb72c422c7f14ec533999cd',
657             'info_dict': {
658                 'id': 'k2mm4bCdJ6CQ2i7c8o2',
659                 'ext': 'mp4',
660                 'title': 'Le Zap de Spi0n n°216 - Zapping du Web',
661                 'description': 'md5:faf028e48a461b8b7fad38f1e104b119',
662                 'uploader': 'Spi0n',
663                 'uploader_id': 'xgditw',
664                 'upload_date': '20140425',
665                 'timestamp': 1398441542,
666             },
667             'add_ie': ['Dailymotion'],
668         },
669         # YouTube embed
670         {
671             'url': 'http://www.badzine.de/ansicht/datum/2014/06/09/so-funktioniert-die-neue-englische-badminton-liga.html',
672             'info_dict': {
673                 'id': 'FXRb4ykk4S0',
674                 'ext': 'mp4',
675                 'title': 'The NBL Auction 2014',
676                 'uploader': 'BADMINTON England',
677                 'uploader_id': 'BADMINTONEvents',
678                 'upload_date': '20140603',
679                 'description': 'md5:9ef128a69f1e262a700ed83edb163a73',
680             },
681             'add_ie': ['Youtube'],
682             'params': {
683                 'skip_download': True,
684             }
685         },
686         # MTVSercices embed
687         {
688             'url': 'http://www.vulture.com/2016/06/new-key-peele-sketches-released.html',
689             'md5': 'ca1aef97695ef2c1d6973256a57e5252',
690             'info_dict': {
691                 'id': '769f7ec0-0692-4d62-9b45-0d88074bffc1',
692                 'ext': 'mp4',
693                 'title': 'Key and Peele|October 10, 2012|2|203|Liam Neesons - Uncensored',
694                 'description': 'Two valets share their love for movie star Liam Neesons.',
695                 'timestamp': 1349922600,
696                 'upload_date': '20121011',
697             },
698         },
699         # YouTube embed via <data-embed-url="">
700         {
701             'url': 'https://play.google.com/store/apps/details?id=com.gameloft.android.ANMP.GloftA8HM',
702             'info_dict': {
703                 'id': '4vAffPZIT44',
704                 'ext': 'mp4',
705                 'title': 'Asphalt 8: Airborne - Update - Welcome to Dubai!',
706                 'uploader': 'Gameloft',
707                 'uploader_id': 'gameloft',
708                 'upload_date': '20140828',
709                 'description': 'md5:c80da9ed3d83ae6d1876c834de03e1c4',
710             },
711             'params': {
712                 'skip_download': True,
713             }
714         },
715         # Camtasia studio
716         {
717             'url': 'http://www.ll.mit.edu/workshops/education/videocourses/antennas/lecture1/video/',
718             'playlist': [{
719                 'md5': '0c5e352edabf715d762b0ad4e6d9ee67',
720                 'info_dict': {
721                     'id': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final',
722                     'title': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final - video1',
723                     'ext': 'flv',
724                     'duration': 2235.90,
725                 }
726             }, {
727                 'md5': '10e4bb3aaca9fd630e273ff92d9f3c63',
728                 'info_dict': {
729                     'id': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final_PIP',
730                     'title': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final - pip',
731                     'ext': 'flv',
732                     'duration': 2235.93,
733                 }
734             }],
735             'info_dict': {
736                 'title': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final',
737             }
738         },
739         # Flowplayer
740         {
741             'url': 'http://www.handjobhub.com/video/busty-blonde-siri-tit-fuck-while-wank-6313.html',
742             'md5': '9d65602bf31c6e20014319c7d07fba27',
743             'info_dict': {
744                 'id': '5123ea6d5e5a7',
745                 'ext': 'mp4',
746                 'age_limit': 18,
747                 'uploader': 'www.handjobhub.com',
748                 'title': 'Busty Blonde Siri Tit Fuck While Wank at HandjobHub.com',
749             }
750         },
751         # Multiple brightcove videos
752         # https://github.com/rg3/youtube-dl/issues/2283
753         {
754             'url': 'http://www.newyorker.com/online/blogs/newsdesk/2014/01/always-never-nuclear-command-and-control.html',
755             'info_dict': {
756                 'id': 'always-never',
757                 'title': 'Always / Never - The New Yorker',
758             },
759             'playlist_count': 3,
760             'params': {
761                 'extract_flat': False,
762                 'skip_download': True,
763             }
764         },
765         # MLB embed
766         {
767             'url': 'http://umpire-empire.com/index.php/topic/58125-laz-decides-no-thats-low/',
768             'md5': '96f09a37e44da40dd083e12d9a683327',
769             'info_dict': {
770                 'id': '33322633',
771                 'ext': 'mp4',
772                 'title': 'Ump changes call to ball',
773                 'description': 'md5:71c11215384298a172a6dcb4c2e20685',
774                 'duration': 48,
775                 'timestamp': 1401537900,
776                 'upload_date': '20140531',
777                 'thumbnail': r're:^https?://.*\.jpg$',
778             },
779         },
780         # Wistia embed
781         {
782             'url': 'http://study.com/academy/lesson/north-american-exploration-failed-colonies-of-spain-france-england.html#lesson',
783             'md5': '1953f3a698ab51cfc948ed3992a0b7ff',
784             'info_dict': {
785                 'id': '6e2wtrbdaf',
786                 'ext': 'mov',
787                 'title': 'paywall_north-american-exploration-failed-colonies-of-spain-france-england',
788                 'description': 'a Paywall Videos video from Remilon',
789                 'duration': 644.072,
790                 'uploader': 'study.com',
791                 'timestamp': 1459678540,
792                 'upload_date': '20160403',
793                 'filesize': 24687186,
794             },
795         },
796         {
797             'url': 'http://thoughtworks.wistia.com/medias/uxjb0lwrcz',
798             'md5': 'baf49c2baa8a7de5f3fc145a8506dcd4',
799             'info_dict': {
800                 'id': 'uxjb0lwrcz',
801                 'ext': 'mp4',
802                 'title': 'Conversation about Hexagonal Rails Part 1',
803                 'description': 'a Martin Fowler video from ThoughtWorks',
804                 'duration': 1715.0,
805                 'uploader': 'thoughtworks.wistia.com',
806                 'timestamp': 1401832161,
807                 'upload_date': '20140603',
808             },
809         },
810         # Wistia standard embed (async)
811         {
812             'url': 'https://www.getdrip.com/university/brennan-dunn-drip-workshop/',
813             'info_dict': {
814                 'id': '807fafadvk',
815                 'ext': 'mp4',
816                 'title': 'Drip Brennan Dunn Workshop',
817                 'description': 'a JV Webinars video from getdrip-1',
818                 'duration': 4986.95,
819                 'timestamp': 1463607249,
820                 'upload_date': '20160518',
821             },
822             'params': {
823                 'skip_download': True,
824             }
825         },
826         # Soundcloud embed
827         {
828             'url': 'http://nakedsecurity.sophos.com/2014/10/29/sscc-171-are-you-sure-that-1234-is-a-bad-password-podcast/',
829             'info_dict': {
830                 'id': '174391317',
831                 'ext': 'mp3',
832                 'description': 'md5:ff867d6b555488ad3c52572bb33d432c',
833                 'uploader': 'Sophos Security',
834                 'title': 'Chet Chat 171 - Oct 29, 2014',
835                 'upload_date': '20141029',
836             }
837         },
838         # Soundcloud multiple embeds
839         {
840             'url': 'http://www.guitarplayer.com/lessons/1014/legato-workout-one-hour-to-more-fluid-performance---tab/52809',
841             'info_dict': {
842                 'id': '52809',
843                 'title': 'Guitar Essentials: Legato Workout—One-Hour to Fluid Performance  | TAB + AUDIO',
844             },
845             'playlist_mincount': 7,
846         },
847         # TuneIn station embed
848         {
849             'url': 'http://radiocnrv.com/promouvoir-radio-cnrv/',
850             'info_dict': {
851                 'id': '204146',
852                 'ext': 'mp3',
853                 'title': 'CNRV',
854                 'location': 'Paris, France',
855                 'is_live': True,
856             },
857             'params': {
858                 # Live stream
859                 'skip_download': True,
860             },
861         },
862         # Livestream embed
863         {
864             'url': 'http://www.esa.int/Our_Activities/Space_Science/Rosetta/Philae_comet_touch-down_webcast',
865             'info_dict': {
866                 'id': '67864563',
867                 'ext': 'flv',
868                 'upload_date': '20141112',
869                 'title': 'Rosetta #CometLanding webcast HL 10',
870             }
871         },
872         # Another Livestream embed, without 'new.' in URL
873         {
874             'url': 'https://www.freespeech.org/',
875             'info_dict': {
876                 'id': '123537347',
877                 'ext': 'mp4',
878                 'title': 're:^FSTV [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
879             },
880             'params': {
881                 # Live stream
882                 'skip_download': True,
883             },
884         },
885         # LazyYT
886         {
887             'url': 'http://discourse.ubuntu.com/t/unity-8-desktop-mode-windows-on-mir/1986',
888             'info_dict': {
889                 'id': '1986',
890                 'title': 'Unity 8 desktop-mode windows on Mir! - Ubuntu Discourse',
891             },
892             'playlist_mincount': 2,
893         },
894         # Cinchcast embed
895         {
896             'url': 'http://undergroundwellness.com/podcasts/306-5-steps-to-permanent-gut-healing/',
897             'info_dict': {
898                 'id': '7141703',
899                 'ext': 'mp3',
900                 'upload_date': '20141126',
901                 'title': 'Jack Tips: 5 Steps to Permanent Gut Healing',
902             }
903         },
904         # Cinerama player
905         {
906             'url': 'http://www.abc.net.au/7.30/content/2015/s4164797.htm',
907             'info_dict': {
908                 'id': '730m_DandD_1901_512k',
909                 'ext': 'mp4',
910                 'uploader': 'www.abc.net.au',
911                 'title': 'Game of Thrones with dice - Dungeons and Dragons fantasy role-playing game gets new life - 19/01/2015',
912             }
913         },
914         # embedded viddler video
915         {
916             'url': 'http://deadspin.com/i-cant-stop-watching-john-wall-chop-the-nuggets-with-th-1681801597',
917             'info_dict': {
918                 'id': '4d03aad9',
919                 'ext': 'mp4',
920                 'uploader': 'deadspin',
921                 'title': 'WALL-TO-GORTAT',
922                 'timestamp': 1422285291,
923                 'upload_date': '20150126',
924             },
925             'add_ie': ['Viddler'],
926         },
927         # Libsyn embed
928         {
929             'url': 'http://thedailyshow.cc.com/podcast/episodetwelve',
930             'info_dict': {
931                 'id': '3377616',
932                 'ext': 'mp3',
933                 'title': "The Daily Show Podcast without Jon Stewart - Episode 12: Bassem Youssef: Egypt's Jon Stewart",
934                 'description': 'md5:601cb790edd05908957dae8aaa866465',
935                 'upload_date': '20150220',
936             },
937             'skip': 'All The Daily Show URLs now redirect to http://www.cc.com/shows/',
938         },
939         # jwplayer YouTube
940         {
941             'url': 'http://media.nationalarchives.gov.uk/index.php/webinar-using-discovery-national-archives-online-catalogue/',
942             'info_dict': {
943                 'id': 'Mrj4DVp2zeA',
944                 'ext': 'mp4',
945                 'upload_date': '20150212',
946                 'uploader': 'The National Archives UK',
947                 'description': 'md5:a236581cd2449dd2df4f93412f3f01c6',
948                 'uploader_id': 'NationalArchives08',
949                 'title': 'Webinar: Using Discovery, The National Archives’ online catalogue',
950             },
951         },
952         # jwplayer rtmp
953         {
954             'url': 'http://www.suffolk.edu/sjc/',
955             'info_dict': {
956                 'id': 'sjclive',
957                 'ext': 'flv',
958                 'title': 'Massachusetts Supreme Judicial Court Oral Arguments',
959                 'uploader': 'www.suffolk.edu',
960             },
961             'params': {
962                 'skip_download': True,
963             }
964         },
965         # Complex jwplayer
966         {
967             'url': 'http://www.indiedb.com/games/king-machine/videos',
968             'info_dict': {
969                 'id': 'videos',
970                 'ext': 'mp4',
971                 'title': 'king machine trailer 1',
972                 'thumbnail': r're:^https?://.*\.jpg$',
973             },
974         },
975         {
976             # JWPlayer config passed as variable
977             'url': 'http://www.txxx.com/videos/3326530/ariele/',
978             'info_dict': {
979                 'id': '3326530_hq',
980                 'ext': 'mp4',
981                 'title': 'ARIELE | Tube Cup',
982                 'uploader': 'www.txxx.com',
983                 'age_limit': 18,
984             },
985             'params': {
986                 'skip_download': True,
987             }
988         },
989         # rtl.nl embed
990         {
991             'url': 'http://www.rtlnieuws.nl/nieuws/buitenland/aanslagen-kopenhagen',
992             'playlist_mincount': 5,
993             'info_dict': {
994                 'id': 'aanslagen-kopenhagen',
995                 'title': 'Aanslagen Kopenhagen | RTL Nieuws',
996             }
997         },
998         # Zapiks embed
999         {
1000             'url': 'http://www.skipass.com/news/116090-bon-appetit-s5ep3-baqueira-mi-cor.html',
1001             'info_dict': {
1002                 'id': '118046',
1003                 'ext': 'mp4',
1004                 'title': 'EP3S5 - Bon Appétit - Baqueira Mi Corazon !',
1005             }
1006         },
1007         # Kaltura embed (different embed code)
1008         {
1009             'url': 'http://www.premierchristianradio.com/Shows/Saturday/Unbelievable/Conference-Videos/Os-Guinness-Is-It-Fools-Talk-Unbelievable-Conference-2014',
1010             'info_dict': {
1011                 'id': '1_a52wc67y',
1012                 'ext': 'flv',
1013                 'upload_date': '20150127',
1014                 'uploader_id': 'PremierMedia',
1015                 'timestamp': int,
1016                 'title': 'Os Guinness // Is It Fools Talk? // Unbelievable? Conference 2014',
1017             },
1018         },
1019         # Kaltura embed with single quotes
1020         {
1021             'url': 'http://fod.infobase.com/p_ViewPlaylist.aspx?AssignmentID=NUN8ZY',
1022             'info_dict': {
1023                 'id': '0_izeg5utt',
1024                 'ext': 'mp4',
1025                 'title': '35871',
1026                 'timestamp': 1355743100,
1027                 'upload_date': '20121217',
1028                 'uploader_id': 'batchUser',
1029             },
1030             'add_ie': ['Kaltura'],
1031         },
1032         {
1033             # Kaltura embedded via quoted entry_id
1034             'url': 'https://www.oreilly.com/ideas/my-cloud-makes-pretty-pictures',
1035             'info_dict': {
1036                 'id': '0_utuok90b',
1037                 'ext': 'mp4',
1038                 'title': '06_matthew_brender_raj_dutt',
1039                 'timestamp': 1466638791,
1040                 'upload_date': '20160622',
1041             },
1042             'add_ie': ['Kaltura'],
1043             'expected_warnings': [
1044                 'Could not send HEAD request'
1045             ],
1046             'params': {
1047                 'skip_download': True,
1048             }
1049         },
1050         {
1051             # Kaltura embedded, some fileExt broken (#11480)
1052             'url': 'http://www.cornell.edu/video/nima-arkani-hamed-standard-models-of-particle-physics',
1053             'info_dict': {
1054                 'id': '1_sgtvehim',
1055                 'ext': 'mp4',
1056                 'title': 'Our "Standard Models" of particle physics and cosmology',
1057                 'description': 'md5:67ea74807b8c4fea92a6f38d6d323861',
1058                 'timestamp': 1321158993,
1059                 'upload_date': '20111113',
1060                 'uploader_id': 'kps1',
1061             },
1062             'add_ie': ['Kaltura'],
1063         },
1064         # Eagle.Platform embed (generic URL)
1065         {
1066             'url': 'http://lenta.ru/news/2015/03/06/navalny/',
1067             # Not checking MD5 as sometimes the direct HTTP link results in 404 and HLS is used
1068             'info_dict': {
1069                 'id': '227304',
1070                 'ext': 'mp4',
1071                 'title': 'Навальный вышел на свободу',
1072                 'description': 'md5:d97861ac9ae77377f3f20eaf9d04b4f5',
1073                 'thumbnail': r're:^https?://.*\.jpg$',
1074                 'duration': 87,
1075                 'view_count': int,
1076                 'age_limit': 0,
1077             },
1078         },
1079         # ClipYou (Eagle.Platform) embed (custom URL)
1080         {
1081             'url': 'http://muz-tv.ru/play/7129/',
1082             # Not checking MD5 as sometimes the direct HTTP link results in 404 and HLS is used
1083             'info_dict': {
1084                 'id': '12820',
1085                 'ext': 'mp4',
1086                 'title': "'O Sole Mio",
1087                 'thumbnail': r're:^https?://.*\.jpg$',
1088                 'duration': 216,
1089                 'view_count': int,
1090             },
1091         },
1092         # Pladform embed
1093         {
1094             'url': 'http://muz-tv.ru/kinozal/view/7400/',
1095             'info_dict': {
1096                 'id': '100183293',
1097                 'ext': 'mp4',
1098                 'title': 'Тайны перевала Дятлова • 1 серия 2 часть',
1099                 'description': 'Документальный сериал-расследование одной из самых жутких тайн ХХ века',
1100                 'thumbnail': r're:^https?://.*\.jpg$',
1101                 'duration': 694,
1102                 'age_limit': 0,
1103             },
1104         },
1105         # Playwire embed
1106         {
1107             'url': 'http://www.cinemablend.com/new/First-Joe-Dirt-2-Trailer-Teaser-Stupid-Greatness-70874.html',
1108             'info_dict': {
1109                 'id': '3519514',
1110                 'ext': 'mp4',
1111                 'title': 'Joe Dirt 2 Beautiful Loser Teaser Trailer',
1112                 'thumbnail': r're:^https?://.*\.png$',
1113                 'duration': 45.115,
1114             },
1115         },
1116         # 5min embed
1117         {
1118             'url': 'http://techcrunch.com/video/facebook-creates-on-this-day-crunch-report/518726732/',
1119             'md5': '4c6f127a30736b59b3e2c19234ee2bf7',
1120             'info_dict': {
1121                 'id': '518726732',
1122                 'ext': 'mp4',
1123                 'title': 'Facebook Creates "On This Day" | Crunch Report',
1124             },
1125         },
1126         # SVT embed
1127         {
1128             'url': 'http://www.svt.se/sport/ishockey/jagr-tacklar-giroux-under-intervjun',
1129             'info_dict': {
1130                 'id': '2900353',
1131                 'ext': 'flv',
1132                 'title': 'Här trycker Jagr till Giroux (under SVT-intervjun)',
1133                 'duration': 27,
1134                 'age_limit': 0,
1135             },
1136         },
1137         # Crooks and Liars embed
1138         {
1139             'url': 'http://crooksandliars.com/2015/04/fox-friends-says-protecting-atheists',
1140             'info_dict': {
1141                 'id': '8RUoRhRi',
1142                 'ext': 'mp4',
1143                 'title': "Fox & Friends Says Protecting Atheists From Discrimination Is Anti-Christian!",
1144                 'description': 'md5:e1a46ad1650e3a5ec7196d432799127f',
1145                 'timestamp': 1428207000,
1146                 'upload_date': '20150405',
1147                 'uploader': 'Heather',
1148             },
1149         },
1150         # Crooks and Liars external embed
1151         {
1152             'url': 'http://theothermccain.com/2010/02/02/video-proves-that-bill-kristol-has-been-watching-glenn-beck/comment-page-1/',
1153             'info_dict': {
1154                 'id': 'MTE3MjUtMzQ2MzA',
1155                 'ext': 'mp4',
1156                 'title': 'md5:5e3662a81a4014d24c250d76d41a08d5',
1157                 'description': 'md5:9b8e9542d6c3c5de42d6451b7d780cec',
1158                 'timestamp': 1265032391,
1159                 'upload_date': '20100201',
1160                 'uploader': 'Heather',
1161             },
1162         },
1163         # NBC Sports vplayer embed
1164         {
1165             'url': 'http://www.riderfans.com/forum/showthread.php?121827-Freeman&s=e98fa1ea6dc08e886b1678d35212494a',
1166             'info_dict': {
1167                 'id': 'ln7x1qSThw4k',
1168                 'ext': 'flv',
1169                 'title': "PFT Live: New leader in the 'new-look' defense",
1170                 'description': 'md5:65a19b4bbfb3b0c0c5768bed1dfad74e',
1171                 'uploader': 'NBCU-SPORTS',
1172                 'upload_date': '20140107',
1173                 'timestamp': 1389118457,
1174             },
1175         },
1176         # NBC News embed
1177         {
1178             'url': 'http://www.vulture.com/2016/06/letterman-couldnt-care-less-about-late-night.html',
1179             'md5': '1aa589c675898ae6d37a17913cf68d66',
1180             'info_dict': {
1181                 'id': '701714499682',
1182                 'ext': 'mp4',
1183                 'title': 'PREVIEW: On Assignment: David Letterman',
1184                 'description': 'A preview of Tom Brokaw\'s interview with David Letterman as part of the On Assignment series powered by Dateline. Airs Sunday June 12 at 7/6c.',
1185             },
1186         },
1187         # UDN embed
1188         {
1189             'url': 'https://video.udn.com/news/300346',
1190             'md5': 'fd2060e988c326991037b9aff9df21a6',
1191             'info_dict': {
1192                 'id': '300346',
1193                 'ext': 'mp4',
1194                 'title': '中一中男師變性 全校師生力挺',
1195                 'thumbnail': r're:^https?://.*\.jpg$',
1196             },
1197             'params': {
1198                 # m3u8 download
1199                 'skip_download': True,
1200             },
1201         },
1202         # Ooyala embed
1203         {
1204             'url': 'http://www.businessinsider.com/excel-index-match-vlookup-video-how-to-2015-2?IR=T',
1205             'info_dict': {
1206                 'id': '50YnY4czr4ms1vJ7yz3xzq0excz_pUMs',
1207                 'ext': 'mp4',
1208                 'description': 'VIDEO: INDEX/MATCH versus VLOOKUP.',
1209                 'title': 'This is what separates the Excel masters from the wannabes',
1210                 'duration': 191.933,
1211             },
1212             'params': {
1213                 # m3u8 downloads
1214                 'skip_download': True,
1215             }
1216         },
1217         # Brightcove URL in single quotes
1218         {
1219             'url': 'http://www.sportsnet.ca/baseball/mlb/sn-presents-russell-martin-world-citizen/',
1220             'md5': '4ae374f1f8b91c889c4b9203c8c752af',
1221             'info_dict': {
1222                 'id': '4255764656001',
1223                 'ext': 'mp4',
1224                 'title': 'SN Presents: Russell Martin, World Citizen',
1225                 'description': 'To understand why he was the Toronto Blue Jays’ top off-season priority is to appreciate his background and upbringing in Montreal, where he first developed his baseball skills. Written and narrated by Stephen Brunt.',
1226                 'uploader': 'Rogers Sportsnet',
1227                 'uploader_id': '1704050871',
1228                 'upload_date': '20150525',
1229                 'timestamp': 1432570283,
1230             },
1231         },
1232         # Dailymotion Cloud video
1233         {
1234             'url': 'http://replay.publicsenat.fr/vod/le-debat/florent-kolandjian,dominique-cena,axel-decourtye,laurence-abeille,bruno-parmentier/175910',
1235             'md5': 'dcaf23ad0c67a256f4278bce6e0bae38',
1236             'info_dict': {
1237                 'id': 'x2uy8t3',
1238                 'ext': 'mp4',
1239                 'title': 'Sauvons les abeilles ! - Le débat',
1240                 'description': 'md5:d9082128b1c5277987825d684939ca26',
1241                 'thumbnail': r're:^https?://.*\.jpe?g$',
1242                 'timestamp': 1434970506,
1243                 'upload_date': '20150622',
1244                 'uploader': 'Public Sénat',
1245                 'uploader_id': 'xa9gza',
1246             }
1247         },
1248         # OnionStudios embed
1249         {
1250             'url': 'http://www.clickhole.com/video/dont-understand-bitcoin-man-will-mumble-explanatio-2537',
1251             'info_dict': {
1252                 'id': '2855',
1253                 'ext': 'mp4',
1254                 'title': 'Don’t Understand Bitcoin? This Man Will Mumble An Explanation At You',
1255                 'thumbnail': r're:^https?://.*\.jpe?g$',
1256                 'uploader': 'ClickHole',
1257                 'uploader_id': 'clickhole',
1258             }
1259         },
1260         # SnagFilms embed
1261         {
1262             'url': 'http://whilewewatch.blogspot.ru/2012/06/whilewewatch-whilewewatch-gripping.html',
1263             'info_dict': {
1264                 'id': '74849a00-85a9-11e1-9660-123139220831',
1265                 'ext': 'mp4',
1266                 'title': '#whilewewatch',
1267             }
1268         },
1269         # AdobeTVVideo embed
1270         {
1271             'url': 'https://helpx.adobe.com/acrobat/how-to/new-experience-acrobat-dc.html?set=acrobat--get-started--essential-beginners',
1272             'md5': '43662b577c018ad707a63766462b1e87',
1273             'info_dict': {
1274                 'id': '2456',
1275                 'ext': 'mp4',
1276                 'title': 'New experience with Acrobat DC',
1277                 'description': 'New experience with Acrobat DC',
1278                 'duration': 248.667,
1279             },
1280         },
1281         # BrightcoveInPageEmbed embed
1282         {
1283             'url': 'http://www.geekandsundry.com/tabletop-bonus-wils-final-thoughts-on-dread/',
1284             'info_dict': {
1285                 'id': '4238694884001',
1286                 'ext': 'flv',
1287                 'title': 'Tabletop: Dread, Last Thoughts',
1288                 'description': 'Tabletop: Dread, Last Thoughts',
1289                 'duration': 51690,
1290             },
1291         },
1292         # Brightcove embed, with no valid 'renditions' but valid 'IOSRenditions'
1293         # This video can't be played in browsers if Flash disabled and UA set to iPhone, which is actually a false alarm
1294         {
1295             'url': 'https://dl.dropboxusercontent.com/u/29092637/interview.html',
1296             'info_dict': {
1297                 'id': '4785848093001',
1298                 'ext': 'mp4',
1299                 'title': 'The Cardinal Pell Interview',
1300                 'description': 'Sky News Contributor Andrew Bolt interviews George Pell in Rome, following the Cardinal\'s evidence before the Royal Commission into Child Abuse. ',
1301                 'uploader': 'GlobeCast Australia - GlobeStream',
1302                 'uploader_id': '2733773828001',
1303                 'upload_date': '20160304',
1304                 'timestamp': 1457083087,
1305             },
1306             'params': {
1307                 # m3u8 downloads
1308                 'skip_download': True,
1309             },
1310         },
1311         # Another form of arte.tv embed
1312         {
1313             'url': 'http://www.tv-replay.fr/redirection/09-04-16/arte-reportage-arte-11508975.html',
1314             'md5': '850bfe45417ddf221288c88a0cffe2e2',
1315             'info_dict': {
1316                 'id': '030273-562_PLUS7-F',
1317                 'ext': 'mp4',
1318                 'title': 'ARTE Reportage - Nulle part, en France',
1319                 'description': 'md5:e3a0e8868ed7303ed509b9e3af2b870d',
1320                 'upload_date': '20160409',
1321             },
1322         },
1323         # LiveLeak embed
1324         {
1325             'url': 'http://www.wykop.pl/link/3088787/',
1326             'md5': 'ace83b9ed19b21f68e1b50e844fdf95d',
1327             'info_dict': {
1328                 'id': '874_1459135191',
1329                 'ext': 'mp4',
1330                 'title': 'Man shows poor quality of new apartment building',
1331                 'description': 'The wall is like a sand pile.',
1332                 'uploader': 'Lake8737',
1333             }
1334         },
1335         # Duplicated embedded video URLs
1336         {
1337             'url': 'http://www.hudl.com/athlete/2538180/highlights/149298443',
1338             'info_dict': {
1339                 'id': '149298443_480_16c25b74_2',
1340                 'ext': 'mp4',
1341                 'title': 'vs. Blue Orange Spring Game',
1342                 'uploader': 'www.hudl.com',
1343             },
1344         },
1345         # twitter:player:stream embed
1346         {
1347             'url': 'http://www.rtl.be/info/video/589263.aspx?CategoryID=288',
1348             'info_dict': {
1349                 'id': 'master',
1350                 'ext': 'mp4',
1351                 'title': 'Une nouvelle espèce de dinosaure découverte en Argentine',
1352                 'uploader': 'www.rtl.be',
1353             },
1354             'params': {
1355                 # m3u8 downloads
1356                 'skip_download': True,
1357             },
1358         },
1359         # twitter:player embed
1360         {
1361             'url': 'http://www.theatlantic.com/video/index/484130/what-do-black-holes-sound-like/',
1362             'md5': 'a3e0df96369831de324f0778e126653c',
1363             'info_dict': {
1364                 'id': '4909620399001',
1365                 'ext': 'mp4',
1366                 'title': 'What Do Black Holes Sound Like?',
1367                 'description': 'what do black holes sound like',
1368                 'upload_date': '20160524',
1369                 'uploader_id': '29913724001',
1370                 'timestamp': 1464107587,
1371                 'uploader': 'TheAtlantic',
1372             },
1373             'add_ie': ['BrightcoveLegacy'],
1374         },
1375         # Facebook <iframe> embed
1376         {
1377             'url': 'https://www.hostblogger.de/blog/archives/6181-Auto-jagt-Betonmischer.html',
1378             'md5': 'fbcde74f534176ecb015849146dd3aee',
1379             'info_dict': {
1380                 'id': '599637780109885',
1381                 'ext': 'mp4',
1382                 'title': 'Facebook video #599637780109885',
1383             },
1384         },
1385         # Facebook API embed
1386         {
1387             'url': 'http://www.lothype.com/blue-stars-2016-preview-standstill-full-show/',
1388             'md5': 'a47372ee61b39a7b90287094d447d94e',
1389             'info_dict': {
1390                 'id': '10153467542406923',
1391                 'ext': 'mp4',
1392                 'title': 'Facebook video #10153467542406923',
1393             },
1394         },
1395         # Wordpress "YouTube Video Importer" plugin
1396         {
1397             'url': 'http://www.lothype.com/blue-devils-drumline-stanford-lot-2016/',
1398             'md5': 'd16797741b560b485194eddda8121b48',
1399             'info_dict': {
1400                 'id': 'HNTXWDXV9Is',
1401                 'ext': 'mp4',
1402                 'title': 'Blue Devils Drumline Stanford lot 2016',
1403                 'upload_date': '20160627',
1404                 'uploader_id': 'GENOCIDE8GENERAL10',
1405                 'uploader': 'cylus cyrus',
1406             },
1407         },
1408         {
1409             # video stored on custom kaltura server
1410             'url': 'http://www.expansion.com/multimedia/videos.html?media=EQcM30NHIPv',
1411             'md5': '537617d06e64dfed891fa1593c4b30cc',
1412             'info_dict': {
1413                 'id': '0_1iotm5bh',
1414                 'ext': 'mp4',
1415                 'title': 'Elecciones británicas: 5 lecciones para Rajoy',
1416                 'description': 'md5:435a89d68b9760b92ce67ed227055f16',
1417                 'uploader_id': 'videos.expansion@el-mundo.net',
1418                 'upload_date': '20150429',
1419                 'timestamp': 1430303472,
1420             },
1421             'add_ie': ['Kaltura'],
1422         },
1423         {
1424             # Non-standard Vimeo embed
1425             'url': 'https://openclassrooms.com/courses/understanding-the-web',
1426             'md5': '64d86f1c7d369afd9a78b38cbb88d80a',
1427             'info_dict': {
1428                 'id': '148867247',
1429                 'ext': 'mp4',
1430                 'title': 'Understanding the web - Teaser',
1431                 'description': 'This is "Understanding the web - Teaser" by openclassrooms on Vimeo, the home for high quality videos and the people who love them.',
1432                 'upload_date': '20151214',
1433                 'uploader': 'OpenClassrooms',
1434                 'uploader_id': 'openclassrooms',
1435             },
1436             'add_ie': ['Vimeo'],
1437         },
1438         {
1439             # generic vimeo embed that requires original URL passed as Referer
1440             'url': 'http://racing4everyone.eu/2016/07/30/formula-1-2016-round12-germany/',
1441             'only_matching': True,
1442         },
1443         {
1444             'url': 'https://support.arkena.com/display/PLAY/Ways+to+embed+your+video',
1445             'md5': 'b96f2f71b359a8ecd05ce4e1daa72365',
1446             'info_dict': {
1447                 'id': 'b41dda37-d8e7-4d3f-b1b5-9a9db578bdfe',
1448                 'ext': 'mp4',
1449                 'title': 'Big Buck Bunny',
1450                 'description': 'Royalty free test video',
1451                 'timestamp': 1432816365,
1452                 'upload_date': '20150528',
1453                 'is_live': False,
1454             },
1455             'params': {
1456                 'skip_download': True,
1457             },
1458             'add_ie': [ArkenaIE.ie_key()],
1459         },
1460         {
1461             'url': 'http://nova.bg/news/view/2016/08/16/156543/%D0%BD%D0%B0-%D0%BA%D0%BE%D1%81%D1%8A%D0%BC-%D0%BE%D1%82-%D0%B2%D0%B7%D1%80%D0%B8%D0%B2-%D0%BE%D1%82%D1%86%D0%B5%D0%BF%D0%B8%D1%85%D0%B0-%D1%86%D1%8F%D0%BB-%D0%BA%D0%B2%D0%B0%D1%80%D1%82%D0%B0%D0%BB-%D0%B7%D0%B0%D1%80%D0%B0%D0%B4%D0%B8-%D0%B8%D0%B7%D1%82%D0%B8%D1%87%D0%B0%D0%BD%D0%B5-%D0%BD%D0%B0-%D0%B3%D0%B0%D0%B7-%D0%B2-%D0%BF%D0%BB%D0%BE%D0%B2%D0%B4%D0%B8%D0%B2/',
1462             'info_dict': {
1463                 'id': '1c7141f46c',
1464                 'ext': 'mp4',
1465                 'title': 'НА КОСЪМ ОТ ВЗРИВ: Изтичане на газ на бензиностанция в Пловдив',
1466             },
1467             'params': {
1468                 'skip_download': True,
1469             },
1470             'add_ie': [Vbox7IE.ie_key()],
1471         },
1472         {
1473             # DBTV embeds
1474             'url': 'http://www.dagbladet.no/2016/02/23/nyheter/nordlys/ski/troms/ver/43254897/',
1475             'info_dict': {
1476                 'id': '43254897',
1477                 'title': 'Etter ett års planlegging, klaffet endelig alt: - Jeg måtte ta en liten dans',
1478             },
1479             'playlist_mincount': 3,
1480         },
1481         {
1482             # Videa embeds
1483             'url': 'http://forum.dvdtalk.com/movie-talk/623756-deleted-magic-star-wars-ot-deleted-alt-scenes-docu-style.html',
1484             'info_dict': {
1485                 'id': '623756-deleted-magic-star-wars-ot-deleted-alt-scenes-docu-style',
1486                 'title': 'Deleted Magic - Star Wars: OT Deleted / Alt. Scenes Docu. Style - DVD Talk Forum',
1487             },
1488             'playlist_mincount': 2,
1489         },
1490         {
1491             # 20 minuten embed
1492             'url': 'http://www.20min.ch/schweiz/news/story/So-kommen-Sie-bei-Eis-und-Schnee-sicher-an-27032552',
1493             'info_dict': {
1494                 'id': '523629',
1495                 'ext': 'mp4',
1496                 'title': 'So kommen Sie bei Eis und Schnee sicher an',
1497                 'description': 'md5:117c212f64b25e3d95747e5276863f7d',
1498             },
1499             'params': {
1500                 'skip_download': True,
1501             },
1502             'add_ie': [TwentyMinutenIE.ie_key()],
1503         },
1504         {
1505             # VideoPress embed
1506             'url': 'https://en.support.wordpress.com/videopress/',
1507             'info_dict': {
1508                 'id': 'OcobLTqC',
1509                 'ext': 'm4v',
1510                 'title': 'IMG_5786',
1511                 'timestamp': 1435711927,
1512                 'upload_date': '20150701',
1513             },
1514             'params': {
1515                 'skip_download': True,
1516             },
1517             'add_ie': [VideoPressIE.ie_key()],
1518         },
1519         {
1520             # ThePlatform embedded with whitespaces in URLs
1521             'url': 'http://www.golfchannel.com/topics/shows/golftalkcentral.htm',
1522             'only_matching': True,
1523         },
1524         # {
1525         #     # TODO: find another test
1526         #     # http://schema.org/VideoObject
1527         #     'url': 'https://flipagram.com/f/nyvTSJMKId',
1528         #     'md5': '888dcf08b7ea671381f00fab74692755',
1529         #     'info_dict': {
1530         #         'id': 'nyvTSJMKId',
1531         #         'ext': 'mp4',
1532         #         'title': 'Flipagram by sjuria101 featuring Midnight Memories by One Direction',
1533         #         'description': '#love for cats.',
1534         #         'timestamp': 1461244995,
1535         #         'upload_date': '20160421',
1536         #     },
1537         #     'params': {
1538         #         'force_generic_extractor': True,
1539         #     },
1540         # }
1541     ]
1542
1543     def report_following_redirect(self, new_url):
1544         """Report information extraction."""
1545         self._downloader.to_screen('[redirect] Following redirect to %s' % new_url)
1546
1547     def _extract_rss(self, url, video_id, doc):
1548         playlist_title = doc.find('./channel/title').text
1549         playlist_desc_el = doc.find('./channel/description')
1550         playlist_desc = None if playlist_desc_el is None else playlist_desc_el.text
1551
1552         entries = []
1553         for it in doc.findall('./channel/item'):
1554             next_url = xpath_text(it, 'link', fatal=False)
1555             if not next_url:
1556                 enclosure_nodes = it.findall('./enclosure')
1557                 for e in enclosure_nodes:
1558                     next_url = e.attrib.get('url')
1559                     if next_url:
1560                         break
1561
1562             if not next_url:
1563                 continue
1564
1565             entries.append({
1566                 '_type': 'url',
1567                 'url': next_url,
1568                 'title': it.find('title').text,
1569             })
1570
1571         return {
1572             '_type': 'playlist',
1573             'id': url,
1574             'title': playlist_title,
1575             'description': playlist_desc,
1576             'entries': entries,
1577         }
1578
1579     def _extract_camtasia(self, url, video_id, webpage):
1580         """ Returns None if no camtasia video can be found. """
1581
1582         camtasia_cfg = self._search_regex(
1583             r'fo\.addVariable\(\s*"csConfigFile",\s*"([^"]+)"\s*\);',
1584             webpage, 'camtasia configuration file', default=None)
1585         if camtasia_cfg is None:
1586             return None
1587
1588         title = self._html_search_meta('DC.title', webpage, fatal=True)
1589
1590         camtasia_url = compat_urlparse.urljoin(url, camtasia_cfg)
1591         camtasia_cfg = self._download_xml(
1592             camtasia_url, video_id,
1593             note='Downloading camtasia configuration',
1594             errnote='Failed to download camtasia configuration')
1595         fileset_node = camtasia_cfg.find('./playlist/array/fileset')
1596
1597         entries = []
1598         for n in fileset_node.getchildren():
1599             url_n = n.find('./uri')
1600             if url_n is None:
1601                 continue
1602
1603             entries.append({
1604                 'id': os.path.splitext(url_n.text.rpartition('/')[2])[0],
1605                 'title': '%s - %s' % (title, n.tag),
1606                 'url': compat_urlparse.urljoin(url, url_n.text),
1607                 'duration': float_or_none(n.find('./duration').text),
1608             })
1609
1610         return {
1611             '_type': 'playlist',
1612             'entries': entries,
1613             'title': title,
1614         }
1615
1616     def _real_extract(self, url):
1617         if url.startswith('//'):
1618             return {
1619                 '_type': 'url',
1620                 'url': self.http_scheme() + url,
1621             }
1622
1623         parsed_url = compat_urlparse.urlparse(url)
1624         if not parsed_url.scheme:
1625             default_search = self._downloader.params.get('default_search')
1626             if default_search is None:
1627                 default_search = 'fixup_error'
1628
1629             if default_search in ('auto', 'auto_warning', 'fixup_error'):
1630                 if '/' in url:
1631                     self._downloader.report_warning('The url doesn\'t specify the protocol, trying with http')
1632                     return self.url_result('http://' + url)
1633                 elif default_search != 'fixup_error':
1634                     if default_search == 'auto_warning':
1635                         if re.match(r'^(?:url|URL)$', url):
1636                             raise ExtractorError(
1637                                 'Invalid URL:  %r . Call youtube-dl like this:  youtube-dl -v "https://www.youtube.com/watch?v=BaW_jenozKc"  ' % url,
1638                                 expected=True)
1639                         else:
1640                             self._downloader.report_warning(
1641                                 'Falling back to youtube search for  %s . Set --default-search "auto" to suppress this warning.' % url)
1642                     return self.url_result('ytsearch:' + url)
1643
1644             if default_search in ('error', 'fixup_error'):
1645                 raise ExtractorError(
1646                     '%r is not a valid URL. '
1647                     'Set --default-search "ytsearch" (or run  youtube-dl "ytsearch:%s" ) to search YouTube'
1648                     % (url, url), expected=True)
1649             else:
1650                 if ':' not in default_search:
1651                     default_search += ':'
1652                 return self.url_result(default_search + url)
1653
1654         url, smuggled_data = unsmuggle_url(url)
1655         force_videoid = None
1656         is_intentional = smuggled_data and smuggled_data.get('to_generic')
1657         if smuggled_data and 'force_videoid' in smuggled_data:
1658             force_videoid = smuggled_data['force_videoid']
1659             video_id = force_videoid
1660         else:
1661             video_id = self._generic_id(url)
1662
1663         self.to_screen('%s: Requesting header' % video_id)
1664
1665         head_req = HEADRequest(url)
1666         head_response = self._request_webpage(
1667             head_req, video_id,
1668             note=False, errnote='Could not send HEAD request to %s' % url,
1669             fatal=False)
1670
1671         if head_response is not False:
1672             # Check for redirect
1673             new_url = head_response.geturl()
1674             if url != new_url:
1675                 self.report_following_redirect(new_url)
1676                 if force_videoid:
1677                     new_url = smuggle_url(
1678                         new_url, {'force_videoid': force_videoid})
1679                 return self.url_result(new_url)
1680
1681         full_response = None
1682         if head_response is False:
1683             request = sanitized_Request(url)
1684             request.add_header('Accept-Encoding', '*')
1685             full_response = self._request_webpage(request, video_id)
1686             head_response = full_response
1687
1688         info_dict = {
1689             'id': video_id,
1690             'title': self._generic_title(url),
1691             'upload_date': unified_strdate(head_response.headers.get('Last-Modified'))
1692         }
1693
1694         # Check for direct link to a video
1695         content_type = head_response.headers.get('Content-Type', '').lower()
1696         m = re.match(r'^(?P<type>audio|video|application(?=/(?:ogg$|(?:vnd\.apple\.|x-)?mpegurl)))/(?P<format_id>[^;\s]+)', content_type)
1697         if m:
1698             format_id = m.group('format_id')
1699             if format_id.endswith('mpegurl'):
1700                 formats = self._extract_m3u8_formats(url, video_id, 'mp4')
1701             elif format_id == 'f4m':
1702                 formats = self._extract_f4m_formats(url, video_id)
1703             else:
1704                 formats = [{
1705                     'format_id': m.group('format_id'),
1706                     'url': url,
1707                     'vcodec': 'none' if m.group('type') == 'audio' else None
1708                 }]
1709                 info_dict['direct'] = True
1710             self._sort_formats(formats)
1711             info_dict['formats'] = formats
1712             return info_dict
1713
1714         if not self._downloader.params.get('test', False) and not is_intentional:
1715             force = self._downloader.params.get('force_generic_extractor', False)
1716             self._downloader.report_warning(
1717                 '%s on generic information extractor.' % ('Forcing' if force else 'Falling back'))
1718
1719         if not full_response:
1720             request = sanitized_Request(url)
1721             # Some webservers may serve compressed content of rather big size (e.g. gzipped flac)
1722             # making it impossible to download only chunk of the file (yet we need only 512kB to
1723             # test whether it's HTML or not). According to youtube-dl default Accept-Encoding
1724             # that will always result in downloading the whole file that is not desirable.
1725             # Therefore for extraction pass we have to override Accept-Encoding to any in order
1726             # to accept raw bytes and being able to download only a chunk.
1727             # It may probably better to solve this by checking Content-Type for application/octet-stream
1728             # after HEAD request finishes, but not sure if we can rely on this.
1729             request.add_header('Accept-Encoding', '*')
1730             full_response = self._request_webpage(request, video_id)
1731
1732         first_bytes = full_response.read(512)
1733
1734         # Is it an M3U playlist?
1735         if first_bytes.startswith(b'#EXTM3U'):
1736             info_dict['formats'] = self._extract_m3u8_formats(url, video_id, 'mp4')
1737             self._sort_formats(info_dict['formats'])
1738             return info_dict
1739
1740         # Maybe it's a direct link to a video?
1741         # Be careful not to download the whole thing!
1742         if not is_html(first_bytes):
1743             self._downloader.report_warning(
1744                 'URL could be a direct video link, returning it as such.')
1745             info_dict.update({
1746                 'direct': True,
1747                 'url': url,
1748             })
1749             return info_dict
1750
1751         webpage = self._webpage_read_content(
1752             full_response, url, video_id, prefix=first_bytes)
1753
1754         self.report_extraction(video_id)
1755
1756         # Is it an RSS feed, a SMIL file, an XSPF playlist or a MPD manifest?
1757         try:
1758             doc = compat_etree_fromstring(webpage.encode('utf-8'))
1759             if doc.tag == 'rss':
1760                 return self._extract_rss(url, video_id, doc)
1761             elif doc.tag == 'SmoothStreamingMedia':
1762                 info_dict['formats'] = self._parse_ism_formats(doc, url)
1763                 self._sort_formats(info_dict['formats'])
1764                 return info_dict
1765             elif re.match(r'^(?:{[^}]+})?smil$', doc.tag):
1766                 smil = self._parse_smil(doc, url, video_id)
1767                 self._sort_formats(smil['formats'])
1768                 return smil
1769             elif doc.tag == '{http://xspf.org/ns/0/}playlist':
1770                 return self.playlist_result(self._parse_xspf(doc, video_id), video_id)
1771             elif re.match(r'(?i)^(?:{[^}]+})?MPD$', doc.tag):
1772                 info_dict['formats'] = self._parse_mpd_formats(
1773                     doc, video_id,
1774                     mpd_base_url=full_response.geturl().rpartition('/')[0],
1775                     mpd_url=url)
1776                 self._sort_formats(info_dict['formats'])
1777                 return info_dict
1778             elif re.match(r'^{http://ns\.adobe\.com/f4m/[12]\.0}manifest$', doc.tag):
1779                 info_dict['formats'] = self._parse_f4m_formats(doc, url, video_id)
1780                 self._sort_formats(info_dict['formats'])
1781                 return info_dict
1782         except compat_xml_parse_error:
1783             pass
1784
1785         # Is it a Camtasia project?
1786         camtasia_res = self._extract_camtasia(url, video_id, webpage)
1787         if camtasia_res is not None:
1788             return camtasia_res
1789
1790         # Sometimes embedded video player is hidden behind percent encoding
1791         # (e.g. https://github.com/rg3/youtube-dl/issues/2448)
1792         # Unescaping the whole page allows to handle those cases in a generic way
1793         webpage = compat_urllib_parse_unquote(webpage)
1794
1795         # it's tempting to parse this further, but you would
1796         # have to take into account all the variations like
1797         #   Video Title - Site Name
1798         #   Site Name | Video Title
1799         #   Video Title - Tagline | Site Name
1800         # and so on and so forth; it's just not practical
1801         video_title = self._og_search_title(
1802             webpage, default=None) or self._html_search_regex(
1803             r'(?s)<title>(.*?)</title>', webpage, 'video title',
1804             default='video')
1805
1806         # Try to detect age limit automatically
1807         age_limit = self._rta_search(webpage)
1808         # And then there are the jokers who advertise that they use RTA,
1809         # but actually don't.
1810         AGE_LIMIT_MARKERS = [
1811             r'Proudly Labeled <a href="http://www.rtalabel.org/" title="Restricted to Adults">RTA</a>',
1812         ]
1813         if any(re.search(marker, webpage) for marker in AGE_LIMIT_MARKERS):
1814             age_limit = 18
1815
1816         # video uploader is domain name
1817         video_uploader = self._search_regex(
1818             r'^(?:https?://)?([^/]*)/.*', url, 'video uploader')
1819
1820         video_description = self._og_search_description(webpage, default=None)
1821         video_thumbnail = self._og_search_thumbnail(webpage, default=None)
1822
1823         # Helper method
1824         def _playlist_from_matches(matches, getter=None, ie=None):
1825             urlrs = orderedSet(
1826                 self.url_result(self._proto_relative_url(getter(m) if getter else m), ie)
1827                 for m in matches)
1828             return self.playlist_result(
1829                 urlrs, playlist_id=video_id, playlist_title=video_title)
1830
1831         # Look for Brightcove Legacy Studio embeds
1832         bc_urls = BrightcoveLegacyIE._extract_brightcove_urls(webpage)
1833         if bc_urls:
1834             self.to_screen('Brightcove video detected.')
1835             entries = [{
1836                 '_type': 'url',
1837                 'url': smuggle_url(bc_url, {'Referer': url}),
1838                 'ie_key': 'BrightcoveLegacy'
1839             } for bc_url in bc_urls]
1840
1841             return {
1842                 '_type': 'playlist',
1843                 'title': video_title,
1844                 'id': video_id,
1845                 'entries': entries,
1846             }
1847
1848         # Look for Brightcove New Studio embeds
1849         bc_urls = BrightcoveNewIE._extract_urls(webpage)
1850         if bc_urls:
1851             return _playlist_from_matches(bc_urls, ie='BrightcoveNew')
1852
1853         # Look for ThePlatform embeds
1854         tp_urls = ThePlatformIE._extract_urls(webpage)
1855         if tp_urls:
1856             return _playlist_from_matches(tp_urls, ie='ThePlatform')
1857
1858         # Look for Vessel embeds
1859         vessel_urls = VesselIE._extract_urls(webpage)
1860         if vessel_urls:
1861             return _playlist_from_matches(vessel_urls, ie=VesselIE.ie_key())
1862
1863         # Look for embedded rtl.nl player
1864         matches = re.findall(
1865             r'<iframe[^>]+?src="((?:https?:)?//(?:www\.)?rtl\.nl/system/videoplayer/[^"]+(?:video_)?embed[^"]+)"',
1866             webpage)
1867         if matches:
1868             return _playlist_from_matches(matches, ie='RtlNl')
1869
1870         vimeo_urls = VimeoIE._extract_urls(url, webpage)
1871         if vimeo_urls:
1872             return _playlist_from_matches(vimeo_urls, ie=VimeoIE.ie_key())
1873
1874         vid_me_embed_url = self._search_regex(
1875             r'src=[\'"](https?://vid\.me/[^\'"]+)[\'"]',
1876             webpage, 'vid.me embed', default=None)
1877         if vid_me_embed_url is not None:
1878             return self.url_result(vid_me_embed_url, 'Vidme')
1879
1880         # Look for embedded YouTube player
1881         matches = re.findall(r'''(?x)
1882             (?:
1883                 <iframe[^>]+?src=|
1884                 data-video-url=|
1885                 <embed[^>]+?src=|
1886                 embedSWF\(?:\s*|
1887                 new\s+SWFObject\(
1888             )
1889             (["\'])
1890                 (?P<url>(?:https?:)?//(?:www\.)?youtube(?:-nocookie)?\.com/
1891                 (?:embed|v|p)/.+?)
1892             \1''', webpage)
1893         if matches:
1894             return _playlist_from_matches(
1895                 matches, lambda m: unescapeHTML(m[1]))
1896
1897         # Look for lazyYT YouTube embed
1898         matches = re.findall(
1899             r'class="lazyYT" data-youtube-id="([^"]+)"', webpage)
1900         if matches:
1901             return _playlist_from_matches(matches, lambda m: unescapeHTML(m))
1902
1903         # Look for Wordpress "YouTube Video Importer" plugin
1904         matches = re.findall(r'''(?x)<div[^>]+
1905             class=(?P<q1>[\'"])[^\'"]*\byvii_single_video_player\b[^\'"]*(?P=q1)[^>]+
1906             data-video_id=(?P<q2>[\'"])([^\'"]+)(?P=q2)''', webpage)
1907         if matches:
1908             return _playlist_from_matches(matches, lambda m: m[-1])
1909
1910         matches = DailymotionIE._extract_urls(webpage)
1911         if matches:
1912             return _playlist_from_matches(matches)
1913
1914         # Look for embedded Dailymotion playlist player (#3822)
1915         m = re.search(
1916             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.[a-z]{2,3}/widget/jukebox\?.+?)\1', webpage)
1917         if m:
1918             playlists = re.findall(
1919                 r'list\[\]=/playlist/([^/]+)/', unescapeHTML(m.group('url')))
1920             if playlists:
1921                 return _playlist_from_matches(
1922                     playlists, lambda p: '//dailymotion.com/playlist/%s' % p)
1923
1924         # Look for embedded Wistia player
1925         match = re.search(
1926             r'<(?:meta[^>]+?content|iframe[^>]+?src)=(["\'])(?P<url>(?:https?:)?//(?:fast\.)?wistia\.net/embed/iframe/.+?)\1', webpage)
1927         if match:
1928             embed_url = self._proto_relative_url(
1929                 unescapeHTML(match.group('url')))
1930             return {
1931                 '_type': 'url_transparent',
1932                 'url': embed_url,
1933                 'ie_key': 'Wistia',
1934                 'uploader': video_uploader,
1935             }
1936
1937         match = re.search(r'(?:id=["\']wistia_|data-wistia-?id=["\']|Wistia\.embed\(["\'])(?P<id>[^"\']+)', webpage)
1938         if match:
1939             return {
1940                 '_type': 'url_transparent',
1941                 'url': 'wistia:%s' % match.group('id'),
1942                 'ie_key': 'Wistia',
1943                 'uploader': video_uploader,
1944             }
1945
1946         match = re.search(
1947             r'''(?sx)
1948                 <script[^>]+src=(["'])(?:https?:)?//fast\.wistia\.com/assets/external/E-v1\.js\1[^>]*>.*?
1949                 <div[^>]+class=(["']).*?\bwistia_async_(?P<id>[a-z0-9]+)\b.*?\2
1950             ''', webpage)
1951         if match:
1952             return self.url_result(self._proto_relative_url(
1953                 'wistia:%s' % match.group('id')), 'Wistia')
1954
1955         # Look for SVT player
1956         svt_url = SVTIE._extract_url(webpage)
1957         if svt_url:
1958             return self.url_result(svt_url, 'SVT')
1959
1960         # Look for embedded condenast player
1961         matches = re.findall(
1962             r'<iframe\s+(?:[a-zA-Z-]+="[^"]+"\s+)*?src="(https?://player\.cnevids\.com/embed/[^"]+")',
1963             webpage)
1964         if matches:
1965             return {
1966                 '_type': 'playlist',
1967                 'entries': [{
1968                     '_type': 'url',
1969                     'ie_key': 'CondeNast',
1970                     'url': ma,
1971                 } for ma in matches],
1972                 'title': video_title,
1973                 'id': video_id,
1974             }
1975
1976         # Look for Bandcamp pages with custom domain
1977         mobj = re.search(r'<meta property="og:url"[^>]*?content="(.*?bandcamp\.com.*?)"', webpage)
1978         if mobj is not None:
1979             burl = unescapeHTML(mobj.group(1))
1980             # Don't set the extractor because it can be a track url or an album
1981             return self.url_result(burl)
1982
1983         # Look for embedded Vevo player
1984         mobj = re.search(
1985             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:cache\.)?vevo\.com/.+?)\1', webpage)
1986         if mobj is not None:
1987             return self.url_result(mobj.group('url'))
1988
1989         # Look for embedded Viddler player
1990         mobj = re.search(
1991             r'<(?:iframe[^>]+?src|param[^>]+?value)=(["\'])(?P<url>(?:https?:)?//(?:www\.)?viddler\.com/(?:embed|player)/.+?)\1',
1992             webpage)
1993         if mobj is not None:
1994             return self.url_result(mobj.group('url'))
1995
1996         # Look for NYTimes player
1997         mobj = re.search(
1998             r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//graphics8\.nytimes\.com/bcvideo/[^/]+/iframe/embed\.html.+?)\1>',
1999             webpage)
2000         if mobj is not None:
2001             return self.url_result(mobj.group('url'))
2002
2003         # Look for Libsyn player
2004         mobj = re.search(
2005             r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//html5-player\.libsyn\.com/embed/.+?)\1', webpage)
2006         if mobj is not None:
2007             return self.url_result(mobj.group('url'))
2008
2009         # Look for Ooyala videos
2010         mobj = (re.search(r'player\.ooyala\.com/[^"?]+[?#][^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage) or
2011                 re.search(r'OO\.Player\.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage) or
2012                 re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage) or
2013                 re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
2014         if mobj is not None:
2015             embed_token = self._search_regex(
2016                 r'embedToken[\'"]?\s*:\s*[\'"]([^\'"]+)',
2017                 webpage, 'ooyala embed token', default=None)
2018             return OoyalaIE._build_url_result(smuggle_url(
2019                 mobj.group('ec'), {
2020                     'domain': url,
2021                     'embed_token': embed_token,
2022                 }))
2023
2024         # Look for multiple Ooyala embeds on SBN network websites
2025         mobj = re.search(r'SBN\.VideoLinkset\.entryGroup\((\[.*?\])', webpage)
2026         if mobj is not None:
2027             embeds = self._parse_json(mobj.group(1), video_id, fatal=False)
2028             if embeds:
2029                 return _playlist_from_matches(
2030                     embeds, getter=lambda v: OoyalaIE._url_for_embed_code(smuggle_url(v['provider_video_id'], {'domain': url})), ie='Ooyala')
2031
2032         # Look for Aparat videos
2033         mobj = re.search(r'<iframe .*?src="(http://www\.aparat\.com/video/[^"]+)"', webpage)
2034         if mobj is not None:
2035             return self.url_result(mobj.group(1), 'Aparat')
2036
2037         # Look for MPORA videos
2038         mobj = re.search(r'<iframe .*?src="(http://mpora\.(?:com|de)/videos/[^"]+)"', webpage)
2039         if mobj is not None:
2040             return self.url_result(mobj.group(1), 'Mpora')
2041
2042         # Look for embedded NovaMov-based player
2043         mobj = re.search(
2044             r'''(?x)<(?:pagespeed_)?iframe[^>]+?src=(["\'])
2045                     (?P<url>http://(?:(?:embed|www)\.)?
2046                         (?:novamov\.com|
2047                            nowvideo\.(?:ch|sx|eu|at|ag|co)|
2048                            videoweed\.(?:es|com)|
2049                            movshare\.(?:net|sx|ag)|
2050                            divxstage\.(?:eu|net|ch|co|at|ag))
2051                         /embed\.php.+?)\1''', webpage)
2052         if mobj is not None:
2053             return self.url_result(mobj.group('url'))
2054
2055         # Look for embedded Facebook player
2056         facebook_url = FacebookIE._extract_url(webpage)
2057         if facebook_url is not None:
2058             return self.url_result(facebook_url, 'Facebook')
2059
2060         # Look for embedded VK player
2061         mobj = re.search(r'<iframe[^>]+?src=(["\'])(?P<url>https?://vk\.com/video_ext\.php.+?)\1', webpage)
2062         if mobj is not None:
2063             return self.url_result(mobj.group('url'), 'VK')
2064
2065         # Look for embedded Odnoklassniki player
2066         mobj = re.search(r'<iframe[^>]+?src=(["\'])(?P<url>https?://(?:odnoklassniki|ok)\.ru/videoembed/.+?)\1', webpage)
2067         if mobj is not None:
2068             return self.url_result(mobj.group('url'), 'Odnoklassniki')
2069
2070         # Look for embedded ivi player
2071         mobj = re.search(r'<embed[^>]+?src=(["\'])(?P<url>https?://(?:www\.)?ivi\.ru/video/player.+?)\1', webpage)
2072         if mobj is not None:
2073             return self.url_result(mobj.group('url'), 'Ivi')
2074
2075         # Look for embedded Huffington Post player
2076         mobj = re.search(
2077             r'<iframe[^>]+?src=(["\'])(?P<url>https?://embed\.live\.huffingtonpost\.com/.+?)\1', webpage)
2078         if mobj is not None:
2079             return self.url_result(mobj.group('url'), 'HuffPost')
2080
2081         # Look for embed.ly
2082         mobj = re.search(r'class=["\']embedly-card["\'][^>]href=["\'](?P<url>[^"\']+)', webpage)
2083         if mobj is not None:
2084             return self.url_result(mobj.group('url'))
2085         mobj = re.search(r'class=["\']embedly-embed["\'][^>]src=["\'][^"\']*url=(?P<url>[^&]+)', webpage)
2086         if mobj is not None:
2087             return self.url_result(compat_urllib_parse_unquote(mobj.group('url')))
2088
2089         # Look for funnyordie embed
2090         matches = re.findall(r'<iframe[^>]+?src="(https?://(?:www\.)?funnyordie\.com/embed/[^"]+)"', webpage)
2091         if matches:
2092             return _playlist_from_matches(
2093                 matches, getter=unescapeHTML, ie='FunnyOrDie')
2094
2095         # Look for BBC iPlayer embed
2096         matches = re.findall(r'setPlaylist\("(https?://www\.bbc\.co\.uk/iplayer/[^/]+/[\da-z]{8})"\)', webpage)
2097         if matches:
2098             return _playlist_from_matches(matches, ie='BBCCoUk')
2099
2100         # Look for embedded RUTV player
2101         rutv_url = RUTVIE._extract_url(webpage)
2102         if rutv_url:
2103             return self.url_result(rutv_url, 'RUTV')
2104
2105         # Look for embedded TVC player
2106         tvc_url = TVCIE._extract_url(webpage)
2107         if tvc_url:
2108             return self.url_result(tvc_url, 'TVC')
2109
2110         # Look for embedded SportBox player
2111         sportbox_urls = SportBoxEmbedIE._extract_urls(webpage)
2112         if sportbox_urls:
2113             return _playlist_from_matches(sportbox_urls, ie='SportBoxEmbed')
2114
2115         # Look for embedded XHamster player
2116         xhamster_urls = XHamsterEmbedIE._extract_urls(webpage)
2117         if xhamster_urls:
2118             return _playlist_from_matches(xhamster_urls, ie='XHamsterEmbed')
2119
2120         # Look for embedded TNAFlixNetwork player
2121         tnaflix_urls = TNAFlixNetworkEmbedIE._extract_urls(webpage)
2122         if tnaflix_urls:
2123             return _playlist_from_matches(tnaflix_urls, ie=TNAFlixNetworkEmbedIE.ie_key())
2124
2125         # Look for embedded PornHub player
2126         pornhub_urls = PornHubIE._extract_urls(webpage)
2127         if pornhub_urls:
2128             return _playlist_from_matches(pornhub_urls, ie=PornHubIE.ie_key())
2129
2130         # Look for embedded DrTuber player
2131         drtuber_urls = DrTuberIE._extract_urls(webpage)
2132         if drtuber_urls:
2133             return _playlist_from_matches(drtuber_urls, ie=DrTuberIE.ie_key())
2134
2135         # Look for embedded RedTube player
2136         redtube_urls = RedTubeIE._extract_urls(webpage)
2137         if redtube_urls:
2138             return _playlist_from_matches(redtube_urls, ie=RedTubeIE.ie_key())
2139
2140         # Look for embedded Tvigle player
2141         mobj = re.search(
2142             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//cloud\.tvigle\.ru/video/.+?)\1', webpage)
2143         if mobj is not None:
2144             return self.url_result(mobj.group('url'), 'Tvigle')
2145
2146         # Look for embedded TED player
2147         mobj = re.search(
2148             r'<iframe[^>]+?src=(["\'])(?P<url>https?://embed(?:-ssl)?\.ted\.com/.+?)\1', webpage)
2149         if mobj is not None:
2150             return self.url_result(mobj.group('url'), 'TED')
2151
2152         # Look for embedded Ustream videos
2153         ustream_url = UstreamIE._extract_url(webpage)
2154         if ustream_url:
2155             return self.url_result(ustream_url, UstreamIE.ie_key())
2156
2157         # Look for embedded arte.tv player
2158         mobj = re.search(
2159             r'<(?:script|iframe) [^>]*?src="(?P<url>http://www\.arte\.tv/(?:playerv2/embed|arte_vp/index)[^"]+)"',
2160             webpage)
2161         if mobj is not None:
2162             return self.url_result(mobj.group('url'), 'ArteTVEmbed')
2163
2164         # Look for embedded francetv player
2165         mobj = re.search(
2166             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?://)?embed\.francetv\.fr/\?ue=.+?)\1',
2167             webpage)
2168         if mobj is not None:
2169             return self.url_result(mobj.group('url'))
2170
2171         # Look for embedded smotri.com player
2172         smotri_url = SmotriIE._extract_url(webpage)
2173         if smotri_url:
2174             return self.url_result(smotri_url, 'Smotri')
2175
2176         # Look for embedded Myvi.ru player
2177         myvi_url = MyviIE._extract_url(webpage)
2178         if myvi_url:
2179             return self.url_result(myvi_url)
2180
2181         # Look for embedded soundcloud player
2182         soundcloud_urls = SoundcloudIE._extract_urls(webpage)
2183         if soundcloud_urls:
2184             return _playlist_from_matches(soundcloud_urls, getter=unescapeHTML, ie=SoundcloudIE.ie_key())
2185
2186         # Look for tunein player
2187         tunein_urls = TuneInBaseIE._extract_urls(webpage)
2188         if tunein_urls:
2189             return _playlist_from_matches(tunein_urls)
2190
2191         # Look for embedded mtvservices player
2192         mtvservices_url = MTVServicesEmbeddedIE._extract_url(webpage)
2193         if mtvservices_url:
2194             return self.url_result(mtvservices_url, ie='MTVServicesEmbedded')
2195
2196         # Look for embedded yahoo player
2197         mobj = re.search(
2198             r'<iframe[^>]+?src=(["\'])(?P<url>https?://(?:screen|movies)\.yahoo\.com/.+?\.html\?format=embed)\1',
2199             webpage)
2200         if mobj is not None:
2201             return self.url_result(mobj.group('url'), 'Yahoo')
2202
2203         # Look for embedded sbs.com.au player
2204         mobj = re.search(
2205             r'''(?x)
2206             (?:
2207                 <meta\s+property="og:video"\s+content=|
2208                 <iframe[^>]+?src=
2209             )
2210             (["\'])(?P<url>https?://(?:www\.)?sbs\.com\.au/ondemand/video/.+?)\1''',
2211             webpage)
2212         if mobj is not None:
2213             return self.url_result(mobj.group('url'), 'SBS')
2214
2215         # Look for embedded Cinchcast player
2216         mobj = re.search(
2217             r'<iframe[^>]+?src=(["\'])(?P<url>https?://player\.cinchcast\.com/.+?)\1',
2218             webpage)
2219         if mobj is not None:
2220             return self.url_result(mobj.group('url'), 'Cinchcast')
2221
2222         mobj = re.search(
2223             r'<iframe[^>]+?src=(["\'])(?P<url>https?://m(?:lb)?\.mlb\.com/shared/video/embed/embed\.html\?.+?)\1',
2224             webpage)
2225         if not mobj:
2226             mobj = re.search(
2227                 r'data-video-link=["\'](?P<url>http://m.mlb.com/video/[^"\']+)',
2228                 webpage)
2229         if mobj is not None:
2230             return self.url_result(mobj.group('url'), 'MLB')
2231
2232         mobj = re.search(
2233             r'<(?:iframe|script)[^>]+?src=(["\'])(?P<url>%s)\1' % CondeNastIE.EMBED_URL,
2234             webpage)
2235         if mobj is not None:
2236             return self.url_result(self._proto_relative_url(mobj.group('url'), scheme='http:'), 'CondeNast')
2237
2238         mobj = re.search(
2239             r'<iframe[^>]+src="(?P<url>https?://(?:new\.)?livestream\.com/[^"]+/player[^"]+)"',
2240             webpage)
2241         if mobj is not None:
2242             return self.url_result(mobj.group('url'), 'Livestream')
2243
2244         # Look for Zapiks embed
2245         mobj = re.search(
2246             r'<iframe[^>]+src="(?P<url>https?://(?:www\.)?zapiks\.fr/index\.php\?.+?)"', webpage)
2247         if mobj is not None:
2248             return self.url_result(mobj.group('url'), 'Zapiks')
2249
2250         # Look for Kaltura embeds
2251         kaltura_url = KalturaIE._extract_url(webpage)
2252         if kaltura_url:
2253             return self.url_result(smuggle_url(kaltura_url, {'source_url': url}), KalturaIE.ie_key())
2254
2255         # Look for Eagle.Platform embeds
2256         eagleplatform_url = EaglePlatformIE._extract_url(webpage)
2257         if eagleplatform_url:
2258             return self.url_result(eagleplatform_url, EaglePlatformIE.ie_key())
2259
2260         # Look for ClipYou (uses Eagle.Platform) embeds
2261         mobj = re.search(
2262             r'<iframe[^>]+src="https?://(?P<host>media\.clipyou\.ru)/index/player\?.*\brecord_id=(?P<id>\d+).*"', webpage)
2263         if mobj is not None:
2264             return self.url_result('eagleplatform:%(host)s:%(id)s' % mobj.groupdict(), 'EaglePlatform')
2265
2266         # Look for Pladform embeds
2267         pladform_url = PladformIE._extract_url(webpage)
2268         if pladform_url:
2269             return self.url_result(pladform_url)
2270
2271         # Look for Videomore embeds
2272         videomore_url = VideomoreIE._extract_url(webpage)
2273         if videomore_url:
2274             return self.url_result(videomore_url)
2275
2276         # Look for Webcaster embeds
2277         webcaster_url = WebcasterFeedIE._extract_url(self, webpage)
2278         if webcaster_url:
2279             return self.url_result(webcaster_url, ie=WebcasterFeedIE.ie_key())
2280
2281         # Look for Playwire embeds
2282         mobj = re.search(
2283             r'<script[^>]+data-config=(["\'])(?P<url>(?:https?:)?//config\.playwire\.com/.+?)\1', webpage)
2284         if mobj is not None:
2285             return self.url_result(mobj.group('url'))
2286
2287         # Look for 5min embeds
2288         mobj = re.search(
2289             r'<meta[^>]+property="og:video"[^>]+content="https?://embed\.5min\.com/(?P<id>[0-9]+)/?', webpage)
2290         if mobj is not None:
2291             return self.url_result('5min:%s' % mobj.group('id'), 'FiveMin')
2292
2293         # Look for Crooks and Liars embeds
2294         mobj = re.search(
2295             r'<(?:iframe[^>]+src|param[^>]+value)=(["\'])(?P<url>(?:https?:)?//embed\.crooksandliars\.com/(?:embed|v)/.+?)\1', webpage)
2296         if mobj is not None:
2297             return self.url_result(mobj.group('url'))
2298
2299         # Look for NBC Sports VPlayer embeds
2300         nbc_sports_url = NBCSportsVPlayerIE._extract_url(webpage)
2301         if nbc_sports_url:
2302             return self.url_result(nbc_sports_url, 'NBCSportsVPlayer')
2303
2304         # Look for NBC News embeds
2305         nbc_news_embed_url = re.search(
2306             r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//www\.nbcnews\.com/widget/video-embed/[^"\']+)\1', webpage)
2307         if nbc_news_embed_url:
2308             return self.url_result(nbc_news_embed_url.group('url'), 'NBCNews')
2309
2310         # Look for Google Drive embeds
2311         google_drive_url = GoogleDriveIE._extract_url(webpage)
2312         if google_drive_url:
2313             return self.url_result(google_drive_url, 'GoogleDrive')
2314
2315         # Look for UDN embeds
2316         mobj = re.search(
2317             r'<iframe[^>]+src="(?P<url>%s)"' % UDNEmbedIE._PROTOCOL_RELATIVE_VALID_URL, webpage)
2318         if mobj is not None:
2319             return self.url_result(
2320                 compat_urlparse.urljoin(url, mobj.group('url')), 'UDNEmbed')
2321
2322         # Look for Senate ISVP iframe
2323         senate_isvp_url = SenateISVPIE._search_iframe_url(webpage)
2324         if senate_isvp_url:
2325             return self.url_result(senate_isvp_url, 'SenateISVP')
2326
2327         # Look for Dailymotion Cloud videos
2328         dmcloud_url = DailymotionCloudIE._extract_dmcloud_url(webpage)
2329         if dmcloud_url:
2330             return self.url_result(dmcloud_url, 'DailymotionCloud')
2331
2332         # Look for OnionStudios embeds
2333         onionstudios_url = OnionStudiosIE._extract_url(webpage)
2334         if onionstudios_url:
2335             return self.url_result(onionstudios_url)
2336
2337         # Look for ViewLift embeds
2338         viewlift_url = ViewLiftEmbedIE._extract_url(webpage)
2339         if viewlift_url:
2340             return self.url_result(viewlift_url)
2341
2342         # Look for JWPlatform embeds
2343         jwplatform_url = JWPlatformIE._extract_url(webpage)
2344         if jwplatform_url:
2345             return self.url_result(jwplatform_url, 'JWPlatform')
2346
2347         # Look for Digiteka embeds
2348         digiteka_url = DigitekaIE._extract_url(webpage)
2349         if digiteka_url:
2350             return self.url_result(self._proto_relative_url(digiteka_url), DigitekaIE.ie_key())
2351
2352         # Look for Arkena embeds
2353         arkena_url = ArkenaIE._extract_url(webpage)
2354         if arkena_url:
2355             return self.url_result(arkena_url, ArkenaIE.ie_key())
2356
2357         # Look for Piksel embeds
2358         piksel_url = PikselIE._extract_url(webpage)
2359         if piksel_url:
2360             return self.url_result(piksel_url, PikselIE.ie_key())
2361
2362         # Look for Limelight embeds
2363         mobj = re.search(r'LimelightPlayer\.doLoad(Media|Channel|ChannelList)\(["\'](?P<id>[a-z0-9]{32})', webpage)
2364         if mobj:
2365             lm = {
2366                 'Media': 'media',
2367                 'Channel': 'channel',
2368                 'ChannelList': 'channel_list',
2369             }
2370             return self.url_result(smuggle_url('limelight:%s:%s' % (
2371                 lm[mobj.group(1)], mobj.group(2)), {'source_url': url}),
2372                 'Limelight%s' % mobj.group(1), mobj.group(2))
2373
2374         mobj = re.search(
2375             r'''(?sx)
2376                 <object[^>]+class=(["\'])LimelightEmbeddedPlayerFlash\1[^>]*>.*?
2377                     <param[^>]+
2378                         name=(["\'])flashVars\2[^>]+
2379                         value=(["\'])(?:(?!\3).)*mediaId=(?P<id>[a-z0-9]{32})
2380             ''', webpage)
2381         if mobj:
2382             return self.url_result(smuggle_url(
2383                 'limelight:media:%s' % mobj.group('id'),
2384                 {'source_url': url}), 'LimelightMedia', mobj.group('id'))
2385
2386         # Look for AdobeTVVideo embeds
2387         mobj = re.search(
2388             r'<iframe[^>]+src=[\'"]((?:https?:)?//video\.tv\.adobe\.com/v/\d+[^"]+)[\'"]',
2389             webpage)
2390         if mobj is not None:
2391             return self.url_result(
2392                 self._proto_relative_url(unescapeHTML(mobj.group(1))),
2393                 'AdobeTVVideo')
2394
2395         # Look for Vine embeds
2396         mobj = re.search(
2397             r'<iframe[^>]+src=[\'"]((?:https?:)?//(?:www\.)?vine\.co/v/[^/]+/embed/(?:simple|postcard))',
2398             webpage)
2399         if mobj is not None:
2400             return self.url_result(
2401                 self._proto_relative_url(unescapeHTML(mobj.group(1))), 'Vine')
2402
2403         # Look for VODPlatform embeds
2404         mobj = re.search(
2405             r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?vod-platform\.net/[eE]mbed/.+?)\1',
2406             webpage)
2407         if mobj is not None:
2408             return self.url_result(
2409                 self._proto_relative_url(unescapeHTML(mobj.group('url'))), 'VODPlatform')
2410
2411         # Look for Mangomolo embeds
2412         mobj = re.search(
2413             r'''(?x)<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?admin\.mangomolo\.com/analytics/index\.php/customers/embed/
2414                 (?:
2415                     video\?.*?\bid=(?P<video_id>\d+)|
2416                     index\?.*?\bchannelid=(?P<channel_id>(?:[A-Za-z0-9+/=]|%2B|%2F|%3D)+)
2417                 ).+?)\1''', webpage)
2418         if mobj is not None:
2419             info = {
2420                 '_type': 'url_transparent',
2421                 'url': self._proto_relative_url(unescapeHTML(mobj.group('url'))),
2422                 'title': video_title,
2423                 'description': video_description,
2424                 'thumbnail': video_thumbnail,
2425                 'uploader': video_uploader,
2426             }
2427             video_id = mobj.group('video_id')
2428             if video_id:
2429                 info.update({
2430                     'ie_key': 'MangomoloVideo',
2431                     'id': video_id,
2432                 })
2433             else:
2434                 info.update({
2435                     'ie_key': 'MangomoloLive',
2436                     'id': mobj.group('channel_id'),
2437                 })
2438             return info
2439
2440         # Look for Instagram embeds
2441         instagram_embed_url = InstagramIE._extract_embed_url(webpage)
2442         if instagram_embed_url is not None:
2443             return self.url_result(
2444                 self._proto_relative_url(instagram_embed_url), InstagramIE.ie_key())
2445
2446         # Look for LiveLeak embeds
2447         liveleak_url = LiveLeakIE._extract_url(webpage)
2448         if liveleak_url:
2449             return self.url_result(liveleak_url, 'LiveLeak')
2450
2451         # Look for 3Q SDN embeds
2452         threeqsdn_url = ThreeQSDNIE._extract_url(webpage)
2453         if threeqsdn_url:
2454             return {
2455                 '_type': 'url_transparent',
2456                 'ie_key': ThreeQSDNIE.ie_key(),
2457                 'url': self._proto_relative_url(threeqsdn_url),
2458                 'title': video_title,
2459                 'description': video_description,
2460                 'thumbnail': video_thumbnail,
2461                 'uploader': video_uploader,
2462             }
2463
2464         # Look for VBOX7 embeds
2465         vbox7_url = Vbox7IE._extract_url(webpage)
2466         if vbox7_url:
2467             return self.url_result(vbox7_url, Vbox7IE.ie_key())
2468
2469         # Look for DBTV embeds
2470         dbtv_urls = DBTVIE._extract_urls(webpage)
2471         if dbtv_urls:
2472             return _playlist_from_matches(dbtv_urls, ie=DBTVIE.ie_key())
2473
2474         # Look for Videa embeds
2475         videa_urls = VideaIE._extract_urls(webpage)
2476         if videa_urls:
2477             return _playlist_from_matches(videa_urls, ie=VideaIE.ie_key())
2478
2479         # Look for 20 minuten embeds
2480         twentymin_urls = TwentyMinutenIE._extract_urls(webpage)
2481         if twentymin_urls:
2482             return _playlist_from_matches(
2483                 twentymin_urls, ie=TwentyMinutenIE.ie_key())
2484
2485         # Look for Openload embeds
2486         openload_urls = OpenloadIE._extract_urls(webpage)
2487         if openload_urls:
2488             return _playlist_from_matches(
2489                 openload_urls, ie=OpenloadIE.ie_key())
2490
2491         # Look for VideoPress embeds
2492         videopress_urls = VideoPressIE._extract_urls(webpage)
2493         if videopress_urls:
2494             return _playlist_from_matches(
2495                 videopress_urls, ie=VideoPressIE.ie_key())
2496
2497         # Looking for http://schema.org/VideoObject
2498         json_ld = self._search_json_ld(
2499             webpage, video_id, default={}, expected_type='VideoObject')
2500         if json_ld.get('url'):
2501             info_dict.update({
2502                 'title': video_title or info_dict['title'],
2503                 'description': video_description,
2504                 'thumbnail': video_thumbnail,
2505                 'age_limit': age_limit
2506             })
2507             info_dict.update(json_ld)
2508             return info_dict
2509
2510         # Look for HTML5 media
2511         entries = self._parse_html5_media_entries(url, webpage, video_id, m3u8_id='hls')
2512         if entries:
2513             for entry in entries:
2514                 entry.update({
2515                     'id': video_id,
2516                     'title': video_title,
2517                 })
2518                 self._sort_formats(entry['formats'])
2519             return self.playlist_result(entries)
2520
2521         jwplayer_data = self._find_jwplayer_data(
2522             webpage, video_id, transform_source=js_to_json)
2523         if jwplayer_data:
2524             return self._parse_jwplayer_data(jwplayer_data, video_id)
2525
2526         def check_video(vurl):
2527             if YoutubeIE.suitable(vurl):
2528                 return True
2529             if RtmpIE.suitable(vurl):
2530                 return True
2531             vpath = compat_urlparse.urlparse(vurl).path
2532             vext = determine_ext(vpath)
2533             return '.' in vpath and vext not in ('swf', 'png', 'jpg', 'srt', 'sbv', 'sub', 'vtt', 'ttml', 'js')
2534
2535         def filter_video(urls):
2536             return list(filter(check_video, urls))
2537
2538         # Start with something easy: JW Player in SWFObject
2539         found = filter_video(re.findall(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage))
2540         if not found:
2541             # Look for gorilla-vid style embedding
2542             found = filter_video(re.findall(r'''(?sx)
2543                 (?:
2544                     jw_plugins|
2545                     JWPlayerOptions|
2546                     jwplayer\s*\(\s*["'][^'"]+["']\s*\)\s*\.setup
2547                 )
2548                 .*?
2549                 ['"]?file['"]?\s*:\s*["\'](.*?)["\']''', webpage))
2550         if not found:
2551             # Broaden the search a little bit
2552             found = filter_video(re.findall(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage))
2553         if not found:
2554             # Broaden the findall a little bit: JWPlayer JS loader
2555             found = filter_video(re.findall(
2556                 r'[^A-Za-z0-9]?(?:file|video_url)["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage))
2557         if not found:
2558             # Flow player
2559             found = filter_video(re.findall(r'''(?xs)
2560                 flowplayer\("[^"]+",\s*
2561                     \{[^}]+?\}\s*,
2562                     \s*\{[^}]+? ["']?clip["']?\s*:\s*\{\s*
2563                         ["']?url["']?\s*:\s*["']([^"']+)["']
2564             ''', webpage))
2565         if not found:
2566             # Cinerama player
2567             found = re.findall(
2568                 r"cinerama\.embedPlayer\(\s*\'[^']+\',\s*'([^']+)'", webpage)
2569         if not found:
2570             # Try to find twitter cards info
2571             # twitter:player:stream should be checked before twitter:player since
2572             # it is expected to contain a raw stream (see
2573             # https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser)
2574             found = filter_video(re.findall(
2575                 r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage))
2576         if not found:
2577             # We look for Open Graph info:
2578             # We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am)
2579             m_video_type = re.findall(r'<meta.*?property="og:video:type".*?content="video/(.*?)"', webpage)
2580             # We only look in og:video if the MIME type is a video, don't try if it's a Flash player:
2581             if m_video_type is not None:
2582                 found = filter_video(re.findall(r'<meta.*?property="og:video".*?content="(.*?)"', webpage))
2583         if not found:
2584             REDIRECT_REGEX = r'[0-9]{,2};\s*(?:URL|url)=\'?([^\'"]+)'
2585             found = re.search(
2586                 r'(?i)<meta\s+(?=(?:[a-z-]+="[^"]+"\s+)*http-equiv="refresh")'
2587                 r'(?:[a-z-]+="[^"]+"\s+)*?content="%s' % REDIRECT_REGEX,
2588                 webpage)
2589             if not found:
2590                 # Look also in Refresh HTTP header
2591                 refresh_header = head_response.headers.get('Refresh')
2592                 if refresh_header:
2593                     # In python 2 response HTTP headers are bytestrings
2594                     if sys.version_info < (3, 0) and isinstance(refresh_header, str):
2595                         refresh_header = refresh_header.decode('iso-8859-1')
2596                     found = re.search(REDIRECT_REGEX, refresh_header)
2597             if found:
2598                 new_url = compat_urlparse.urljoin(url, unescapeHTML(found.group(1)))
2599                 self.report_following_redirect(new_url)
2600                 return {
2601                     '_type': 'url',
2602                     'url': new_url,
2603                 }
2604
2605         if not found:
2606             # twitter:player is a https URL to iframe player that may or may not
2607             # be supported by youtube-dl thus this is checked the very last (see
2608             # https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser)
2609             embed_url = self._html_search_meta('twitter:player', webpage, default=None)
2610             if embed_url:
2611                 return self.url_result(embed_url)
2612
2613         if not found:
2614             raise UnsupportedError(url)
2615
2616         entries = []
2617         for video_url in orderedSet(found):
2618             video_url = unescapeHTML(video_url)
2619             video_url = video_url.replace('\\/', '/')
2620             video_url = compat_urlparse.urljoin(url, video_url)
2621             video_id = compat_urllib_parse_unquote(os.path.basename(video_url))
2622
2623             # Sometimes, jwplayer extraction will result in a YouTube URL
2624             if YoutubeIE.suitable(video_url):
2625                 entries.append(self.url_result(video_url, 'Youtube'))
2626                 continue
2627
2628             # here's a fun little line of code for you:
2629             video_id = os.path.splitext(video_id)[0]
2630
2631             entry_info_dict = {
2632                 'id': video_id,
2633                 'uploader': video_uploader,
2634                 'title': video_title,
2635                 'age_limit': age_limit,
2636             }
2637
2638             if RtmpIE.suitable(video_url):
2639                 entry_info_dict.update({
2640                     '_type': 'url_transparent',
2641                     'ie_key': RtmpIE.ie_key(),
2642                     'url': video_url,
2643                 })
2644                 entries.append(entry_info_dict)
2645                 continue
2646
2647             ext = determine_ext(video_url)
2648             if ext == 'smil':
2649                 entry_info_dict['formats'] = self._extract_smil_formats(video_url, video_id)
2650             elif ext == 'xspf':
2651                 return self.playlist_result(self._extract_xspf_playlist(video_url, video_id), video_id)
2652             elif ext == 'm3u8':
2653                 entry_info_dict['formats'] = self._extract_m3u8_formats(video_url, video_id, ext='mp4')
2654             elif ext == 'mpd':
2655                 entry_info_dict['formats'] = self._extract_mpd_formats(video_url, video_id)
2656             elif ext == 'f4m':
2657                 entry_info_dict['formats'] = self._extract_f4m_formats(video_url, video_id)
2658             elif re.search(r'(?i)\.(?:ism|smil)/manifest', video_url) and video_url != url:
2659                 # Just matching .ism/manifest is not enough to be reliably sure
2660                 # whether it's actually an ISM manifest or some other streaming
2661                 # manifest since there are various streaming URL formats
2662                 # possible (see [1]) as well as some other shenanigans like
2663                 # .smil/manifest URLs that actually serve an ISM (see [2]) and
2664                 # so on.
2665                 # Thus the most reasonable way to solve this is to delegate
2666                 # to generic extractor in order to look into the contents of
2667                 # the manifest itself.
2668                 # 1. https://azure.microsoft.com/en-us/documentation/articles/media-services-deliver-content-overview/#streaming-url-formats
2669                 # 2. https://svs.itworkscdn.net/lbcivod/smil:itwfcdn/lbci/170976.smil/Manifest
2670                 entry_info_dict = self.url_result(
2671                     smuggle_url(video_url, {'to_generic': True}),
2672                     GenericIE.ie_key())
2673             else:
2674                 entry_info_dict['url'] = video_url
2675
2676             if entry_info_dict.get('formats'):
2677                 self._sort_formats(entry_info_dict['formats'])
2678
2679             entries.append(entry_info_dict)
2680
2681         if len(entries) == 1:
2682             return entries[0]
2683         else:
2684             for num, e in enumerate(entries, start=1):
2685                 # 'url' results don't have a title
2686                 if e.get('title') is not None:
2687                     e['title'] = '%s (%d)' % (e['title'], num)
2688             return {
2689                 '_type': 'playlist',
2690                 'entries': entries,
2691             }