2 from __future__
import unicode_literals
7 from .common
import InfoExtractor
8 from ..compat
import compat_str
27 class ERTFlixBaseIE(InfoExtractor
):
29 self
, video_id
, method
='Player/AcquireContent', api_version
=1,
30 param_headers
=None, data
=None, headers
=None, **params
):
31 platform_codename
= {'platformCodename': 'www'}
32 headers_as_param
= {'X-Api-Date-Format': 'iso', 'X-Api-Camel-Case': False}
33 headers_as_param
.update(param_headers
or {})
34 headers
= headers
or {}
36 headers
['Content-Type'] = headers_as_param
['Content-Type'] = 'application/json;charset=utf-8'
37 data
= json
.dumps(merge_dicts(platform_codename
, data
)).encode('utf-8')
39 {} if data
else platform_codename
,
40 {'$headers': json.dumps(headers_as_param)}
,
42 response
= self
._download
_json
(
43 'https://api.app.ertflix.gr/v%s/%s' % (str(api_version
), method
),
44 video_id
, fatal
=False, query
=query
, data
=data
, headers
=headers
)
45 if try_get(response
, lambda x
: x
['Result']['Success']) is True:
48 def _call_api_get_tiles(self
, video_id
, *tile_ids
):
49 requested_tile_ids
= [video_id
] + list(tile_ids
)
50 requested_tiles
= [{'Id': tile_id}
for tile_id
in requested_tile_ids
]
51 tiles_response
= self
._call
_api
(
52 video_id
, method
='Tile/GetTiles', api_version
=2,
53 data
={'RequestedTiles': requested_tiles}
)
54 tiles
= try_get(tiles_response
, lambda x
: x
['Tiles'], list) or []
56 if sorted([tile
['Id'] for tile
in tiles
]) != sorted(requested_tile_ids
):
57 raise ExtractorError('Requested tiles not found', video_id
=video_id
)
60 return next(tile
for tile
in tiles
if tile
['Id'] == video_id
)
62 raise ExtractorError('No matching tile found', video_id
=video_id
)
65 class ERTFlixCodenameIE(ERTFlixBaseIE
):
66 IE_NAME
= 'ertflix:codename'
67 IE_DESC
= 'ERTFLIX videos by codename'
68 _VALID_URL
= r
'ertflix:(?P<id>[\w-]+)'
70 'url': 'ertflix:monogramma-praxitelis-tzanoylinos',
71 'md5': '5b9c2cd171f09126167e4082fc1dd0ef',
73 'id': 'monogramma-praxitelis-tzanoylinos',
75 'title': 'md5:ef0b439902963d56c43ac83c3f41dd0e',
80 def _extract_formats_and_subs(self
, video_id
, allow_none
=True):
81 media_info
= self
._call
_api
(video_id
, codename
=video_id
)
82 formats
, subs
= [], {}
83 for media_file
in try_get(media_info
, lambda x
: x
['MediaFiles'], list) or []:
84 for media
in try_get(media_file
, lambda x
: x
['Formats'], list) or []:
85 fmt_url
= url_or_none(try_get(media
, lambda x
: x
['Url']))
88 ext
= determine_ext(fmt_url
)
90 formats_
, subs_
= self
._extract
_m
3u8_formats
_and
_subtitles
(
91 fmt_url
, video_id
, m3u8_id
='hls', ext
='mp4', fatal
=False)
93 formats_
, subs_
= self
._extract
_mpd
_formats
_and
_subtitles
(
94 fmt_url
, video_id
, mpd_id
='dash', fatal
=False)
98 'format_id': str_or_none(media
.get('Id')),
101 formats
.extend(formats_
)
102 self
._merge
_subtitles
(subs_
, target
=subs
)
104 if formats
or not allow_none
:
105 self
._sort
_formats
(formats
)
108 def _real_extract(self
, url
):
109 video_id
= self
._match
_id
(url
)
111 formats
, subs
= self
._extract
_formats
_and
_subs
(video_id
)
118 'title': self
._generic
_title
(url
),
122 class ERTFlixIE(ERTFlixBaseIE
):
124 IE_DESC
= 'ERTFLIX videos'
125 _VALID_URL
= r
'https?://www\.ertflix\.gr/(?:series|vod)/(?P<id>[a-z]{3}\.\d+)'
127 'url': 'https://www.ertflix.gr/vod/vod.173258-aoratoi-ergates',
128 'md5': '6479d5e60fd7e520b07ba5411dcdd6e7',
130 'id': 'aoratoi-ergates',
132 'title': 'md5:c1433d598fbba0211b0069021517f8b4',
133 'description': 'md5:01a64d113c31957eb7eb07719ab18ff4',
134 'thumbnail': r
're:https?://.+\.jpg',
135 'episode_id': 'vod.173258',
136 'timestamp': 1639648800,
137 'upload_date': '20211216',
142 'url': 'https://www.ertflix.gr/series/ser.3448-monogramma',
146 'description': 'Η εκπομπή σαράντα ετών που σημάδεψε τον πολιτισμό μας.',
147 'title': 'Μονόγραμμα',
149 'playlist_mincount': 64,
151 'url': 'https://www.ertflix.gr/series/ser.3448-monogramma?season=1',
155 'description': 'Η εκπομπή σαράντα ετών που σημάδεψε τον πολιτισμό μας.',
156 'title': 'Μονόγραμμα',
158 'playlist_count': 22,
160 'url': 'https://www.ertflix.gr/series/ser.3448-monogramma?season=1&season=2021%20-%202022',
164 'description': 'Η εκπομπή σαράντα ετών που σημάδεψε τον πολιτισμό μας.',
165 'title': 'Μονόγραμμα',
167 'playlist_mincount': 36,
169 'url': 'https://www.ertflix.gr/series/ser.164991-to-diktuo-1?season=1-9',
173 'description': 'Η πρώτη ελληνική εκπομπή με θεματολογία αποκλειστικά γύρω από το ίντερνετ.',
174 'title': 'Το δίκτυο',
176 'playlist_mincount': 9,
179 def _extract_episode(self
, episode
):
180 codename
= try_get(episode
, lambda x
: x
['Codename'], compat_str
)
181 title
= episode
.get('Title')
182 description
= clean_html(dict_get(episode
, ('ShortDescription', 'TinyDescription', )))
183 if not codename
or not title
or not episode
.get('HasPlayableStream', True):
186 url_or_none(thumb
.get('Url'))
187 for thumb
in variadic(dict_get(episode
, ('Images', 'Image')) or {})
188 if thumb
.get('IsMain')),
191 '_type': 'url_transparent',
192 'thumbnail': thumbnail
,
194 'episode_id': episode
.get('Id'),
196 'alt_title': episode
.get('Subtitle'),
197 'description': description
,
198 'timestamp': parse_iso8601(episode
.get('PublishDate')),
199 'duration': episode
.get('DurationSeconds'),
200 'age_limit': self
._parse
_age
_rating
(episode
),
201 'url': 'ertflix:%s' % (codename
, ),
205 def _parse_age_rating(info_dict
):
206 return parse_age_limit(
207 info_dict
.get('AgeRating')
208 or (info_dict
.get('IsAdultContent') and 18)
209 or (info_dict
.get('IsKidsContent') and 0))
211 def _extract_series(self
, video_id
, season_titles
=None, season_numbers
=None):
212 media_info
= self
._call
_api
(video_id
, method
='Tile/GetSeriesDetails', id=video_id
)
214 series
= try_get(media_info
, lambda x
: x
['Series'], dict) or {}
216 'age_limit': self
._parse
_age
_rating
(series
),
217 'title': series
.get('Title'),
218 'description': dict_get(series
, ('ShortDescription', 'TinyDescription', )),
221 season_titles
= season_titles
or []
222 for season
in try_get(series
, lambda x
: x
['Seasons'], list) or []:
223 if season
.get('SeasonNumber') in season_numbers
and season
.get('Title'):
224 season_titles
.append(season
['Title'])
226 def gen_episode(m_info
, season_titles
):
227 for episode_group
in try_get(m_info
, lambda x
: x
['EpisodeGroups'], list) or []:
228 if season_titles
and episode_group
.get('Title') not in season_titles
:
230 episodes
= try_get(episode_group
, lambda x
: x
['Episodes'], list)
234 'season': episode_group
.get('Title'),
235 'season_number': int_or_none(episode_group
.get('SeasonNumber')),
238 episodes
= [(int(ep
['EpisodeNumber']), ep
) for ep
in episodes
]
240 except (KeyError, ValueError):
241 episodes
= enumerate(episodes
, 1)
242 for n
, episode
in episodes
:
243 info
= self
._extract
_episode
(episode
)
246 info
['episode_number'] = n
247 info
.update(season_info
)
250 return self
.playlist_result(
251 gen_episode(media_info
, season_titles
), playlist_id
=video_id
, **series_info
)
253 def _real_extract(self
, url
):
254 video_id
= self
._match
_id
(url
)
255 if video_id
.startswith('ser.'):
256 param_season
= parse_qs(url
).get('season', [None])
258 (have_number
, int_or_none(v
) if have_number
else str_or_none(v
))
259 for have_number
, v
in
260 [(int_or_none(ps
) is not None, ps
) for ps
in param_season
]
264 k
: [v
for is_num
, v
in param_season
if is_num
is c
] or None
266 [('season_titles', False), ('season_numbers', True)]
268 return self
._extract
_series
(video_id
, **season_kwargs
)
270 return self
._extract
_episode
(self
._call
_api
_get
_tiles
(video_id
))
273 class ERTWebtvEmbedIE(InfoExtractor
):
274 IE_NAME
= 'ertwebtv:embed'
275 IE_DESC
= 'ert.gr webtv embedded videos'
276 _BASE_PLAYER_URL_RE
= re
.escape('//www.ert.gr/webtv/live-uni/vod/dt-uni-vod.php')
277 _VALID_URL
= rf
'https?:{_BASE_PLAYER_URL_RE}\?([^#]+&)?f=(?P<id>[^#&]+)'
280 'url': 'https://www.ert.gr/webtv/live-uni/vod/dt-uni-vod.php?f=trailers/E2251_TO_DIKTYO_E09_16-01_1900.mp4&bgimg=/photos/2022/1/to_diktio_ep09_i_istoria_tou_diadiktiou_stin_Ellada_1021x576.jpg',
281 'md5': 'f9e9900c25c26f4ecfbddbb4b6305854',
283 'id': 'trailers/E2251_TO_DIKTYO_E09_16-01_1900.mp4',
284 'title': 'md5:914f06a73cd8b62fbcd6fb90c636e497',
286 'thumbnail': 'https://program.ert.gr/photos/2022/1/to_diktio_ep09_i_istoria_tou_diadiktiou_stin_Ellada_1021x576.jpg'
291 def _extract_urls(cls
, webpage
):
292 EMBED_URL_RE
= rf
'(?:https?:)?{cls._BASE_PLAYER_URL_RE}\?(?:(?!(?P=_q1)).)+'
293 EMBED_RE
= rf
'<iframe[^>]+?src=(?P<_q1>["\'])(?P<url>{EMBED_URL_RE})(?P=_q1)'
295 for mobj
in re
.finditer(EMBED_RE
, webpage
):
296 url
= unescapeHTML(mobj
.group('url'))
297 if not cls
.suitable(url
):
301 def _real_extract(self
, url
):
302 video_id
= self
._match
_id
(url
)
303 formats
, subs
= self
._extract
_m
3u8_formats
_and
_subtitles
(
304 f
'https://mediastream.ert.gr/vodedge/_definst_/mp4:dvrorigin/{video_id}/playlist.m3u8',
306 self
._sort
_formats
(formats
)
307 thumbnail_id
= parse_qs(url
).get('bgimg', [None])[0]
308 if thumbnail_id
and not thumbnail_id
.startswith('http'):
309 thumbnail_id
= f
'https://program.ert.gr{thumbnail_id}'
312 'title': f
'VOD - {video_id}',
313 'thumbnail': thumbnail_id
,