1 from .theplatform
import ThePlatformIE
13 class AENetworksBaseIE(ThePlatformIE
): # XXX: Do not subclass from concrete IE
14 _BASE_URL_REGEX
= r
'''(?x)https?://
15 (?:(?:www|play|watch)\.)?
17 (?:history(?:vault)?|aetv|mylifetime|lifetimemovieclub)\.com|
20 _THEPLATFORM_KEY
= '43jXaGRQud'
21 _THEPLATFORM_SECRET
= 'S10BPXHMlb'
23 'history.com': ('HISTORY', 'history'),
24 'aetv.com': ('AETV', 'aetv'),
25 'mylifetime.com': ('LIFETIME', 'lifetime'),
26 'lifetimemovieclub.com': ('LIFETIMEMOVIECLUB', 'lmc'),
27 'fyi.tv': ('FYI', 'fyi'),
28 'historyvault.com': (None, 'historyvault'),
29 'biography.com': (None, 'biography'),
32 def _extract_aen_smil(self
, smil_url
, video_id
, auth
=None):
35 'formats': 'M3U+none,MPEG-DASH+none,MPEG4,MP3',
40 'assetTypes': 'high_video_ak',
41 'switch': 'hls_high_ak',
43 'assetTypes': 'high_video_s3',
45 'assetTypes': 'high_video_s3',
46 'switch': 'hls_high_fastly',
51 for q
in TP_SMIL_QUERY
:
53 m_url
= update_url_query(smil_url
, q
)
54 m_url
= self
._sign
_url
(m_url
, self
._THEPLATFORM
_KEY
, self
._THEPLATFORM
_SECRET
)
56 tp_formats
, tp_subtitles
= self
._extract
_theplatform
_smil
(
57 m_url
, video_id
, 'Downloading %s SMIL data' % (q
.get('switch') or q
['assetTypes']))
58 except ExtractorError
as e
:
59 if isinstance(e
, GeoRestrictedError
):
63 formats
.extend(tp_formats
)
64 subtitles
= self
._merge
_subtitles
(subtitles
, tp_subtitles
)
65 if last_e
and not formats
:
70 'subtitles': subtitles
,
73 def _extract_aetn_info(self
, domain
, filter_key
, filter_value
, url
):
74 requestor_id
, brand
= self
._DOMAIN
_MAP
[domain
]
75 result
= self
._download
_json
(
76 'https://feeds.video.aetnd.com/api/v2/%s/videos' % brand
,
77 filter_value
, query
={'filter[%s]' % filter_key: filter_value}
)
78 result
= traverse_obj(
80 lambda k
, v
: k
== 0 and v
[filter_key
] == filter_value
),
83 raise ExtractorError('Show not found in A&E feed (too new?)', expected
=True,
84 video_id
=remove_start(filter_value
, '/'))
85 title
= result
['title']
86 video_id
= result
['id']
87 media_url
= result
['publicUrl']
88 theplatform_metadata
= self
._download
_theplatform
_metadata
(self
._search
_regex
(
89 r
'https?://link\.theplatform\.com/s/([^?]+)', media_url
, 'theplatform_path'), video_id
)
90 info
= self
._parse
_theplatform
_metadata
(theplatform_metadata
)
92 if theplatform_metadata
.get('AETN$isBehindWall'):
93 resource
= self
._get
_mvpd
_resource
(
94 requestor_id
, theplatform_metadata
['title'],
95 theplatform_metadata
.get('AETN$PPL_pplProgramId') or theplatform_metadata
.get('AETN$PPL_pplProgramId_OLD'),
96 theplatform_metadata
['ratings'][0]['rating'])
97 auth
= self
._extract
_mvpd
_auth
(
98 url
, video_id
, requestor_id
, resource
)
99 info
.update(self
._extract
_aen
_smil
(media_url
, video_id
, auth
))
102 'series': result
.get('seriesName'),
103 'season_number': int_or_none(result
.get('tvSeasonNumber')),
104 'episode_number': int_or_none(result
.get('tvSeasonEpisodeNumber')),
109 class AENetworksIE(AENetworksBaseIE
):
110 IE_NAME
= 'aenetworks'
111 IE_DESC
= 'A+E Networks: A&E, Lifetime, History.com, FYI Network and History Vault'
112 _VALID_URL
= AENetworksBaseIE
._BASE
_URL
_REGEX
+ r
'''(?P<id>
113 shows/[^/]+/season-\d+/episode-\d+|
115 (?:movie|special)s/[^/]+|
116 (?:shows/[^/]+/)?videos
120 'url': 'http://www.history.com/shows/mountain-men/season-1/episode-1',
124 'title': 'Winter is Coming',
125 'description': 'md5:641f424b7a19d8e24f26dea22cf59d74',
126 'timestamp': 1338306241,
127 'upload_date': '20120529',
128 'uploader': 'AENE-NEW',
132 'skip_download': True,
134 'add_ie': ['ThePlatform'],
135 'skip': 'Geo-restricted - This content is not available in your location.'
137 'url': 'http://www.aetv.com/shows/duck-dynasty/season-9/episode-1',
139 'id': '600587331957',
141 'title': 'Inlawful Entry',
142 'description': 'md5:57c12115a2b384d883fe64ca50529e08',
143 'timestamp': 1452634428,
144 'upload_date': '20160112',
145 'uploader': 'AENE-NEW',
149 'skip_download': True,
151 'add_ie': ['ThePlatform'],
152 'skip': 'This video is only available for users of participating TV providers.',
154 'url': 'http://www.fyi.tv/shows/tiny-house-nation/season-1/episode-8',
155 'only_matching': True
157 'url': 'http://www.mylifetime.com/shows/project-runway-junior/season-1/episode-6',
158 'only_matching': True
160 'url': 'http://www.mylifetime.com/movies/center-stage-on-pointe/full-movie',
161 'only_matching': True
163 'url': 'https://watch.lifetimemovieclub.com/movies/10-year-reunion/full-movie',
164 'only_matching': True
166 'url': 'http://www.history.com/specials/sniper-into-the-kill-zone/full-special',
167 'only_matching': True
169 'url': 'https://www.aetv.com/specials/hunting-jonbenets-killer-the-untold-story/preview-hunting-jonbenets-killer-the-untold-story',
170 'only_matching': True
172 'url': 'http://www.history.com/videos/history-of-valentines-day',
173 'only_matching': True
175 'url': 'https://play.aetv.com/shows/duck-dynasty/videos/best-of-duck-dynasty-getting-quack-in-shape',
176 'only_matching': True
179 def _real_extract(self
, url
):
180 domain
, canonical
= self
._match
_valid
_url
(url
).groups()
181 return self
._extract
_aetn
_info
(domain
, 'canonical', '/' + canonical
, url
)
184 class AENetworksListBaseIE(AENetworksBaseIE
):
185 def _call_api(self
, resource
, slug
, brand
, fields
):
186 return self
._download
_json
(
187 'https://yoga.appsvcs.aetnd.com/graphql',
188 slug
, query
={'brand': brand}
, data
=urlencode_postdata({
193 }''' % (resource
, slug
, fields
),
194 }))['data'][resource
]
196 def _real_extract(self
, url
):
197 domain
, slug
= self
._match
_valid
_url
(url
).groups()
198 _
, brand
= self
._DOMAIN
_MAP
[domain
]
199 playlist
= self
._call
_api
(self
._RESOURCE
, slug
, brand
, self
._FIELDS
)
200 base_url
= 'http://watch.%s' % domain
203 for item
in (playlist
.get(self
._ITEMS
_KEY
) or []):
204 doc
= self
._get
_doc
(item
)
205 canonical
= doc
.get('canonical')
208 entries
.append(self
.url_result(
209 base_url
+ canonical
, AENetworksIE
.ie_key(), doc
.get('id')))
212 if self
._PLAYLIST
_DESCRIPTION
_KEY
:
213 description
= playlist
.get(self
._PLAYLIST
_DESCRIPTION
_KEY
)
215 return self
.playlist_result(
216 entries
, playlist
.get('id'),
217 playlist
.get(self
._PLAYLIST
_TITLE
_KEY
), description
)
220 class AENetworksCollectionIE(AENetworksListBaseIE
):
221 IE_NAME
= 'aenetworks:collection'
222 _VALID_URL
= AENetworksBaseIE
._BASE
_URL
_REGEX
+ r
'(?:[^/]+/)*(?:list|collections)/(?P<id>[^/?#&]+)/?(?:[?#&]|$)'
224 'url': 'https://watch.historyvault.com/list/america-the-story-of-us',
227 'title': 'America The Story of Us',
229 'playlist_mincount': 12,
231 'url': 'https://watch.historyvault.com/shows/america-the-story-of-us-2/season-1/list/america-the-story-of-us',
232 'only_matching': True
234 'url': 'https://www.historyvault.com/collections/mysteryquest',
235 'only_matching': True
239 _PLAYLIST_TITLE_KEY
= 'display_title'
240 _PLAYLIST_DESCRIPTION_KEY
= None
244 ... on ListVideoItem {
252 def _get_doc(self
, item
):
253 return item
.get('doc') or {}
256 class AENetworksShowIE(AENetworksListBaseIE
):
257 IE_NAME
= 'aenetworks:show'
258 _VALID_URL
= AENetworksBaseIE
._BASE
_URL
_REGEX
+ r
'shows/(?P<id>[^/?#&]+)/?(?:[?#&]|$)'
260 'url': 'http://www.history.com/shows/ancient-aliens',
263 'title': 'Ancient Aliens',
264 'description': 'md5:3f6d74daf2672ff3ae29ed732e37ea7f',
266 'playlist_mincount': 150,
269 _ITEMS_KEY
= 'episodes'
270 _PLAYLIST_TITLE_KEY
= 'title'
271 _PLAYLIST_DESCRIPTION_KEY
= 'description'
272 _FIELDS
= '''description
280 def _get_doc(self
, item
):
284 class HistoryTopicIE(AENetworksBaseIE
):
285 IE_NAME
= 'history:topic'
286 IE_DESC
= 'History.com Topic'
287 _VALID_URL
= r
'https?://(?:www\.)?history\.com/topics/[^/]+/(?P<id>[\w+-]+?)-video'
289 'url': 'https://www.history.com/topics/valentines-day/history-of-valentines-day-video',
293 'title': "History of Valentine’s Day",
294 'description': 'md5:7b57ea4829b391995b405fa60bd7b5f7',
295 'timestamp': 1375819729,
296 'upload_date': '20130806',
297 'uploader': 'AENE-NEW',
301 'skip_download': True,
303 'add_ie': ['ThePlatform'],
306 def _real_extract(self
, url
):
307 display_id
= self
._match
_id
(url
)
308 return self
.url_result(
309 'http://www.history.com/videos/' + display_id
,
310 AENetworksIE
.ie_key())
313 class HistoryPlayerIE(AENetworksBaseIE
):
314 IE_NAME
= 'history:player'
315 _VALID_URL
= r
'https?://(?:www\.)?(?P<domain>(?:history|biography)\.com)/player/(?P<id>\d+)'
318 def _real_extract(self
, url
):
319 domain
, video_id
= self
._match
_valid
_url
(url
).groups()
320 return self
._extract
_aetn
_info
(domain
, 'id', video_id
, url
)
323 class BiographyIE(AENetworksBaseIE
):
324 _VALID_URL
= r
'https?://(?:www\.)?biography\.com/video/(?P<id>[^/?#&]+)'
326 'url': 'https://www.biography.com/video/vincent-van-gogh-full-episode-2075049808',
330 'title': 'Vincent Van Gogh - Full Episode',
331 'description': 'A full biography about the most influential 20th century painter, Vincent Van Gogh.',
332 'timestamp': 1311970571,
333 'upload_date': '20110729',
334 'uploader': 'AENE-NEW',
338 'skip_download': True,
340 'add_ie': ['ThePlatform'],
341 'skip': '404 Not Found',
344 def _real_extract(self
, url
):
345 display_id
= self
._match
_id
(url
)
346 webpage
= self
._download
_webpage
(url
, display_id
)
347 player_url
= self
._search
_regex
(
348 r
'<phoenix-iframe[^>]+src="(%s)' % HistoryPlayerIE
._VALID
_URL
,
349 webpage
, 'player URL')
350 return self
.url_result(player_url
, HistoryPlayerIE
.ie_key())