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 f
'https://feeds.video.aetnd.com/api/v2/{brand}/videos',
77 filter_value
, query
={f'filter[{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 traverse_obj(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
:a40e370925074260b1c8a633c632c63a
',
126 'timestamp
': 1338306241,
127 'upload_date
': '20120529',
128 'uploader
': 'AENE
-NEW
',
130 'thumbnail
': r're
:^https?
://.*\
.jpe?g$
',
131 'chapters
': 'count
:5',
133 'categories
': ['Mountain Men
'],
135 'episode
': 'Episode
1',
136 'season
': 'Season
1',
138 'series
': 'Mountain Men
',
142 'skip_download
': True,
144 'add_ie
': ['ThePlatform
'],
145 'skip
': 'Geo
-restricted
- This content
is not available
in your location
.',
147 'url
': 'http
://www
.aetv
.com
/shows
/duck
-dynasty
/season
-9/episode
-1',
149 'id': '600587331957',
151 'title
': 'Inlawful Entry
',
152 'description
': 'md5
:57c12115a2b384d883fe64ca50529e08
',
153 'timestamp
': 1452634428,
154 'upload_date
': '20160112',
155 'uploader
': 'AENE
-NEW
',
156 'duration
': 1277.695,
157 'thumbnail
': r're
:^https?
://.*\
.jpe?g$
',
158 'chapters
': 'count
:4',
160 'episode
': 'Episode
1',
162 'season
': 'Season
9',
164 'series
': 'Duck Dynasty
',
168 'skip_download
': True,
170 'add_ie
': ['ThePlatform
'],
171 'skip
': 'This video
is only available
for users of participating TV providers
.',
173 'url
': 'http
://www
.fyi
.tv
/shows
/tiny
-house
-nation
/season
-1/episode
-8',
174 'only_matching
': True,
176 'url
': 'http
://www
.mylifetime
.com
/shows
/project
-runway
-junior
/season
-1/episode
-6',
177 'only_matching
': True,
179 'url
': 'http
://www
.mylifetime
.com
/movies
/center
-stage
-on
-pointe
/full
-movie
',
180 'only_matching
': True,
182 'url
': 'https
://watch
.lifetimemovieclub
.com
/movies
/10-year
-reunion
/full
-movie
',
183 'only_matching
': True,
185 'url
': 'http
://www
.history
.com
/specials
/sniper
-into
-the
-kill
-zone
/full
-special
',
186 'only_matching
': True,
188 'url
': 'https
://www
.aetv
.com
/specials
/hunting
-jonbenets
-killer
-the
-untold
-story
/preview
-hunting
-jonbenets
-killer
-the
-untold
-story
',
189 'only_matching
': True,
191 'url
': 'http
://www
.history
.com
/videos
/history
-of
-valentines
-day
',
192 'only_matching
': True,
194 'url
': 'https
://play
.aetv
.com
/shows
/duck
-dynasty
/videos
/best
-of
-duck
-dynasty
-getting
-quack
-in-shape
',
195 'only_matching
': True,
198 def _real_extract(self, url):
199 domain, canonical = self._match_valid_url(url).groups()
200 return self._extract_aetn_info(domain, 'canonical
', '/' + canonical, url)
203 class AENetworksListBaseIE(AENetworksBaseIE):
204 def _call_api(self, resource, slug, brand, fields):
205 return self._download_json(
206 'https
://yoga
.appsvcs
.aetnd
.com
/graphql
',
207 slug, query={'brand': brand}, data=urlencode_postdata({
212 }''' % (resource, slug, fields), # noqa: UP031
213 }))['data
'][resource]
215 def _real_extract(self, url):
216 domain, slug = self._match_valid_url(url).groups()
217 _, brand = self._DOMAIN_MAP[domain]
218 playlist = self._call_api(self._RESOURCE, slug, brand, self._FIELDS)
219 base_url = f'http
://watch
.{domain}
'
222 for item in (playlist.get(self._ITEMS_KEY) or []):
223 doc = self._get_doc(item)
224 canonical = doc.get('canonical
')
227 entries.append(self.url_result(
228 base_url + canonical, AENetworksIE.ie_key(), doc.get('id')))
231 if self._PLAYLIST_DESCRIPTION_KEY:
232 description = playlist.get(self._PLAYLIST_DESCRIPTION_KEY)
234 return self.playlist_result(
235 entries, playlist.get('id'),
236 playlist.get(self._PLAYLIST_TITLE_KEY), description)
239 class AENetworksCollectionIE(AENetworksListBaseIE):
240 IE_NAME = 'aenetworks
:collection
'
241 _VALID_URL = AENetworksBaseIE._BASE_URL_REGEX + r'(?
:[^
/]+/)*(?
:list|collections
)/(?P
<id>[^
/?
#&]+)/?(?:[?#&]|$)'
243 'url': 'https://watch.historyvault.com/list/america-the-story-of-us',
246 'title': 'America The Story of Us',
248 'playlist_mincount': 12,
250 'url': 'https://watch.historyvault.com/shows/america-the-story-of-us-2/season-1/list/america-the-story-of-us',
251 'only_matching': True,
253 'url': 'https://www.historyvault.com/collections/mysteryquest',
254 'only_matching': True,
258 _PLAYLIST_TITLE_KEY
= 'display_title'
259 _PLAYLIST_DESCRIPTION_KEY
= None
263 ... on ListVideoItem {
271 def _get_doc(self
, item
):
272 return item
.get('doc') or {}
275 class AENetworksShowIE(AENetworksListBaseIE
):
276 IE_NAME
= 'aenetworks:show'
277 _VALID_URL
= AENetworksBaseIE
._BASE
_URL
_REGEX
+ r
'shows/(?P<id>[^/?#&]+)/?(?:[?#&]|$)'
279 'url': 'http://www.history.com/shows/ancient-aliens',
282 'title': 'Ancient Aliens',
283 'description': 'md5:3f6d74daf2672ff3ae29ed732e37ea7f',
285 'playlist_mincount': 150,
288 _ITEMS_KEY
= 'episodes'
289 _PLAYLIST_TITLE_KEY
= 'title'
290 _PLAYLIST_DESCRIPTION_KEY
= 'description'
291 _FIELDS
= '''description
299 def _get_doc(self
, item
):
303 class HistoryTopicIE(AENetworksBaseIE
):
304 IE_NAME
= 'history:topic'
305 IE_DESC
= 'History.com Topic'
306 _VALID_URL
= r
'https?://(?:www\.)?history\.com/topics/[^/]+/(?P<id>[\w+-]+?)-video'
308 'url': 'https://www.history.com/topics/valentines-day/history-of-valentines-day-video',
312 'title': 'History of Valentine’s Day',
313 'description': 'md5:7b57ea4829b391995b405fa60bd7b5f7',
314 'timestamp': 1375819729,
315 'upload_date': '20130806',
316 'uploader': 'AENE-NEW',
320 'skip_download': True,
322 'add_ie': ['ThePlatform'],
325 def _real_extract(self
, url
):
326 display_id
= self
._match
_id
(url
)
327 return self
.url_result(
328 'http://www.history.com/videos/' + display_id
,
329 AENetworksIE
.ie_key())
332 class HistoryPlayerIE(AENetworksBaseIE
):
333 IE_NAME
= 'history:player'
334 _VALID_URL
= r
'https?://(?:www\.)?(?P<domain>(?:history|biography)\.com)/player/(?P<id>\d+)'
337 def _real_extract(self
, url
):
338 domain
, video_id
= self
._match
_valid
_url
(url
).groups()
339 return self
._extract
_aetn
_info
(domain
, 'id', video_id
, url
)
342 class BiographyIE(AENetworksBaseIE
):
343 _VALID_URL
= r
'https?://(?:www\.)?biography\.com/video/(?P<id>[^/?#&]+)'
345 'url': 'https://www.biography.com/video/vincent-van-gogh-full-episode-2075049808',
349 'title': 'Vincent Van Gogh - Full Episode',
350 'description': 'A full biography about the most influential 20th century painter, Vincent Van Gogh.',
351 'timestamp': 1311970571,
352 'upload_date': '20110729',
353 'uploader': 'AENE-NEW',
357 'skip_download': True,
359 'add_ie': ['ThePlatform'],
360 'skip': '404 Not Found',
363 def _real_extract(self
, url
):
364 display_id
= self
._match
_id
(url
)
365 webpage
= self
._download
_webpage
(url
, display_id
)
366 player_url
= self
._search
_regex
(
367 rf
'<phoenix-iframe[^>]+src="({HistoryPlayerIE._VALID_URL})',
368 webpage
, 'player URL')
369 return self
.url_result(player_url
, HistoryPlayerIE
.ie_key())