telequebec.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. from .common import InfoExtractor
  2. from ..compat import compat_str
  3. from ..utils import (
  4. int_or_none,
  5. smuggle_url,
  6. try_get,
  7. unified_timestamp,
  8. )
  9. class TeleQuebecBaseIE(InfoExtractor):
  10. BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/%s_default/index.html?videoId=%s'
  11. @staticmethod
  12. def _brightcove_result(brightcove_id, player_id, account_id='6150020952001'):
  13. return {
  14. '_type': 'url_transparent',
  15. 'url': smuggle_url(TeleQuebecBaseIE.BRIGHTCOVE_URL_TEMPLATE % (account_id, player_id, brightcove_id), {'geo_countries': ['CA']}),
  16. 'ie_key': 'BrightcoveNew',
  17. }
  18. class TeleQuebecIE(TeleQuebecBaseIE):
  19. _VALID_URL = r'''(?x)
  20. https?://
  21. (?:
  22. zonevideo\.telequebec\.tv/media|
  23. coucou\.telequebec\.tv/videos
  24. )/(?P<id>\d+)
  25. '''
  26. _TESTS = [{
  27. # available till 01.01.2023
  28. 'url': 'http://zonevideo.telequebec.tv/media/37578/un-petit-choc-et-puis-repart/un-chef-a-la-cabane',
  29. 'info_dict': {
  30. 'id': '6155972771001',
  31. 'ext': 'mp4',
  32. 'title': 'Un petit choc et puis repart!',
  33. 'description': 'md5:b04a7e6b3f74e32d7b294cffe8658374',
  34. 'timestamp': 1589262469,
  35. 'uploader_id': '6150020952001',
  36. 'upload_date': '20200512',
  37. },
  38. 'add_ie': ['BrightcoveNew'],
  39. }, {
  40. 'url': 'https://zonevideo.telequebec.tv/media/55267/le-soleil/passe-partout',
  41. 'info_dict': {
  42. 'id': '6167180337001',
  43. 'ext': 'mp4',
  44. 'title': 'Le soleil',
  45. 'description': 'md5:64289c922a8de2abbe99c354daffde02',
  46. 'uploader_id': '6150020952001',
  47. 'upload_date': '20200625',
  48. 'timestamp': 1593090307,
  49. },
  50. 'add_ie': ['BrightcoveNew'],
  51. }, {
  52. # no description
  53. 'url': 'http://zonevideo.telequebec.tv/media/30261',
  54. 'only_matching': True,
  55. }, {
  56. 'url': 'https://coucou.telequebec.tv/videos/41788/idee-de-genie/l-heure-du-bain',
  57. 'only_matching': True,
  58. }]
  59. def _real_extract(self, url):
  60. media_id = self._match_id(url)
  61. media = self._download_json(
  62. 'https://mnmedias.api.telequebec.tv/api/v3/media/' + media_id,
  63. media_id)['media']
  64. source_id = next(source_info['sourceId'] for source_info in media['streamInfos'] if source_info.get('source') == 'Brightcove')
  65. info = self._brightcove_result(source_id, '22gPKdt7f')
  66. product = media.get('product') or {}
  67. season = product.get('season') or {}
  68. info.update({
  69. 'description': try_get(media, lambda x: x['descriptions'][-1]['text'], compat_str),
  70. 'series': try_get(season, lambda x: x['serie']['titre']),
  71. 'season': season.get('name'),
  72. 'season_number': int_or_none(season.get('seasonNo')),
  73. 'episode': product.get('titre'),
  74. 'episode_number': int_or_none(product.get('episodeNo')),
  75. })
  76. return info
  77. class TeleQuebecSquatIE(InfoExtractor):
  78. _VALID_URL = r'https://squat\.telequebec\.tv/videos/(?P<id>\d+)'
  79. _TESTS = [{
  80. 'url': 'https://squat.telequebec.tv/videos/9314',
  81. 'info_dict': {
  82. 'id': 'd59ae78112d542e793d83cc9d3a5b530',
  83. 'ext': 'mp4',
  84. 'title': 'Poupeflekta',
  85. 'description': 'md5:2f0718f8d2f8fece1646ee25fb7bce75',
  86. 'duration': 1351,
  87. 'timestamp': 1569057600,
  88. 'upload_date': '20190921',
  89. 'series': 'Miraculous : Les Aventures de Ladybug et Chat Noir',
  90. 'season': 'Saison 3',
  91. 'season_number': 3,
  92. 'episode_number': 57,
  93. },
  94. 'params': {
  95. 'skip_download': True,
  96. },
  97. }]
  98. def _real_extract(self, url):
  99. video_id = self._match_id(url)
  100. video = self._download_json(
  101. 'https://squat.api.telequebec.tv/v1/videos/%s' % video_id,
  102. video_id)
  103. media_id = video['sourceId']
  104. return {
  105. '_type': 'url_transparent',
  106. 'url': 'http://zonevideo.telequebec.tv/media/%s' % media_id,
  107. 'ie_key': TeleQuebecIE.ie_key(),
  108. 'id': media_id,
  109. 'title': video.get('titre'),
  110. 'description': video.get('description'),
  111. 'timestamp': unified_timestamp(video.get('datePublication')),
  112. 'series': video.get('container'),
  113. 'season': video.get('saison'),
  114. 'season_number': int_or_none(video.get('noSaison')),
  115. 'episode_number': int_or_none(video.get('episode')),
  116. }
  117. class TeleQuebecEmissionIE(InfoExtractor):
  118. _VALID_URL = r'''(?x)
  119. https?://
  120. (?:
  121. [^/]+\.telequebec\.tv/emissions/|
  122. (?:www\.)?telequebec\.tv/
  123. )
  124. (?P<id>[^?#&]+)
  125. '''
  126. _TESTS = [{
  127. 'url': 'http://lindicemcsween.telequebec.tv/emissions/100430013/des-soins-esthetiques-a-377-d-interets-annuels-ca-vous-tente',
  128. 'info_dict': {
  129. 'id': '6154476028001',
  130. 'ext': 'mp4',
  131. 'title': 'Des soins esthétiques à 377 % d’intérêts annuels, ça vous tente?',
  132. 'description': 'md5:cb4d378e073fae6cce1f87c00f84ae9f',
  133. 'upload_date': '20200505',
  134. 'timestamp': 1588713424,
  135. 'uploader_id': '6150020952001',
  136. },
  137. }, {
  138. 'url': 'http://bancpublic.telequebec.tv/emissions/emission-49/31986/jeunes-meres-sous-pression',
  139. 'only_matching': True,
  140. }, {
  141. 'url': 'http://www.telequebec.tv/masha-et-michka/epi059masha-et-michka-3-053-078',
  142. 'only_matching': True,
  143. }, {
  144. 'url': 'http://www.telequebec.tv/documentaire/bebes-sur-mesure/',
  145. 'only_matching': True,
  146. }]
  147. def _real_extract(self, url):
  148. display_id = self._match_id(url)
  149. webpage = self._download_webpage(url, display_id)
  150. media_id = self._search_regex(
  151. r'mediaId\s*:\s*(?P<id>\d+)', webpage, 'media id')
  152. return self.url_result(
  153. 'http://zonevideo.telequebec.tv/media/' + media_id,
  154. TeleQuebecIE.ie_key())
  155. class TeleQuebecLiveIE(TeleQuebecBaseIE):
  156. _VALID_URL = r'https?://zonevideo\.telequebec\.tv/(?P<id>endirect)'
  157. _TEST = {
  158. 'url': 'http://zonevideo.telequebec.tv/endirect/',
  159. 'info_dict': {
  160. 'id': '6159095684001',
  161. 'ext': 'mp4',
  162. 'title': 're:^Télé-Québec [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
  163. 'is_live': True,
  164. 'description': 'Canal principal de Télé-Québec',
  165. 'uploader_id': '6150020952001',
  166. 'timestamp': 1590439901,
  167. 'upload_date': '20200525',
  168. },
  169. 'params': {
  170. 'skip_download': True,
  171. },
  172. }
  173. def _real_extract(self, url):
  174. return self._brightcove_result('6159095684001', 'skCsmi2Uw')
  175. class TeleQuebecVideoIE(TeleQuebecBaseIE):
  176. _VALID_URL = r'https?://video\.telequebec\.tv/player(?:-live)?/(?P<id>\d+)'
  177. _TESTS = [{
  178. 'url': 'https://video.telequebec.tv/player/31110/stream',
  179. 'info_dict': {
  180. 'id': '6202570652001',
  181. 'ext': 'mp4',
  182. 'title': 'Le coût du véhicule le plus vendu au Canada / Tous les frais liés à la procréation assistée',
  183. 'description': 'md5:685a7e4c450ba777c60adb6e71e41526',
  184. 'upload_date': '20201019',
  185. 'timestamp': 1603115930,
  186. 'uploader_id': '6101674910001',
  187. },
  188. }, {
  189. 'url': 'https://video.telequebec.tv/player-live/28527',
  190. 'only_matching': True,
  191. }]
  192. def _call_api(self, path, video_id):
  193. return self._download_json(
  194. 'http://beacon.playback.api.brightcove.com/telequebec/api/assets/' + path,
  195. video_id, query={'device_layout': 'web', 'device_type': 'web'})['data']
  196. def _real_extract(self, url):
  197. asset_id = self._match_id(url)
  198. asset = self._call_api(asset_id, asset_id)['asset']
  199. stream = self._call_api(
  200. asset_id + '/streams/' + asset['streams'][0]['id'], asset_id)['stream']
  201. stream_url = stream['url']
  202. account_id = try_get(
  203. stream, lambda x: x['video_provider_details']['account_id']) or '6101674910001'
  204. info = self._brightcove_result(stream_url, 'default', account_id)
  205. info.update({
  206. 'description': asset.get('long_description') or asset.get('short_description'),
  207. 'series': asset.get('series_original_name'),
  208. 'season_number': int_or_none(asset.get('season_number')),
  209. 'episode': asset.get('original_name'),
  210. 'episode_number': int_or_none(asset.get('episode_number')),
  211. })
  212. return info