]>
Commit | Line | Data |
---|---|---|
65de7d20 | 1 | from __future__ import annotations |
e3a3ed8a | 2 | import functools |
65de7d20 | 3 | import json |
e3a3ed8a | 4 | import textwrap |
65de7d20 | 5 | |
856150d0 | 6 | from .common import InfoExtractor |
65de7d20 SS |
7 | from ..utils import ExtractorError, format_field, int_or_none, parse_iso8601 |
8 | from ..utils.traversal import traverse_obj | |
856150d0 YCH |
9 | |
10 | ||
65de7d20 | 11 | def _fmt_url(url): |
e3a3ed8a | 12 | return functools.partial(format_field, template=url, default=None) |
856150d0 | 13 | |
65de7d20 SS |
14 | |
15 | class TelewebionIE(InfoExtractor): | |
16 | _VALID_URL = r'https?://(?:www\.)?telewebion\.com/episode/(?P<id>(?:0x[a-fA-F\d]+|\d+))' | |
17 | _TESTS = [{ | |
18 | 'url': 'http://www.telewebion.com/episode/0x1b3139c/', | |
856150d0 | 19 | 'info_dict': { |
65de7d20 | 20 | 'id': '0x1b3139c', |
856150d0 | 21 | 'ext': 'mp4', |
65de7d20 SS |
22 | 'title': 'قرعهکشی لیگ قهرمانان اروپا', |
23 | 'series': '+ فوتبال', | |
24 | 'series_id': '0x1b2505c', | |
25 | 'channel': 'شبکه 3', | |
26 | 'channel_id': '0x1b1a761', | |
27 | 'channel_url': 'https://telewebion.com/live/tv3', | |
28 | 'timestamp': 1425522414, | |
29 | 'upload_date': '20150305', | |
30 | 'release_timestamp': 1425517020, | |
31 | 'release_date': '20150305', | |
32 | 'duration': 420, | |
856150d0 | 33 | 'view_count': int, |
65de7d20 SS |
34 | 'tags': ['ورزشی', 'لیگ اروپا', 'اروپا'], |
35 | 'thumbnail': 'https://static.telewebion.com/episodeImages/YjFhM2MxMDBkMDNiZTU0MjE5YjQ3ZDY0Mjk1ZDE0ZmUwZWU3OTE3OWRmMDAyODNhNzNkNjdmMWMzMWIyM2NmMA/default', | |
856150d0 | 36 | }, |
65de7d20 SS |
37 | 'skip_download': 'm3u8', |
38 | }, { | |
39 | 'url': 'https://telewebion.com/episode/162175536', | |
40 | 'info_dict': { | |
41 | 'id': '0x9aa9a30', | |
42 | 'ext': 'mp4', | |
43 | 'title': 'کارما یعنی این !', | |
44 | 'series': 'پاورقی', | |
45 | 'series_id': '0x29a7426', | |
46 | 'channel': 'شبکه 2', | |
47 | 'channel_id': '0x1b1a719', | |
48 | 'channel_url': 'https://telewebion.com/live/tv2', | |
49 | 'timestamp': 1699979968, | |
50 | 'upload_date': '20231114', | |
51 | 'release_timestamp': 1699991638, | |
52 | 'release_date': '20231114', | |
53 | 'duration': 78, | |
54 | 'view_count': int, | |
55 | 'tags': ['کلیپ های منتخب', ' کلیپ طنز ', ' کلیپ سیاست ', 'پاورقی', 'ویژه فلسطین'], | |
56 | 'thumbnail': 'https://static.telewebion.com/episodeImages/871e9455-7567-49a5-9648-34c22c197f5f/default', | |
856150d0 | 57 | }, |
65de7d20 SS |
58 | 'skip_download': 'm3u8', |
59 | }] | |
60 | ||
61 | def _call_graphql_api( | |
62 | self, operation, video_id, query, | |
63 | variables: dict[str, tuple[str, str]] | None = None, | |
64 | note='Downloading GraphQL JSON metadata', | |
65 | ): | |
66 | parameters = '' | |
67 | if variables: | |
68 | parameters = ', '.join(f'${name}: {type_}' for name, (type_, _) in variables.items()) | |
69 | parameters = f'({parameters})' | |
70 | ||
71 | result = self._download_json('https://graph.telewebion.com/graphql', video_id, note, data=json.dumps({ | |
72 | 'operationName': operation, | |
73 | 'query': f'query {operation}{parameters} @cacheControl(maxAge: 60) {{{query}\n}}\n', | |
74 | 'variables': {name: value for name, (_, value) in (variables or {}).items()} | |
75 | }, separators=(',', ':')).encode(), headers={ | |
76 | 'Content-Type': 'application/json', | |
77 | 'Accept': 'application/json', | |
78 | }) | |
79 | if not result or traverse_obj(result, 'errors'): | |
80 | message = ', '.join(traverse_obj(result, ('errors', ..., 'message', {str}))) | |
81 | raise ExtractorError(message or 'Unknown GraphQL API error') | |
82 | ||
83 | return result['data'] | |
856150d0 YCH |
84 | |
85 | def _real_extract(self, url): | |
86 | video_id = self._match_id(url) | |
65de7d20 SS |
87 | if not video_id.startswith('0x'): |
88 | video_id = hex(int(video_id)) | |
89 | ||
e3a3ed8a | 90 | episode_data = self._call_graphql_api('getEpisodeDetail', video_id, textwrap.dedent(''' |
65de7d20 SS |
91 | queryEpisode(filter: {EpisodeID: $EpisodeId}, first: 1) { |
92 | title | |
93 | program { | |
94 | ProgramID | |
95 | title | |
96 | } | |
97 | image | |
98 | view_count | |
99 | duration | |
100 | started_at | |
101 | created_at | |
102 | channel { | |
103 | ChannelID | |
104 | name | |
105 | descriptor | |
106 | } | |
107 | tags { | |
108 | name | |
109 | } | |
110 | } | |
111 | '''), {'EpisodeId': ('[ID!]', video_id)}) | |
856150d0 | 112 | |
65de7d20 SS |
113 | info_dict = traverse_obj(episode_data, ('queryEpisode', 0, { |
114 | 'title': ('title', {str}), | |
115 | 'view_count': ('view_count', {int_or_none}), | |
116 | 'duration': ('duration', {int_or_none}), | |
117 | 'tags': ('tags', ..., 'name', {str}), | |
118 | 'release_timestamp': ('started_at', {parse_iso8601}), | |
119 | 'timestamp': ('created_at', {parse_iso8601}), | |
120 | 'series': ('program', 'title', {str}), | |
121 | 'series_id': ('program', 'ProgramID', {str}), | |
122 | 'channel': ('channel', 'name', {str}), | |
123 | 'channel_id': ('channel', 'ChannelID', {str}), | |
124 | 'channel_url': ('channel', 'descriptor', {_fmt_url('https://telewebion.com/live/%s')}), | |
125 | 'thumbnail': ('image', {_fmt_url('https://static.telewebion.com/episodeImages/%s/default')}), | |
126 | 'formats': ( | |
127 | 'channel', 'descriptor', {str}, | |
128 | {_fmt_url(f'https://cdna.telewebion.com/%s/episode/{video_id}/playlist.m3u8')}, | |
e3a3ed8a | 129 | {functools.partial(self._extract_m3u8_formats, video_id=video_id, ext='mp4', m3u8_id='hls')}), |
65de7d20 SS |
130 | })) |
131 | info_dict['id'] = video_id | |
132 | return info_dict |