3 from .common
import InfoExtractor
4 from ..networking
.exceptions
import HTTPError
14 class OnDemandChinaEpisodeIE(InfoExtractor
):
15 _VALID_URL
= r
'https?://www\.ondemandchina\.com/\w+/watch/(?P<series>[\w-]+)/(?P<id>ep-(?P<ep>\d+))'
17 'url': 'https://www.ondemandchina.com/en/watch/together-against-covid-19/ep-1',
22 'title': 'EP 1 The Calling',
23 'alt_title': '第1集 令出如山',
24 'thumbnail': 'https://d2y2efdi5wgkcl.cloudfront.net/fit-in/256x256/media-io/2020/9/11/image.d9816e81.jpg',
25 'description': '疫情严峻,党政军民学、东西南北中协同应考',
26 'tags': ['Social Humanities', 'Documentary', 'Medical', 'Social'],
31 query Episode($programSlug: String!, $episodeNumber: Int!) {
33 programSlug: $programSlug
34 episodeNumber: $episodeNumber
56 def _real_extract(self
, url
):
57 program_slug
, display_id
, ep_number
= self
._match
_valid
_url
(url
).group('series', 'id', 'ep')
58 webpage
= self
._download
_webpage
(url
, display_id
)
60 video_info
= self
._download
_json
(
61 'https://odc-graphql.odkmedia.io/graphql', display_id
,
62 headers
={'Content-type': 'application/json'},
64 'operationName': 'Episode',
67 'programSlug': program_slug
,
68 'episodeNumber': int(ep_number
),
70 }).encode())['data']['episode']
73 source_json
= self
._download
_json
(
74 f
'https://odkmedia.io/odc/api/v2/playback/{video_info["id"]}/', display_id
,
75 headers
={'Authorization': '', 'service-name': 'odc'})
76 except ExtractorError
as e
:
77 if isinstance(e
.cause
, HTTPError
):
78 error_data
= self
._parse
_json
(e
.cause
.response
.read(), display_id
)['detail']
79 raise GeoRestrictedError(error_data
)
81 formats
, subtitles
= [], {}
82 for source
in traverse_obj(source_json
, ('sources', ...)):
83 if source
.get('type') == 'hls':
84 fmts
, subs
= self
._extract
_m
3u8_formats
_and
_subtitles
(source
.get('url'), display_id
)
86 self
._merge
_subtitles
(subs
, target
=subtitles
)
88 self
.report_warning(f
'Unsupported format {source.get("type")}', display_id
)
91 'id': str(video_info
['id']),
92 'duration': float_or_none(video_info
.get('videoDuration'), 1000),
93 'thumbnail': (traverse_obj(video_info
, ('images', 'thumbnail'))
94 or self
._html
_search
_meta
(['og:image', 'twitter:image'], webpage
)),
95 'title': (traverse_obj(video_info
, 'title', 'titleEn')
96 or self
._html
_search
_meta
(['og:title', 'twitter:title'], webpage
)
97 or self
._html
_extract
_title
(webpage
)),
98 'alt_title': traverse_obj(video_info
, 'titleKo', 'titleZhHans', 'titleZhHant'),
99 'description': (traverse_obj(
100 video_info
, 'synopsisEn', 'synopsisKo', 'synopsisZhHans', 'synopsisZhHant', 'synopisis')
101 or self
._html
_search
_meta
(['og:description', 'twitter:description', 'description'], webpage
)),
103 'subtitles': subtitles
,
104 'tags': try_call(lambda: self
._html
_search
_meta
('keywords', webpage
).split(', ')),