1 from .common
import InfoExtractor
14 class RedTubeIE(InfoExtractor
):
15 _VALID_URL
= r
'https?://(?:(?:\w+\.)?redtube\.com(?:\.br)?/|embed\.redtube\.com/\?.*?\bid=)(?P<id>[0-9]+)'
16 _EMBED_REGEX
= [r
'<iframe[^>]+?src=["\'](?P
<url
>(?
:https?
:)?
//embed\
.redtube\
.com
/\?.*?
\bid
=\d
+)']
18 'url
': 'https
://www
.redtube
.com
/38864951',
19 'md5
': '4fba70cbca3aefd25767ab4b523c9878
',
23 'title
': 'Public Sex on the Balcony
in Freezing Paris
! Amateur Couple LeoLulu
',
24 'description
': 'Watch video Public Sex on the Balcony
in Freezing Paris
! Amateur Couple LeoLulu on Redtube
, home of free Blowjob porn videos
and Blonde sex movies online
. Video length
: (10:46) - Uploaded by leolulu
- Verified User
- Starring Pornstar
: Leolulu
',
25 'upload_date
': '20210111',
26 'timestamp
': 1610343109,
30 'thumbnail
': r're
:https
://\wi
-ph\
.rdtcdn\
.com
/videos
/.+/.+\
.jpg
',
33 'url
': 'http
://embed
.redtube
.com
/?bgcolor
=000000&id=1443286',
34 'only_matching
': True,
36 'url
': 'http
://it
.redtube
.com
/66418',
37 'only_matching
': True,
39 'url
': 'https
://www
.redtube
.com
.br
/103224331',
40 'only_matching
': True,
43 def _real_extract(self, url):
44 video_id = self._match_id(url)
45 webpage = self._download_webpage(
46 f'https
://www
.redtube
.com
/{video_id}
', video_id)
49 (('video
-deleted
-info
', '>This video has been removed
'), 'has been removed
'),
50 (('private_video_text
', '>This video
is private
', '>Send a friend request to its owner to be able to view it
'), 'is private
'),
53 for patterns, message in ERRORS:
54 if any(p in webpage for p in patterns):
56 f'Video {video_id} {message}
', expected=True)
58 info = self._search_json_ld(webpage, video_id, default={})
60 if not info.get('title
'):
61 info['title
'] = self._html_search_regex(
62 (r'<h(\d
)[^
>]+class="(?:video_title_text|videoTitle|video_title)[^"]*">(?P<title>(?:(?!\1).)+)</h\1>',
63 r'(?:videoTitle|title)\s*:\s*(["\'])(?P
<title
>(?
:(?
!\
1).)+)\
1'),
64 webpage, 'title
', group='title
',
65 default=None) or self._og_search_title(webpage)
68 sources = self._parse_json(
70 r'sources\s
*:\s
*({.+?
})', webpage, 'source
', default='{}'),
71 video_id, fatal=False)
72 if sources and isinstance(sources, dict):
73 for format_id, format_url in sources.items():
77 'format_id
': format_id,
78 'height
': int_or_none(format_id),
80 medias = self._parse_json(
82 r'mediaDefinition
["\']?\s*:\s*(\[.+?}\s*\])', webpage,
83 'media definitions', default='{}'),
84 video_id, fatal=False)
85 for media in medias if isinstance(medias, list) else []:
86 format_url = urljoin('https://www.redtube.com', media.get('videoUrl'))
89 format_id = media.get('format')
90 quality = media.get('quality')
91 if format_id == 'hls' or (format_id == 'mp4' and not quality):
92 more_media = self._download_json(format_url, video_id, fatal=False)
95 for media in more_media if isinstance(more_media, list) else []:
96 format_url = url_or_none(media.get('videoUrl'))
99 format_id = media.get('format')
100 if format_id == 'hls' or determine_ext(format_url) == 'm3u8':
101 formats.extend(self._extract_m3u8_formats(
102 format_url, video_id, 'mp4',
103 entry_protocol='m3u8_native', m3u8_id=format_id or 'hls',
106 format_id = media.get('quality')
110 'format_id': format_id,
111 'height': int_or_none(format_id),
114 video_url = self._html_search_regex(
115 r'<source src="(.+?
)" type="video
/mp4
">', webpage, 'video URL')
116 formats.append({'url': video_url, 'ext': 'mp4'})
118 thumbnail = self._og_search_thumbnail(webpage)
119 upload_date = unified_strdate(self._search_regex(
120 r'<span[^>]+>(?:ADDED|Published on) ([^<]+)<',
121 webpage, 'upload date', default=None))
122 duration = int_or_none(self._og_search_property(
123 'video:duration', webpage, default=None) or self._search_regex(
124 r'videoDuration\s*:\s*(\d+)', webpage, 'duration', default=None))
125 view_count = str_to_int(self._search_regex(
126 (r'<div[^>]*>Views</div>\s*<div[^>]*>\s*([\d,.]+)',
127 r'<span[^>]*>VIEWS</span>\s*</td>\s*<td>\s*([\d,.]+)',
128 r'<span[^>]+\bclass=["\']video_view_count
[^
>]*>\s
*([\d
,.]+)'),
129 webpage, 'view count
', default=None))
131 # No self-labeling, but they describe themselves as
132 # "Home of Videos Porno"
135 return merge_dicts(info, {
138 'thumbnail
': thumbnail,
139 'upload_date
': upload_date,
140 'duration
': duration,
141 'view_count
': view_count,
142 'age_limit
': age_limit,