diff options
Diffstat (limited to 'youtube_dl/extractor/tvplay.py')
| -rw-r--r-- | youtube_dl/extractor/tvplay.py | 205 | 
1 files changed, 134 insertions, 71 deletions
diff --git a/youtube_dl/extractor/tvplay.py b/youtube_dl/extractor/tvplay.py index 99ff82a5d..0d858c025 100644 --- a/youtube_dl/extractor/tvplay.py +++ b/youtube_dl/extractor/tvplay.py @@ -6,17 +6,19 @@ import re  from .common import InfoExtractor  from ..compat import (      compat_HTTPError, -    compat_str,      compat_urlparse,  )  from ..utils import (      determine_ext,      ExtractorError,      int_or_none, +    parse_duration,      parse_iso8601,      qualities,      try_get,      update_url_query, +    url_or_none, +    urljoin,  ) @@ -29,12 +31,12 @@ class TVPlayIE(InfoExtractor):                          https?://                              (?:www\.)?                              (?: -                                tvplay(?:\.skaties)?\.lv/parraides| -                                (?:tv3play|play\.tv3)\.lt/programos| +                                tvplay(?:\.skaties)?\.lv(?:/parraides)?| +                                (?:tv3play|play\.tv3)\.lt(?:/programos)?|                                  tv3play(?:\.tv3)?\.ee/sisu|                                  (?:tv(?:3|6|8|10)play|viafree)\.se/program|                                  (?:(?:tv3play|viasat4play|tv6play|viafree)\.no|(?:tv3play|viafree)\.dk)/programmer| -                                play\.novatv\.bg/programi +                                play\.nova(?:tv)?\.bg/programi                              )                              /(?:[^/]+/)+                          ) @@ -201,10 +203,18 @@ class TVPlayIE(InfoExtractor):              },          },          { +            'url': 'https://play.nova.bg/programi/zdravei-bulgariya/764300?autostart=true', +            'only_matching': True, +        }, +        {              'url': 'http://tvplay.skaties.lv/parraides/vinas-melo-labak/418113?autostart=true',              'only_matching': True,          },          { +            'url': 'https://tvplay.skaties.lv/vinas-melo-labak/418113/?autostart=true', +            'only_matching': True, +        }, +        {              # views is null              'url': 'http://tvplay.skaties.lv/parraides/tv3-zinas/760183',              'only_matching': True, @@ -229,7 +239,7 @@ class TVPlayIE(InfoExtractor):              r'https?://[^/]+\.([a-z]{2})', url,              'geo country', default=None)          if geo_country: -            self._initialize_geo_bypass([geo_country.upper()]) +            self._initialize_geo_bypass({'countries': [geo_country.upper()]})          video = self._download_json(              'http://playapi.mtgx.tv/v3/videos/%s' % video_id, video_id, 'Downloading video JSON') @@ -248,7 +258,8 @@ class TVPlayIE(InfoExtractor):          quality = qualities(['hls', 'medium', 'high'])          formats = []          for format_id, video_url in streams.get('streams', {}).items(): -            if not video_url or not isinstance(video_url, compat_str): +            video_url = url_or_none(video_url) +            if not video_url:                  continue              ext = determine_ext(video_url)              if ext == 'f4m': @@ -277,6 +288,7 @@ class TVPlayIE(InfoExtractor):                          'url': m.group('url'),                          'app': m.group('app'),                          'play_path': m.group('playpath'), +                        'preference': -1,                      })                  else:                      fmt.update({ @@ -327,103 +339,154 @@ class ViafreeIE(InfoExtractor):      _VALID_URL = r'''(?x)                      https?://                          (?:www\.)? -                        viafree\. -                        (?: -                            (?:dk|no)/programmer| -                            se/program -                        ) -                        /(?:[^/]+/)+(?P<id>[^/?#&]+) +                        viafree\.(?P<country>dk|no|se) +                        /(?P<id>program(?:mer)?/(?:[^/]+/)+[^/?#&]+)                      '''      _TESTS = [{ -        'url': 'http://www.viafree.se/program/livsstil/husraddarna/sasong-2/avsnitt-2', +        'url': 'http://www.viafree.no/programmer/underholdning/det-beste-vorspielet/sesong-2/episode-1',          'info_dict': { -            'id': '395375', +            'id': '757786',              'ext': 'mp4', -            'title': 'Husräddarna S02E02', -            'description': 'md5:4db5c933e37db629b5a2f75dfb34829e', -            'series': 'Husräddarna', -            'season': 'Säsong 2', +            'title': 'Det beste vorspielet - Sesong 2 - Episode 1', +            'description': 'md5:b632cb848331404ccacd8cd03e83b4c3', +            'series': 'Det beste vorspielet',              'season_number': 2, -            'duration': 2576, -            'timestamp': 1400596321, -            'upload_date': '20140520', +            'duration': 1116, +            'timestamp': 1471200600, +            'upload_date': '20160814',          },          'params': {              'skip_download': True,          }, -        'add_ie': [TVPlayIE.ie_key()],      }, {          # with relatedClips          'url': 'http://www.viafree.se/program/reality/sommaren-med-youtube-stjarnorna/sasong-1/avsnitt-1', -        'info_dict': { -            'id': '758770', -            'ext': 'mp4', -            'title': 'Sommaren med YouTube-stjärnorna S01E01', -            'description': 'md5:2bc69dce2c4bb48391e858539bbb0e3f', -            'series': 'Sommaren med YouTube-stjärnorna', -            'season': 'Säsong 1', -            'season_number': 1, -            'duration': 1326, -            'timestamp': 1470905572, -            'upload_date': '20160811', -        }, -        'params': { -            'skip_download': True, -        }, -        'add_ie': [TVPlayIE.ie_key()], +        'only_matching': True,      }, {          # Different og:image URL schema          'url': 'http://www.viafree.se/program/reality/sommaren-med-youtube-stjarnorna/sasong-1/avsnitt-2',          'only_matching': True,      }, { -        'url': 'http://www.viafree.no/programmer/underholdning/det-beste-vorspielet/sesong-2/episode-1', +        'url': 'http://www.viafree.se/program/livsstil/husraddarna/sasong-2/avsnitt-2',          'only_matching': True,      }, {          'url': 'http://www.viafree.dk/programmer/reality/paradise-hotel/saeson-7/episode-5',          'only_matching': True,      }] +    _GEO_BYPASS = False      @classmethod      def suitable(cls, url):          return False if TVPlayIE.suitable(url) else super(ViafreeIE, cls).suitable(url)      def _real_extract(self, url): -        video_id = self._match_id(url) +        country, path = re.match(self._VALID_URL, url).groups() +        content = self._download_json( +            'https://viafree-content.mtg-api.com/viafree-content/v1/%s/path/%s' % (country, path), path) +        program = content['_embedded']['viafreeBlocks'][0]['_embedded']['program'] +        guid = program['guid'] +        meta = content['meta'] +        title = meta['title'] + +        try: +            stream_href = self._download_json( +                program['_links']['streamLink']['href'], guid, +                headers=self.geo_verification_headers())['embedded']['prioritizedStreams'][0]['links']['stream']['href'] +        except ExtractorError as e: +            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: +                self.raise_geo_restricted(countries=[country]) +            raise + +        formats = self._extract_m3u8_formats(stream_href, guid, 'mp4') +        self._sort_formats(formats) +        episode = program.get('episode') or {} + +        return { +            'id': guid, +            'title': title, +            'thumbnail': meta.get('image'), +            'description': meta.get('description'), +            'series': episode.get('seriesTitle'), +            'episode_number': int_or_none(episode.get('episodeNumber')), +            'season_number': int_or_none(episode.get('seasonNumber')), +            'duration': int_or_none(try_get(program, lambda x: x['video']['duration']['milliseconds']), 1000), +            'timestamp': parse_iso8601(try_get(program, lambda x: x['availability']['start'])), +            'formats': formats, +        } -        webpage = self._download_webpage(url, video_id) -        data = self._parse_json( -            self._search_regex( -                r'(?s)window\.App\s*=\s*({.+?})\s*;\s*</script', -                webpage, 'data', default='{}'), -            video_id, transform_source=lambda x: re.sub( -                r'(?s)function\s+[a-zA-Z_][\da-zA-Z_]*\s*\([^)]*\)\s*{[^}]*}\s*', -                'null', x), fatal=False) +class TVPlayHomeIE(InfoExtractor): +    _VALID_URL = r'https?://(?:tv3?)?play\.(?:tv3\.lt|skaties\.lv|tv3\.ee)/(?:[^/]+/)*[^/?#&]+-(?P<id>\d+)' +    _TESTS = [{ +        'url': 'https://tvplay.tv3.lt/aferistai-n-7/aferistai-10047125/', +        'info_dict': { +            'id': '366367', +            'ext': 'mp4', +            'title': 'Aferistai', +            'description': 'Aferistai. Kalėdinė pasaka.', +            'series': 'Aferistai [N-7]', +            'season': '1 sezonas', +            'season_number': 1, +            'duration': 464, +            'timestamp': 1394209658, +            'upload_date': '20140307', +            'age_limit': 18, +        }, +        'params': { +            'skip_download': True, +        }, +    }, { +        'url': 'https://tvplay.skaties.lv/vinas-melo-labak/vinas-melo-labak-10280317/', +        'only_matching': True, +    }, { +        'url': 'https://tvplay.tv3.ee/cool-d-ga-mehhikosse/cool-d-ga-mehhikosse-10044354/', +        'only_matching': True, +    }, { +        'url': 'https://play.tv3.lt/aferistai-10047125', +        'only_matching': True, +    }, { +        'url': 'https://tv3play.skaties.lv/vinas-melo-labak-10280317', +        'only_matching': True, +    }, { +        'url': 'https://play.tv3.ee/cool-d-ga-mehhikosse-10044354', +        'only_matching': True, +    }] -        video_id = None +    def _real_extract(self, url): +        video_id = self._match_id(url) -        if data: -            video_id = try_get( -                data, lambda x: x['context']['dispatcher']['stores'][ -                    'ContentPageProgramStore']['currentVideo']['id'], -                compat_str) +        asset = self._download_json( +            urljoin(url, '/sb/public/asset/' + video_id), video_id) -        # Fallback #1 (extract from og:image URL schema) -        if not video_id: -            thumbnail = self._og_search_thumbnail(webpage, default=None) -            if thumbnail: -                video_id = self._search_regex( -                    # Patterns seen: -                    #  http://cdn.playapi.mtgx.tv/imagecache/600x315/cloud/content-images/inbox/765166/a2e95e5f1d735bab9f309fa345cc3f25.jpg -                    #  http://cdn.playapi.mtgx.tv/imagecache/600x315/cloud/content-images/seasons/15204/758770/4a5ba509ca8bc043e1ebd1a76131cdf2.jpg -                    r'https?://[^/]+/imagecache/(?:[^/]+/)+(\d{6,})/', -                    thumbnail, 'video id', default=None) +        m3u8_url = asset['movie']['contentUrl'] +        video_id = asset['assetId'] +        asset_title = asset['title'] +        title = asset_title['title'] -        # Fallback #2. Extract from raw JSON string. -        # May extract wrong video id if relatedClips is present. -        if not video_id: -            video_id = self._search_regex( -                r'currentVideo["\']\s*:\s*.+?["\']id["\']\s*:\s*["\'](\d{6,})', -                webpage, 'video id') +        formats = self._extract_m3u8_formats( +            m3u8_url, video_id, 'mp4', 'm3u8_native', m3u8_id='hls') +        self._sort_formats(formats) -        return self.url_result('mtg:%s' % video_id, TVPlayIE.ie_key()) +        thumbnails = None +        image_url = asset.get('imageUrl') +        if image_url: +            thumbnails = [{ +                'url': urljoin(url, image_url), +                'ext': 'jpg', +            }] + +        metadata = asset.get('metadata') or {} + +        return { +            'id': video_id, +            'title': title, +            'description': asset_title.get('summaryLong') or asset_title.get('summaryShort'), +            'thumbnails': thumbnails, +            'duration': parse_duration(asset_title.get('runTime')), +            'series': asset.get('tvSeriesTitle'), +            'season': asset.get('tvSeasonTitle'), +            'season_number': int_or_none(metadata.get('seasonNumber')), +            'episode': asset_title.get('titleBrief'), +            'episode_number': int_or_none(metadata.get('episodeNumber')), +            'formats': formats, +        }  | 
