3 from __future__ import unicode_literals
5 from .common import InfoExtractor
6 from ..compat import compat_HTTPError
18 class KakaoIE(InfoExtractor):
19 _VALID_URL = r'https?://(?:play-)?tv\.kakao\.com/(?:channel/\d+|embed/player)/cliplink/(?P<id>\d+|[^?#&]+@my)'
20 _API_BASE_TMPL = 'http://tv.kakao.com/api/v1/ft/cliplinks/%s/'
23 'url': 'http://tv.kakao.com/channel/2671005/cliplink/301965083',
24 'md5': '702b2fbdeb51ad82f5c904e8c0766340',
28 'title': '乃木坂46 バナナマン 「3期生紹介コーナーが始動!顔高低差GPも!」 『乃木坂工事中』',
29 'uploader_id': '2671005',
31 'timestamp': 1488160199,
32 'upload_date': '20170227',
35 'url': 'http://tv.kakao.com/channel/2653210/cliplink/300103180',
36 'md5': 'a8917742069a4dd442516b86e7d66529',
40 'description': '러블리즈 - Destiny (나의 지구) (Lovelyz - Destiny)\r\n\r\n[쇼! 음악중심] 20160611, 507회',
41 'title': '러블리즈 - Destiny (나의 지구) (Lovelyz - Destiny)',
42 'uploader_id': '2653210',
43 'uploader': '쇼! 음악중심',
44 'timestamp': 1485684628,
45 'upload_date': '20170129',
49 'url': 'https://tv.kakao.com/channel/3643855/cliplink/412069491',
50 'only_matching': True,
53 def _real_extract(self, url):
54 video_id = self._match_id(url)
55 display_id = video_id.rstrip('@my')
56 api_base = self._API_BASE_TMPL % video_id
59 'Referer': update_url_query(
60 'http://tv.kakao.com/embed/player/cliplink/%s' % video_id, {
61 'service': 'kakao_tv',
64 'wmode': 'transparent',
69 'player': 'monet_html5',
72 'service': 'kakao_tv',
76 '-*', 'tid', 'clipLink', 'displayTitle', 'clip', 'title',
77 'description', 'channelId', 'createTime', 'duration', 'playCount',
78 'likeCount', 'commentCount', 'tagList', 'channel', 'name', 'thumbnailUrl',
79 'videoOutputList', 'width', 'height', 'kbps', 'profile', 'label'])
82 impress = self._download_json(
83 api_base + 'impress', display_id, 'Downloading video info',
84 query=query, headers=player_header)
86 clip_link = impress['clipLink']
87 clip = clip_link['clip']
89 title = clip.get('title') or clip_link.get('displayTitle')
92 'fields': '-*,code,message,url',
93 'tid': impress.get('tid') or '',
97 for fmt in (clip.get('videoOutputList') or []):
99 profile_name = fmt['profile']
100 if profile_name == 'AUDIO':
102 query['profile'] = profile_name
104 fmt_url_json = self._download_json(
105 api_base + 'raw/videolocation', display_id,
106 'Downloading video URL for profile %s' % profile_name,
107 query=query, headers=player_header)
108 except ExtractorError as e:
109 if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
110 resp = self._parse_json(e.cause.read().decode(), video_id)
111 if resp.get('code') == 'GeoBlocked':
112 self.raise_geo_restricted()
115 fmt_url = fmt_url_json['url']
118 'format_id': profile_name,
119 'width': int_or_none(fmt.get('width')),
120 'height': int_or_none(fmt.get('height')),
121 'format_note': fmt.get('label'),
122 'filesize': int_or_none(fmt.get('filesize')),
123 'tbr': int_or_none(fmt.get('kbps')),
127 self._sort_formats(formats)
132 'description': strip_or_none(clip.get('description')),
133 'uploader': try_get(clip_link, lambda x: x['channel']['name']),
134 'uploader_id': str_or_none(clip_link.get('channelId')),
135 'thumbnail': clip.get('thumbnailUrl'),
136 'timestamp': unified_timestamp(clip_link.get('createTime')),
137 'duration': int_or_none(clip.get('duration')),
138 'view_count': int_or_none(clip.get('playCount')),
139 'like_count': int_or_none(clip.get('likeCount')),
140 'comment_count': int_or_none(clip.get('commentCount')),
142 'tags': clip.get('tagList'),