Merge branch 'qqmusic-playlist' of https://github.com/ping/youtube-dl into ping-qqmus...
[youtube-dl] / youtube_dl / extractor / generic.py
1 # encoding: utf-8
2
3 from __future__ import unicode_literals
4
5 import os
6 import re
7
8 from .common import InfoExtractor
9 from .youtube import YoutubeIE
10 from ..compat import (
11     compat_urllib_parse,
12     compat_urllib_parse_unquote,
13     compat_urllib_request,
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     parse_xml,
25     smuggle_url,
26     unescapeHTML,
27     unified_strdate,
28     unsmuggle_url,
29     UnsupportedError,
30     url_basename,
31     xpath_text,
32 )
33 from .brightcove import BrightcoveIE
34 from .nbc import NBCSportsVPlayerIE
35 from .ooyala import OoyalaIE
36 from .rutv import RUTVIE
37 from .tvc import TVCIE
38 from .sportbox import SportBoxEmbedIE
39 from .smotri import SmotriIE
40 from .condenast import CondeNastIE
41 from .udn import UDNEmbedIE
42 from .senateisvp import SenateISVPIE
43 from .bliptv import BlipTVIE
44 from .svt import SVTIE
45 from .pornhub import PornHubIE
46 from .xhamster import XHamsterEmbedIE
47 from .vimeo import VimeoIE
48 from .dailymotion import DailymotionCloudIE
49 from .onionstudios import OnionStudiosIE
50 from .snagfilms import SnagFilmsEmbedIE
51
52
53 class GenericIE(InfoExtractor):
54     IE_DESC = 'Generic downloader that works on some sites'
55     _VALID_URL = r'.*'
56     IE_NAME = 'generic'
57     _TESTS = [
58         # Direct link to a video
59         {
60             'url': 'http://media.w3.org/2010/05/sintel/trailer.mp4',
61             'md5': '67d406c2bcb6af27fa886f31aa934bbe',
62             'info_dict': {
63                 'id': 'trailer',
64                 'ext': 'mp4',
65                 'title': 'trailer',
66                 'upload_date': '20100513',
67             }
68         },
69         # Direct link to media delivered compressed (until Accept-Encoding is *)
70         {
71             'url': 'http://calimero.tk/muzik/FictionJunction-Parallel_Hearts.flac',
72             'md5': '128c42e68b13950268b648275386fc74',
73             'info_dict': {
74                 'id': 'FictionJunction-Parallel_Hearts',
75                 'ext': 'flac',
76                 'title': 'FictionJunction-Parallel_Hearts',
77                 'upload_date': '20140522',
78             },
79             'expected_warnings': [
80                 'URL could be a direct video link, returning it as such.'
81             ]
82         },
83         # Direct download with broken HEAD
84         {
85             'url': 'http://ai-radio.org:8000/radio.opus',
86             'info_dict': {
87                 'id': 'radio',
88                 'ext': 'opus',
89                 'title': 'radio',
90             },
91             'params': {
92                 'skip_download': True,  # infinite live stream
93             },
94             'expected_warnings': [
95                 r'501.*Not Implemented'
96             ],
97         },
98         # Direct link with incorrect MIME type
99         {
100             'url': 'http://ftp.nluug.nl/video/nluug/2014-11-20_nj14/zaal-2/5_Lennart_Poettering_-_Systemd.webm',
101             'md5': '4ccbebe5f36706d85221f204d7eb5913',
102             'info_dict': {
103                 'url': 'http://ftp.nluug.nl/video/nluug/2014-11-20_nj14/zaal-2/5_Lennart_Poettering_-_Systemd.webm',
104                 'id': '5_Lennart_Poettering_-_Systemd',
105                 'ext': 'webm',
106                 'title': '5_Lennart_Poettering_-_Systemd',
107                 'upload_date': '20141120',
108             },
109             'expected_warnings': [
110                 'URL could be a direct video link, returning it as such.'
111             ]
112         },
113         # RSS feed
114         {
115             'url': 'http://phihag.de/2014/youtube-dl/rss2.xml',
116             'info_dict': {
117                 'id': 'http://phihag.de/2014/youtube-dl/rss2.xml',
118                 'title': 'Zero Punctuation',
119                 'description': 're:.*groundbreaking video review series.*'
120             },
121             'playlist_mincount': 11,
122         },
123         # RSS feed with enclosure
124         {
125             'url': 'http://podcastfeeds.nbcnews.com/audio/podcast/MSNBC-MADDOW-NETCAST-M4V.xml',
126             'info_dict': {
127                 'id': 'pdv_maddow_netcast_m4v-02-27-2015-201624',
128                 'ext': 'm4v',
129                 'upload_date': '20150228',
130                 'title': 'pdv_maddow_netcast_m4v-02-27-2015-201624',
131             }
132         },
133         # google redirect
134         {
135             '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',
136             'info_dict': {
137                 'id': 'cmQHVoWB5FY',
138                 'ext': 'mp4',
139                 'upload_date': '20130224',
140                 'uploader_id': 'TheVerge',
141                 'description': 're:^Chris Ziegler takes a look at the\.*',
142                 'uploader': 'The Verge',
143                 'title': 'First Firefox OS phones side-by-side',
144             },
145             'params': {
146                 'skip_download': False,
147             }
148         },
149         {
150             'url': 'http://www.hodiho.fr/2013/02/regis-plante-sa-jeep.html',
151             'md5': '85b90ccc9d73b4acd9138d3af4c27f89',
152             'info_dict': {
153                 'id': '13601338388002',
154                 'ext': 'mp4',
155                 'uploader': 'www.hodiho.fr',
156                 'title': 'R\u00e9gis plante sa Jeep',
157             }
158         },
159         # bandcamp page with custom domain
160         {
161             'add_ie': ['Bandcamp'],
162             'url': 'http://bronyrock.com/track/the-pony-mash',
163             'info_dict': {
164                 'id': '3235767654',
165                 'ext': 'mp3',
166                 'title': 'The Pony Mash',
167                 'uploader': 'M_Pallante',
168             },
169             'skip': 'There is a limit of 200 free downloads / month for the test song',
170         },
171         # embedded brightcove video
172         # it also tests brightcove videos that need to set the 'Referer' in the
173         # http requests
174         {
175             'add_ie': ['Brightcove'],
176             'url': 'http://www.bfmtv.com/video/bfmbusiness/cours-bourse/cours-bourse-l-analyse-technique-154522/',
177             'info_dict': {
178                 'id': '2765128793001',
179                 'ext': 'mp4',
180                 'title': 'Le cours de bourse : l’analyse technique',
181                 'description': 'md5:7e9ad046e968cb2d1114004aba466fd9',
182                 'uploader': 'BFM BUSINESS',
183             },
184             'params': {
185                 'skip_download': True,
186             },
187         },
188         {
189             # https://github.com/rg3/youtube-dl/issues/2253
190             'url': 'http://bcove.me/i6nfkrc3',
191             'md5': '0ba9446db037002366bab3b3eb30c88c',
192             'info_dict': {
193                 'id': '3101154703001',
194                 'ext': 'mp4',
195                 'title': 'Still no power',
196                 'uploader': 'thestar.com',
197                 '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.',
198             },
199             'add_ie': ['Brightcove'],
200         },
201         {
202             'url': 'http://www.championat.com/video/football/v/87/87499.html',
203             'md5': 'fb973ecf6e4a78a67453647444222983',
204             'info_dict': {
205                 'id': '3414141473001',
206                 'ext': 'mp4',
207                 'title': 'Видео. Удаление Дзагоева (ЦСКА)',
208                 'description': 'Онлайн-трансляция матча ЦСКА - "Волга"',
209                 'uploader': 'Championat',
210             },
211         },
212         {
213             # https://github.com/rg3/youtube-dl/issues/3541
214             'add_ie': ['Brightcove'],
215             'url': 'http://www.kijk.nl/sbs6/leermijvrouwenkennen/videos/jqMiXKAYan2S/aflevering-1',
216             'info_dict': {
217                 'id': '3866516442001',
218                 'ext': 'mp4',
219                 'title': 'Leer mij vrouwen kennen: Aflevering 1',
220                 'description': 'Leer mij vrouwen kennen: Aflevering 1',
221                 'uploader': 'SBS Broadcasting',
222             },
223             'skip': 'Restricted to Netherlands',
224             'params': {
225                 'skip_download': True,  # m3u8 download
226             },
227         },
228         # ooyala video
229         {
230             'url': 'http://www.rollingstone.com/music/videos/norwegian-dj-cashmere-cat-goes-spartan-on-with-me-premiere-20131219',
231             'md5': '166dd577b433b4d4ebfee10b0824d8ff',
232             'info_dict': {
233                 'id': 'BwY2RxaTrTkslxOfcan0UCf0YqyvWysJ',
234                 'ext': 'mp4',
235                 'title': '2cc213299525360.mov',  # that's what we get
236             },
237             'add_ie': ['Ooyala'],
238         },
239         # multiple ooyala embeds on SBN network websites
240         {
241             'url': 'http://www.sbnation.com/college-football-recruiting/2015/2/3/7970291/national-signing-day-rationalizations-itll-be-ok-itll-be-ok',
242             'info_dict': {
243                 'id': 'national-signing-day-rationalizations-itll-be-ok-itll-be-ok',
244                 'title': '25 lies you will tell yourself on National Signing Day - SBNation.com',
245             },
246             'playlist_mincount': 3,
247             'params': {
248                 'skip_download': True,
249             },
250             'add_ie': ['Ooyala'],
251         },
252         # embed.ly video
253         {
254             'url': 'http://www.tested.com/science/weird/460206-tested-grinding-coffee-2000-frames-second/',
255             'info_dict': {
256                 'id': '9ODmcdjQcHQ',
257                 'ext': 'mp4',
258                 'title': 'Tested: Grinding Coffee at 2000 Frames Per Second',
259                 'upload_date': '20140225',
260                 'description': 'md5:06a40fbf30b220468f1e0957c0f558ff',
261                 'uploader': 'Tested',
262                 'uploader_id': 'testedcom',
263             },
264             # No need to test YoutubeIE here
265             'params': {
266                 'skip_download': True,
267             },
268         },
269         # funnyordie embed
270         {
271             'url': 'http://www.theguardian.com/world/2014/mar/11/obama-zach-galifianakis-between-two-ferns',
272             'info_dict': {
273                 'id': '18e820ec3f',
274                 'ext': 'mp4',
275                 'title': 'Between Two Ferns with Zach Galifianakis: President Barack Obama',
276                 'description': 'Episode 18: President Barack Obama sits down with Zach Galifianakis for his most memorable interview yet.',
277             },
278         },
279         # BBC iPlayer embeds
280         {
281             'url': 'http://www.bbc.co.uk/blogs/adamcurtis/posts/BUGGER',
282             'info_dict': {
283                 'title': 'BBC - Blogs -  Adam Curtis - BUGGER',
284             },
285             'playlist_mincount': 18,
286         },
287         # RUTV embed
288         {
289             'url': 'http://www.rg.ru/2014/03/15/reg-dfo/anklav-anons.html',
290             'info_dict': {
291                 'id': '776940',
292                 'ext': 'mp4',
293                 'title': 'Охотское море стало целиком российским',
294                 'description': 'md5:5ed62483b14663e2a95ebbe115eb8f43',
295             },
296             'params': {
297                 # m3u8 download
298                 'skip_download': True,
299             },
300         },
301         # TVC embed
302         {
303             '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/',
304             'info_dict': {
305                 'id': '55304',
306                 'ext': 'mp4',
307                 'title': 'Дошкольное воспитание',
308             },
309         },
310         # SportBox embed
311         {
312             'url': 'http://www.vestifinance.ru/articles/25753',
313             'info_dict': {
314                 'id': '25753',
315                 'title': 'Вести Экономика ― Прямые трансляции с Форума-выставки "Госзаказ-2013"',
316             },
317             'playlist': [{
318                 'info_dict': {
319                     'id': '370908',
320                     'title': 'Госзаказ. День 3',
321                     'ext': 'mp4',
322                 }
323             }, {
324                 'info_dict': {
325                     'id': '370905',
326                     'title': 'Госзаказ. День 2',
327                     'ext': 'mp4',
328                 }
329             }, {
330                 'info_dict': {
331                     'id': '370902',
332                     'title': 'Госзаказ. День 1',
333                     'ext': 'mp4',
334                 }
335             }],
336             'params': {
337                 # m3u8 download
338                 'skip_download': True,
339             },
340         },
341         # XHamster embed
342         {
343             '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',
344             'info_dict': {
345                 'id': 'showthread',
346                 'title': '[NSFL] [FM15] which pumiscer was this ( vid ) ( alfa as fuck srx )',
347             },
348             'playlist_mincount': 7,
349         },
350         # Embedded TED video
351         {
352             'url': 'http://en.support.wordpress.com/videos/ted-talks/',
353             'md5': '65fdff94098e4a607385a60c5177c638',
354             'info_dict': {
355                 'id': '1969',
356                 'ext': 'mp4',
357                 'title': 'Hidden miracles of the natural world',
358                 'uploader': 'Louie Schwartzberg',
359                 'description': 'md5:8145d19d320ff3e52f28401f4c4283b9',
360             }
361         },
362         # Embeded Ustream video
363         {
364             'url': 'http://www.american.edu/spa/pti/nsa-privacy-janus-2014.cfm',
365             'md5': '27b99cdb639c9b12a79bca876a073417',
366             'info_dict': {
367                 'id': '45734260',
368                 'ext': 'flv',
369                 'uploader': 'AU SPA:  The NSA and Privacy',
370                 'title': 'NSA and Privacy Forum Debate featuring General Hayden and Barton Gellman'
371             }
372         },
373         # nowvideo embed hidden behind percent encoding
374         {
375             'url': 'http://www.waoanime.tv/the-super-dimension-fortress-macross-episode-1/',
376             'md5': '2baf4ddd70f697d94b1c18cf796d5107',
377             'info_dict': {
378                 'id': '06e53103ca9aa',
379                 'ext': 'flv',
380                 'title': 'Macross Episode 001  Watch Macross Episode 001 onl',
381                 'description': 'No description',
382             },
383         },
384         # arte embed
385         {
386             'url': 'http://www.tv-replay.fr/redirection/20-03-14/x-enius-arte-10753389.html',
387             'md5': '7653032cbb25bf6c80d80f217055fa43',
388             'info_dict': {
389                 'id': '048195-004_PLUS7-F',
390                 'ext': 'flv',
391                 'title': 'X:enius',
392                 'description': 'md5:d5fdf32ef6613cdbfd516ae658abf168',
393                 'upload_date': '20140320',
394             },
395             'params': {
396                 'skip_download': 'Requires rtmpdump'
397             }
398         },
399         # Condé Nast embed
400         {
401             'url': 'http://www.wired.com/2014/04/honda-asimo/',
402             'md5': 'ba0dfe966fa007657bd1443ee672db0f',
403             'info_dict': {
404                 'id': '53501be369702d3275860000',
405                 'ext': 'mp4',
406                 'title': 'Honda’s  New Asimo Robot Is More Human Than Ever',
407             }
408         },
409         # Dailymotion embed
410         {
411             'url': 'http://www.spi0n.com/zap-spi0n-com-n216/',
412             'md5': '441aeeb82eb72c422c7f14ec533999cd',
413             'info_dict': {
414                 'id': 'k2mm4bCdJ6CQ2i7c8o2',
415                 'ext': 'mp4',
416                 'title': 'Le Zap de Spi0n n°216 - Zapping du Web',
417                 'uploader': 'Spi0n',
418             },
419             'add_ie': ['Dailymotion'],
420         },
421         # YouTube embed
422         {
423             'url': 'http://www.badzine.de/ansicht/datum/2014/06/09/so-funktioniert-die-neue-englische-badminton-liga.html',
424             'info_dict': {
425                 'id': 'FXRb4ykk4S0',
426                 'ext': 'mp4',
427                 'title': 'The NBL Auction 2014',
428                 'uploader': 'BADMINTON England',
429                 'uploader_id': 'BADMINTONEvents',
430                 'upload_date': '20140603',
431                 'description': 'md5:9ef128a69f1e262a700ed83edb163a73',
432             },
433             'add_ie': ['Youtube'],
434             'params': {
435                 'skip_download': True,
436             }
437         },
438         # MTVSercices embed
439         {
440             'url': 'http://www.gametrailers.com/news-post/76093/north-america-europe-is-getting-that-mario-kart-8-mercedes-dlc-too',
441             'md5': '35727f82f58c76d996fc188f9755b0d5',
442             'info_dict': {
443                 'id': '0306a69b-8adf-4fb5-aace-75f8e8cbfca9',
444                 'ext': 'mp4',
445                 'title': 'Review',
446                 'description': 'Mario\'s life in the fast lane has never looked so good.',
447             },
448         },
449         # YouTube embed via <data-embed-url="">
450         {
451             'url': 'https://play.google.com/store/apps/details?id=com.gameloft.android.ANMP.GloftA8HM',
452             'info_dict': {
453                 'id': '4vAffPZIT44',
454                 'ext': 'mp4',
455                 'title': 'Asphalt 8: Airborne - Update - Welcome to Dubai!',
456                 'uploader': 'Gameloft',
457                 'uploader_id': 'gameloft',
458                 'upload_date': '20140828',
459                 'description': 'md5:c80da9ed3d83ae6d1876c834de03e1c4',
460             },
461             'params': {
462                 'skip_download': True,
463             }
464         },
465         # Camtasia studio
466         {
467             'url': 'http://www.ll.mit.edu/workshops/education/videocourses/antennas/lecture1/video/',
468             'playlist': [{
469                 'md5': '0c5e352edabf715d762b0ad4e6d9ee67',
470                 'info_dict': {
471                     'id': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final',
472                     'title': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final - video1',
473                     'ext': 'flv',
474                     'duration': 2235.90,
475                 }
476             }, {
477                 'md5': '10e4bb3aaca9fd630e273ff92d9f3c63',
478                 'info_dict': {
479                     'id': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final_PIP',
480                     'title': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final - pip',
481                     'ext': 'flv',
482                     'duration': 2235.93,
483                 }
484             }],
485             'info_dict': {
486                 'title': 'Fenn-AA_PA_Radar_Course_Lecture_1c_Final',
487             }
488         },
489         # Flowplayer
490         {
491             'url': 'http://www.handjobhub.com/video/busty-blonde-siri-tit-fuck-while-wank-6313.html',
492             'md5': '9d65602bf31c6e20014319c7d07fba27',
493             'info_dict': {
494                 'id': '5123ea6d5e5a7',
495                 'ext': 'mp4',
496                 'age_limit': 18,
497                 'uploader': 'www.handjobhub.com',
498                 'title': 'Busty Blonde Siri Tit Fuck While Wank at HandjobHub.com',
499             }
500         },
501         # Multiple brightcove videos
502         # https://github.com/rg3/youtube-dl/issues/2283
503         {
504             'url': 'http://www.newyorker.com/online/blogs/newsdesk/2014/01/always-never-nuclear-command-and-control.html',
505             'info_dict': {
506                 'id': 'always-never',
507                 'title': 'Always / Never - The New Yorker',
508             },
509             'playlist_count': 3,
510             'params': {
511                 'extract_flat': False,
512                 'skip_download': True,
513             }
514         },
515         # MLB embed
516         {
517             'url': 'http://umpire-empire.com/index.php/topic/58125-laz-decides-no-thats-low/',
518             'md5': '96f09a37e44da40dd083e12d9a683327',
519             'info_dict': {
520                 'id': '33322633',
521                 'ext': 'mp4',
522                 'title': 'Ump changes call to ball',
523                 'description': 'md5:71c11215384298a172a6dcb4c2e20685',
524                 'duration': 48,
525                 'timestamp': 1401537900,
526                 'upload_date': '20140531',
527                 'thumbnail': 're:^https?://.*\.jpg$',
528             },
529         },
530         # Wistia embed
531         {
532             'url': 'http://education-portal.com/academy/lesson/north-american-exploration-failed-colonies-of-spain-france-england.html#lesson',
533             'md5': '8788b683c777a5cf25621eaf286d0c23',
534             'info_dict': {
535                 'id': '1cfaf6b7ea',
536                 'ext': 'mov',
537                 'title': 'md5:51364a8d3d009997ba99656004b5e20d',
538                 'duration': 643.0,
539                 'filesize': 182808282,
540                 'uploader': 'education-portal.com',
541             },
542         },
543         {
544             'url': 'http://thoughtworks.wistia.com/medias/uxjb0lwrcz',
545             'md5': 'baf49c2baa8a7de5f3fc145a8506dcd4',
546             'info_dict': {
547                 'id': 'uxjb0lwrcz',
548                 'ext': 'mp4',
549                 'title': 'Conversation about Hexagonal Rails Part 1 - ThoughtWorks',
550                 'duration': 1715.0,
551                 'uploader': 'thoughtworks.wistia.com',
552             },
553         },
554         # Soundcloud embed
555         {
556             'url': 'http://nakedsecurity.sophos.com/2014/10/29/sscc-171-are-you-sure-that-1234-is-a-bad-password-podcast/',
557             'info_dict': {
558                 'id': '174391317',
559                 'ext': 'mp3',
560                 'description': 'md5:ff867d6b555488ad3c52572bb33d432c',
561                 'uploader': 'Sophos Security',
562                 'title': 'Chet Chat 171 - Oct 29, 2014',
563                 'upload_date': '20141029',
564             }
565         },
566         # Livestream embed
567         {
568             'url': 'http://www.esa.int/Our_Activities/Space_Science/Rosetta/Philae_comet_touch-down_webcast',
569             'info_dict': {
570                 'id': '67864563',
571                 'ext': 'flv',
572                 'upload_date': '20141112',
573                 'title': 'Rosetta #CometLanding webcast HL 10',
574             }
575         },
576         # LazyYT
577         {
578             'url': 'http://discourse.ubuntu.com/t/unity-8-desktop-mode-windows-on-mir/1986',
579             'info_dict': {
580                 'id': '1986',
581                 'title': 'Unity 8 desktop-mode windows on Mir! - Ubuntu Discourse',
582             },
583             'playlist_mincount': 2,
584         },
585         # Cinchcast embed
586         {
587             'url': 'http://undergroundwellness.com/podcasts/306-5-steps-to-permanent-gut-healing/',
588             'info_dict': {
589                 'id': '7141703',
590                 'ext': 'mp3',
591                 'upload_date': '20141126',
592                 'title': 'Jack Tips: 5 Steps to Permanent Gut Healing',
593             }
594         },
595         # Cinerama player
596         {
597             'url': 'http://www.abc.net.au/7.30/content/2015/s4164797.htm',
598             'info_dict': {
599                 'id': '730m_DandD_1901_512k',
600                 'ext': 'mp4',
601                 'uploader': 'www.abc.net.au',
602                 'title': 'Game of Thrones with dice - Dungeons and Dragons fantasy role-playing game gets new life - 19/01/2015',
603             }
604         },
605         # embedded viddler video
606         {
607             'url': 'http://deadspin.com/i-cant-stop-watching-john-wall-chop-the-nuggets-with-th-1681801597',
608             'info_dict': {
609                 'id': '4d03aad9',
610                 'ext': 'mp4',
611                 'uploader': 'deadspin',
612                 'title': 'WALL-TO-GORTAT',
613                 'timestamp': 1422285291,
614                 'upload_date': '20150126',
615             },
616             'add_ie': ['Viddler'],
617         },
618         # Libsyn embed
619         {
620             'url': 'http://thedailyshow.cc.com/podcast/episodetwelve',
621             'info_dict': {
622                 'id': '3377616',
623                 'ext': 'mp3',
624                 'title': "The Daily Show Podcast without Jon Stewart - Episode 12: Bassem Youssef: Egypt's Jon Stewart",
625                 'description': 'md5:601cb790edd05908957dae8aaa866465',
626                 'upload_date': '20150220',
627             },
628         },
629         # jwplayer YouTube
630         {
631             'url': 'http://media.nationalarchives.gov.uk/index.php/webinar-using-discovery-national-archives-online-catalogue/',
632             'info_dict': {
633                 'id': 'Mrj4DVp2zeA',
634                 'ext': 'mp4',
635                 'upload_date': '20150212',
636                 'uploader': 'The National Archives UK',
637                 'description': 'md5:a236581cd2449dd2df4f93412f3f01c6',
638                 'uploader_id': 'NationalArchives08',
639                 'title': 'Webinar: Using Discovery, The National Archives’ online catalogue',
640             },
641         },
642         # rtl.nl embed
643         {
644             'url': 'http://www.rtlnieuws.nl/nieuws/buitenland/aanslagen-kopenhagen',
645             'playlist_mincount': 5,
646             'info_dict': {
647                 'id': 'aanslagen-kopenhagen',
648                 'title': 'Aanslagen Kopenhagen | RTL Nieuws',
649             }
650         },
651         # Zapiks embed
652         {
653             'url': 'http://www.skipass.com/news/116090-bon-appetit-s5ep3-baqueira-mi-cor.html',
654             'info_dict': {
655                 'id': '118046',
656                 'ext': 'mp4',
657                 'title': 'EP3S5 - Bon Appétit - Baqueira Mi Corazon !',
658             }
659         },
660         # Kaltura embed
661         {
662             'url': 'http://www.monumentalnetwork.com/videos/john-carlson-postgame-2-25-15',
663             'info_dict': {
664                 'id': '1_eergr3h1',
665                 'ext': 'mp4',
666                 'upload_date': '20150226',
667                 'uploader_id': 'MonumentalSports-Kaltura@perfectsensedigital.com',
668                 'timestamp': int,
669                 'title': 'John Carlson Postgame 2/25/15',
670             },
671         },
672         # Kaltura embed (different embed code)
673         {
674             'url': 'http://www.premierchristianradio.com/Shows/Saturday/Unbelievable/Conference-Videos/Os-Guinness-Is-It-Fools-Talk-Unbelievable-Conference-2014',
675             'info_dict': {
676                 'id': '1_a52wc67y',
677                 'ext': 'flv',
678                 'upload_date': '20150127',
679                 'uploader_id': 'PremierMedia',
680                 'timestamp': int,
681                 'title': 'Os Guinness // Is It Fools Talk? // Unbelievable? Conference 2014',
682             },
683         },
684         # Eagle.Platform embed (generic URL)
685         {
686             'url': 'http://lenta.ru/news/2015/03/06/navalny/',
687             'info_dict': {
688                 'id': '227304',
689                 'ext': 'mp4',
690                 'title': 'Навальный вышел на свободу',
691                 'description': 'md5:d97861ac9ae77377f3f20eaf9d04b4f5',
692                 'thumbnail': 're:^https?://.*\.jpg$',
693                 'duration': 87,
694                 'view_count': int,
695                 'age_limit': 0,
696             },
697         },
698         # ClipYou (Eagle.Platform) embed (custom URL)
699         {
700             'url': 'http://muz-tv.ru/play/7129/',
701             'info_dict': {
702                 'id': '12820',
703                 'ext': 'mp4',
704                 'title': "'O Sole Mio",
705                 'thumbnail': 're:^https?://.*\.jpg$',
706                 'duration': 216,
707                 'view_count': int,
708             },
709         },
710         # Pladform embed
711         {
712             'url': 'http://muz-tv.ru/kinozal/view/7400/',
713             'info_dict': {
714                 'id': '100183293',
715                 'ext': 'mp4',
716                 'title': 'Тайны перевала Дятлова • 1 серия 2 часть',
717                 'description': 'Документальный сериал-расследование одной из самых жутких тайн ХХ века',
718                 'thumbnail': 're:^https?://.*\.jpg$',
719                 'duration': 694,
720                 'age_limit': 0,
721             },
722         },
723         # Playwire embed
724         {
725             'url': 'http://www.cinemablend.com/new/First-Joe-Dirt-2-Trailer-Teaser-Stupid-Greatness-70874.html',
726             'info_dict': {
727                 'id': '3519514',
728                 'ext': 'mp4',
729                 'title': 'Joe Dirt 2 Beautiful Loser Teaser Trailer',
730                 'thumbnail': 're:^https?://.*\.png$',
731                 'duration': 45.115,
732             },
733         },
734         # 5min embed
735         {
736             'url': 'http://techcrunch.com/video/facebook-creates-on-this-day-crunch-report/518726732/',
737             'md5': '4c6f127a30736b59b3e2c19234ee2bf7',
738             'info_dict': {
739                 'id': '518726732',
740                 'ext': 'mp4',
741                 'title': 'Facebook Creates "On This Day" | Crunch Report',
742             },
743         },
744         # SVT embed
745         {
746             'url': 'http://www.svt.se/sport/ishockey/jagr-tacklar-giroux-under-intervjun',
747             'info_dict': {
748                 'id': '2900353',
749                 'ext': 'flv',
750                 'title': 'Här trycker Jagr till Giroux (under SVT-intervjun)',
751                 'duration': 27,
752                 'age_limit': 0,
753             },
754         },
755         # Crooks and Liars embed
756         {
757             'url': 'http://crooksandliars.com/2015/04/fox-friends-says-protecting-atheists',
758             'info_dict': {
759                 'id': '8RUoRhRi',
760                 'ext': 'mp4',
761                 'title': "Fox & Friends Says Protecting Atheists From Discrimination Is Anti-Christian!",
762                 'description': 'md5:e1a46ad1650e3a5ec7196d432799127f',
763                 'timestamp': 1428207000,
764                 'upload_date': '20150405',
765                 'uploader': 'Heather',
766             },
767         },
768         # Crooks and Liars external embed
769         {
770             'url': 'http://theothermccain.com/2010/02/02/video-proves-that-bill-kristol-has-been-watching-glenn-beck/comment-page-1/',
771             'info_dict': {
772                 'id': 'MTE3MjUtMzQ2MzA',
773                 'ext': 'mp4',
774                 'title': 'md5:5e3662a81a4014d24c250d76d41a08d5',
775                 'description': 'md5:9b8e9542d6c3c5de42d6451b7d780cec',
776                 'timestamp': 1265032391,
777                 'upload_date': '20100201',
778                 'uploader': 'Heather',
779             },
780         },
781         # NBC Sports vplayer embed
782         {
783             'url': 'http://www.riderfans.com/forum/showthread.php?121827-Freeman&s=e98fa1ea6dc08e886b1678d35212494a',
784             'info_dict': {
785                 'id': 'ln7x1qSThw4k',
786                 'ext': 'flv',
787                 'title': "PFT Live: New leader in the 'new-look' defense",
788                 'description': 'md5:65a19b4bbfb3b0c0c5768bed1dfad74e',
789             },
790         },
791         # UDN embed
792         {
793             'url': 'http://www.udn.com/news/story/7314/822787',
794             'md5': 'fd2060e988c326991037b9aff9df21a6',
795             'info_dict': {
796                 'id': '300346',
797                 'ext': 'mp4',
798                 'title': '中一中男師變性 全校師生力挺',
799                 'thumbnail': 're:^https?://.*\.jpg$',
800             }
801         },
802         # Ooyala embed
803         {
804             'url': 'http://www.businessinsider.com/excel-index-match-vlookup-video-how-to-2015-2?IR=T',
805             'info_dict': {
806                 'id': '50YnY4czr4ms1vJ7yz3xzq0excz_pUMs',
807                 'ext': 'mp4',
808                 'description': 'VIDEO: Index/Match versus VLOOKUP.',
809                 'title': 'This is what separates the Excel masters from the wannabes',
810             },
811             'params': {
812                 # m3u8 downloads
813                 'skip_download': True,
814             }
815         },
816         # Contains a SMIL manifest
817         {
818             'url': 'http://www.telewebion.com/fa/1263668/%D9%82%D8%B1%D8%B9%D9%87%E2%80%8C%DA%A9%D8%B4%DB%8C-%D9%84%DB%8C%DA%AF-%D9%82%D9%87%D8%B1%D9%85%D8%A7%D9%86%D8%A7%D9%86-%D8%A7%D8%B1%D9%88%D9%BE%D8%A7/%2B-%D9%81%D9%88%D8%AA%D8%A8%D8%A7%D9%84.html',
819             'info_dict': {
820                 'id': 'file',
821                 'ext': 'flv',
822                 'title': '+ Football: Lottery Champions League Europe',
823                 'uploader': 'www.telewebion.com',
824             },
825             'params': {
826                 # rtmpe downloads
827                 'skip_download': True,
828             }
829         },
830         # Brightcove URL in single quotes
831         {
832             'url': 'http://www.sportsnet.ca/baseball/mlb/sn-presents-russell-martin-world-citizen/',
833             'md5': '4ae374f1f8b91c889c4b9203c8c752af',
834             'info_dict': {
835                 'id': '4255764656001',
836                 'ext': 'mp4',
837                 'title': 'SN Presents: Russell Martin, World Citizen',
838                 '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.',
839                 'uploader': 'Rogers Sportsnet',
840             },
841         },
842         # Dailymotion Cloud video
843         {
844             'url': 'http://replay.publicsenat.fr/vod/le-debat/florent-kolandjian,dominique-cena,axel-decourtye,laurence-abeille,bruno-parmentier/175910',
845             'md5': '49444254273501a64675a7e68c502681',
846             'info_dict': {
847                 'id': '5585de919473990de4bee11b',
848                 'ext': 'mp4',
849                 'title': 'Le débat',
850                 'thumbnail': 're:^https?://.*\.jpe?g$',
851             }
852         },
853         # OnionStudios embed
854         {
855             'url': 'http://www.clickhole.com/video/dont-understand-bitcoin-man-will-mumble-explanatio-2537',
856             'info_dict': {
857                 'id': '2855',
858                 'ext': 'mp4',
859                 'title': 'Don’t Understand Bitcoin? This Man Will Mumble An Explanation At You',
860                 'thumbnail': 're:^https?://.*\.jpe?g$',
861                 'uploader': 'ClickHole',
862                 'uploader_id': 'clickhole',
863             }
864         },
865         # SnagFilms embed
866         {
867             'url': 'http://whilewewatch.blogspot.ru/2012/06/whilewewatch-whilewewatch-gripping.html',
868             'info_dict': {
869                 'id': '74849a00-85a9-11e1-9660-123139220831',
870                 'ext': 'mp4',
871                 'title': '#whilewewatch',
872             }
873         },
874         # AdobeTVVideo embed
875         {
876             'url': 'https://helpx.adobe.com/acrobat/how-to/new-experience-acrobat-dc.html?set=acrobat--get-started--essential-beginners',
877             'md5': '43662b577c018ad707a63766462b1e87',
878             'info_dict': {
879                 'id': '2456',
880                 'ext': 'mp4',
881                 'title': 'New experience with Acrobat DC',
882                 'description': 'New experience with Acrobat DC',
883                 'duration': 248.667,
884             },
885         }
886     ]
887
888     def report_following_redirect(self, new_url):
889         """Report information extraction."""
890         self._downloader.to_screen('[redirect] Following redirect to %s' % new_url)
891
892     def _extract_rss(self, url, video_id, doc):
893         playlist_title = doc.find('./channel/title').text
894         playlist_desc_el = doc.find('./channel/description')
895         playlist_desc = None if playlist_desc_el is None else playlist_desc_el.text
896
897         entries = []
898         for it in doc.findall('./channel/item'):
899             next_url = xpath_text(it, 'link', fatal=False)
900             if not next_url:
901                 enclosure_nodes = it.findall('./enclosure')
902                 for e in enclosure_nodes:
903                     next_url = e.attrib.get('url')
904                     if next_url:
905                         break
906
907             if not next_url:
908                 continue
909
910             entries.append({
911                 '_type': 'url',
912                 'url': next_url,
913                 'title': it.find('title').text,
914             })
915
916         return {
917             '_type': 'playlist',
918             'id': url,
919             'title': playlist_title,
920             'description': playlist_desc,
921             'entries': entries,
922         }
923
924     def _extract_camtasia(self, url, video_id, webpage):
925         """ Returns None if no camtasia video can be found. """
926
927         camtasia_cfg = self._search_regex(
928             r'fo\.addVariable\(\s*"csConfigFile",\s*"([^"]+)"\s*\);',
929             webpage, 'camtasia configuration file', default=None)
930         if camtasia_cfg is None:
931             return None
932
933         title = self._html_search_meta('DC.title', webpage, fatal=True)
934
935         camtasia_url = compat_urlparse.urljoin(url, camtasia_cfg)
936         camtasia_cfg = self._download_xml(
937             camtasia_url, video_id,
938             note='Downloading camtasia configuration',
939             errnote='Failed to download camtasia configuration')
940         fileset_node = camtasia_cfg.find('./playlist/array/fileset')
941
942         entries = []
943         for n in fileset_node.getchildren():
944             url_n = n.find('./uri')
945             if url_n is None:
946                 continue
947
948             entries.append({
949                 'id': os.path.splitext(url_n.text.rpartition('/')[2])[0],
950                 'title': '%s - %s' % (title, n.tag),
951                 'url': compat_urlparse.urljoin(url, url_n.text),
952                 'duration': float_or_none(n.find('./duration').text),
953             })
954
955         return {
956             '_type': 'playlist',
957             'entries': entries,
958             'title': title,
959         }
960
961     def _real_extract(self, url):
962         if url.startswith('//'):
963             return {
964                 '_type': 'url',
965                 'url': self.http_scheme() + url,
966             }
967
968         parsed_url = compat_urlparse.urlparse(url)
969         if not parsed_url.scheme:
970             default_search = self._downloader.params.get('default_search')
971             if default_search is None:
972                 default_search = 'fixup_error'
973
974             if default_search in ('auto', 'auto_warning', 'fixup_error'):
975                 if '/' in url:
976                     self._downloader.report_warning('The url doesn\'t specify the protocol, trying with http')
977                     return self.url_result('http://' + url)
978                 elif default_search != 'fixup_error':
979                     if default_search == 'auto_warning':
980                         if re.match(r'^(?:url|URL)$', url):
981                             raise ExtractorError(
982                                 'Invalid URL:  %r . Call youtube-dl like this:  youtube-dl -v "https://www.youtube.com/watch?v=BaW_jenozKc"  ' % url,
983                                 expected=True)
984                         else:
985                             self._downloader.report_warning(
986                                 'Falling back to youtube search for  %s . Set --default-search "auto" to suppress this warning.' % url)
987                     return self.url_result('ytsearch:' + url)
988
989             if default_search in ('error', 'fixup_error'):
990                 raise ExtractorError(
991                     '%r is not a valid URL. '
992                     'Set --default-search "ytsearch" (or run  youtube-dl "ytsearch:%s" ) to search YouTube'
993                     % (url, url), expected=True)
994             else:
995                 if ':' not in default_search:
996                     default_search += ':'
997                 return self.url_result(default_search + url)
998
999         url, smuggled_data = unsmuggle_url(url)
1000         force_videoid = None
1001         is_intentional = smuggled_data and smuggled_data.get('to_generic')
1002         if smuggled_data and 'force_videoid' in smuggled_data:
1003             force_videoid = smuggled_data['force_videoid']
1004             video_id = force_videoid
1005         else:
1006             video_id = compat_urllib_parse_unquote(os.path.splitext(url.rstrip('/').split('/')[-1])[0])
1007
1008         self.to_screen('%s: Requesting header' % video_id)
1009
1010         head_req = HEADRequest(url)
1011         head_response = self._request_webpage(
1012             head_req, video_id,
1013             note=False, errnote='Could not send HEAD request to %s' % url,
1014             fatal=False)
1015
1016         if head_response is not False:
1017             # Check for redirect
1018             new_url = head_response.geturl()
1019             if url != new_url:
1020                 self.report_following_redirect(new_url)
1021                 if force_videoid:
1022                     new_url = smuggle_url(
1023                         new_url, {'force_videoid': force_videoid})
1024                 return self.url_result(new_url)
1025
1026         full_response = None
1027         if head_response is False:
1028             request = compat_urllib_request.Request(url)
1029             request.add_header('Accept-Encoding', '*')
1030             full_response = self._request_webpage(request, video_id)
1031             head_response = full_response
1032
1033         # Check for direct link to a video
1034         content_type = head_response.headers.get('Content-Type', '')
1035         m = re.match(r'^(?P<type>audio|video|application(?=/ogg$))/(?P<format_id>.+)$', content_type)
1036         if m:
1037             upload_date = unified_strdate(
1038                 head_response.headers.get('Last-Modified'))
1039             return {
1040                 'id': video_id,
1041                 'title': compat_urllib_parse_unquote(os.path.splitext(url_basename(url))[0]),
1042                 'direct': True,
1043                 'formats': [{
1044                     'format_id': m.group('format_id'),
1045                     'url': url,
1046                     'vcodec': 'none' if m.group('type') == 'audio' else None
1047                 }],
1048                 'upload_date': upload_date,
1049             }
1050
1051         if not self._downloader.params.get('test', False) and not is_intentional:
1052             force = self._downloader.params.get('force_generic_extractor', False)
1053             self._downloader.report_warning(
1054                 '%s on generic information extractor.' % ('Forcing' if force else 'Falling back'))
1055
1056         if not full_response:
1057             request = compat_urllib_request.Request(url)
1058             # Some webservers may serve compressed content of rather big size (e.g. gzipped flac)
1059             # making it impossible to download only chunk of the file (yet we need only 512kB to
1060             # test whether it's HTML or not). According to youtube-dl default Accept-Encoding
1061             # that will always result in downloading the whole file that is not desirable.
1062             # Therefore for extraction pass we have to override Accept-Encoding to any in order
1063             # to accept raw bytes and being able to download only a chunk.
1064             # It may probably better to solve this by checking Content-Type for application/octet-stream
1065             # after HEAD request finishes, but not sure if we can rely on this.
1066             request.add_header('Accept-Encoding', '*')
1067             full_response = self._request_webpage(request, video_id)
1068
1069         # Maybe it's a direct link to a video?
1070         # Be careful not to download the whole thing!
1071         first_bytes = full_response.read(512)
1072         if not is_html(first_bytes):
1073             self._downloader.report_warning(
1074                 'URL could be a direct video link, returning it as such.')
1075             upload_date = unified_strdate(
1076                 head_response.headers.get('Last-Modified'))
1077             return {
1078                 'id': video_id,
1079                 'title': compat_urllib_parse_unquote(os.path.splitext(url_basename(url))[0]),
1080                 'direct': True,
1081                 'url': url,
1082                 'upload_date': upload_date,
1083             }
1084
1085         webpage = self._webpage_read_content(
1086             full_response, url, video_id, prefix=first_bytes)
1087
1088         self.report_extraction(video_id)
1089
1090         # Is it an RSS feed?
1091         try:
1092             doc = parse_xml(webpage)
1093             if doc.tag == 'rss':
1094                 return self._extract_rss(url, video_id, doc)
1095         except compat_xml_parse_error:
1096             pass
1097
1098         # Is it a Camtasia project?
1099         camtasia_res = self._extract_camtasia(url, video_id, webpage)
1100         if camtasia_res is not None:
1101             return camtasia_res
1102
1103         # Sometimes embedded video player is hidden behind percent encoding
1104         # (e.g. https://github.com/rg3/youtube-dl/issues/2448)
1105         # Unescaping the whole page allows to handle those cases in a generic way
1106         webpage = compat_urllib_parse.unquote(webpage)
1107
1108         # it's tempting to parse this further, but you would
1109         # have to take into account all the variations like
1110         #   Video Title - Site Name
1111         #   Site Name | Video Title
1112         #   Video Title - Tagline | Site Name
1113         # and so on and so forth; it's just not practical
1114         video_title = self._html_search_regex(
1115             r'(?s)<title>(.*?)</title>', webpage, 'video title',
1116             default='video')
1117
1118         # Try to detect age limit automatically
1119         age_limit = self._rta_search(webpage)
1120         # And then there are the jokers who advertise that they use RTA,
1121         # but actually don't.
1122         AGE_LIMIT_MARKERS = [
1123             r'Proudly Labeled <a href="http://www.rtalabel.org/" title="Restricted to Adults">RTA</a>',
1124         ]
1125         if any(re.search(marker, webpage) for marker in AGE_LIMIT_MARKERS):
1126             age_limit = 18
1127
1128         # video uploader is domain name
1129         video_uploader = self._search_regex(
1130             r'^(?:https?://)?([^/]*)/.*', url, 'video uploader')
1131
1132         # Helper method
1133         def _playlist_from_matches(matches, getter=None, ie=None):
1134             urlrs = orderedSet(
1135                 self.url_result(self._proto_relative_url(getter(m) if getter else m), ie)
1136                 for m in matches)
1137             return self.playlist_result(
1138                 urlrs, playlist_id=video_id, playlist_title=video_title)
1139
1140         # Look for BrightCove:
1141         bc_urls = BrightcoveIE._extract_brightcove_urls(webpage)
1142         if bc_urls:
1143             self.to_screen('Brightcove video detected.')
1144             entries = [{
1145                 '_type': 'url',
1146                 'url': smuggle_url(bc_url, {'Referer': url}),
1147                 'ie_key': 'Brightcove'
1148             } for bc_url in bc_urls]
1149
1150             return {
1151                 '_type': 'playlist',
1152                 'title': video_title,
1153                 'id': video_id,
1154                 'entries': entries,
1155             }
1156
1157         # Look for embedded rtl.nl player
1158         matches = re.findall(
1159             r'<iframe[^>]+?src="((?:https?:)?//(?:www\.)?rtl\.nl/system/videoplayer/[^"]+(?:video_)?embed[^"]+)"',
1160             webpage)
1161         if matches:
1162             return _playlist_from_matches(matches, ie='RtlNl')
1163
1164         vimeo_url = VimeoIE._extract_vimeo_url(url, webpage)
1165         if vimeo_url is not None:
1166             return self.url_result(vimeo_url)
1167
1168         # Look for embedded YouTube player
1169         matches = re.findall(r'''(?x)
1170             (?:
1171                 <iframe[^>]+?src=|
1172                 data-video-url=|
1173                 <embed[^>]+?src=|
1174                 embedSWF\(?:\s*|
1175                 new\s+SWFObject\(
1176             )
1177             (["\'])
1178                 (?P<url>(?:https?:)?//(?:www\.)?youtube(?:-nocookie)?\.com/
1179                 (?:embed|v|p)/.+?)
1180             \1''', webpage)
1181         if matches:
1182             return _playlist_from_matches(
1183                 matches, lambda m: unescapeHTML(m[1]))
1184
1185         # Look for lazyYT YouTube embed
1186         matches = re.findall(
1187             r'class="lazyYT" data-youtube-id="([^"]+)"', webpage)
1188         if matches:
1189             return _playlist_from_matches(matches, lambda m: unescapeHTML(m))
1190
1191         # Look for embedded Dailymotion player
1192         matches = re.findall(
1193             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.com/embed/video/.+?)\1', webpage)
1194         if matches:
1195             return _playlist_from_matches(
1196                 matches, lambda m: unescapeHTML(m[1]))
1197
1198         # Look for embedded Dailymotion playlist player (#3822)
1199         m = re.search(
1200             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.[a-z]{2,3}/widget/jukebox\?.+?)\1', webpage)
1201         if m:
1202             playlists = re.findall(
1203                 r'list\[\]=/playlist/([^/]+)/', unescapeHTML(m.group('url')))
1204             if playlists:
1205                 return _playlist_from_matches(
1206                     playlists, lambda p: '//dailymotion.com/playlist/%s' % p)
1207
1208         # Look for embedded Wistia player
1209         match = re.search(
1210             r'<(?:meta[^>]+?content|iframe[^>]+?src)=(["\'])(?P<url>(?:https?:)?//(?:fast\.)?wistia\.net/embed/iframe/.+?)\1', webpage)
1211         if match:
1212             embed_url = self._proto_relative_url(
1213                 unescapeHTML(match.group('url')))
1214             return {
1215                 '_type': 'url_transparent',
1216                 'url': embed_url,
1217                 'ie_key': 'Wistia',
1218                 'uploader': video_uploader,
1219                 'title': video_title,
1220                 'id': video_id,
1221             }
1222
1223         match = re.search(r'(?:id=["\']wistia_|data-wistia-?id=["\']|Wistia\.embed\(["\'])(?P<id>[^"\']+)', webpage)
1224         if match:
1225             return {
1226                 '_type': 'url_transparent',
1227                 'url': 'http://fast.wistia.net/embed/iframe/{0:}'.format(match.group('id')),
1228                 'ie_key': 'Wistia',
1229                 'uploader': video_uploader,
1230                 'title': video_title,
1231                 'id': match.group('id')
1232             }
1233
1234         # Look for embedded blip.tv player
1235         bliptv_url = BlipTVIE._extract_url(webpage)
1236         if bliptv_url:
1237             return self.url_result(bliptv_url, 'BlipTV')
1238
1239         # Look for SVT player
1240         svt_url = SVTIE._extract_url(webpage)
1241         if svt_url:
1242             return self.url_result(svt_url, 'SVT')
1243
1244         # Look for embedded condenast player
1245         matches = re.findall(
1246             r'<iframe\s+(?:[a-zA-Z-]+="[^"]+"\s+)*?src="(https?://player\.cnevids\.com/embed/[^"]+")',
1247             webpage)
1248         if matches:
1249             return {
1250                 '_type': 'playlist',
1251                 'entries': [{
1252                     '_type': 'url',
1253                     'ie_key': 'CondeNast',
1254                     'url': ma,
1255                 } for ma in matches],
1256                 'title': video_title,
1257                 'id': video_id,
1258             }
1259
1260         # Look for Bandcamp pages with custom domain
1261         mobj = re.search(r'<meta property="og:url"[^>]*?content="(.*?bandcamp\.com.*?)"', webpage)
1262         if mobj is not None:
1263             burl = unescapeHTML(mobj.group(1))
1264             # Don't set the extractor because it can be a track url or an album
1265             return self.url_result(burl)
1266
1267         # Look for embedded Vevo player
1268         mobj = re.search(
1269             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:cache\.)?vevo\.com/.+?)\1', webpage)
1270         if mobj is not None:
1271             return self.url_result(mobj.group('url'))
1272
1273         # Look for embedded Viddler player
1274         mobj = re.search(
1275             r'<(?:iframe[^>]+?src|param[^>]+?value)=(["\'])(?P<url>(?:https?:)?//(?:www\.)?viddler\.com/(?:embed|player)/.+?)\1',
1276             webpage)
1277         if mobj is not None:
1278             return self.url_result(mobj.group('url'))
1279
1280         # Look for NYTimes player
1281         mobj = re.search(
1282             r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//graphics8\.nytimes\.com/bcvideo/[^/]+/iframe/embed\.html.+?)\1>',
1283             webpage)
1284         if mobj is not None:
1285             return self.url_result(mobj.group('url'))
1286
1287         # Look for Libsyn player
1288         mobj = re.search(
1289             r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//html5-player\.libsyn\.com/embed/.+?)\1', webpage)
1290         if mobj is not None:
1291             return self.url_result(mobj.group('url'))
1292
1293         # Look for Ooyala videos
1294         mobj = (re.search(r'player\.ooyala\.com/[^"?]+\?[^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage) or
1295                 re.search(r'OO\.Player\.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage) or
1296                 re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage) or
1297                 re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
1298         if mobj is not None:
1299             return OoyalaIE._build_url_result(mobj.group('ec'))
1300
1301         # Look for multiple Ooyala embeds on SBN network websites
1302         mobj = re.search(r'SBN\.VideoLinkset\.entryGroup\((\[.*?\])', webpage)
1303         if mobj is not None:
1304             embeds = self._parse_json(mobj.group(1), video_id, fatal=False)
1305             if embeds:
1306                 return _playlist_from_matches(
1307                     embeds, getter=lambda v: OoyalaIE._url_for_embed_code(v['provider_video_id']), ie='Ooyala')
1308
1309         # Look for Aparat videos
1310         mobj = re.search(r'<iframe .*?src="(http://www\.aparat\.com/video/[^"]+)"', webpage)
1311         if mobj is not None:
1312             return self.url_result(mobj.group(1), 'Aparat')
1313
1314         # Look for MPORA videos
1315         mobj = re.search(r'<iframe .*?src="(http://mpora\.(?:com|de)/videos/[^"]+)"', webpage)
1316         if mobj is not None:
1317             return self.url_result(mobj.group(1), 'Mpora')
1318
1319         # Look for embedded NovaMov-based player
1320         mobj = re.search(
1321             r'''(?x)<(?:pagespeed_)?iframe[^>]+?src=(["\'])
1322                     (?P<url>http://(?:(?:embed|www)\.)?
1323                         (?:novamov\.com|
1324                            nowvideo\.(?:ch|sx|eu|at|ag|co)|
1325                            videoweed\.(?:es|com)|
1326                            movshare\.(?:net|sx|ag)|
1327                            divxstage\.(?:eu|net|ch|co|at|ag))
1328                         /embed\.php.+?)\1''', webpage)
1329         if mobj is not None:
1330             return self.url_result(mobj.group('url'))
1331
1332         # Look for embedded Facebook player
1333         mobj = re.search(
1334             r'<iframe[^>]+?src=(["\'])(?P<url>https://www\.facebook\.com/video/embed.+?)\1', webpage)
1335         if mobj is not None:
1336             return self.url_result(mobj.group('url'), 'Facebook')
1337
1338         # Look for embedded VK player
1339         mobj = re.search(r'<iframe[^>]+?src=(["\'])(?P<url>https?://vk\.com/video_ext\.php.+?)\1', webpage)
1340         if mobj is not None:
1341             return self.url_result(mobj.group('url'), 'VK')
1342
1343         # Look for embedded ivi player
1344         mobj = re.search(r'<embed[^>]+?src=(["\'])(?P<url>https?://(?:www\.)?ivi\.ru/video/player.+?)\1', webpage)
1345         if mobj is not None:
1346             return self.url_result(mobj.group('url'), 'Ivi')
1347
1348         # Look for embedded Huffington Post player
1349         mobj = re.search(
1350             r'<iframe[^>]+?src=(["\'])(?P<url>https?://embed\.live\.huffingtonpost\.com/.+?)\1', webpage)
1351         if mobj is not None:
1352             return self.url_result(mobj.group('url'), 'HuffPost')
1353
1354         # Look for embed.ly
1355         mobj = re.search(r'class=["\']embedly-card["\'][^>]href=["\'](?P<url>[^"\']+)', webpage)
1356         if mobj is not None:
1357             return self.url_result(mobj.group('url'))
1358         mobj = re.search(r'class=["\']embedly-embed["\'][^>]src=["\'][^"\']*url=(?P<url>[^&]+)', webpage)
1359         if mobj is not None:
1360             return self.url_result(compat_urllib_parse.unquote(mobj.group('url')))
1361
1362         # Look for funnyordie embed
1363         matches = re.findall(r'<iframe[^>]+?src="(https?://(?:www\.)?funnyordie\.com/embed/[^"]+)"', webpage)
1364         if matches:
1365             return _playlist_from_matches(
1366                 matches, getter=unescapeHTML, ie='FunnyOrDie')
1367
1368         # Look for BBC iPlayer embed
1369         matches = re.findall(r'setPlaylist\("(https?://www\.bbc\.co\.uk/iplayer/[^/]+/[\da-z]{8})"\)', webpage)
1370         if matches:
1371             return _playlist_from_matches(matches, ie='BBCCoUk')
1372
1373         # Look for embedded RUTV player
1374         rutv_url = RUTVIE._extract_url(webpage)
1375         if rutv_url:
1376             return self.url_result(rutv_url, 'RUTV')
1377
1378         # Look for embedded TVC player
1379         tvc_url = TVCIE._extract_url(webpage)
1380         if tvc_url:
1381             return self.url_result(tvc_url, 'TVC')
1382
1383         # Look for embedded SportBox player
1384         sportbox_urls = SportBoxEmbedIE._extract_urls(webpage)
1385         if sportbox_urls:
1386             return _playlist_from_matches(sportbox_urls, ie='SportBoxEmbed')
1387
1388         # Look for embedded PornHub player
1389         pornhub_url = PornHubIE._extract_url(webpage)
1390         if pornhub_url:
1391             return self.url_result(pornhub_url, 'PornHub')
1392
1393         # Look for embedded XHamster player
1394         xhamster_urls = XHamsterEmbedIE._extract_urls(webpage)
1395         if xhamster_urls:
1396             return _playlist_from_matches(xhamster_urls, ie='XHamsterEmbed')
1397
1398         # Look for embedded Tvigle player
1399         mobj = re.search(
1400             r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//cloud\.tvigle\.ru/video/.+?)\1', webpage)
1401         if mobj is not None:
1402             return self.url_result(mobj.group('url'), 'Tvigle')
1403
1404         # Look for embedded TED player
1405         mobj = re.search(
1406             r'<iframe[^>]+?src=(["\'])(?P<url>https?://embed(?:-ssl)?\.ted\.com/.+?)\1', webpage)
1407         if mobj is not None:
1408             return self.url_result(mobj.group('url'), 'TED')
1409
1410         # Look for embedded Ustream videos
1411         mobj = re.search(
1412             r'<iframe[^>]+?src=(["\'])(?P<url>http://www\.ustream\.tv/embed/.+?)\1', webpage)
1413         if mobj is not None:
1414             return self.url_result(mobj.group('url'), 'Ustream')
1415
1416         # Look for embedded arte.tv player
1417         mobj = re.search(
1418             r'<script [^>]*?src="(?P<url>http://www\.arte\.tv/playerv2/embed[^"]+)"',
1419             webpage)
1420         if mobj is not None:
1421             return self.url_result(mobj.group('url'), 'ArteTVEmbed')
1422
1423         # Look for embedded smotri.com player
1424         smotri_url = SmotriIE._extract_url(webpage)
1425         if smotri_url:
1426             return self.url_result(smotri_url, 'Smotri')
1427
1428         # Look for embeded soundcloud player
1429         mobj = re.search(
1430             r'<iframe\s+(?:[a-zA-Z0-9_-]+="[^"]+"\s+)*src="(?P<url>https?://(?:w\.)?soundcloud\.com/player[^"]+)"',
1431             webpage)
1432         if mobj is not None:
1433             url = unescapeHTML(mobj.group('url'))
1434             return self.url_result(url)
1435
1436         # Look for embedded vulture.com player
1437         mobj = re.search(
1438             r'<iframe src="(?P<url>https?://video\.vulture\.com/[^"]+)"',
1439             webpage)
1440         if mobj is not None:
1441             url = unescapeHTML(mobj.group('url'))
1442             return self.url_result(url, ie='Vulture')
1443
1444         # Look for embedded mtvservices player
1445         mobj = re.search(
1446             r'<iframe src="(?P<url>https?://media\.mtvnservices\.com/embed/[^"]+)"',
1447             webpage)
1448         if mobj is not None:
1449             url = unescapeHTML(mobj.group('url'))
1450             return self.url_result(url, ie='MTVServicesEmbedded')
1451
1452         # Look for embedded yahoo player
1453         mobj = re.search(
1454             r'<iframe[^>]+?src=(["\'])(?P<url>https?://(?:screen|movies)\.yahoo\.com/.+?\.html\?format=embed)\1',
1455             webpage)
1456         if mobj is not None:
1457             return self.url_result(mobj.group('url'), 'Yahoo')
1458
1459         # Look for embedded sbs.com.au player
1460         mobj = re.search(
1461             r'''(?x)
1462             (?:
1463                 <meta\s+property="og:video"\s+content=|
1464                 <iframe[^>]+?src=
1465             )
1466             (["\'])(?P<url>https?://(?:www\.)?sbs\.com\.au/ondemand/video/.+?)\1''',
1467             webpage)
1468         if mobj is not None:
1469             return self.url_result(mobj.group('url'), 'SBS')
1470
1471         # Look for embedded Cinchcast player
1472         mobj = re.search(
1473             r'<iframe[^>]+?src=(["\'])(?P<url>https?://player\.cinchcast\.com/.+?)\1',
1474             webpage)
1475         if mobj is not None:
1476             return self.url_result(mobj.group('url'), 'Cinchcast')
1477
1478         mobj = re.search(
1479             r'<iframe[^>]+?src=(["\'])(?P<url>https?://m(?:lb)?\.mlb\.com/shared/video/embed/embed\.html\?.+?)\1',
1480             webpage)
1481         if not mobj:
1482             mobj = re.search(
1483                 r'data-video-link=["\'](?P<url>http://m.mlb.com/video/[^"\']+)',
1484                 webpage)
1485         if mobj is not None:
1486             return self.url_result(mobj.group('url'), 'MLB')
1487
1488         mobj = re.search(
1489             r'<iframe[^>]+?src=(["\'])(?P<url>%s)\1' % CondeNastIE.EMBED_URL,
1490             webpage)
1491         if mobj is not None:
1492             return self.url_result(self._proto_relative_url(mobj.group('url'), scheme='http:'), 'CondeNast')
1493
1494         mobj = re.search(
1495             r'<iframe[^>]+src="(?P<url>https?://new\.livestream\.com/[^"]+/player[^"]+)"',
1496             webpage)
1497         if mobj is not None:
1498             return self.url_result(mobj.group('url'), 'Livestream')
1499
1500         # Look for Zapiks embed
1501         mobj = re.search(
1502             r'<iframe[^>]+src="(?P<url>https?://(?:www\.)?zapiks\.fr/index\.php\?.+?)"', webpage)
1503         if mobj is not None:
1504             return self.url_result(mobj.group('url'), 'Zapiks')
1505
1506         # Look for Kaltura embeds
1507         mobj = (re.search(r"(?s)kWidget\.(?:thumb)?[Ee]mbed\(\{.*?'wid'\s*:\s*'_?(?P<partner_id>[^']+)',.*?'entry_id'\s*:\s*'(?P<id>[^']+)',", webpage) or
1508                 re.search(r'(?s)(["\'])(?:https?:)?//cdnapisec\.kaltura\.com/.*?(?:p|partner_id)/(?P<partner_id>\d+).*?\1.*?entry_id\s*:\s*(["\'])(?P<id>[^\2]+?)\2', webpage))
1509         if mobj is not None:
1510             return self.url_result('kaltura:%(partner_id)s:%(id)s' % mobj.groupdict(), 'Kaltura')
1511
1512         # Look for Eagle.Platform embeds
1513         mobj = re.search(
1514             r'<iframe[^>]+src="(?P<url>https?://.+?\.media\.eagleplatform\.com/index/player\?.+?)"', webpage)
1515         if mobj is not None:
1516             return self.url_result(mobj.group('url'), 'EaglePlatform')
1517
1518         # Look for ClipYou (uses Eagle.Platform) embeds
1519         mobj = re.search(
1520             r'<iframe[^>]+src="https?://(?P<host>media\.clipyou\.ru)/index/player\?.*\brecord_id=(?P<id>\d+).*"', webpage)
1521         if mobj is not None:
1522             return self.url_result('eagleplatform:%(host)s:%(id)s' % mobj.groupdict(), 'EaglePlatform')
1523
1524         # Look for Pladform embeds
1525         mobj = re.search(
1526             r'<iframe[^>]+src="(?P<url>https?://out\.pladform\.ru/player\?.+?)"', webpage)
1527         if mobj is not None:
1528             return self.url_result(mobj.group('url'), 'Pladform')
1529
1530         # Look for Playwire embeds
1531         mobj = re.search(
1532             r'<script[^>]+data-config=(["\'])(?P<url>(?:https?:)?//config\.playwire\.com/.+?)\1', webpage)
1533         if mobj is not None:
1534             return self.url_result(mobj.group('url'))
1535
1536         # Look for 5min embeds
1537         mobj = re.search(
1538             r'<meta[^>]+property="og:video"[^>]+content="https?://embed\.5min\.com/(?P<id>[0-9]+)/?', webpage)
1539         if mobj is not None:
1540             return self.url_result('5min:%s' % mobj.group('id'), 'FiveMin')
1541
1542         # Look for Crooks and Liars embeds
1543         mobj = re.search(
1544             r'<(?:iframe[^>]+src|param[^>]+value)=(["\'])(?P<url>(?:https?:)?//embed\.crooksandliars\.com/(?:embed|v)/.+?)\1', webpage)
1545         if mobj is not None:
1546             return self.url_result(mobj.group('url'))
1547
1548         # Look for NBC Sports VPlayer embeds
1549         nbc_sports_url = NBCSportsVPlayerIE._extract_url(webpage)
1550         if nbc_sports_url:
1551             return self.url_result(nbc_sports_url, 'NBCSportsVPlayer')
1552
1553         # Look for UDN embeds
1554         mobj = re.search(
1555             r'<iframe[^>]+src="(?P<url>%s)"' % UDNEmbedIE._VALID_URL, webpage)
1556         if mobj is not None:
1557             return self.url_result(
1558                 compat_urlparse.urljoin(url, mobj.group('url')), 'UDNEmbed')
1559
1560         # Look for Senate ISVP iframe
1561         senate_isvp_url = SenateISVPIE._search_iframe_url(webpage)
1562         if senate_isvp_url:
1563             return self.url_result(senate_isvp_url, 'SenateISVP')
1564
1565         # Look for Dailymotion Cloud videos
1566         dmcloud_url = DailymotionCloudIE._extract_dmcloud_url(webpage)
1567         if dmcloud_url:
1568             return self.url_result(dmcloud_url, 'DailymotionCloud')
1569
1570         # Look for OnionStudios embeds
1571         onionstudios_url = OnionStudiosIE._extract_url(webpage)
1572         if onionstudios_url:
1573             return self.url_result(onionstudios_url)
1574
1575         # Look for SnagFilms embeds
1576         snagfilms_url = SnagFilmsEmbedIE._extract_url(webpage)
1577         if snagfilms_url:
1578             return self.url_result(snagfilms_url)
1579
1580         # Look for AdobeTVVideo embeds
1581         mobj = re.search(
1582             r'<iframe[^>]+src=[\'"]((?:https?:)?//video\.tv\.adobe\.com/v/\d+[^"]+)[\'"]',
1583             webpage)
1584         if mobj is not None:
1585             return self.url_result(
1586                 self._proto_relative_url(unescapeHTML(mobj.group(1))),
1587                 'AdobeTVVideo')
1588
1589         def check_video(vurl):
1590             if YoutubeIE.suitable(vurl):
1591                 return True
1592             vpath = compat_urlparse.urlparse(vurl).path
1593             vext = determine_ext(vpath)
1594             return '.' in vpath and vext not in ('swf', 'png', 'jpg', 'srt', 'sbv', 'sub', 'vtt', 'ttml')
1595
1596         def filter_video(urls):
1597             return list(filter(check_video, urls))
1598
1599         # Start with something easy: JW Player in SWFObject
1600         found = filter_video(re.findall(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage))
1601         if not found:
1602             # Look for gorilla-vid style embedding
1603             found = filter_video(re.findall(r'''(?sx)
1604                 (?:
1605                     jw_plugins|
1606                     JWPlayerOptions|
1607                     jwplayer\s*\(\s*["'][^'"]+["']\s*\)\s*\.setup
1608                 )
1609                 .*?
1610                 ['"]?file['"]?\s*:\s*["\'](.*?)["\']''', webpage))
1611         if not found:
1612             # Broaden the search a little bit
1613             found = filter_video(re.findall(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage))
1614         if not found:
1615             # Broaden the findall a little bit: JWPlayer JS loader
1616             found = filter_video(re.findall(
1617                 r'[^A-Za-z0-9]?file["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage))
1618         if not found:
1619             # Flow player
1620             found = filter_video(re.findall(r'''(?xs)
1621                 flowplayer\("[^"]+",\s*
1622                     \{[^}]+?\}\s*,
1623                     \s*\{[^}]+? ["']?clip["']?\s*:\s*\{\s*
1624                         ["']?url["']?\s*:\s*["']([^"']+)["']
1625             ''', webpage))
1626         if not found:
1627             # Cinerama player
1628             found = re.findall(
1629                 r"cinerama\.embedPlayer\(\s*\'[^']+\',\s*'([^']+)'", webpage)
1630         if not found:
1631             # Try to find twitter cards info
1632             found = filter_video(re.findall(
1633                 r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage))
1634         if not found:
1635             # We look for Open Graph info:
1636             # We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am)
1637             m_video_type = re.findall(r'<meta.*?property="og:video:type".*?content="video/(.*?)"', webpage)
1638             # We only look in og:video if the MIME type is a video, don't try if it's a Flash player:
1639             if m_video_type is not None:
1640                 found = filter_video(re.findall(r'<meta.*?property="og:video".*?content="(.*?)"', webpage))
1641         if not found:
1642             # HTML5 video
1643             found = re.findall(r'(?s)<video[^<]*(?:>.*?<source[^>]*)?\s+src=["\'](.*?)["\']', webpage)
1644         if not found:
1645             REDIRECT_REGEX = r'[0-9]{,2};\s*(?:URL|url)=\'?([^\'"]+)'
1646             found = re.search(
1647                 r'(?i)<meta\s+(?=(?:[a-z-]+="[^"]+"\s+)*http-equiv="refresh")'
1648                 r'(?:[a-z-]+="[^"]+"\s+)*?content="%s' % REDIRECT_REGEX,
1649                 webpage)
1650             if not found:
1651                 # Look also in Refresh HTTP header
1652                 refresh_header = head_response.headers.get('Refresh')
1653                 if refresh_header:
1654                     found = re.search(REDIRECT_REGEX, refresh_header)
1655             if found:
1656                 new_url = compat_urlparse.urljoin(url, found.group(1))
1657                 self.report_following_redirect(new_url)
1658                 return {
1659                     '_type': 'url',
1660                     'url': new_url,
1661                 }
1662         if not found:
1663             raise UnsupportedError(url)
1664
1665         entries = []
1666         for video_url in found:
1667             video_url = compat_urlparse.urljoin(url, video_url)
1668             video_id = compat_urllib_parse.unquote(os.path.basename(video_url))
1669
1670             # Sometimes, jwplayer extraction will result in a YouTube URL
1671             if YoutubeIE.suitable(video_url):
1672                 entries.append(self.url_result(video_url, 'Youtube'))
1673                 continue
1674
1675             # here's a fun little line of code for you:
1676             video_id = os.path.splitext(video_id)[0]
1677
1678             if determine_ext(video_url) == 'smil':
1679                 entries.append({
1680                     'id': video_id,
1681                     'formats': self._extract_smil_formats(video_url, video_id),
1682                     'uploader': video_uploader,
1683                     'title': video_title,
1684                     'age_limit': age_limit,
1685                 })
1686             else:
1687                 entries.append({
1688                     'id': video_id,
1689                     'url': video_url,
1690                     'uploader': video_uploader,
1691                     'title': video_title,
1692                     'age_limit': age_limit,
1693                 })
1694
1695         if len(entries) == 1:
1696             return entries[0]
1697         else:
1698             for num, e in enumerate(entries, start=1):
1699                 # 'url' results don't have a title
1700                 if e.get('title') is not None:
1701                     e['title'] = '%s (%d)' % (e['title'], num)
1702             return {
1703                 '_type': 'playlist',
1704                 'entries': entries,
1705             }