filmon.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. from .common import InfoExtractor
  2. from ..compat import (
  3. compat_str,
  4. compat_HTTPError,
  5. )
  6. from ..utils import (
  7. qualities,
  8. strip_or_none,
  9. int_or_none,
  10. ExtractorError,
  11. )
  12. class FilmOnIE(InfoExtractor):
  13. IE_NAME = 'filmon'
  14. _VALID_URL = r'(?:https?://(?:www\.)?filmon\.com/vod/view/|filmon:)(?P<id>\d+)'
  15. _TESTS = [{
  16. 'url': 'https://www.filmon.com/vod/view/24869-0-plan-9-from-outer-space',
  17. 'info_dict': {
  18. 'id': '24869',
  19. 'ext': 'mp4',
  20. 'title': 'Plan 9 From Outer Space',
  21. 'description': 'Dead human, zombies and vampires',
  22. },
  23. }, {
  24. 'url': 'https://www.filmon.com/vod/view/2825-1-popeye-series-1',
  25. 'info_dict': {
  26. 'id': '2825',
  27. 'title': 'Popeye Series 1',
  28. 'description': 'The original series of Popeye.',
  29. },
  30. 'playlist_mincount': 8,
  31. }]
  32. def _real_extract(self, url):
  33. video_id = self._match_id(url)
  34. try:
  35. response = self._download_json(
  36. 'https://www.filmon.com/api/vod/movie?id=%s' % video_id,
  37. video_id)['response']
  38. except ExtractorError as e:
  39. if isinstance(e.cause, compat_HTTPError):
  40. errmsg = self._parse_json(e.cause.read().decode(), video_id)['reason']
  41. raise ExtractorError('%s said: %s' % (self.IE_NAME, errmsg), expected=True)
  42. raise
  43. title = response['title']
  44. description = strip_or_none(response.get('description'))
  45. if response.get('type_id') == 1:
  46. entries = [self.url_result('filmon:' + episode_id) for episode_id in response.get('episodes', [])]
  47. return self.playlist_result(entries, video_id, title, description)
  48. QUALITY = qualities(('low', 'high'))
  49. formats = []
  50. for format_id, stream in response.get('streams', {}).items():
  51. stream_url = stream.get('url')
  52. if not stream_url:
  53. continue
  54. formats.append({
  55. 'format_id': format_id,
  56. 'url': stream_url,
  57. 'ext': 'mp4',
  58. 'quality': QUALITY(stream.get('quality')),
  59. 'protocol': 'm3u8_native',
  60. })
  61. thumbnails = []
  62. poster = response.get('poster', {})
  63. thumbs = poster.get('thumbs', {})
  64. thumbs['poster'] = poster
  65. for thumb_id, thumb in thumbs.items():
  66. thumb_url = thumb.get('url')
  67. if not thumb_url:
  68. continue
  69. thumbnails.append({
  70. 'id': thumb_id,
  71. 'url': thumb_url,
  72. 'width': int_or_none(thumb.get('width')),
  73. 'height': int_or_none(thumb.get('height')),
  74. })
  75. return {
  76. 'id': video_id,
  77. 'title': title,
  78. 'formats': formats,
  79. 'description': description,
  80. 'thumbnails': thumbnails,
  81. }
  82. class FilmOnChannelIE(InfoExtractor):
  83. IE_NAME = 'filmon:channel'
  84. _VALID_URL = r'https?://(?:www\.)?filmon\.com/(?:tv|channel)/(?P<id>[a-z0-9-]+)'
  85. _TESTS = [{
  86. # VOD
  87. 'url': 'http://www.filmon.com/tv/sports-haters',
  88. 'info_dict': {
  89. 'id': '4190',
  90. 'ext': 'mp4',
  91. 'title': 'Sports Haters',
  92. 'description': 'md5:dabcb4c1d9cfc77085612f1a85f8275d',
  93. },
  94. }, {
  95. # LIVE
  96. 'url': 'https://www.filmon.com/channel/filmon-sports',
  97. 'only_matching': True,
  98. }, {
  99. 'url': 'https://www.filmon.com/tv/2894',
  100. 'only_matching': True,
  101. }]
  102. _THUMBNAIL_RES = [
  103. ('logo', 56, 28),
  104. ('big_logo', 106, 106),
  105. ('extra_big_logo', 300, 300),
  106. ]
  107. def _real_extract(self, url):
  108. channel_id = self._match_id(url)
  109. try:
  110. channel_data = self._download_json(
  111. 'http://www.filmon.com/api-v2/channel/' + channel_id, channel_id)['data']
  112. except ExtractorError as e:
  113. if isinstance(e.cause, compat_HTTPError):
  114. errmsg = self._parse_json(e.cause.read().decode(), channel_id)['message']
  115. raise ExtractorError('%s said: %s' % (self.IE_NAME, errmsg), expected=True)
  116. raise
  117. channel_id = compat_str(channel_data['id'])
  118. is_live = not channel_data.get('is_vod') and not channel_data.get('is_vox')
  119. title = channel_data['title']
  120. QUALITY = qualities(('low', 'high'))
  121. formats = []
  122. for stream in channel_data.get('streams', []):
  123. stream_url = stream.get('url')
  124. if not stream_url:
  125. continue
  126. if not is_live:
  127. formats.extend(self._extract_wowza_formats(
  128. stream_url, channel_id, skip_protocols=['dash', 'rtmp', 'rtsp']))
  129. continue
  130. quality = stream.get('quality')
  131. formats.append({
  132. 'format_id': quality,
  133. # this is an m3u8 stream, but we are deliberately not using _extract_m3u8_formats
  134. # because it doesn't have bitrate variants anyway
  135. 'url': stream_url,
  136. 'ext': 'mp4',
  137. 'quality': QUALITY(quality),
  138. })
  139. thumbnails = []
  140. for name, width, height in self._THUMBNAIL_RES:
  141. thumbnails.append({
  142. 'id': name,
  143. 'url': 'http://static.filmon.com/assets/channels/%s/%s.png' % (channel_id, name),
  144. 'width': width,
  145. 'height': height,
  146. })
  147. return {
  148. 'id': channel_id,
  149. 'display_id': channel_data.get('alias'),
  150. 'title': title,
  151. 'description': channel_data.get('description'),
  152. 'thumbnails': thumbnails,
  153. 'formats': formats,
  154. 'is_live': is_live,
  155. }