3 from .common
import InfoExtractor
9 from ..utils
.traversal
import traverse_obj
12 class JTBCIE(InfoExtractor
):
13 IE_DESC
= 'jtbc.co.kr'
16 vod\.jtbc\.co\.kr/player/(?:program|clip)
17 |tv\.jtbc\.co\.kr/(?:replay|trailer|clip)/pr\d+/pm\d+
18 )/(?P<id>(?:ep|vo)\d+)'''
19 _GEO_COUNTRIES
= ['KR']
22 'url': 'https://tv.jtbc.co.kr/replay/pr10011629/pm10067930/ep20216321/view',
23 'md5': 'e6ade71d8c8685bbfd6e6ce4167c6a6c',
26 'display_id': 'ep20216321',
28 'title': '힘쎈여자 강남순 2회 다시보기',
29 'description': 'md5:043c1d9019100ce271dba09995dbd1e2',
31 'release_date': '20231008',
33 'thumbnail': 'https://fs.jtbc.co.kr//joydata/CP00000001/prog/drama/stronggirlnamsoon/img/20231008_163541_522_1.jpg',
37 'url': 'https://vod.jtbc.co.kr/player/program/ep20216733',
38 'md5': '217a6d190f115a75e4bda0ceaa4cd7f4',
41 'display_id': 'ep20216733',
43 'title': '헬로 마이 닥터 친절한 진료실 149회 다시보기',
44 'description': 'md5:1d70788a982dd5de26874a92fcffddb8',
46 'release_date': '20231009',
48 'thumbnail': 'https://fs.jtbc.co.kr//joydata/CP00000001/prog/culture/hellomydoctor/img/20231009_095002_528_1.jpg',
49 'series': '헬로 마이 닥터 친절한 진료실',
52 'url': 'https://vod.jtbc.co.kr/player/clip/vo10721270',
53 'md5': '05782e2dc22a9c548aebefe62ae4328a',
56 'display_id': 'vo10721270',
58 'title': '뭉쳐야 찬다3 2회 예고편 - A매치로 향하는 마지막 관문💥',
59 'description': 'md5:d48b51a8655c84843b4ed8d0c39aae68',
61 'release_date': '20231015',
63 'thumbnail': 'https://fs.jtbc.co.kr//joydata/CP00000001/prog/enter/soccer3/img/20231008_210957_775_1.jpg',
67 'url': 'https://tv.jtbc.co.kr/trailer/pr10010392/pm10032526/vo10720912/view',
68 'md5': '367d480eb3ef54a9cd7a4b4d69c4b32d',
71 'display_id': 'vo10720912',
73 'title': '아는 형님 404회 예고편 | 10월 14일(토) 저녁 8시 50분 방송!',
74 'description': 'md5:2743bb1079ceb85bb00060f2ad8f0280',
76 'release_date': '20231014',
78 'thumbnail': 'https://fs.jtbc.co.kr//joydata/CP00000001/prog/enter/jtbcbros/img/20231006_230023_802_1.jpg',
83 def _real_extract(self
, url
):
84 display_id
= self
._match
_id
(url
)
86 if display_id
.startswith('vo'):
87 video_id
= display_id
.upper()
89 webpage
= self
._download
_webpage
(url
, display_id
)
90 video_id
= self
._search
_regex
(r
'data-vod="(VO\d+)"', webpage
, 'vod id')
92 playback_data
= self
._download
_json
(
93 f
'https://api.jtbc.co.kr/vod/{video_id}', video_id
, note
='Downloading VOD playback data')
96 for sub
in traverse_obj(playback_data
, ('tracks', lambda _
, v
: v
['file'])):
97 subtitles
.setdefault(sub
.get('label', 'und'), []).append({'url': sub
['file']})
100 for stream_url
in traverse_obj(playback_data
, ('sources', 'HLS', ..., 'file', {url_or_none}
)):
101 stream_url
= re
.sub(r
'/playlist(?:_pd\d+)?\.m3u8', '/index.m3u8', stream_url
)
102 formats
.extend(self
._extract
_m
3u8_formats
(stream_url
, video_id
, fatal
=False))
104 metadata
= self
._download
_json
(
105 'https://now-api.jtbc.co.kr/v1/vod/detail', video_id
,
106 note
='Downloading mobile details', fatal
=False, query
={'vodFileId': video_id
})
109 'display_id': display_id
,
110 **traverse_obj(metadata
, ('vodDetail', {
111 'title': 'vodTitleView',
112 'series': 'programTitle',
113 'age_limit': ('watchAge', {int_or_none}
),
114 'release_date': ('broadcastDate', {lambda x
: re
.match(r
'\d{8}', x
.replace('.', ''))}, 0),
115 'description': 'episodeContents',
116 'thumbnail': ('imgFileUrl', {url_or_none}
),
118 'duration': parse_duration(playback_data
.get('playTime')),
120 'subtitles': subtitles
,
124 class JTBCProgramIE(InfoExtractor
):
125 IE_NAME
= 'JTBC:program'
126 _VALID_URL
= r
'https?://(?:vod\.jtbc\.co\.kr/program|tv\.jtbc\.co\.kr/replay)/(?P<id>pr\d+)/(?:replay|pm\d+)/?(?:$|[?#])'
129 'url': 'https://tv.jtbc.co.kr/replay/pr10010392/pm10032710',
134 'playlist_count': 398,
136 'url': 'https://vod.jtbc.co.kr/program/pr10011491/replay',
141 'playlist_count': 59,
144 def _real_extract(self
, url
):
145 program_id
= self
._match
_id
(url
)
147 vod_list
= self
._download
_json
(
148 'https://now-api.jtbc.co.kr/v1/vodClip/programHome/programReplayVodList', program_id
,
149 note
='Downloading program replay list', query
={
150 'programId': program_id
,
154 entries
= [self
.url_result(f
'https://vod.jtbc.co.kr/player/program/{video_id}', JTBCIE
, video_id
)
155 for video_id
in traverse_obj(vod_list
, ('programReplayVodList', ..., 'episodeId'))]
156 return self
.playlist_result(entries
, program_id
)