]>
Commit | Line | Data |
---|---|---|
151f8f1c HTL |
1 | import hashlib |
2 | import time | |
3 | import urllib.parse | |
4 | ||
5 | from .common import InfoExtractor | |
6 | from ..utils import ( | |
42a4f21a | 7 | clean_html, |
151f8f1c | 8 | join_nonempty, |
42a4f21a | 9 | strip_or_none, |
151f8f1c HTL |
10 | ) |
11 | ||
12 | ||
13 | class FptplayIE(InfoExtractor): | |
42a4f21a | 14 | _VALID_URL = r'https?://fptplay\.vn/xem-video/[^/]+\-(?P<id>\w+)(?:/tap-(?P<episode>\d+)?/?(?:[?#]|$)|)' |
151f8f1c HTL |
15 | _GEO_COUNTRIES = ['VN'] |
16 | IE_NAME = 'fptplay' | |
17 | IE_DESC = 'fptplay.vn' | |
18 | _TESTS = [{ | |
19 | 'url': 'https://fptplay.vn/xem-video/nhan-duyen-dai-nhan-xin-dung-buoc-621a123016f369ebbde55945', | |
20 | 'md5': 'ca0ee9bc63446c0c3e9a90186f7d6b33', | |
21 | 'info_dict': { | |
22 | 'id': '621a123016f369ebbde55945', | |
23 | 'ext': 'mp4', | |
42a4f21a | 24 | 'title': 'Nhân Duyên Đại Nhân Xin Dừng Bước - Tập 1A', |
151f8f1c HTL |
25 | 'description': 'md5:23cf7d1ce0ade8e21e76ae482e6a8c6c', |
26 | }, | |
27 | }, { | |
28 | 'url': 'https://fptplay.vn/xem-video/ma-toi-la-dai-gia-61f3aa8a6b3b1d2e73c60eb5/tap-3', | |
29 | 'md5': 'b35be968c909b3e4e1e20ca45dd261b1', | |
30 | 'info_dict': { | |
31 | 'id': '61f3aa8a6b3b1d2e73c60eb5', | |
32 | 'ext': 'mp4', | |
42a4f21a | 33 | 'title': 'Má Tôi Là Đại Gia - Tập 3', |
151f8f1c HTL |
34 | 'description': 'md5:ff8ba62fb6e98ef8875c42edff641d1c', |
35 | }, | |
42a4f21a HTL |
36 | }, { |
37 | 'url': 'https://fptplay.vn/xem-video/lap-toi-do-giam-under-the-skin-6222d9684ec7230fa6e627a2/tap-4', | |
38 | 'md5': 'bcb06c55ec14786d7d4eda07fa1ccbb9', | |
39 | 'info_dict': { | |
40 | 'id': '6222d9684ec7230fa6e627a2', | |
41 | 'ext': 'mp4', | |
42 | 'title': 'Lạp Tội Đồ Giám - Tập 2B', | |
43 | 'description': 'md5:e5a47e9d35fbf7e9479ca8a77204908b', | |
44 | }, | |
151f8f1c HTL |
45 | }, { |
46 | 'url': 'https://fptplay.vn/xem-video/nha-co-chuyen-hi-alls-well-ends-well-1997-6218995f6af792ee370459f0', | |
47 | 'only_matching': True, | |
48 | }] | |
49 | ||
50 | def _real_extract(self, url): | |
42a4f21a HTL |
51 | video_id, slug_episode = self._match_valid_url(url).group('id', 'episode') |
52 | webpage = self._download_webpage(url, video_id=video_id, fatal=False) or '' | |
53 | title = self._search_regex( | |
54 | r'(?s)<h4\s+class="mb-1 text-2xl text-white"[^>]*>(.+)</h4>', webpage, 'title', fatal=False) | |
55 | real_episode = slug_episode if not title else self._search_regex( | |
56 | r'<p.+title="(?P<episode>[^">]+)"\s+class="epi-title active"', webpage, 'episode', fatal=False) | |
57 | title = strip_or_none(title) or self._html_search_meta(('og:title', 'twitter:title'), webpage) | |
58 | ||
59 | info = self._download_json( | |
60 | self.get_api_with_st_token(video_id, int(slug_episode) - 1 if slug_episode else 0), video_id) | |
151f8f1c HTL |
61 | formats, subtitles = self._extract_m3u8_formats_and_subtitles(info['data']['url'], video_id, 'mp4') |
62 | self._sort_formats(formats) | |
63 | return { | |
64 | 'id': video_id, | |
42a4f21a HTL |
65 | 'title': join_nonempty(title, real_episode, delim=' - '), |
66 | 'description': ( | |
67 | clean_html(self._search_regex(r'<p\s+class="overflow-hidden"[^>]*>(.+)</p>', webpage, 'description')) | |
68 | or self._html_search_meta(('og:description', 'twitter:description'), webpage)), | |
151f8f1c HTL |
69 | 'formats': formats, |
70 | 'subtitles': subtitles, | |
71 | } | |
72 | ||
73 | def get_api_with_st_token(self, video_id, episode): | |
74 | path = f'/api/v6.2_w/stream/vod/{video_id}/{episode}/auto_vip' | |
75 | timestamp = int(time.time()) + 10800 | |
76 | ||
77 | t = hashlib.md5(f'WEBv6Dkdsad90dasdjlALDDDS{timestamp}{path}'.encode()).hexdigest().upper() | |
78 | r = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | |
79 | n = [int(f'0x{t[2 * o: 2 * o + 2]}', 16) for o in range(len(t) // 2)] | |
80 | ||
81 | def convert(e): | |
82 | t = '' | |
83 | n = 0 | |
84 | i = [0, 0, 0] | |
85 | a = [0, 0, 0, 0] | |
86 | s = len(e) | |
87 | c = 0 | |
88 | for z in range(s, 0, -1): | |
89 | if n <= 3: | |
90 | i[n] = e[c] | |
91 | n += 1 | |
92 | c += 1 | |
93 | if 3 == n: | |
94 | a[0] = (252 & i[0]) >> 2 | |
95 | a[1] = ((3 & i[0]) << 4) + ((240 & i[1]) >> 4) | |
96 | a[2] = ((15 & i[1]) << 2) + ((192 & i[2]) >> 6) | |
97 | a[3] = (63 & i[2]) | |
98 | for v in range(4): | |
99 | t += r[a[v]] | |
100 | n = 0 | |
101 | if n: | |
102 | for o in range(n, 3): | |
103 | i[o] = 0 | |
104 | ||
105 | for o in range(n + 1): | |
106 | a[0] = (252 & i[0]) >> 2 | |
107 | a[1] = ((3 & i[0]) << 4) + ((240 & i[1]) >> 4) | |
108 | a[2] = ((15 & i[1]) << 2) + ((192 & i[2]) >> 6) | |
109 | a[3] = (63 & i[2]) | |
110 | t += r[a[o]] | |
111 | n += 1 | |
112 | while n < 3: | |
113 | t += '' | |
114 | n += 1 | |
115 | return t | |
116 | ||
117 | st_token = convert(n).replace('+', '-').replace('/', '_').replace('=', '') | |
118 | return f'https://api.fptplay.net{path}?{urllib.parse.urlencode({"st": st_token, "e": timestamp})}' |