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