[rutube] Add support for channels and movies
[youtube-dl] / youtube_dl / extractor / rutube.py
1 # encoding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5 import json
6 import itertools
7
8 from .common import InfoExtractor
9 from ..utils import (
10     compat_urlparse,
11     compat_str,
12     ExtractorError,
13 )
14
15
16 class RutubeIE(InfoExtractor):
17     IE_NAME = 'rutube'
18     IE_DESC = 'Rutube videos'    
19     _VALID_URL = r'https?://rutube\.ru/video/(?P<long_id>\w+)'
20
21     _TEST = {
22         'url': 'http://rutube.ru/video/3eac3b4561676c17df9132a9a1e62e3e/',
23         'file': '3eac3b4561676c17df9132a9a1e62e3e.mp4',
24         'info_dict': {
25             'title': 'Раненный кенгуру забежал в аптеку',
26             'uploader': 'NTDRussian',
27             'uploader_id': '29790',
28         },
29         'params': {
30             # It requires ffmpeg (m3u8 download)
31             'skip_download': True,
32         },
33     }
34
35     def _get_api_response(self, short_id, subpath):
36         api_url = 'http://rutube.ru/api/play/%s/%s/?format=json' % (subpath, short_id)
37         response_json = self._download_webpage(api_url, short_id,
38             'Downloading %s json' % subpath)
39         return json.loads(response_json)
40
41     def _real_extract(self, url):
42         mobj = re.match(self._VALID_URL, url)
43         long_id = mobj.group('long_id')
44         webpage = self._download_webpage(url, long_id)
45         og_video = self._og_search_video_url(webpage)
46         short_id = compat_urlparse.urlparse(og_video).path[1:]
47         options = self._get_api_response(short_id, 'options')
48         trackinfo = self._get_api_response(short_id, 'trackinfo')
49         # Some videos don't have the author field
50         author = trackinfo.get('author') or {}
51         m3u8_url = trackinfo['video_balancer'].get('m3u8')
52         if m3u8_url is None:
53             raise ExtractorError('Couldn\'t find m3u8 manifest url')
54
55         return {
56             'id': trackinfo['id'],
57             'title': trackinfo['title'],
58             'url': m3u8_url,
59             'ext': 'mp4',
60             'thumbnail': options['thumbnail_url'],
61             'uploader': author.get('name'),
62             'uploader_id': compat_str(author['id']) if author else None,
63         }
64
65
66 class RutubeChannelIE(InfoExtractor):
67     IE_NAME = 'rutube:channel'
68     IE_DESC = 'Rutube channels'    
69     _VALID_URL = r'http://rutube\.ru/tags/video/(?P<id>\d+)'
70
71     _PAGE_TEMPLATE = 'http://rutube.ru/api/tags/video/%s/?page=%s&format=json'
72
73     def _extract_videos(self, channel_id, channel_title=None):
74         entries = []
75         for pagenum in itertools.count(1):
76             response_json = self._download_webpage(self._PAGE_TEMPLATE % (channel_id, pagenum),
77                                                    channel_id, 'Downloading page %s' % pagenum)
78             page = json.loads(response_json)
79             if 'detail' in page and page['detail'] == 'Not found':
80                 raise ExtractorError('Channel %s does not exist' % channel_id, expected=True)
81             results = page['results']
82             if len(results) == 0:
83                 break;
84             entries.extend(self.url_result(v['video_url'], 'Rutube') for v in results)
85             if page['has_next'] is False:
86                 break;
87         return self.playlist_result(entries, channel_id, channel_title)
88
89     def _real_extract(self, url):
90         mobj = re.match(self._VALID_URL, url)
91         channel_id = mobj.group('id')
92         return self._extract_videos(channel_id)
93
94
95 class RutubeMovieIE(RutubeChannelIE):
96     IE_NAME = 'rutube:movie'
97     IE_DESC = 'Rutube movies'    
98     _VALID_URL = r'http://rutube\.ru/metainfo/tv/(?P<id>\d+)'
99
100     _MOVIE_TEMPLATE = 'http://rutube.ru/api/metainfo/tv/%s/?format=json'
101     _PAGE_TEMPLATE = 'http://rutube.ru/api/metainfo/tv/%s/video?page=%s&format=json'
102
103     def _real_extract(self, url):
104         mobj = re.match(self._VALID_URL, url)
105         movie_id = mobj.group('id')
106         movie_json = self._download_webpage(self._MOVIE_TEMPLATE % movie_id, movie_id,
107                                             'Downloading movie JSON')
108         movie = json.loads(movie_json)
109         if 'detail' in movie and movie['detail'] == 'Not found':
110             raise ExtractorError('Movie %s does not exist' % movie_id, expected=True)
111         movie_name = movie['name']
112         return self._extract_videos(movie_id, movie_name)