toutv.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import json
  2. from .radiocanada import RadioCanadaIE
  3. from ..networking.exceptions import HTTPError
  4. from ..utils import (
  5. ExtractorError,
  6. int_or_none,
  7. merge_dicts,
  8. )
  9. class TouTvIE(RadioCanadaIE): # XXX: Do not subclass from concrete IE
  10. _NETRC_MACHINE = 'toutv'
  11. IE_NAME = 'tou.tv'
  12. _VALID_URL = r'https?://ici\.tou\.tv/(?P<id>[a-zA-Z0-9_-]+(?:/S[0-9]+[EC][0-9]+)?)'
  13. _TESTS = [{
  14. 'url': 'http://ici.tou.tv/garfield-tout-court/S2015E17',
  15. 'info_dict': {
  16. 'id': '122017',
  17. 'ext': 'mp4',
  18. 'title': 'Saison 2015 Épisode 17',
  19. 'description': 'La photo de famille 2',
  20. 'upload_date': '20100717',
  21. },
  22. 'params': {
  23. # m3u8 download
  24. 'skip_download': True,
  25. },
  26. 'skip': '404 Not Found',
  27. }, {
  28. 'url': 'http://ici.tou.tv/hackers',
  29. 'only_matching': True,
  30. }, {
  31. 'url': 'https://ici.tou.tv/l-age-adulte/S01C501',
  32. 'only_matching': True,
  33. }]
  34. _CLIENT_KEY = '90505c8d-9c34-4f34-8da1-3a85bdc6d4f4'
  35. def _perform_login(self, username, password):
  36. try:
  37. self._access_token = self._download_json(
  38. 'https://services.radio-canada.ca/toutv/profiling/accounts/login',
  39. None, 'Logging in', data=json.dumps({
  40. 'ClientId': self._CLIENT_KEY,
  41. 'ClientSecret': '34026772-244b-49b6-8b06-317b30ac9a20',
  42. 'Email': username,
  43. 'Password': password,
  44. 'Scope': 'id.write media-validation.read',
  45. }).encode(), headers={
  46. 'Authorization': 'client-key ' + self._CLIENT_KEY,
  47. 'Content-Type': 'application/json;charset=utf-8',
  48. })['access_token']
  49. except ExtractorError as e:
  50. if isinstance(e.cause, HTTPError) and e.cause.status == 401:
  51. error = self._parse_json(e.cause.response.read().decode(), None)['Message']
  52. raise ExtractorError(error, expected=True)
  53. raise
  54. self._claims = self._call_api('validation/v2/getClaims')['claims']
  55. def _real_extract(self, url):
  56. path = self._match_id(url)
  57. metadata = self._download_json(
  58. f'https://services.radio-canada.ca/toutv/presentation/{path}', path, query={
  59. 'client_key': self._CLIENT_KEY,
  60. 'device': 'web',
  61. 'version': 4,
  62. })
  63. # IsDrm does not necessarily mean the video is DRM protected (see
  64. # https://github.com/ytdl-org/youtube-dl/issues/13994).
  65. if not self.get_param('allow_unplayable_formats') and metadata.get('IsDrm'):
  66. self.report_warning('This video is probably DRM protected.', path)
  67. video_id = metadata['IdMedia']
  68. details = metadata['Details']
  69. return merge_dicts({
  70. 'id': video_id,
  71. 'title': details.get('OriginalTitle'),
  72. 'description': details.get('Description'),
  73. 'thumbnail': details.get('ImageUrl'),
  74. 'duration': int_or_none(details.get('LengthInSeconds')),
  75. 'series': metadata.get('ProgramTitle'),
  76. 'season_number': int_or_none(metadata.get('SeasonNumber')),
  77. 'season': metadata.get('SeasonTitle'),
  78. 'episode_number': int_or_none(metadata.get('EpisodeNumber')),
  79. 'episode': metadata.get('EpisodeTitle'),
  80. }, self._extract_info(metadata.get('AppCode', 'toutv'), video_id))