vier.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import re
  4. import itertools
  5. from .common import InfoExtractor
  6. from ..utils import (
  7. urlencode_postdata,
  8. int_or_none,
  9. unified_strdate,
  10. )
  11. class VierIE(InfoExtractor):
  12. IE_NAME = 'vier'
  13. IE_DESC = 'vier.be and vijf.be'
  14. _VALID_URL = r'https?://(?:www\.)?(?P<site>vier|vijf)\.be/(?:[^/]+/videos/(?P<display_id>[^/]+)(?:/(?P<id>\d+))?|video/v3/embed/(?P<embed_id>\d+))'
  15. _NETRC_MACHINE = 'vier'
  16. _TESTS = [{
  17. 'url': 'http://www.vier.be/planb/videos/het-wordt-warm-de-moestuin/16129',
  18. 'md5': 'e4ae2054a6b040ef1e289e20d111b46e',
  19. 'info_dict': {
  20. 'id': '16129',
  21. 'display_id': 'het-wordt-warm-de-moestuin',
  22. 'ext': 'mp4',
  23. 'title': 'Het wordt warm in De Moestuin',
  24. 'description': 'De vele uren werk eisen hun tol. Wim droomt van assistentie...',
  25. 'upload_date': '20121025',
  26. },
  27. }, {
  28. 'url': 'http://www.vijf.be/temptationisland/videos/zo-grappig-temptation-island-hosts-moeten-kiezen-tussen-onmogelijke-dilemmas/2561614',
  29. 'info_dict': {
  30. 'id': '2561614',
  31. 'display_id': 'zo-grappig-temptation-island-hosts-moeten-kiezen-tussen-onmogelijke-dilemmas',
  32. 'ext': 'mp4',
  33. 'title': 'md5:84f45fe48b8c1fa296a7f6d208d080a7',
  34. 'description': 'md5:0356d4981e58b8cbee19355cbd51a8fe',
  35. 'upload_date': '20170228',
  36. },
  37. 'params': {
  38. 'skip_download': True,
  39. },
  40. }, {
  41. 'url': 'http://www.vier.be/janigaat/videos/jani-gaat-naar-tokio-aflevering-4/2674839',
  42. 'info_dict': {
  43. 'id': '2674839',
  44. 'display_id': 'jani-gaat-naar-tokio-aflevering-4',
  45. 'ext': 'mp4',
  46. 'title': 'Jani gaat naar Tokio - Aflevering 4',
  47. 'description': 'md5:aa8d611541db6ae9e863125704511f88',
  48. 'upload_date': '20170501',
  49. 'episode_number': 4,
  50. },
  51. 'params': {
  52. 'skip_download': True,
  53. },
  54. 'skip': 'Requires account credentials',
  55. }, {
  56. # Requires account credentials but bypassed extraction via v3/embed page
  57. # without metadata
  58. 'url': 'http://www.vier.be/janigaat/videos/jani-gaat-naar-tokio-aflevering-4/2674839',
  59. 'info_dict': {
  60. 'id': '2674839',
  61. 'display_id': 'jani-gaat-naar-tokio-aflevering-4',
  62. 'ext': 'mp4',
  63. 'title': 'jani-gaat-naar-tokio-aflevering-4',
  64. },
  65. 'params': {
  66. 'skip_download': True,
  67. },
  68. 'expected_warnings': ['Log in to extract metadata'],
  69. }, {
  70. # Without video id in URL
  71. 'url': 'http://www.vier.be/planb/videos/dit-najaar-plan-b',
  72. 'only_matching': True,
  73. }, {
  74. 'url': 'http://www.vier.be/video/v3/embed/16129',
  75. 'only_matching': True,
  76. }]
  77. def _real_initialize(self):
  78. self._logged_in = False
  79. def _login(self, site):
  80. username, password = self._get_login_info()
  81. if username is None or password is None:
  82. return
  83. login_page = self._download_webpage(
  84. 'http://www.%s.be/user/login' % site,
  85. None, note='Logging in', errnote='Unable to log in',
  86. data=urlencode_postdata({
  87. 'form_id': 'user_login',
  88. 'name': username,
  89. 'pass': password,
  90. }),
  91. headers={'Content-Type': 'application/x-www-form-urlencoded'})
  92. login_error = self._html_search_regex(
  93. r'(?s)<div class="messages error">\s*<div>\s*<h2.+?</h2>(.+?)<',
  94. login_page, 'login error', default=None)
  95. if login_error:
  96. self.report_warning('Unable to log in: %s' % login_error)
  97. else:
  98. self._logged_in = True
  99. def _real_extract(self, url):
  100. mobj = re.match(self._VALID_URL, url)
  101. embed_id = mobj.group('embed_id')
  102. display_id = mobj.group('display_id') or embed_id
  103. video_id = mobj.group('id') or embed_id
  104. site = mobj.group('site')
  105. if not self._logged_in:
  106. self._login(site)
  107. webpage = self._download_webpage(url, display_id)
  108. if r'id="user-login"' in webpage:
  109. self.report_warning(
  110. 'Log in to extract metadata', video_id=display_id)
  111. webpage = self._download_webpage(
  112. 'http://www.%s.be/video/v3/embed/%s' % (site, video_id),
  113. display_id)
  114. video_id = self._search_regex(
  115. [r'data-nid="(\d+)"', r'"nid"\s*:\s*"(\d+)"'],
  116. webpage, 'video id', default=video_id or display_id)
  117. application = self._search_regex(
  118. [r'data-application="([^"]+)"', r'"application"\s*:\s*"([^"]+)"'],
  119. webpage, 'application', default=site + '_vod')
  120. filename = self._search_regex(
  121. [r'data-filename="([^"]+)"', r'"filename"\s*:\s*"([^"]+)"'],
  122. webpage, 'filename')
  123. playlist_url = 'http://vod.streamcloud.be/%s/_definst_/mp4:%s.mp4/playlist.m3u8' % (application, filename)
  124. formats = self._extract_wowza_formats(playlist_url, display_id, skip_protocols=['dash'])
  125. self._sort_formats(formats)
  126. title = self._og_search_title(webpage, default=display_id)
  127. thumbnail = self._og_search_thumbnail(webpage, default=None)
  128. description = self._html_search_regex(
  129. r'''(?x)<div\ class="[^"]*field-type-text-with-summary[^"]*">\s*
  130. (?:<div\ class="[^"]+">\s*)*
  131. <p>\s*(?:<span>)?(.+?)</''',
  132. webpage, 'description', default=None)
  133. episode_number = int_or_none(self._search_regex(
  134. r'(?i)aflevering (\d+)', title, 'episode_number', default=None,
  135. fatal=False))
  136. upload_date = unified_strdate(self._html_search_regex(
  137. r'''(?x)<div\ class="[^"]*field-name-post-date[^"]*">\s*
  138. (?:<div\ class="[^"]+">\s*)*
  139. (\d{2}/\d{2}/\d{4})''',
  140. webpage, 'upload_date', default=None))
  141. return {
  142. 'id': video_id,
  143. 'display_id': display_id,
  144. 'title': title,
  145. 'description': description,
  146. 'episode_number': episode_number,
  147. 'upload_date': upload_date,
  148. 'thumbnail': thumbnail,
  149. 'formats': formats,
  150. }
  151. class VierVideosIE(InfoExtractor):
  152. IE_NAME = 'vier:videos'
  153. _VALID_URL = r'https?://(?:www\.)?(?P<site>vier|vijf)\.be/(?P<program>[^/]+)/videos(?:\?.*\bpage=(?P<page>\d+)|$)'
  154. _TESTS = [{
  155. 'url': 'http://www.vier.be/demoestuin/videos',
  156. 'info_dict': {
  157. 'id': 'demoestuin',
  158. },
  159. 'playlist_mincount': 153,
  160. }, {
  161. 'url': 'http://www.vijf.be/temptationisland/videos',
  162. 'info_dict': {
  163. 'id': 'temptationisland',
  164. },
  165. 'playlist_mincount': 159,
  166. }, {
  167. 'url': 'http://www.vier.be/demoestuin/videos?page=6',
  168. 'info_dict': {
  169. 'id': 'demoestuin-page6',
  170. },
  171. 'playlist_mincount': 20,
  172. }, {
  173. 'url': 'http://www.vier.be/demoestuin/videos?page=7',
  174. 'info_dict': {
  175. 'id': 'demoestuin-page7',
  176. },
  177. 'playlist_mincount': 13,
  178. }]
  179. def _real_extract(self, url):
  180. mobj = re.match(self._VALID_URL, url)
  181. program = mobj.group('program')
  182. site = mobj.group('site')
  183. page_id = mobj.group('page')
  184. if page_id:
  185. page_id = int(page_id)
  186. start_page = page_id
  187. playlist_id = '%s-page%d' % (program, page_id)
  188. else:
  189. start_page = 0
  190. playlist_id = program
  191. entries = []
  192. for current_page_id in itertools.count(start_page):
  193. current_page = self._download_webpage(
  194. 'http://www.%s.be/%s/videos?page=%d' % (site, program, current_page_id),
  195. program,
  196. 'Downloading page %d' % (current_page_id + 1))
  197. page_entries = [
  198. self.url_result('http://www.' + site + '.be' + video_url, 'Vier')
  199. for video_url in re.findall(
  200. r'<h[23]><a href="(/[^/]+/videos/[^/]+(?:/\d+)?)">', current_page)]
  201. entries.extend(page_entries)
  202. if page_id or '>Meer<' not in current_page:
  203. break
  204. return self.playlist_result(entries, playlist_id)