toggle.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import json
  2. import re
  3. from .common import InfoExtractor
  4. from ..utils import (
  5. determine_ext,
  6. float_or_none,
  7. int_or_none,
  8. parse_iso8601,
  9. strip_or_none,
  10. )
  11. class ToggleIE(InfoExtractor):
  12. IE_NAME = 'toggle'
  13. _VALID_URL = r'(?:https?://(?:(?:www\.)?mewatch|video\.toggle)\.sg/(?:en|zh)/(?:[^/]+/){2,}|toggle:)(?P<id>[0-9]+)'
  14. _TESTS = [{
  15. 'url': 'http://www.mewatch.sg/en/series/lion-moms-tif/trailers/lion-moms-premier/343115',
  16. 'info_dict': {
  17. 'id': '343115',
  18. 'ext': 'mp4',
  19. 'title': 'Lion Moms Premiere',
  20. 'description': 'md5:aea1149404bff4d7f7b6da11fafd8e6b',
  21. 'upload_date': '20150910',
  22. 'timestamp': 1441858274,
  23. },
  24. 'params': {
  25. 'skip_download': 'm3u8 download',
  26. },
  27. }, {
  28. 'note': 'DRM-protected video',
  29. 'url': 'http://www.mewatch.sg/en/movies/dug-s-special-mission/341413',
  30. 'info_dict': {
  31. 'id': '341413',
  32. 'ext': 'wvm',
  33. 'title': 'Dug\'s Special Mission',
  34. 'description': 'md5:e86c6f4458214905c1772398fabc93e0',
  35. 'upload_date': '20150827',
  36. 'timestamp': 1440644006,
  37. },
  38. 'params': {
  39. 'skip_download': 'DRM-protected wvm download',
  40. },
  41. }, {
  42. # this also tests correct video id extraction
  43. 'note': 'm3u8 links are geo-restricted, but Android/mp4 is okay',
  44. 'url': 'http://www.mewatch.sg/en/series/28th-sea-games-5-show/28th-sea-games-5-show-ep11/332861',
  45. 'info_dict': {
  46. 'id': '332861',
  47. 'ext': 'mp4',
  48. 'title': '28th SEA Games (5 Show) - Episode 11',
  49. 'description': 'md5:3cd4f5f56c7c3b1340c50a863f896faa',
  50. 'upload_date': '20150605',
  51. 'timestamp': 1433480166,
  52. },
  53. 'params': {
  54. 'skip_download': 'DRM-protected wvm download',
  55. },
  56. 'skip': 'm3u8 links are geo-restricted',
  57. }, {
  58. 'url': 'http://video.toggle.sg/en/clips/seraph-sun-aloysius-will-suddenly-sing-some-old-songs-in-high-pitch-on-set/343331',
  59. 'only_matching': True,
  60. }, {
  61. 'url': 'http://www.mewatch.sg/en/clips/seraph-sun-aloysius-will-suddenly-sing-some-old-songs-in-high-pitch-on-set/343331',
  62. 'only_matching': True,
  63. }, {
  64. 'url': 'http://www.mewatch.sg/zh/series/zero-calling-s2-hd/ep13/336367',
  65. 'only_matching': True,
  66. }, {
  67. 'url': 'http://www.mewatch.sg/en/series/vetri-s2/webisodes/jeeva-is-an-orphan-vetri-s2-webisode-7/342302',
  68. 'only_matching': True,
  69. }, {
  70. 'url': 'http://www.mewatch.sg/en/movies/seven-days/321936',
  71. 'only_matching': True,
  72. }, {
  73. 'url': 'https://www.mewatch.sg/en/tv-show/news/may-2017-cna-singapore-tonight/fri-19-may-2017/512456',
  74. 'only_matching': True,
  75. }, {
  76. 'url': 'http://www.mewatch.sg/en/channels/eleven-plus/401585',
  77. 'only_matching': True,
  78. }]
  79. _API_USER = 'tvpapi_147'
  80. _API_PASS = '11111'
  81. def _real_extract(self, url):
  82. video_id = self._match_id(url)
  83. params = {
  84. 'initObj': {
  85. 'Locale': {
  86. 'LocaleLanguage': '',
  87. 'LocaleCountry': '',
  88. 'LocaleDevice': '',
  89. 'LocaleUserState': 0,
  90. },
  91. 'Platform': 0,
  92. 'SiteGuid': 0,
  93. 'DomainID': '0',
  94. 'UDID': '',
  95. 'ApiUser': self._API_USER,
  96. 'ApiPass': self._API_PASS,
  97. },
  98. 'MediaID': video_id,
  99. 'mediaType': 0,
  100. }
  101. info = self._download_json(
  102. 'http://tvpapi.as.tvinci.com/v2_9/gateways/jsonpostgw.aspx?m=GetMediaInfo',
  103. video_id, 'Downloading video info json', data=json.dumps(params).encode())
  104. title = info['MediaName']
  105. formats = []
  106. for video_file in info.get('Files', []):
  107. video_url, vid_format = video_file.get('URL'), video_file.get('Format')
  108. if not video_url or video_url == 'NA' or not vid_format:
  109. continue
  110. ext = determine_ext(video_url)
  111. vid_format = vid_format.replace(' ', '')
  112. # if geo-restricted, m3u8 is inaccessible, but mp4 is okay
  113. if ext == 'm3u8':
  114. m3u8_formats = self._extract_m3u8_formats(
  115. video_url, video_id, ext='mp4', m3u8_id=vid_format,
  116. note=f'Downloading {vid_format} m3u8 information',
  117. errnote=f'Failed to download {vid_format} m3u8 information',
  118. fatal=False)
  119. for f in m3u8_formats:
  120. # Apple FairPlay Streaming
  121. if '/fpshls/' in f['url']:
  122. continue
  123. formats.append(f)
  124. elif ext == 'mpd':
  125. formats.extend(self._extract_mpd_formats(
  126. video_url, video_id, mpd_id=vid_format,
  127. note=f'Downloading {vid_format} MPD manifest',
  128. errnote=f'Failed to download {vid_format} MPD manifest',
  129. fatal=False))
  130. elif ext == 'ism':
  131. formats.extend(self._extract_ism_formats(
  132. video_url, video_id, ism_id=vid_format,
  133. note=f'Downloading {vid_format} ISM manifest',
  134. errnote=f'Failed to download {vid_format} ISM manifest',
  135. fatal=False))
  136. elif ext == 'mp4':
  137. formats.append({
  138. 'ext': ext,
  139. 'url': video_url,
  140. 'format_id': vid_format,
  141. })
  142. if not formats:
  143. for meta in (info.get('Metas') or []):
  144. if (not self.get_param('allow_unplayable_formats')
  145. and meta.get('Key') == 'Encryption' and meta.get('Value') == '1'):
  146. self.report_drm(video_id)
  147. # Most likely because geo-blocked if no formats and no DRM
  148. thumbnails = []
  149. for picture in info.get('Pictures', []):
  150. if not isinstance(picture, dict):
  151. continue
  152. pic_url = picture.get('URL')
  153. if not pic_url:
  154. continue
  155. thumbnail = {
  156. 'url': pic_url,
  157. }
  158. pic_size = picture.get('PicSize', '')
  159. m = re.search(r'(?P<width>\d+)[xX](?P<height>\d+)', pic_size)
  160. if m:
  161. thumbnail.update({
  162. 'width': int(m.group('width')),
  163. 'height': int(m.group('height')),
  164. })
  165. thumbnails.append(thumbnail)
  166. def counter(prefix):
  167. return int_or_none(
  168. info.get(prefix + 'Counter') or info.get(prefix.lower() + '_counter'))
  169. return {
  170. 'id': video_id,
  171. 'title': title,
  172. 'description': strip_or_none(info.get('Description')),
  173. 'duration': int_or_none(info.get('Duration')),
  174. 'timestamp': parse_iso8601(info.get('CreationDate') or None),
  175. 'average_rating': float_or_none(info.get('Rating')),
  176. 'view_count': counter('View'),
  177. 'like_count': counter('Like'),
  178. 'thumbnails': thumbnails,
  179. 'formats': formats,
  180. }
  181. class MeWatchIE(InfoExtractor):
  182. IE_NAME = 'mewatch'
  183. _VALID_URL = r'https?://(?:(?:www|live)\.)?mewatch\.sg/watch/[^/?#&]+-(?P<id>[0-9]+)'
  184. _TESTS = [{
  185. 'url': 'https://www.mewatch.sg/watch/Recipe-Of-Life-E1-179371',
  186. 'info_dict': {
  187. 'id': '1008625',
  188. 'ext': 'mp4',
  189. 'title': 'Recipe Of Life 味之道',
  190. 'timestamp': 1603306526,
  191. 'description': 'md5:6e88cde8af2068444fc8e1bc3ebf257c',
  192. 'upload_date': '20201021',
  193. },
  194. 'params': {
  195. 'skip_download': 'm3u8 download',
  196. },
  197. }, {
  198. 'url': 'https://www.mewatch.sg/watch/Little-Red-Dot-Detectives-S2-搜密。打卡。小红点-S2-E1-176232',
  199. 'only_matching': True,
  200. }, {
  201. 'url': 'https://www.mewatch.sg/watch/Little-Red-Dot-Detectives-S2-%E6%90%9C%E5%AF%86%E3%80%82%E6%89%93%E5%8D%A1%E3%80%82%E5%B0%8F%E7%BA%A2%E7%82%B9-S2-E1-176232',
  202. 'only_matching': True,
  203. }, {
  204. 'url': 'https://live.mewatch.sg/watch/Recipe-Of-Life-E41-189759',
  205. 'only_matching': True,
  206. }]
  207. def _real_extract(self, url):
  208. item_id = self._match_id(url)
  209. custom_id = self._download_json(
  210. 'https://cdn.mewatch.sg/api/items/' + item_id,
  211. item_id, query={'segments': 'all'})['customId']
  212. return self.url_result(
  213. 'toggle:' + custom_id, ToggleIE.ie_key(), custom_id)