freetv.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import itertools
  2. import re
  3. from .common import InfoExtractor
  4. from ..utils import int_or_none, traverse_obj, urlencode_postdata
  5. class FreeTvBaseIE(InfoExtractor):
  6. def _get_api_response(self, content_id, resource_type, postdata):
  7. return self._download_json(
  8. 'https://www.freetv.com/wordpress/wp-admin/admin-ajax.php',
  9. content_id, data=urlencode_postdata(postdata),
  10. note=f'Downloading {content_id} {resource_type} JSON')['data']
  11. class FreeTvMoviesIE(FreeTvBaseIE):
  12. _VALID_URL = r'https?://(?:www\.)?freetv\.com/peliculas/(?P<id>[^/]+)'
  13. _TESTS = [{
  14. 'url': 'https://www.freetv.com/peliculas/atrapame-si-puedes/',
  15. 'md5': 'dc62d5abf0514726640077cd1591aa92',
  16. 'info_dict': {
  17. 'id': '428021',
  18. 'title': 'Atrápame Si Puedes',
  19. 'description': 'md5:ca63bc00898aeb2f64ec87c6d3a5b982',
  20. 'ext': 'mp4',
  21. },
  22. }, {
  23. 'url': 'https://www.freetv.com/peliculas/monstruoso/',
  24. 'md5': '509c15c68de41cb708d1f92d071f20aa',
  25. 'info_dict': {
  26. 'id': '377652',
  27. 'title': 'Monstruoso',
  28. 'description': 'md5:333fc19ee327b457b980e54a911ea4a3',
  29. 'ext': 'mp4',
  30. },
  31. }]
  32. def _extract_video(self, content_id, action='olyott_video_play'):
  33. api_response = self._get_api_response(content_id, 'video', {
  34. 'action': action,
  35. 'contentID': content_id,
  36. })
  37. video_id, video_url = api_response['displayMeta']['contentID'], api_response['displayMeta']['streamURLVideo']
  38. formats, subtitles = self._extract_m3u8_formats_and_subtitles(video_url, video_id, 'mp4')
  39. return {
  40. 'id': video_id,
  41. 'title': traverse_obj(api_response, ('displayMeta', 'title')),
  42. 'description': traverse_obj(api_response, ('displayMeta', 'desc')),
  43. 'formats': formats,
  44. 'subtitles': subtitles,
  45. }
  46. def _real_extract(self, url):
  47. display_id = self._match_id(url)
  48. webpage = self._download_webpage(url, display_id)
  49. return self._extract_video(
  50. self._search_regex((
  51. r'class=["\'][^>]+postid-(?P<video_id>\d+)',
  52. r'<link[^>]+freetv.com/\?p=(?P<video_id>\d+)',
  53. r'<div[^>]+data-params=["\'][^>]+post_id=(?P<video_id>\d+)',
  54. ), webpage, 'video id', group='video_id'))
  55. class FreeTvIE(FreeTvBaseIE):
  56. IE_NAME = 'freetv:series'
  57. _VALID_URL = r'https?://(?:www\.)?freetv\.com/series/(?P<id>[^/]+)'
  58. _TESTS = [{
  59. 'url': 'https://www.freetv.com/series/el-detective-l/',
  60. 'info_dict': {
  61. 'id': 'el-detective-l',
  62. 'title': 'El Detective L',
  63. 'description': 'md5:f9f1143bc33e9856ecbfcbfb97a759be',
  64. },
  65. 'playlist_count': 24,
  66. }, {
  67. 'url': 'https://www.freetv.com/series/esmeraldas/',
  68. 'info_dict': {
  69. 'id': 'esmeraldas',
  70. 'title': 'Esmeraldas',
  71. 'description': 'md5:43d7ec45bd931d8268a4f5afaf4c77bf',
  72. },
  73. 'playlist_count': 62,
  74. }, {
  75. 'url': 'https://www.freetv.com/series/las-aventuras-de-leonardo/',
  76. 'info_dict': {
  77. 'id': 'las-aventuras-de-leonardo',
  78. 'title': 'Las Aventuras de Leonardo',
  79. 'description': 'md5:0c47130846c141120a382aca059288f6',
  80. },
  81. 'playlist_count': 13,
  82. },
  83. ]
  84. def _extract_series_season(self, season_id, series_title):
  85. episodes = self._get_api_response(season_id, 'series', {
  86. 'contentID': season_id,
  87. 'action': 'olyott_get_dynamic_series_content',
  88. 'type': 'list',
  89. 'perPage': '1000',
  90. })['1']
  91. for episode in episodes:
  92. video_id = str(episode['contentID'])
  93. formats, subtitles = self._extract_m3u8_formats_and_subtitles(episode['streamURL'], video_id, 'mp4')
  94. yield {
  95. 'id': video_id,
  96. 'title': episode.get('fullTitle'),
  97. 'description': episode.get('description'),
  98. 'formats': formats,
  99. 'subtitles': subtitles,
  100. 'thumbnail': episode.get('thumbnail'),
  101. 'series': series_title,
  102. 'series_id': traverse_obj(episode, ('contentMeta', 'displayMeta', 'seriesID')),
  103. 'season_id': traverse_obj(episode, ('contentMeta', 'displayMeta', 'seasonID')),
  104. 'season_number': traverse_obj(
  105. episode, ('contentMeta', 'displayMeta', 'seasonNum'), expected_type=int_or_none),
  106. 'episode_number': traverse_obj(
  107. episode, ('contentMeta', 'displayMeta', 'episodeNum'), expected_type=int_or_none),
  108. }
  109. def _real_extract(self, url):
  110. display_id = self._match_id(url)
  111. webpage = self._download_webpage(url, display_id)
  112. title = self._html_search_regex(
  113. r'<h1[^>]+class=["\']synopis[^>]>(?P<title>[^<]+)', webpage, 'title', group='title', fatal=False)
  114. description = self._html_search_regex(
  115. r'<div[^>]+class=["\']+synopis content[^>]><p>(?P<description>[^<]+)',
  116. webpage, 'description', group='description', fatal=False)
  117. return self.playlist_result(
  118. itertools.chain.from_iterable(
  119. self._extract_series_season(season_id, title)
  120. for season_id in re.findall(r'<option[^>]+value=["\'](\d+)["\']', webpage)),
  121. display_id, title, description)