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.

161 lines
5.8 KiB

  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import re
  4. from .common import InfoExtractor
  5. from ..compat import compat_str
  6. from ..utils import (
  7. int_or_none,
  8. js_to_json,
  9. strip_or_none,
  10. try_get,
  11. unescapeHTML,
  12. unified_timestamp,
  13. )
  14. class WatchBoxIE(InfoExtractor):
  15. _VALID_URL = r'https?://(?:www\.)?watchbox\.de/(?P<kind>serien|filme)/(?:[^/]+/)*[^/]+-(?P<id>\d+)'
  16. _TESTS = [{
  17. # film
  18. 'url': 'https://www.watchbox.de/filme/free-jimmy-12325.html',
  19. 'info_dict': {
  20. 'id': '341368',
  21. 'ext': 'mp4',
  22. 'title': 'Free Jimmy',
  23. 'description': 'md5:bcd8bafbbf9dc0ef98063d344d7cc5f6',
  24. 'thumbnail': r're:^https?://.*\.jpg$',
  25. 'duration': 4890,
  26. 'age_limit': 16,
  27. 'release_year': 2009,
  28. },
  29. 'params': {
  30. 'format': 'bestvideo',
  31. 'skip_download': True,
  32. },
  33. 'expected_warnings': ['Failed to download m3u8 information'],
  34. }, {
  35. # episode
  36. 'url': 'https://www.watchbox.de/serien/ugly-americans-12231/staffel-1/date-in-der-hoelle-328286.html',
  37. 'info_dict': {
  38. 'id': '328286',
  39. 'ext': 'mp4',
  40. 'title': 'S01 E01 - Date in der Hölle',
  41. 'description': 'md5:2f31c74a8186899f33cb5114491dae2b',
  42. 'thumbnail': r're:^https?://.*\.jpg$',
  43. 'duration': 1291,
  44. 'age_limit': 12,
  45. 'release_year': 2010,
  46. 'series': 'Ugly Americans',
  47. 'season_number': 1,
  48. 'episode': 'Date in der Hölle',
  49. 'episode_number': 1,
  50. },
  51. 'params': {
  52. 'format': 'bestvideo',
  53. 'skip_download': True,
  54. },
  55. 'expected_warnings': ['Failed to download m3u8 information'],
  56. }, {
  57. 'url': 'https://www.watchbox.de/serien/ugly-americans-12231/staffel-2/der-ring-des-powers-328270',
  58. 'only_matching': True,
  59. }]
  60. def _real_extract(self, url):
  61. mobj = re.match(self._VALID_URL, url)
  62. kind, video_id = mobj.group('kind', 'id')
  63. webpage = self._download_webpage(url, video_id)
  64. player_config = self._parse_json(
  65. self._search_regex(
  66. r'data-player-conf=(["\'])(?P<data>{.+?})\1', webpage,
  67. 'player config', default='{}', group='data'),
  68. video_id, transform_source=unescapeHTML, fatal=False)
  69. if not player_config:
  70. player_config = self._parse_json(
  71. self._search_regex(
  72. r'playerConf\s*=\s*({.+?})\s*;', webpage, 'player config',
  73. default='{}'),
  74. video_id, transform_source=js_to_json, fatal=False) or {}
  75. source = player_config.get('source') or {}
  76. video_id = compat_str(source.get('videoId') or video_id)
  77. devapi = self._download_json(
  78. 'http://api.watchbox.de/devapi/id/%s' % video_id, video_id, query={
  79. 'format': 'json',
  80. 'apikey': 'hbbtv',
  81. }, fatal=False)
  82. item = try_get(devapi, lambda x: x['items'][0], dict) or {}
  83. title = item.get('title') or try_get(
  84. item, lambda x: x['movie']['headline_movie'],
  85. compat_str) or source['title']
  86. formats = []
  87. hls_url = item.get('media_videourl_hls') or source.get('hls')
  88. if hls_url:
  89. formats.extend(self._extract_m3u8_formats(
  90. hls_url, video_id, 'mp4', entry_protocol='m3u8_native',
  91. m3u8_id='hls', fatal=False))
  92. dash_url = item.get('media_videourl_wv') or source.get('dash')
  93. if dash_url:
  94. formats.extend(self._extract_mpd_formats(
  95. dash_url, video_id, mpd_id='dash', fatal=False))
  96. mp4_url = item.get('media_videourl')
  97. if mp4_url:
  98. formats.append({
  99. 'url': mp4_url,
  100. 'format_id': 'mp4',
  101. 'width': int_or_none(item.get('width')),
  102. 'height': int_or_none(item.get('height')),
  103. 'tbr': int_or_none(item.get('bitrate')),
  104. })
  105. self._sort_formats(formats)
  106. description = strip_or_none(item.get('descr'))
  107. thumbnail = item.get('media_content_thumbnail_large') or source.get('poster') or item.get('media_thumbnail')
  108. duration = int_or_none(item.get('media_length') or source.get('length'))
  109. timestamp = unified_timestamp(item.get('pubDate'))
  110. view_count = int_or_none(item.get('media_views'))
  111. age_limit = int_or_none(try_get(item, lambda x: x['movie']['fsk']))
  112. release_year = int_or_none(try_get(item, lambda x: x['movie']['rel_year']))
  113. info = {
  114. 'id': video_id,
  115. 'title': title,
  116. 'description': description,
  117. 'thumbnail': thumbnail,
  118. 'duration': duration,
  119. 'timestamp': timestamp,
  120. 'view_count': view_count,
  121. 'age_limit': age_limit,
  122. 'release_year': release_year,
  123. 'formats': formats,
  124. }
  125. if kind.lower() == 'serien':
  126. series = try_get(
  127. item, lambda x: x['special']['title'],
  128. compat_str) or source.get('format')
  129. season_number = int_or_none(self._search_regex(
  130. r'^S(\d{1,2})\s*E\d{1,2}', title, 'season number',
  131. default=None) or self._search_regex(
  132. r'/staffel-(\d+)/', url, 'season number', default=None))
  133. episode = source.get('title')
  134. episode_number = int_or_none(self._search_regex(
  135. r'^S\d{1,2}\s*E(\d{1,2})', title, 'episode number',
  136. default=None))
  137. info.update({
  138. 'series': series,
  139. 'season_number': season_number,
  140. 'episode': episode,
  141. 'episode_number': episode_number,
  142. })
  143. return info