]>
Commit | Line | Data |
---|---|---|
f670ef1c | 1 | # encoding: utf-8 |
2 | from __future__ import unicode_literals | |
3 | ||
0029071a | 4 | import itertools |
f670ef1c | 5 | |
3793090b | 6 | from .amp import AMPIE |
0029071a S |
7 | from ..compat import ( |
8 | compat_HTTPError, | |
15707c7e | 9 | compat_urllib_parse_urlencode, |
0029071a S |
10 | compat_urlparse, |
11 | ) | |
12 | from ..utils import ( | |
13 | ExtractorError, | |
14 | clean_html, | |
8f4c56f3 | 15 | int_or_none, |
5c2266df | 16 | sanitized_Request, |
0029071a | 17 | ) |
f670ef1c | 18 | |
19 | ||
3793090b | 20 | class DramaFeverBaseIE(AMPIE): |
cbcd1a54 S |
21 | _LOGIN_URL = 'https://www.dramafever.com/accounts/login/' |
22 | _NETRC_MACHINE = 'dramafever' | |
23 | ||
1d1dd597 S |
24 | _CONSUMER_SECRET = 'DA59dtVXYLxajktV' |
25 | ||
26 | _consumer_secret = None | |
27 | ||
28 | def _get_consumer_secret(self): | |
29 | mainjs = self._download_webpage( | |
30 | 'http://www.dramafever.com/static/51afe95/df2014/scripts/main.js', | |
31 | None, 'Downloading main.js', fatal=False) | |
32 | if not mainjs: | |
33 | return self._CONSUMER_SECRET | |
34 | return self._search_regex( | |
35 | r"var\s+cs\s*=\s*'([^']+)'", mainjs, | |
36 | 'consumer secret', default=self._CONSUMER_SECRET) | |
37 | ||
cbcd1a54 S |
38 | def _real_initialize(self): |
39 | self._login() | |
1d1dd597 | 40 | self._consumer_secret = self._get_consumer_secret() |
cbcd1a54 S |
41 | |
42 | def _login(self): | |
43 | (username, password) = self._get_login_info() | |
44 | if username is None: | |
45 | return | |
46 | ||
47 | login_form = { | |
48 | 'username': username, | |
49 | 'password': password, | |
50 | } | |
51 | ||
5c2266df | 52 | request = sanitized_Request( |
15707c7e | 53 | self._LOGIN_URL, compat_urllib_parse_urlencode(login_form).encode('utf-8')) |
cbcd1a54 S |
54 | response = self._download_webpage( |
55 | request, None, 'Logging in as %s' % username) | |
56 | ||
57 | if all(logout_pattern not in response | |
58 | for logout_pattern in ['href="/accounts/logout/"', '>Log out<']): | |
59 | error = self._html_search_regex( | |
60 | r'(?s)class="hidden-xs prompt"[^>]*>(.+?)<', | |
61 | response, 'error message', default=None) | |
62 | if error: | |
63 | raise ExtractorError('Unable to login: %s' % error, expected=True) | |
64 | raise ExtractorError('Unable to log in') | |
65 | ||
66 | ||
67 | class DramaFeverIE(DramaFeverBaseIE): | |
f670ef1c | 68 | IE_NAME = 'dramafever' |
450d89dd | 69 | _VALID_URL = r'https?://(?:www\.)?dramafever\.com/drama/(?P<id>[0-9]+/[0-9]+)(?:/|$)' |
33cee6c7 | 70 | _TESTS = [{ |
f670ef1c | 71 | 'url': 'http://www.dramafever.com/drama/4512/1/Cooking_with_Shin/', |
72 | 'info_dict': { | |
73 | 'id': '4512.1', | |
33cee6c7 | 74 | 'ext': 'mp4', |
f670ef1c | 75 | 'title': 'Cooking with Shin 4512.1', |
0029071a | 76 | 'description': 'md5:a8eec7942e1664a6896fcd5e1287bfd0', |
33cee6c7 S |
77 | 'episode': 'Episode 1', |
78 | 'episode_number': 1, | |
0029071a S |
79 | 'thumbnail': 're:^https?://.*\.jpg', |
80 | 'timestamp': 1404336058, | |
f670ef1c | 81 | 'upload_date': '20140702', |
0029071a | 82 | 'duration': 343, |
3793090b | 83 | }, |
84 | 'params': { | |
85 | # m3u8 download | |
86 | 'skip_download': True, | |
87 | }, | |
33cee6c7 S |
88 | }, { |
89 | 'url': 'http://www.dramafever.com/drama/4826/4/Mnet_Asian_Music_Awards_2015/?ap=1', | |
90 | 'info_dict': { | |
91 | 'id': '4826.4', | |
92 | 'ext': 'mp4', | |
93 | 'title': 'Mnet Asian Music Awards 2015 4826.4', | |
94 | 'description': 'md5:3ff2ee8fedaef86e076791c909cf2e91', | |
95 | 'episode': 'Mnet Asian Music Awards 2015 - Part 3', | |
96 | 'episode_number': 4, | |
97 | 'thumbnail': 're:^https?://.*\.jpg', | |
98 | 'timestamp': 1450213200, | |
99 | 'upload_date': '20151215', | |
100 | 'duration': 5602, | |
101 | }, | |
102 | 'params': { | |
103 | # m3u8 download | |
104 | 'skip_download': True, | |
105 | }, | |
106 | }] | |
f670ef1c | 107 | |
108 | def _real_extract(self, url): | |
0029071a | 109 | video_id = self._match_id(url).replace('/', '.') |
f670ef1c | 110 | |
0029071a | 111 | try: |
c7fa5fa4 | 112 | info = self._extract_feed_info( |
113 | 'http://www.dramafever.com/amp/episode/feed.json?guid=%s' % video_id) | |
0029071a S |
114 | except ExtractorError as e: |
115 | if isinstance(e.cause, compat_HTTPError): | |
116 | raise ExtractorError( | |
117 | 'Currently unavailable in your country.', expected=True) | |
118 | raise | |
f670ef1c | 119 | |
1d1dd597 S |
120 | series_id, episode_number = video_id.split('.') |
121 | episode_info = self._download_json( | |
122 | # We only need a single episode info, so restricting page size to one episode | |
123 | # and dealing with page number as with episode number | |
124 | r'http://www.dramafever.com/api/4/episode/series/?cs=%s&series_id=%s&page_number=%s&page_size=1' | |
125 | % (self._consumer_secret, series_id, episode_number), | |
126 | video_id, 'Downloading episode info JSON', fatal=False) | |
127 | if episode_info: | |
128 | value = episode_info.get('value') | |
1dcc38b2 S |
129 | if isinstance(value, list): |
130 | for v in value: | |
131 | if v.get('type') == 'Episode': | |
132 | subfile = v.get('subfile') or v.get('new_subfile') | |
133 | if subfile and subfile != 'http://www.dramafever.com/st/': | |
134 | info.setdefault('subtitles', {}).setdefault('English', []).append({ | |
135 | 'ext': 'srt', | |
136 | 'url': subfile, | |
137 | }) | |
bd19aa0e S |
138 | episode_number = int_or_none(v.get('number')) |
139 | episode_fallback = 'Episode' | |
140 | if episode_number: | |
141 | episode_fallback += ' %d' % episode_number | |
a2e51e7b | 142 | info['episode'] = v.get('title') or episode_fallback |
bd19aa0e | 143 | info['episode_number'] = episode_number |
1dcc38b2 | 144 | break |
1d1dd597 | 145 | |
3793090b | 146 | return info |
f670ef1c | 147 | |
0029071a | 148 | |
cbcd1a54 | 149 | class DramaFeverSeriesIE(DramaFeverBaseIE): |
f670ef1c | 150 | IE_NAME = 'dramafever:series' |
70a20023 | 151 | _VALID_URL = r'https?://(?:www\.)?dramafever\.com/drama/(?P<id>[0-9]+)(?:/(?:(?!\d+(?:/|$)).+)?)?$' |
f670ef1c | 152 | _TESTS = [{ |
153 | 'url': 'http://www.dramafever.com/drama/4512/Cooking_with_Shin/', | |
154 | 'info_dict': { | |
155 | 'id': '4512', | |
156 | 'title': 'Cooking with Shin', | |
0029071a | 157 | 'description': 'md5:84a3f26e3cdc3fb7f500211b3593b5c1', |
f670ef1c | 158 | }, |
159 | 'playlist_count': 4, | |
160 | }, { | |
161 | 'url': 'http://www.dramafever.com/drama/124/IRIS/', | |
162 | 'info_dict': { | |
163 | 'id': '124', | |
164 | 'title': 'IRIS', | |
0029071a | 165 | 'description': 'md5:b3a30e587cf20c59bd1c01ec0ee1b862', |
f670ef1c | 166 | }, |
167 | 'playlist_count': 20, | |
168 | }] | |
169 | ||
463b2e55 | 170 | _PAGE_SIZE = 60 # max is 60 (see http://api.drama9.com/#get--api-4-episode-series-) |
0029071a | 171 | |
f670ef1c | 172 | def _real_extract(self, url): |
173 | series_id = self._match_id(url) | |
0029071a | 174 | |
0029071a S |
175 | series = self._download_json( |
176 | 'http://www.dramafever.com/api/4/series/query/?cs=%s&series_id=%s' | |
1d1dd597 | 177 | % (self._consumer_secret, series_id), |
0029071a | 178 | series_id, 'Downloading series JSON')['series'][series_id] |
f670ef1c | 179 | |
0029071a S |
180 | title = clean_html(series['name']) |
181 | description = clean_html(series.get('description') or series.get('description_short')) | |
f670ef1c | 182 | |
f670ef1c | 183 | entries = [] |
0029071a S |
184 | for page_num in itertools.count(1): |
185 | episodes = self._download_json( | |
186 | 'http://www.dramafever.com/api/4/episode/series/?cs=%s&series_id=%s&page_size=%d&page_number=%d' | |
1d1dd597 | 187 | % (self._consumer_secret, series_id, self._PAGE_SIZE, page_num), |
0029071a S |
188 | series_id, 'Downloading episodes JSON page #%d' % page_num) |
189 | for episode in episodes.get('value', []): | |
10464af5 S |
190 | episode_url = episode.get('episode_url') |
191 | if not episode_url: | |
192 | continue | |
0029071a | 193 | entries.append(self.url_result( |
10464af5 | 194 | compat_urlparse.urljoin(url, episode_url), |
0029071a S |
195 | 'DramaFever', episode.get('guid'))) |
196 | if page_num == episodes['num_pages']: | |
197 | break | |
198 | ||
f670ef1c | 199 | return self.playlist_result(entries, series_id, title, description) |