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