]> Raphaƫl G. Git Repositories - youtubedl/blob - youtube_dl/extractor/pornhub.py
debian/patches: Removed, they came from upstream.
[youtubedl] / youtube_dl / extractor / pornhub.py
1 from __future__ import unicode_literals
2
3 import os
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import (
8 compat_urllib_parse_unquote,
9 compat_urllib_parse_unquote_plus,
10 compat_urllib_parse_urlparse,
11 )
12 from ..utils import (
13 ExtractorError,
14 int_or_none,
15 sanitized_Request,
16 str_to_int,
17 )
18 from ..aes import (
19 aes_decrypt_text
20 )
21
22
23 class PornHubIE(InfoExtractor):
24 _VALID_URL = r'https?://(?:[a-z]+\.)?pornhub\.com/(?:view_video\.php\?viewkey=|embed/)(?P<id>[0-9a-z]+)'
25 _TESTS = [{
26 'url': 'http://www.pornhub.com/view_video.php?viewkey=648719015',
27 'md5': '1e19b41231a02eba417839222ac9d58e',
28 'info_dict': {
29 'id': '648719015',
30 'ext': 'mp4',
31 'title': 'Seductive Indian beauty strips down and fingers her pink pussy',
32 'uploader': 'Babes',
33 'duration': 361,
34 'view_count': int,
35 'like_count': int,
36 'dislike_count': int,
37 'comment_count': int,
38 'age_limit': 18,
39 }
40 }, {
41 'url': 'http://www.pornhub.com/view_video.php?viewkey=ph557bbb6676d2d',
42 'only_matching': True,
43 }, {
44 'url': 'http://fr.pornhub.com/view_video.php?viewkey=ph55ca2f9760862',
45 'only_matching': True,
46 }]
47
48 @classmethod
49 def _extract_url(cls, webpage):
50 mobj = re.search(
51 r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?pornhub\.com/embed/\d+)\1', webpage)
52 if mobj:
53 return mobj.group('url')
54
55 def _extract_count(self, pattern, webpage, name):
56 return str_to_int(self._search_regex(
57 pattern, webpage, '%s count' % name, fatal=False))
58
59 def _real_extract(self, url):
60 video_id = self._match_id(url)
61
62 req = sanitized_Request(
63 'http://www.pornhub.com/view_video.php?viewkey=%s' % video_id)
64 req.add_header('Cookie', 'age_verified=1')
65 webpage = self._download_webpage(req, video_id)
66
67 error_msg = self._html_search_regex(
68 r'(?s)<div class="userMessageSection[^"]*".*?>(.*?)</div>',
69 webpage, 'error message', default=None)
70 if error_msg:
71 error_msg = re.sub(r'\s+', ' ', error_msg)
72 raise ExtractorError(
73 'PornHub said: %s' % error_msg,
74 expected=True, video_id=video_id)
75
76 flashvars = self._parse_json(
77 self._search_regex(
78 r'var\s+flashv1ars_\d+\s*=\s*({.+?});', webpage, 'flashvars', default='{}'),
79 video_id)
80 if flashvars:
81 video_title = flashvars.get('video_title')
82 thumbnail = flashvars.get('image_url')
83 duration = int_or_none(flashvars.get('video_duration'))
84 else:
85 video_title, thumbnail, duration = [None] * 3
86
87 if not video_title:
88 video_title = self._html_search_regex(r'<h1 [^>]+>([^<]+)', webpage, 'title')
89
90 video_uploader = self._html_search_regex(
91 r'(?s)From:&nbsp;.+?<(?:a href="/users/|a href="/channels/|span class="username)[^>]+>(.+?)<',
92 webpage, 'uploader', fatal=False)
93
94 view_count = self._extract_count(
95 r'<span class="count">([\d,\.]+)</span> views', webpage, 'view')
96 like_count = self._extract_count(
97 r'<span class="votesUp">([\d,\.]+)</span>', webpage, 'like')
98 dislike_count = self._extract_count(
99 r'<span class="votesDown">([\d,\.]+)</span>', webpage, 'dislike')
100 comment_count = self._extract_count(
101 r'All Comments\s*<span>\(([\d,.]+)\)', webpage, 'comment')
102
103 video_urls = list(map(compat_urllib_parse_unquote, re.findall(r"player_quality_[0-9]{3}p\s*=\s*'([^']+)'", webpage)))
104 if webpage.find('"encrypted":true') != -1:
105 password = compat_urllib_parse_unquote_plus(
106 self._search_regex(r'"video_title":"([^"]+)', webpage, 'password'))
107 video_urls = list(map(lambda s: aes_decrypt_text(s, password, 32).decode('utf-8'), video_urls))
108
109 formats = []
110 for video_url in video_urls:
111 path = compat_urllib_parse_urlparse(video_url).path
112 extension = os.path.splitext(path)[1][1:]
113 format = path.split('/')[5].split('_')[:2]
114 format = '-'.join(format)
115
116 m = re.match(r'^(?P<height>[0-9]+)[pP]-(?P<tbr>[0-9]+)[kK]$', format)
117 if m is None:
118 height = None
119 tbr = None
120 else:
121 height = int(m.group('height'))
122 tbr = int(m.group('tbr'))
123
124 formats.append({
125 'url': video_url,
126 'ext': extension,
127 'format': format,
128 'format_id': format,
129 'tbr': tbr,
130 'height': height,
131 })
132 self._sort_formats(formats)
133
134 return {
135 'id': video_id,
136 'uploader': video_uploader,
137 'title': video_title,
138 'thumbnail': thumbnail,
139 'duration': duration,
140 'view_count': view_count,
141 'like_count': like_count,
142 'dislike_count': dislike_count,
143 'comment_count': comment_count,
144 'formats': formats,
145 'age_limit': 18,
146 }
147
148
149 class PornHubPlaylistBaseIE(InfoExtractor):
150 def _extract_entries(self, webpage):
151 return [
152 self.url_result('http://www.pornhub.com/%s' % video_url, PornHubIE.ie_key())
153 for video_url in set(re.findall(
154 r'href="/?(view_video\.php\?.*\bviewkey=[\da-z]+[^"]*)"', webpage))
155 ]
156
157 def _real_extract(self, url):
158 playlist_id = self._match_id(url)
159
160 webpage = self._download_webpage(url, playlist_id)
161
162 entries = self._extract_entries(webpage)
163
164 playlist = self._parse_json(
165 self._search_regex(
166 r'playlistObject\s*=\s*({.+?});', webpage, 'playlist'),
167 playlist_id)
168
169 return self.playlist_result(
170 entries, playlist_id, playlist.get('title'), playlist.get('description'))
171
172
173 class PornHubPlaylistIE(PornHubPlaylistBaseIE):
174 _VALID_URL = r'https?://(?:www\.)?pornhub\.com/playlist/(?P<id>\d+)'
175 _TESTS = [{
176 'url': 'http://www.pornhub.com/playlist/6201671',
177 'info_dict': {
178 'id': '6201671',
179 'title': 'P0p4',
180 },
181 'playlist_mincount': 35,
182 }]
183
184
185 class PornHubUserVideosIE(PornHubPlaylistBaseIE):
186 _VALID_URL = r'https?://(?:www\.)?pornhub\.com/users/(?P<id>[^/]+)/videos'
187 _TESTS = [{
188 'url': 'http://www.pornhub.com/users/rushandlia/videos',
189 'info_dict': {
190 'id': 'rushandlia',
191 },
192 'playlist_mincount': 13,
193 }]
194
195 def _real_extract(self, url):
196 user_id = self._match_id(url)
197
198 webpage = self._download_webpage(url, user_id)
199
200 return self.playlist_result(self._extract_entries(webpage), user_id)