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.

174 lines
6.3 KiB

  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import itertools
  4. import re
  5. from .common import InfoExtractor
  6. from .nexx import NexxIE
  7. from ..compat import compat_str
  8. from ..utils import (
  9. int_or_none,
  10. try_get,
  11. )
  12. class FunkBaseIE(InfoExtractor):
  13. _HEADERS = {
  14. 'Accept': '*/*',
  15. 'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8',
  16. 'authorization': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnROYW1lIjoid2ViYXBwLXYzMSIsInNjb3BlIjoic3RhdGljLWNvbnRlbnQtYXBpLGN1cmF0aW9uLWFwaSxuZXh4LWNvbnRlbnQtYXBpLXYzMSx3ZWJhcHAtYXBpIn0.mbuG9wS9Yf5q6PqgR4fiaRFIagiHk9JhwoKES7ksVX4',
  17. }
  18. _AUTH = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnROYW1lIjoid2ViYXBwLXYzMSIsInNjb3BlIjoic3RhdGljLWNvbnRlbnQtYXBpLGN1cmF0aW9uLWFwaSxuZXh4LWNvbnRlbnQtYXBpLXYzMSx3ZWJhcHAtYXBpIn0.mbuG9wS9Yf5q6PqgR4fiaRFIagiHk9JhwoKES7ksVX4'
  19. @staticmethod
  20. def _make_headers(referer):
  21. headers = FunkBaseIE._HEADERS.copy()
  22. headers['Referer'] = referer
  23. return headers
  24. def _make_url_result(self, video):
  25. return {
  26. '_type': 'url_transparent',
  27. 'url': 'nexx:741:%s' % video['sourceId'],
  28. 'ie_key': NexxIE.ie_key(),
  29. 'id': video['sourceId'],
  30. 'title': video.get('title'),
  31. 'description': video.get('description'),
  32. 'duration': int_or_none(video.get('duration')),
  33. 'season_number': int_or_none(video.get('seasonNr')),
  34. 'episode_number': int_or_none(video.get('episodeNr')),
  35. }
  36. class FunkMixIE(FunkBaseIE):
  37. _VALID_URL = r'https?://(?:www\.)?funk\.net/mix/(?P<id>[^/]+)/(?P<alias>[^/?#&]+)'
  38. _TESTS = [{
  39. 'url': 'https://www.funk.net/mix/59d65d935f8b160001828b5b/die-realste-kifferdoku-aller-zeiten',
  40. 'md5': '8edf617c2f2b7c9847dfda313f199009',
  41. 'info_dict': {
  42. 'id': '123748',
  43. 'ext': 'mp4',
  44. 'title': '"Die realste Kifferdoku aller Zeiten"',
  45. 'description': 'md5:c97160f5bafa8d47ec8e2e461012aa9d',
  46. 'timestamp': 1490274721,
  47. 'upload_date': '20170323',
  48. },
  49. }]
  50. def _real_extract(self, url):
  51. mobj = re.match(self._VALID_URL, url)
  52. mix_id = mobj.group('id')
  53. alias = mobj.group('alias')
  54. lists = self._download_json(
  55. 'https://www.funk.net/api/v3.1/curation/curatedLists/',
  56. mix_id, headers=self._make_headers(url), query={
  57. 'size': 100,
  58. })['_embedded']['curatedListList']
  59. metas = next(
  60. l for l in lists
  61. if mix_id in (l.get('entityId'), l.get('alias')))['videoMetas']
  62. video = next(
  63. meta['videoDataDelegate']
  64. for meta in metas
  65. if try_get(
  66. meta, lambda x: x['videoDataDelegate']['alias'],
  67. compat_str) == alias)
  68. return self._make_url_result(video)
  69. class FunkChannelIE(FunkBaseIE):
  70. _VALID_URL = r'https?://(?:www\.)?funk\.net/channel/(?P<id>[^/]+)/(?P<alias>[^/?#&]+)'
  71. _TESTS = [{
  72. 'url': 'https://www.funk.net/channel/ba/die-lustigsten-instrumente-aus-dem-internet-teil-2',
  73. 'info_dict': {
  74. 'id': '1155821',
  75. 'ext': 'mp4',
  76. 'title': 'Die LUSTIGSTEN INSTRUMENTE aus dem Internet - Teil 2',
  77. 'description': 'md5:a691d0413ef4835588c5b03ded670c1f',
  78. 'timestamp': 1514507395,
  79. 'upload_date': '20171229',
  80. },
  81. 'params': {
  82. 'skip_download': True,
  83. },
  84. }, {
  85. # only available via byIdList API
  86. 'url': 'https://www.funk.net/channel/informr/martin-sonneborn-erklaert-die-eu',
  87. 'info_dict': {
  88. 'id': '205067',
  89. 'ext': 'mp4',
  90. 'title': 'Martin Sonneborn erklärt die EU',
  91. 'description': 'md5:050f74626e4ed87edf4626d2024210c0',
  92. 'timestamp': 1494424042,
  93. 'upload_date': '20170510',
  94. },
  95. 'params': {
  96. 'skip_download': True,
  97. },
  98. }, {
  99. 'url': 'https://www.funk.net/channel/59d5149841dca100012511e3/mein-erster-job-lovemilla-folge-1/lovemilla/',
  100. 'only_matching': True,
  101. }]
  102. def _real_extract(self, url):
  103. mobj = re.match(self._VALID_URL, url)
  104. channel_id = mobj.group('id')
  105. alias = mobj.group('alias')
  106. headers = self._make_headers(url)
  107. video = None
  108. # Id-based channels are currently broken on their side: webplayer
  109. # tries to process them via byChannelAlias endpoint and fails
  110. # predictably.
  111. for page_num in itertools.count():
  112. by_channel_alias = self._download_json(
  113. 'https://www.funk.net/api/v3.1/webapp/videos/byChannelAlias/%s'
  114. % channel_id,
  115. 'Downloading byChannelAlias JSON page %d' % (page_num + 1),
  116. headers=headers, query={
  117. 'filterFsk': 'false',
  118. 'sort': 'creationDate,desc',
  119. 'size': 100,
  120. 'page': page_num,
  121. }, fatal=False)
  122. if not by_channel_alias:
  123. break
  124. video_list = try_get(
  125. by_channel_alias, lambda x: x['_embedded']['videoList'], list)
  126. if not video_list:
  127. break
  128. try:
  129. video = next(r for r in video_list if r.get('alias') == alias)
  130. break
  131. except StopIteration:
  132. pass
  133. if not try_get(
  134. by_channel_alias, lambda x: x['_links']['next']):
  135. break
  136. if not video:
  137. by_id_list = self._download_json(
  138. 'https://www.funk.net/api/v3.0/content/videos/byIdList',
  139. channel_id, 'Downloading byIdList JSON', headers=headers,
  140. query={
  141. 'ids': alias,
  142. }, fatal=False)
  143. if by_id_list:
  144. video = try_get(by_id_list, lambda x: x['result'][0], dict)
  145. if not video:
  146. results = self._download_json(
  147. 'https://www.funk.net/api/v3.0/content/videos/filter',
  148. channel_id, 'Downloading filter JSON', headers=headers, query={
  149. 'channelId': channel_id,
  150. 'size': 100,
  151. })['result']
  152. video = next(r for r in results if r.get('alias') == alias)
  153. return self._make_url_result(video)