diff options
| -rw-r--r-- | CHANGELOG | 14 | ||||
| -rw-r--r-- | test/test_YoutubeDL.py | 4 | ||||
| -rw-r--r-- | youtube_dl/__init__.py | 1 | ||||
| -rw-r--r-- | youtube_dl/extractor/__init__.py | 1 | ||||
| -rw-r--r-- | youtube_dl/extractor/aftonbladet.py | 15 | ||||
| -rw-r--r-- | youtube_dl/extractor/bandcamp.py | 38 | ||||
| -rw-r--r-- | youtube_dl/extractor/blinkx.py | 30 | ||||
| -rw-r--r-- | youtube_dl/extractor/empflix.py | 22 | ||||
| -rw-r--r-- | youtube_dl/extractor/gamekings.py | 2 | ||||
| -rw-r--r-- | youtube_dl/extractor/generic.py | 9 | ||||
| -rw-r--r-- | youtube_dl/extractor/mailru.py | 5 | ||||
| -rw-r--r-- | youtube_dl/extractor/nowness.py | 9 | ||||
| -rw-r--r-- | youtube_dl/extractor/pornhub.py | 2 | ||||
| -rw-r--r-- | youtube_dl/extractor/streamcz.py | 22 | ||||
| -rw-r--r-- | youtube_dl/extractor/swrmediathek.py | 104 | 
15 files changed, 189 insertions, 89 deletions
| diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 3fa116733..000000000 --- a/CHANGELOG +++ /dev/null @@ -1,14 +0,0 @@ -2013.01.02  Codename: GIULIA - -    * Add support for ComedyCentral clips <nto> -    * Corrected Vimeo description fetching <Nick Daniels> -    * Added the --no-post-overwrites argument <Barbu Paul - Gheorghe> -    * --verbose offers more environment info -    * New info_dict field: uploader_id -    * New updates system, with signature checking -    * New IEs: NBA, JustinTV, FunnyOrDie, TweetReel, Steam, Ustream -    * Fixed IEs: BlipTv -    * Fixed for Python 3 IEs: Xvideo, Youku, XNXX, Dailymotion, Vimeo, InfoQ -    * Simplified IEs and test code -    * Various (Python 3 and other) fixes -    * Revamped and expanded tests diff --git a/test/test_YoutubeDL.py b/test/test_YoutubeDL.py index 8735013f7..e794cc97f 100644 --- a/test/test_YoutubeDL.py +++ b/test/test_YoutubeDL.py @@ -67,7 +67,7 @@ class TestFormatSelection(unittest.TestCase):          downloaded = ydl.downloaded_info_dicts[0]          self.assertEqual(downloaded['ext'], 'mp4') -        # No prefer_free_formats => prefer mp4 and flv for greater compatibilty +        # No prefer_free_formats => prefer mp4 and flv for greater compatibility          ydl = YDL()          ydl.params['prefer_free_formats'] = False          formats = [ @@ -279,7 +279,7 @@ class TestFormatSelection(unittest.TestCase):          self.assertEqual(ydl._format_note({}), '')          assertRegexpMatches(self, ydl._format_note({              'vbr': 10, -        }), '^x\s*10k$') +        }), '^\s*10k$')  if __name__ == '__main__':      unittest.main() diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 4e657e297..cbb053e13 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -56,6 +56,7 @@ __authors__  = (      'Nicolas Évrard',      'Jason Normore',      'Hoje Lee', +    'Adam Thalhammer',  )  __license__ = 'Public Domain' diff --git a/youtube_dl/extractor/__init__.py b/youtube_dl/extractor/__init__.py index 3503c76b7..4a4eb4b74 100644 --- a/youtube_dl/extractor/__init__.py +++ b/youtube_dl/extractor/__init__.py @@ -260,6 +260,7 @@ from .stanfordoc import StanfordOpenClassroomIE  from .steam import SteamIE  from .streamcloud import StreamcloudIE  from .streamcz import StreamCZIE +from .swrmediathek import SWRMediathekIE  from .syfy import SyfyIE  from .sztvhu import SztvHuIE  from .teamcoco import TeamcocoIE diff --git a/youtube_dl/extractor/aftonbladet.py b/youtube_dl/extractor/aftonbladet.py index 6a8cd14c9..cfc7370ae 100644 --- a/youtube_dl/extractor/aftonbladet.py +++ b/youtube_dl/extractor/aftonbladet.py @@ -1,7 +1,6 @@  # encoding: utf-8  from __future__ import unicode_literals -import datetime  import re  from .common import InfoExtractor @@ -16,6 +15,7 @@ class AftonbladetIE(InfoExtractor):              'ext': 'mp4',              'title': 'Vulkanutbrott i rymden - nu släpper NASA bilderna',              'description': 'Jupiters måne mest aktiv av alla himlakroppar', +            'timestamp': 1394142732,              'upload_date': '20140306',          },      } @@ -27,17 +27,17 @@ class AftonbladetIE(InfoExtractor):          webpage = self._download_webpage(url, video_id)          # find internal video meta data -        META_URL = 'http://aftonbladet-play.drlib.aptoma.no/video/%s.json' +        meta_url = 'http://aftonbladet-play.drlib.aptoma.no/video/%s.json'          internal_meta_id = self._html_search_regex(              r'data-aptomaId="([\w\d]+)"', webpage, 'internal_meta_id') -        internal_meta_url = META_URL % internal_meta_id +        internal_meta_url = meta_url % internal_meta_id          internal_meta_json = self._download_json(              internal_meta_url, video_id, 'Downloading video meta data')          # find internal video formats -        FORMATS_URL = 'http://aftonbladet-play.videodata.drvideo.aptoma.no/actions/video/?id=%s' +        format_url = 'http://aftonbladet-play.videodata.drvideo.aptoma.no/actions/video/?id=%s'          internal_video_id = internal_meta_json['videoId'] -        internal_formats_url = FORMATS_URL % internal_video_id +        internal_formats_url = format_url % internal_video_id          internal_formats_json = self._download_json(              internal_formats_url, video_id, 'Downloading video formats') @@ -54,16 +54,13 @@ class AftonbladetIE(InfoExtractor):              })          self._sort_formats(formats) -        timestamp = datetime.datetime.fromtimestamp(internal_meta_json['timePublished']) -        upload_date = timestamp.strftime('%Y%m%d') -          return {              'id': video_id,              'title': internal_meta_json['title'],              'formats': formats,              'thumbnail': internal_meta_json['imageUrl'],              'description': internal_meta_json['shortPreamble'], -            'upload_date': upload_date, +            'timestamp': internal_meta_json['timePublished'],              'duration': internal_meta_json['duration'],              'view_count': internal_meta_json['views'],          } diff --git a/youtube_dl/extractor/bandcamp.py b/youtube_dl/extractor/bandcamp.py index 929aafdff..dcbbdef43 100644 --- a/youtube_dl/extractor/bandcamp.py +++ b/youtube_dl/extractor/bandcamp.py @@ -19,7 +19,7 @@ class BandcampIE(InfoExtractor):          'md5': 'c557841d5e50261777a6585648adf439',          'info_dict': {              "title": "youtube-dl  \"'/\\\u00e4\u21ad - youtube-dl test song \"'/\\\u00e4\u21ad", -            "duration": 10, +            "duration": 9.8485,          },          '_skip': 'There is a limit of 200 free downloads / month for the test song'      }] @@ -28,36 +28,32 @@ class BandcampIE(InfoExtractor):          mobj = re.match(self._VALID_URL, url)          title = mobj.group('title')          webpage = self._download_webpage(url, title) -        # We get the link to the free download page          m_download = re.search(r'freeDownloadPage: "(.*?)"', webpage) -        if m_download is None: +        if not m_download:              m_trackinfo = re.search(r'trackinfo: (.+),\s*?\n', webpage)              if m_trackinfo:                  json_code = m_trackinfo.group(1) -                data = json.loads(json_code) -                d = data[0] +                data = json.loads(json_code)[0] -                duration = int(round(d['duration']))                  formats = [] -                for format_id, format_url in d['file'].items(): -                    ext, _, abr_str = format_id.partition('-') - +                for format_id, format_url in data['file'].items(): +                    ext, abr_str = format_id.split('-', 1)                      formats.append({                          'format_id': format_id,                          'url': format_url, -                        'ext': format_id.partition('-')[0], +                        'ext': ext,                          'vcodec': 'none', -                        'acodec': format_id.partition('-')[0], -                        'abr': int(format_id.partition('-')[2]), +                        'acodec': ext, +                        'abr': int(abr_str),                      })                  self._sort_formats(formats)                  return { -                    'id': compat_str(d['id']), -                    'title': d['title'], +                    'id': compat_str(data['id']), +                    'title': data['title'],                      'formats': formats, -                    'duration': duration, +                    'duration': float(data['duration']),                  }              else:                  raise ExtractorError('No free songs found') @@ -67,11 +63,9 @@ class BandcampIE(InfoExtractor):              r'var TralbumData = {(.*?)id: (?P<id>\d*?)$',              webpage, re.MULTILINE | re.DOTALL).group('id') -        download_webpage = self._download_webpage(download_link, video_id, -                                                  'Downloading free downloads page') -        # We get the dictionary of the track from some javascrip code -        info = re.search(r'items: (.*?),$', -                         download_webpage, re.MULTILINE).group(1) +        download_webpage = self._download_webpage(download_link, video_id, 'Downloading free downloads page') +        # We get the dictionary of the track from some javascript code +        info = re.search(r'items: (.*?),$', download_webpage, re.MULTILINE).group(1)          info = json.loads(info)[0]          # We pick mp3-320 for now, until format selection can be easily implemented.          mp3_info = info['downloads']['mp3-320'] @@ -100,7 +94,7 @@ class BandcampIE(InfoExtractor):  class BandcampAlbumIE(InfoExtractor):      IE_NAME = 'Bandcamp:album' -    _VALID_URL = r'https?://(?:(?P<subdomain>[^.]+)\.)?bandcamp\.com(?:/album/(?P<title>[^?#]+))?' +    _VALID_URL = r'https?://(?:(?P<subdomain>[^.]+)\.)?bandcamp\.com(?:/album/(?P<title>[^?#]+))'      _TEST = {          'url': 'http://blazo.bandcamp.com/album/jazz-format-mixtape-vol-1', @@ -123,7 +117,7 @@ class BandcampAlbumIE(InfoExtractor):          'params': {              'playlistend': 2          }, -        'skip': 'Bancamp imposes download limits. See test_playlists:test_bandcamp_album for the playlist test' +        'skip': 'Bandcamp imposes download limits. See test_playlists:test_bandcamp_album for the playlist test'      }      def _real_extract(self, url): diff --git a/youtube_dl/extractor/blinkx.py b/youtube_dl/extractor/blinkx.py index 96408e4e0..38ccd957f 100644 --- a/youtube_dl/extractor/blinkx.py +++ b/youtube_dl/extractor/blinkx.py @@ -1,6 +1,5 @@  from __future__ import unicode_literals -import datetime  import json  import re @@ -19,15 +18,16 @@ class BlinkxIE(InfoExtractor):          'file': '8aQUy7GV.mp4',          'md5': '2e9a07364af40163a908edbf10bb2492',          'info_dict': { -            "title": "Police Car Rolls Away", -            "uploader": "stupidvideos.com", -            "upload_date": "20131215", -            "description": "A police car gently rolls away from a fight. Maybe it felt weird being around a confrontation and just had to get out of there!", -            "duration": 14.886, -            "thumbnails": [{ -                "width": 100, -                "height": 76, -                "url": "http://cdn.blinkx.com/stream/b/41/StupidVideos/20131215/1873969261/1873969261_tn_0.jpg", +            'title': 'Police Car Rolls Away', +            'uploader': 'stupidvideos.com', +            'upload_date': '20131215', +            'timestamp': 1387068000, +            'description': 'A police car gently rolls away from a fight. Maybe it felt weird being around a confrontation and just had to get out of there!', +            'duration': 14.886, +            'thumbnails': [{ +                'width': 100, +                'height': 76, +                'url': 'http://cdn.blinkx.com/stream/b/41/StupidVideos/20131215/1873969261/1873969261_tn_0.jpg',              }],          },      } @@ -41,9 +41,6 @@ class BlinkxIE(InfoExtractor):                     'video=%s' % video_id)          data_json = self._download_webpage(api_url, display_id)          data = json.loads(data_json)['api']['results'][0] -        dt = datetime.datetime.fromtimestamp(data['pubdate_epoch']) -        pload_date = dt.strftime('%Y%m%d') -          duration = None          thumbnails = []          formats = [] @@ -64,10 +61,7 @@ class BlinkxIE(InfoExtractor):                  vcodec = remove_start(m['vcodec'], 'ff')                  acodec = remove_start(m['acodec'], 'ff')                  tbr = (int(m['vbr']) + int(m['abr'])) // 1000 -                format_id = (u'%s-%sk-%s' % -                             (vcodec, -                              tbr, -                              m['w'])) +                format_id = u'%s-%sk-%s' % (vcodec, tbr, m['w'])                  formats.append({                      'format_id': format_id,                      'url': m['link'], @@ -88,7 +82,7 @@ class BlinkxIE(InfoExtractor):              'title': data['title'],              'formats': formats,              'uploader': data['channel_name'], -            'upload_date': pload_date, +            'timestamp': data['pubdate_epoch'],              'description': data.get('description'),              'thumbnails': thumbnails,              'duration': duration, diff --git a/youtube_dl/extractor/empflix.py b/youtube_dl/extractor/empflix.py index eaeee5a51..e6952588f 100644 --- a/youtube_dl/extractor/empflix.py +++ b/youtube_dl/extractor/empflix.py @@ -3,20 +3,18 @@ from __future__ import unicode_literals  import re  from .common import InfoExtractor -from ..utils import ( -    ExtractorError, -)  class EmpflixIE(InfoExtractor):      _VALID_URL = r'^https?://www\.empflix\.com/videos/.*?-(?P<id>[0-9]+)\.html'      _TEST = {          'url': 'http://www.empflix.com/videos/Amateur-Finger-Fuck-33051.html', -        'md5': '5e5cc160f38ca9857f318eb97146e13e', +        'md5': 'b1bc15b6412d33902d6e5952035fcabc',          'info_dict': {              'id': '33051', -            'ext': 'flv', +            'ext': 'mp4',              'title': 'Amateur Finger Fuck', +            'description': 'Amateur solo finger fucking.',              'age_limit': 18,          }      } @@ -30,6 +28,8 @@ class EmpflixIE(InfoExtractor):          video_title = self._html_search_regex(              r'name="title" value="(?P<title>[^"]*)"', webpage, 'title') +        video_description = self._html_search_regex( +            r'name="description" value="([^"]*)"', webpage, 'description', fatal=False)          cfg_url = self._html_search_regex(              r'flashvars\.config = escape\("([^"]+)"', @@ -37,12 +37,18 @@ class EmpflixIE(InfoExtractor):          cfg_xml = self._download_xml(              cfg_url, video_id, note='Downloading metadata') -        video_url = cfg_xml.find('videoLink').text + +        formats = [ +            { +                'url': item.find('videoLink').text, +                'format_id': item.find('res').text, +            } for item in cfg_xml.findall('./quality/item') +        ]          return {              'id': video_id, -            'url': video_url, -            'ext': 'flv',              'title': video_title, +            'description': video_description, +            'formats': formats,              'age_limit': age_limit,          } diff --git a/youtube_dl/extractor/gamekings.py b/youtube_dl/extractor/gamekings.py index 233398966..11fee3d31 100644 --- a/youtube_dl/extractor/gamekings.py +++ b/youtube_dl/extractor/gamekings.py @@ -15,7 +15,7 @@ class GamekingsIE(InfoExtractor):              'id': '20130811',              'ext': 'mp4',              'title': 'Phoenix Wright: Ace Attorney \u2013 Dual Destinies Review', -            'description': 'md5:632e61a9f97d700e83f43d77ddafb6a4', +            'description': 'md5:36fd701e57e8c15ac8682a2374c99731',          }      } diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py index 0e5cf0efb..38a357d3b 100644 --- a/youtube_dl/extractor/generic.py +++ b/youtube_dl/extractor/generic.py @@ -363,8 +363,13 @@ class GenericIE(InfoExtractor):                      return self.url_result('http://' + url)                  else:                      if default_search == 'auto_warning': -                        self._downloader.report_warning( -                            'Falling back to youtube search for  %s . Set --default-search to "auto" to suppress this warning.' % url) +                        if re.match(r'^(?:url|URL)$', url): +                            raise ExtractorError( +                                'Invalid URL:  %r . Call youtube-dl like this:  youtube-dl -v "https://www.youtube.com/watch?v=BaW_jenozKc"  ' % url, +                                expected=True) +                        else: +                            self._downloader.report_warning( +                                'Falling back to youtube search for  %s . Set --default-search to "auto" to suppress this warning.' % url)                      return self.url_result('ytsearch:' + url)              else:                  assert ':' in default_search diff --git a/youtube_dl/extractor/mailru.py b/youtube_dl/extractor/mailru.py index f819c09b3..5016989cc 100644 --- a/youtube_dl/extractor/mailru.py +++ b/youtube_dl/extractor/mailru.py @@ -2,7 +2,6 @@  from __future__ import unicode_literals  import re -import datetime  from .common import InfoExtractor @@ -19,6 +18,7 @@ class MailRuIE(InfoExtractor):              'id': '46301138',              'ext': 'mp4',              'title': 'Новый Человек-Паук. Высокое напряжение. Восстание Электро', +            'timestamp': 1393232740,              'upload_date': '20140224',              'uploader': 'sonypicturesrus',              'uploader_id': 'sonypicturesrus@mail.ru', @@ -43,7 +43,6 @@ class MailRuIE(InfoExtractor):          thumbnail = movie['poster']          duration = movie['duration'] -        upload_date = datetime.datetime.fromtimestamp(video_data['timestamp']).strftime('%Y%m%d')          view_count = video_data['views_count']          formats = [ @@ -57,7 +56,7 @@ class MailRuIE(InfoExtractor):              'id': content_id,              'title': title,              'thumbnail': thumbnail, -            'upload_date': upload_date, +            'timestamp': video_data['timestamp'],              'uploader': uploader,              'uploader_id': uploader_id,              'duration': duration, diff --git a/youtube_dl/extractor/nowness.py b/youtube_dl/extractor/nowness.py index b1bcb7e54..1c5e9401f 100644 --- a/youtube_dl/extractor/nowness.py +++ b/youtube_dl/extractor/nowness.py @@ -4,9 +4,7 @@ import re  from .brightcove import BrightcoveIE  from .common import InfoExtractor -from ..utils import ( -    ExtractorError, -) +from ..utils import ExtractorError  class NownessIE(InfoExtractor): @@ -14,9 +12,10 @@ class NownessIE(InfoExtractor):      _TEST = {          'url': 'http://www.nowness.com/day/2013/6/27/3131/candor--the-art-of-gesticulation', -        'file': '2520295746001.mp4', -        'md5': '0ece2f70a7bd252c7b00f3070182d418', +        'md5': '068bc0202558c2e391924cb8cc470676',          'info_dict': { +            'id': '2520295746001', +            'ext': 'mp4',              'description': 'Candor: The Art of Gesticulation',              'uploader': 'Nowness',              'title': 'Candor: The Art of Gesticulation', diff --git a/youtube_dl/extractor/pornhub.py b/youtube_dl/extractor/pornhub.py index 7dd3dca0d..4118ee956 100644 --- a/youtube_dl/extractor/pornhub.py +++ b/youtube_dl/extractor/pornhub.py @@ -45,7 +45,7 @@ class PornHubIE(InfoExtractor):          video_title = self._html_search_regex(r'<h1 [^>]+>([^<]+)', webpage, 'title')          video_uploader = self._html_search_regex( -            r'(?s)<div class="video-info-row">\s*From: .+?<(?:a href="/users/|<span class="username)[^>]+>(.+?)<', +            r'(?s)From: .+?<(?:a href="/users/|<span class="username)[^>]+>(.+?)<',              webpage, 'uploader', fatal=False)          thumbnail = self._html_search_regex(r'"image_url":"([^"]+)', webpage, 'thumbnail', fatal=False)          if thumbnail: diff --git a/youtube_dl/extractor/streamcz.py b/youtube_dl/extractor/streamcz.py index 7362904db..73efe9542 100644 --- a/youtube_dl/extractor/streamcz.py +++ b/youtube_dl/extractor/streamcz.py @@ -5,13 +5,16 @@ import re  import json  from .common import InfoExtractor -from ..utils import int_or_none +from ..utils import ( +    int_or_none, +    compat_str, +)  class StreamCZIE(InfoExtractor):      _VALID_URL = r'https?://(?:www\.)?stream\.cz/.+/(?P<videoid>.+)' -    _TEST = { +    _TESTS = [{          'url': 'http://www.stream.cz/peklonataliri/765767-ecka-pro-deti',          'md5': '6d3ca61a8d0633c9c542b92fcb936b0c',          'info_dict': { @@ -22,7 +25,18 @@ class StreamCZIE(InfoExtractor):              'thumbnail': 'http://im.stream.cz/episode/52961d7e19d423f8f06f0100',              'duration': 256,          }, -    } +    }, { +        'url': 'http://www.stream.cz/blanik/10002447-tri-roky-pro-mazanka', +        'md5': '246272e753e26bbace7fcd9deca0650c', +        'info_dict': { +            'id': '10002447', +            'ext': 'mp4', +            'title': 'Kancelář Blaník: Tři roky pro Mazánka', +            'description': 'md5:9177695a8b756a0a8ab160de4043b392', +            'thumbnail': 'http://im.stream.cz/episode/537f838c50c11f8d21320000', +            'duration': 368, +        }, +    }]      def _real_extract(self, url):          mobj = re.match(self._VALID_URL, url) @@ -57,7 +71,7 @@ class StreamCZIE(InfoExtractor):          self._sort_formats(formats)          return { -            'id': str(jsonData['id']), +            'id': compat_str(jsonData['episode_id']),              'title': self._og_search_title(webpage),              'thumbnail': jsonData['episode_image_original_url'].replace('//', 'http://'),              'formats': formats, diff --git a/youtube_dl/extractor/swrmediathek.py b/youtube_dl/extractor/swrmediathek.py new file mode 100644 index 000000000..6c688c520 --- /dev/null +++ b/youtube_dl/extractor/swrmediathek.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import re + +from .common import InfoExtractor +from ..utils import parse_duration + + +class SWRMediathekIE(InfoExtractor): +    _VALID_URL = r'https?://(?:www\.)?swrmediathek\.de/player\.htm\?show=(?P<id>[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})' + +    _TESTS = [{ +        'url': 'http://swrmediathek.de/player.htm?show=849790d0-dab8-11e3-a953-0026b975f2e6', +        'md5': '8c5f6f0172753368547ca8413a7768ac', +        'info_dict': { +            'id': '849790d0-dab8-11e3-a953-0026b975f2e6', +            'ext': 'mp4', +            'title': 'SWR odysso', +            'description': 'md5:2012e31baad36162e97ce9eb3f157b8a', +            'thumbnail': 're:^http:.*\.jpg$', +            'duration': 2602, +            'upload_date': '20140515', +            'uploader': 'SWR Fernsehen', +            'uploader_id': '990030', +        }, +    }, { +        'url': 'http://swrmediathek.de/player.htm?show=0e1a8510-ddf2-11e3-9be3-0026b975f2e6', +        'md5': 'b10ab854f912eecc5a6b55cd6fc1f545', +        'info_dict': { +            'id': '0e1a8510-ddf2-11e3-9be3-0026b975f2e6', +            'ext': 'mp4', +            'title': 'Nachtcafé - Alltagsdroge Alkohol - zwischen Sektempfang und Komasaufen', +            'description': 'md5:e0a3adc17e47db2c23aab9ebc36dbee2', +            'thumbnail': 're:http://.*\.jpg', +            'duration': 5305, +            'upload_date': '20140516', +            'uploader': 'SWR Fernsehen', +            'uploader_id': '990030', +        }, +    }, { +        'url': 'http://swrmediathek.de/player.htm?show=bba23e10-cb93-11e3-bf7f-0026b975f2e6', +        'md5': '4382e4ef2c9d7ce6852535fa867a0dd3', +        'info_dict': { +            'id': 'bba23e10-cb93-11e3-bf7f-0026b975f2e6', +            'ext': 'mp3', +            'title': 'Saša Stanišic: Vor dem Fest', +            'description': 'md5:5b792387dc3fbb171eb709060654e8c9', +            'thumbnail': 're:http://.*\.jpg', +            'duration': 3366, +            'upload_date': '20140520', +            'uploader': 'SWR 2', +            'uploader_id': '284670', +        } +    }] + +    def _real_extract(self, url): +        mobj = re.match(self._VALID_URL, url) +        video_id = mobj.group('id') + +        video = self._download_json( +            'http://swrmediathek.de/AjaxEntry?ekey=%s' % video_id, video_id, 'Downloading video JSON') + +        attr = video['attr'] +        media_type = attr['entry_etype'] + +        formats = [] +        for entry in video['sub']: +            if entry['name'] != 'entry_media': +                continue + +            entry_attr = entry['attr'] +            codec = entry_attr['val0'] +            quality = int(entry_attr['val1']) + +            fmt = { +                'url': entry_attr['val2'], +                'quality': quality, +            } + +            if media_type == 'Video': +                fmt.update({ +                    'format_note': ['144p', '288p', '544p'][quality-1], +                    'vcodec': codec, +                }) +            elif media_type == 'Audio': +                fmt.update({ +                    'acodec': codec, +                }) +            formats.append(fmt) + +        self._sort_formats(formats) + +        return { +            'id': video_id, +            'title': attr['entry_title'], +            'description': attr['entry_descl'], +            'thumbnail': attr['entry_image_16_9'], +            'duration': parse_duration(attr['entry_durat']), +            'upload_date': attr['entry_pdatet'][:-4], +            'uploader': attr['channel_title'], +            'uploader_id': attr['channel_idkey'], +            'formats': formats, +        }
\ No newline at end of file | 
