@ -1,42 +1,36 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from . common import InfoExtractor
from . . compat import compat_str
from . . utils import (
determine_ext ,
ExtractorError ,
int_or_none ,
join_nonempty ,
merge_dicts ,
str_to_int ,
T ,
traverse_obj ,
unified_strdate ,
url_or_none ,
urljoin ,
)
class RedTubeIE ( InfoExtractor ) :
_VALID_URL = r ' https?://(?:(?: \ w+ \ .)?redtube \ .com/|embed \ .redtube \ .com/ \ ?.*? \ bid=)(?P<id>[0-9]+) '
_EMBED_REGEX = [ r ' <iframe[^>]+?src=[ " \' ](?P<url>(?:https?:)?//embed \ .redtube \ .com/ \ ?.*? \ bid= \ d+) ' ]
_TESTS = [ {
' url ' : ' http://www.redtube.com/66418 ' ,
' md5 ' : ' fc08071233725f26b8f014dba9590005 ' ,
' info_dict ' : {
' id ' : ' 66418 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Sucked on a toilet ' ,
' upload_date ' : ' 20110811 ' ,
' duration ' : 596 ,
' view_count ' : int ,
' age_limit ' : 18 ,
} ,
' skip ' : ' private video ' ,
} , {
' url ' : ' https://www.redtube.com/38864951 ' ,
' md5 ' : ' 4fba70cbca3aefd25767ab4b523c9878 ' ,
' info_dict ' : {
' id ' : ' 38864951 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Public Sex on the Balcony in Freezing Paris! Amateur Couple LeoLulu ' ,
' 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: Leo l ulu' ,
' 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 ' ,
' upload_date ' : ' 20210111 ' ,
' timestamp ' : 1610343109 ,
' duration ' : 646 ,
@ -52,11 +46,11 @@ class RedTubeIE(InfoExtractor):
' only_matching ' : True ,
} ]
@ static method
def _extract_urls ( webpage ) :
return re . findall (
r ' <iframe[^>]+?src=[ " \' ](?P<url>(?:https?:)?//embed \ .redtube \ .com/ \ ?.*? \ bid= \ d+) ' ,
webpage )
@ class method
def _extract_urls ( cls , webpage ) :
for embed_re in cls . _EMBED_REGEX :
for from_ in re . findall ( embed_re , webpage ) :
yield from_
def _real_extract ( self , url ) :
video_id = self . _match_id ( url )
@ -87,50 +81,53 @@ class RedTubeIE(InfoExtractor):
self . _search_regex (
r ' sources \ s*: \ s*( { .+?}) ' , webpage , ' source ' , default = ' {} ' ) ,
video_id , fatal = False )
if sources and isinstance ( sources , dict ) :
for format_id , format_url in sources . items ( ) :
if format_url :
formats . append ( {
' url ' : format_url ,
' format_id ' : format_id ,
' height ' : int_or_none ( format_id ) ,
} )
medias = self . _parse_json (
self . _search_regex (
r ' mediaDefinition[ " \' ]? \ s*: \ s*( \ [.+?} \ s* \ ]) ' , webpage ,
' media definitions ' , default = ' {} ' ) ,
video_id , fatal = False )
for media in medias if isinstance ( medias , list ) else [ ] :
format_url = url_or_none ( media . get ( ' videoUrl ' ) )
def full_url ( u ) :
return urljoin ( url , u )
for fmt in traverse_obj ( sources , ( T ( dict . items ) , {
' url ' : ( 1 , T ( full_url ) ) ,
' format_id ' : ( 2 , T ( compat_str ) ) ,
' height ' : ( 2 , T ( int_or_none ) ) , } ) ) :
if ' url ' in fmt :
formats . append ( fmt )
medias = self . _search_regex (
r ''' mediaDefinitions?[ " ' ]? \ s*: \ s*( \ [[ \ s \ S]+?} \ s* \ ]) ''' , webpage ,
' media definitions ' , default = ' {} ' )
medias = self . _parse_json ( medias , video_id , fatal = False )
for fmt in traverse_obj ( medias , ( Ellipsis , T ( dict ) ) ) :
format_url = full_url ( fmt . get ( ' videoUrl ' ) )
if not format_url :
continue
format_id = media . get ( ' format ' )
quality = media . get ( ' quality ' )
if format_id == ' hls ' or ( format_id == ' mp4 ' and not quality ) :
more_media = None
if fmt [ ' format ' ] == ' hls ' or ( fmt [ ' format ' ] == ' mp4 ' and not fmt . get ( ' quality ' ) ) :
more_media = self . _download_json ( format_url , video_id , fatal = False )
else :
more_media = [ media ]
for media in more_media if isinstance ( more_media , list ) else [ ] :
format_url = url_or_none ( media . get ( ' videoUrl ' ) )
if more_media is None :
more_media = [ fmt ]
for fmt in traverse_obj ( more_media , ( Ellipsis , {
' url ' : ( ' videoUrl ' , T ( full_url ) ) ,
' ext ' : ( ' format ' , T ( compat_str ) ) ,
' format_id ' : ( ' quality ' , T ( compat_str ) ) , } ) ) :
format_url = fmt . get ( ' url ' )
if not format_url :
continue
format_id = media . get ( ' format ' )
if format_id == ' hls ' or determine_ext ( format_url ) == ' m3u8 ' :
if fmt . get ( ' ext ' ) == ' hls ' or determine_ext ( format_url ) == ' m3u8 ' :
formats . extend ( self . _extract_m3u8_formats (
format_url , video_id , ' mp4 ' ,
entry_protocol = ' m3u8_native ' , m3u8_id = format_id or ' hls ' ,
entry_protocol = ' m3u8_native ' , m3u8_id = ' hls ' ,
fatal = False ) )
else :
format_id = media . get ( ' quality ' )
formats . append ( {
' url ' : format_url ,
' format_id ' : format_id ,
' height ' : int_or_none ( format_id ) ,
} )
continue
fmt [ ' height ' ] = int_or_none ( fmt . get ( ' format_id ' ) )
fmt [ ' format_id ' ] = join_nonempty ( ' ext ' , ' format_id ' , from_dict = fmt )
formats . append ( fmt )
if not formats :
video_url = self . _html_search_regex (
r ' <source src= " (.+?) " type= " video/mp4 " > ' , webpage , ' video URL ' )
formats . append ( { ' url ' : video_url } )
video_url = url_or_none ( self . _html_search_regex (
r ' <source src= " (.+?) " type= " video/mp4 " > ' , webpage , ' video URL ' ) )
if video_url :
formats . append ( { ' url ' : video_url } )
self . _check_formats ( formats , video_id )
self . _sort_formats ( formats )
thumbnail = self . _og_search_thumbnail ( webpage )