[ninecninemedia] use geo bypass mechanism
[youtube-dl] / youtube_dl / extractor / ninecninemedia.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import compat_str
8 from ..utils import (
9     parse_iso8601,
10     float_or_none,
11     ExtractorError,
12     int_or_none,
13 )
14
15
16 class NineCNineMediaBaseIE(InfoExtractor):
17     _API_BASE_TEMPLATE = 'http://capi.9c9media.com/destinations/%s/platforms/desktop/contents/%s/'
18
19
20 class NineCNineMediaStackIE(NineCNineMediaBaseIE):
21     IE_NAME = '9c9media:stack'
22     _GEO_COUNTRIES = ['CA']
23     _VALID_URL = r'9c9media:stack:(?P<destination_code>[^:]+):(?P<content_id>\d+):(?P<content_package>\d+):(?P<id>\d+)'
24
25     def _real_extract(self, url):
26         destination_code, content_id, package_id, stack_id = re.match(self._VALID_URL, url).groups()
27         stack_base_url_template = self._API_BASE_TEMPLATE + 'contentpackages/%s/stacks/%s/manifest.'
28         stack_base_url = stack_base_url_template % (destination_code, content_id, package_id, stack_id)
29
30         formats = []
31         formats.extend(self._extract_m3u8_formats(
32             stack_base_url + 'm3u8', stack_id, 'mp4',
33             'm3u8_native', m3u8_id='hls', fatal=False))
34         formats.extend(self._extract_f4m_formats(
35             stack_base_url + 'f4m', stack_id,
36             f4m_id='hds', fatal=False))
37         mp4_url = self._download_webpage(stack_base_url + 'pd', stack_id, fatal=False)
38         if mp4_url:
39             formats.append({
40                 'url': mp4_url,
41                 'format_id': 'mp4',
42             })
43         self._sort_formats(formats)
44
45         return {
46             'id': stack_id,
47             'formats': formats,
48         }
49
50
51 class NineCNineMediaIE(NineCNineMediaBaseIE):
52     IE_NAME = '9c9media'
53     _VALID_URL = r'9c9media:(?P<destination_code>[^:]+):(?P<id>\d+)'
54
55     def _real_extract(self, url):
56         destination_code, content_id = re.match(self._VALID_URL, url).groups()
57         api_base_url = self._API_BASE_TEMPLATE % (destination_code, content_id)
58         content = self._download_json(api_base_url, content_id, query={
59             '$include': '[Media,Season,ContentPackages]',
60         })
61         title = content['Name']
62         if len(content['ContentPackages']) > 1:
63             raise ExtractorError('multiple content packages')
64         content_package = content['ContentPackages'][0]
65         package_id = content_package['Id']
66         content_package_url = api_base_url + 'contentpackages/%s/' % package_id
67         content_package = self._download_json(content_package_url, content_id)
68
69         if content_package.get('Constraints', {}).get('Security', {}).get('Type') == 'adobe-drm':
70             raise ExtractorError('This video is DRM protected.', expected=True)
71
72         stacks = self._download_json(content_package_url + 'stacks/', package_id)['Items']
73         multistacks = len(stacks) > 1
74
75         thumbnails = []
76         for image in content.get('Images', []):
77             image_url = image.get('Url')
78             if not image_url:
79                 continue
80             thumbnails.append({
81                 'url': image_url,
82                 'width': int_or_none(image.get('Width')),
83                 'height': int_or_none(image.get('Height')),
84             })
85
86         tags, categories = [], []
87         for source_name, container in (('Tags', tags), ('Genres', categories)):
88             for e in content.get(source_name, []):
89                 e_name = e.get('Name')
90                 if not e_name:
91                     continue
92                 container.append(e_name)
93
94         description = content.get('Desc') or content.get('ShortDesc')
95         season = content.get('Season', {})
96         base_info = {
97             'description': description,
98             'timestamp': parse_iso8601(content.get('BroadcastDateTime')),
99             'episode_number': int_or_none(content.get('Episode')),
100             'season': season.get('Name'),
101             'season_number': season.get('Number'),
102             'season_id': season.get('Id'),
103             'series': content.get('Media', {}).get('Name'),
104             'tags': tags,
105             'categories': categories,
106         }
107
108         entries = []
109         for stack in stacks:
110             stack_id = compat_str(stack['Id'])
111             entry = {
112                 '_type': 'url_transparent',
113                 'url': '9c9media:stack:%s:%s:%s:%s' % (destination_code, content_id, package_id, stack_id),
114                 'id': stack_id,
115                 'title': '%s_part%s' % (title, stack['Name']) if multistacks else title,
116                 'duration': float_or_none(stack.get('Duration')),
117                 'ie_key': 'NineCNineMediaStack',
118             }
119             entry.update(base_info)
120             entries.append(entry)
121
122         return {
123             '_type': 'multi_video',
124             'id': content_id,
125             'title': title,
126             'description': description,
127             'entries': entries,
128         }