[spankbang] Extend _VALID_URL
[youtube-dl] / youtube_dl / extractor / spankbang.py
1 from __future__ import unicode_literals
2
3 import re
4
5 from .common import InfoExtractor
6 from ..utils import (
7     ExtractorError,
8     parse_duration,
9     parse_resolution,
10     str_to_int,
11 )
12
13
14 class SpankBangIE(InfoExtractor):
15     _VALID_URL = r'https?://(?:[^/]+\.)?spankbang\.com/(?P<id>[\da-z]+)/(?:video|play|embed)'
16     _TESTS = [{
17         'url': 'http://spankbang.com/3vvn/video/fantasy+solo',
18         'md5': '1cc433e1d6aa14bc376535b8679302f7',
19         'info_dict': {
20             'id': '3vvn',
21             'ext': 'mp4',
22             'title': 'fantasy solo',
23             'description': 'dillion harper masturbates on a bed',
24             'thumbnail': r're:^https?://.*\.jpg$',
25             'uploader': 'silly2587',
26             'age_limit': 18,
27         }
28     }, {
29         # 480p only
30         'url': 'http://spankbang.com/1vt0/video/solvane+gangbang',
31         'only_matching': True,
32     }, {
33         # no uploader
34         'url': 'http://spankbang.com/lklg/video/sex+with+anyone+wedding+edition+2',
35         'only_matching': True,
36     }, {
37         # mobile page
38         'url': 'http://m.spankbang.com/1o2de/video/can+t+remember+her+name',
39         'only_matching': True,
40     }, {
41         # 4k
42         'url': 'https://spankbang.com/1vwqx/video/jade+kush+solo+4k',
43         'only_matching': True,
44     }, {
45         'url': 'https://m.spankbang.com/3vvn/play/fantasy+solo/480p/',
46         'only_matching': True,
47     }, {
48         'url': 'https://m.spankbang.com/3vvn/play',
49         'only_matching': True,
50     }, {
51         'url': 'https://spankbang.com/2y3td/embed/',
52         'only_matching': True,
53     }]
54
55     def _real_extract(self, url):
56         video_id = self._match_id(url)
57         webpage = self._download_webpage(
58             url.replace('/%s/embed' % video_id, '/%s/video' % video_id),
59             video_id, headers={'Cookie': 'country=US'})
60
61         if re.search(r'<[^>]+\bid=["\']video_removed', webpage):
62             raise ExtractorError(
63                 'Video %s is not available' % video_id, expected=True)
64
65         formats = []
66         for mobj in re.finditer(
67                 r'stream_url_(?P<id>[^\s=]+)\s*=\s*(["\'])(?P<url>(?:(?!\2).)+)\2',
68                 webpage):
69             format_id, format_url = mobj.group('id', 'url')
70             f = parse_resolution(format_id)
71             f.update({
72                 'url': format_url,
73                 'format_id': format_id,
74             })
75             formats.append(f)
76         self._sort_formats(formats)
77
78         title = self._html_search_regex(
79             r'(?s)<h1[^>]*>(.+?)</h1>', webpage, 'title')
80         description = self._search_regex(
81             r'<div[^>]+\bclass=["\']bottom[^>]+>\s*<p>[^<]*</p>\s*<p>([^<]+)',
82             webpage, 'description', fatal=False)
83         thumbnail = self._og_search_thumbnail(webpage)
84         uploader = self._search_regex(
85             r'class="user"[^>]*><img[^>]+>([^<]+)',
86             webpage, 'uploader', default=None)
87         duration = parse_duration(self._search_regex(
88             r'<div[^>]+\bclass=["\']right_side[^>]+>\s*<span>([^<]+)',
89             webpage, 'duration', fatal=False))
90         view_count = str_to_int(self._search_regex(
91             r'([\d,.]+)\s+plays', webpage, 'view count', fatal=False))
92
93         age_limit = self._rta_search(webpage)
94
95         return {
96             'id': video_id,
97             'title': title,
98             'description': description,
99             'thumbnail': thumbnail,
100             'uploader': uploader,
101             'duration': duration,
102             'view_count': view_count,
103             'formats': formats,
104             'age_limit': age_limit,
105         }