You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

109 lines
3.6 KiB

  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import json
  4. import re
  5. from .common import InfoExtractor
  6. from ..compat import compat_HTTPError
  7. from ..utils import (
  8. clean_html,
  9. ExtractorError,
  10. int_or_none,
  11. PUTRequest,
  12. )
  13. class PlayPlusTVIE(InfoExtractor):
  14. _VALID_URL = r'https?://(?:www\.)?playplus\.(?:com|tv)/VOD/(?P<project_id>[0-9]+)/(?P<id>[0-9a-f]{32})'
  15. _TEST = {
  16. 'url': 'https://www.playplus.tv/VOD/7572/db8d274a5163424e967f35a30ddafb8e',
  17. 'md5': 'd078cb89d7ab6b9df37ce23c647aef72',
  18. 'info_dict': {
  19. 'id': 'db8d274a5163424e967f35a30ddafb8e',
  20. 'ext': 'mp4',
  21. 'title': 'Capítulo 179 - Final',
  22. 'description': 'md5:01085d62d8033a1e34121d3c3cabc838',
  23. 'timestamp': 1529992740,
  24. 'upload_date': '20180626',
  25. },
  26. 'skip': 'Requires account credential',
  27. }
  28. _NETRC_MACHINE = 'playplustv'
  29. _GEO_COUNTRIES = ['BR']
  30. _token = None
  31. _profile_id = None
  32. def _call_api(self, resource, video_id=None, query=None):
  33. return self._download_json('https://api.playplus.tv/api/media/v2/get' + resource, video_id, headers={
  34. 'Authorization': 'Bearer ' + self._token,
  35. }, query=query)
  36. def _real_initialize(self):
  37. email, password = self._get_login_info()
  38. if email is None:
  39. self.raise_login_required()
  40. req = PUTRequest(
  41. 'https://api.playplus.tv/api/web/login', json.dumps({
  42. 'email': email,
  43. 'password': password,
  44. }).encode(), {
  45. 'Content-Type': 'application/json; charset=utf-8',
  46. })
  47. try:
  48. self._token = self._download_json(req, None)['token']
  49. except ExtractorError as e:
  50. if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
  51. raise ExtractorError(self._parse_json(
  52. e.cause.read(), None)['errorMessage'], expected=True)
  53. raise
  54. self._profile = self._call_api('Profiles')['list'][0]['_id']
  55. def _real_extract(self, url):
  56. project_id, media_id = re.match(self._VALID_URL, url).groups()
  57. media = self._call_api(
  58. 'Media', media_id, {
  59. 'profileId': self._profile,
  60. 'projectId': project_id,
  61. 'mediaId': media_id,
  62. })['obj']
  63. title = media['title']
  64. formats = []
  65. for f in media.get('files', []):
  66. f_url = f.get('url')
  67. if not f_url:
  68. continue
  69. file_info = f.get('fileInfo') or {}
  70. formats.append({
  71. 'url': f_url,
  72. 'width': int_or_none(file_info.get('width')),
  73. 'height': int_or_none(file_info.get('height')),
  74. })
  75. self._sort_formats(formats)
  76. thumbnails = []
  77. for thumb in media.get('thumbs', []):
  78. thumb_url = thumb.get('url')
  79. if not thumb_url:
  80. continue
  81. thumbnails.append({
  82. 'url': thumb_url,
  83. 'width': int_or_none(thumb.get('width')),
  84. 'height': int_or_none(thumb.get('height')),
  85. })
  86. return {
  87. 'id': media_id,
  88. 'title': title,
  89. 'formats': formats,
  90. 'thumbnails': thumbnails,
  91. 'description': clean_html(media.get('description')) or media.get('shortDescription'),
  92. 'timestamp': int_or_none(media.get('publishDate'), 1000),
  93. 'view_count': int_or_none(media.get('numberOfViews')),
  94. 'comment_count': int_or_none(media.get('numberOfComments')),
  95. 'tags': media.get('tags'),
  96. }