1 from __future__ 
import unicode_literals
 
   5 from .common 
import InfoExtractor
 
   9     compat_urllib_parse_unquote
, 
  24 class FourTubeBaseIE(InfoExtractor
): 
  25     _TKN_HOST 
= 'tkn.kodicdn.com' 
  27     def _extract_formats(self
, url
, video_id
, media_id
, sources
): 
  28         token_url 
= 'https://%s/%s/desktop/%s' % ( 
  29             self
._TKN
_HOST
, media_id
, '+'.join(sources
)) 
  31         parsed_url 
= compat_urlparse
.urlparse(url
) 
  32         tokens 
= self
._download
_json
(token_url
, video_id
, data
=b
'', headers
={ 
  33             'Origin': '%s://%s' % (parsed_url
.scheme
, parsed_url
.hostname
), 
  37             'url': tokens
[format
]['token'], 
  38             'format_id': format 
+ 'p', 
  39             'resolution': format 
+ 'p', 
  40             'quality': int(format
), 
  41         } for format 
in sources
] 
  42         self
._sort
_formats
(formats
) 
  45     def _real_extract(self
, url
): 
  46         mobj 
= re
.match(self
._VALID
_URL
, url
) 
  47         kind
, video_id
, display_id 
= mobj
.group('kind', 'id', 'display_id') 
  49         if kind 
== 'm' or not display_id
: 
  50             url 
= self
._URL
_TEMPLATE 
% video_id
 
  52         webpage 
= self
._download
_webpage
(url
, video_id
) 
  54         title 
= self
._html
_search
_meta
('name', webpage
) 
  55         timestamp 
= parse_iso8601(self
._html
_search
_meta
( 
  56             'uploadDate', webpage
)) 
  57         thumbnail 
= self
._html
_search
_meta
('thumbnailUrl', webpage
) 
  58         uploader_id 
= self
._html
_search
_regex
( 
  59             r
'<a class="item-to-subscribe" href="[^"]+/(?:channel|user)s?/([^/"]+)" title="Go to [^"]+ page">', 
  60             webpage
, 'uploader id', fatal
=False) 
  61         uploader 
= self
._html
_search
_regex
( 
  62             r
'<a class="item-to-subscribe" href="[^"]+/(?:channel|user)s?/[^/"]+" title="Go to ([^"]+) page">', 
  63             webpage
, 'uploader', fatal
=False) 
  65         categories_html 
= self
._search
_regex
( 
  66             r
'(?s)><i class="icon icon-tag"></i>\s*Categories / Tags\s*.*?<ul class="[^"]*?list[^"]*?">(.*?)</ul>', 
  67             webpage
, 'categories', fatal
=False) 
  71                 c
.strip() for c 
in re
.findall( 
  72                     r
'(?s)<li><a.*?>(.*?)</a>', categories_html
)] 
  74         view_count 
= str_to_int(self
._search
_regex
( 
  75             r
'<meta[^>]+itemprop="interactionCount"[^>]+content="UserPlays:([0-9,]+)">', 
  76             webpage
, 'view count', default
=None)) 
  77         like_count 
= str_to_int(self
._search
_regex
( 
  78             r
'<meta[^>]+itemprop="interactionCount"[^>]+content="UserLikes:([0-9,]+)">', 
  79             webpage
, 'like count', default
=None)) 
  80         duration 
= parse_duration(self
._html
_search
_meta
('duration', webpage
)) 
  82         media_id 
= self
._search
_regex
( 
  83             r
'<button[^>]+data-id=(["\'])(?P
<id>\d
+)\
1[^
>]+data
-quality
=', webpage, 
  84             'media 
id', default=None, group='id') 
  87             for _, quality in re.findall(r'<button
[^
>]+data
-quality
=(["\'])(.+?)\1', webpage)] 
  88         if not (media_id and sources): 
  89             player_js = self._download_webpage( 
  91                     r'<script[^>]id=(["\'])playerembed\
1[^
>]+src
=(["\'])(?P<url>.+?)\2', 
  92                     webpage, 'player JS', group='url'), 
  93                 video_id, 'Downloading player JS') 
  94             params_js = self._search_regex( 
  95                 r'\$\.ajax\(url,\ opts\);\s*\}\s*\}\)\(([0-9,\[\] ]+)\)', 
  96                 player_js, 'initialization parameters') 
  97             params = self._parse_json('[%s]' % params_js, video_id) 
  99             sources = ['%s' % p for p in params[2]] 
 101         formats = self._extract_formats(url, video_id, media_id, sources) 
 107             'categories': categories, 
 108             'thumbnail': thumbnail, 
 109             'uploader': uploader, 
 110             'uploader_id': uploader_id, 
 111             'timestamp': timestamp, 
 112             'like_count': like_count, 
 113             'view_count': view_count, 
 114             'duration': duration, 
 119 class FourTubeIE(FourTubeBaseIE): 
 121     _VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?4tube\.com/(?:videos|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?' 
 122     _URL_TEMPLATE = 'https://www.4tube.com/videos/%s/video' 
 124         'url': 'http://www.4tube.com/videos/209733/hot-babe-holly-michaels-gets-her-ass-stuffed-by-black', 
 125         'md5': '6516c8ac63b03de06bc8eac14362db4f', 
 129             'title': 'Hot Babe Holly Michaels gets her ass stuffed by black', 
 130             'uploader': 'WCP Club', 
 131             'uploader_id': 'wcp-club', 
 132             'upload_date': '20131031', 
 133             'timestamp': 1383263892, 
 141         'url': 'http://www.4tube.com/embed/209733', 
 142         'only_matching': True, 
 144         'url': 'http://m.4tube.com/videos/209733/hot-babe-holly-michaels-gets-her-ass-stuffed-by-black', 
 145         'only_matching': True, 
 149 class FuxIE(FourTubeBaseIE): 
 150     _VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?fux\.com/(?:video|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?' 
 151     _URL_TEMPLATE = 'https://www.fux.com/video/%s/video' 
 153         'url': 'https://www.fux.com/video/195359/awesome-fucking-kitchen-ends-cum-swallow', 
 157             'title': 'Awesome fucking in the kitchen ends with cum swallow', 
 158             'uploader': 'alenci2342', 
 159             'uploader_id': 'alenci2342', 
 160             'upload_date': '20131230', 
 161             'timestamp': 1388361660, 
 169             'skip_download': True, 
 172         'url': 'https://www.fux.com/embed/195359', 
 173         'only_matching': True, 
 175         'url': 'https://www.fux.com/video/195359/awesome-fucking-kitchen-ends-cum-swallow', 
 176         'only_matching': True, 
 180 class PornTubeIE(FourTubeBaseIE): 
 181     _VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?porntube\.com/(?:videos/(?P<display_id>[^/]+)_|embed/)(?P<id>\d+)' 
 182     _URL_TEMPLATE = 'https://www.porntube.com/videos/video_%s' 
 183     _TKN_HOST = 'tkn.porntube.com' 
 185         'url': 'https://www.porntube.com/videos/teen-couple-doing-anal_7089759', 
 189             'title': 'Teen couple doing anal', 
 191             'uploader_id': '91488', 
 192             'upload_date': '20150606', 
 193             'timestamp': 1433595647, 
 200             'skip_download': True, 
 203         'url': 'https://www.porntube.com/videos/squirting-teen-ballerina-ecg_1331406', 
 207             'title': 'Squirting Teen Ballerina on ECG', 
 208             'uploader': 'Exploited College Girls', 
 209             'uploader_id': '665', 
 210             'channel': 'Exploited College Girls', 
 212             'upload_date': '20130920', 
 213             'timestamp': 1379685485, 
 220             'skip_download': True, 
 223         'url': 'https://www.porntube.com/embed/7089759', 
 224         'only_matching': True, 
 226         'url': 'https://m.porntube.com/videos/teen-couple-doing-anal_7089759', 
 227         'only_matching': True, 
 230     def _real_extract(self, url): 
 231         mobj = re.match(self._VALID_URL, url) 
 232         video_id, display_id = mobj.group('id', 'display_id') 
 234         webpage = self._download_webpage(url, display_id) 
 236         video = self._parse_json( 
 238                 r'INITIALSTATE\s*=\s*(["\'])(?P
<value
>(?
:(?
!\
1).)+)\
1', 
 239                 webpage, 'data
', group='value
'), video_id, 
 240             transform_source=lambda x: compat_urllib_parse_unquote( 
 241                 compat_b64decode(x).decode('utf
-8')))['page
']['video
'] 
 243         title = video['title
'] 
 244         media_id = video['mediaId
'] 
 245         sources = [compat_str(e['height
']) 
 246                    for e in video['encodings
'] if e.get('height
')] 
 247         formats = self._extract_formats(url, video_id, media_id, sources) 
 249         thumbnail = url_or_none(video.get('masterThumb
')) 
 250         uploader = try_get(video, lambda x: x['user
']['username
'], compat_str) 
 251         uploader_id = str_or_none(try_get( 
 252             video, lambda x: x['user
']['id'], int)) 
 253         channel = try_get(video, lambda x: x['channel
']['name
'], compat_str) 
 254         channel_id = str_or_none(try_get( 
 255             video, lambda x: x['channel
']['id'], int)) 
 256         like_count = int_or_none(video.get('likes
')) 
 257         dislike_count = int_or_none(video.get('dislikes
')) 
 258         view_count = int_or_none(video.get('playsQty
')) 
 259         duration = int_or_none(video.get('durationInSeconds
')) 
 260         timestamp = unified_timestamp(video.get('publishedAt
')) 
 266             'thumbnail
': thumbnail, 
 267             'uploader
': uploader or channel, 
 268             'uploader_id
': uploader_id or channel_id, 
 270             'channel_id
': channel_id, 
 271             'timestamp
': timestamp, 
 272             'like_count
': like_count, 
 273             'dislike_count
': dislike_count, 
 274             'view_count
': view_count, 
 275             'duration
': duration, 
 280 class PornerBrosIE(FourTubeBaseIE): 
 281     _VALID_URL = r'https?
://(?
:(?P
<kind
>www|m
)\
.)?pornerbros\
.com
/(?
:videos
/(?P
<display_id
>[^
/]+)_|embed
/)(?P
<id>\d
+)' 
 282     _URL_TEMPLATE = 'https
://www
.pornerbros
.com
/videos
/video_
%s' 
 284         'url
': 'https
://www
.pornerbros
.com
/videos
/skinny
-brunette
-takes
-big
-cock
-down
-her
-anal
-hole_181369
', 
 285         'md5
': '6516c8ac63b03de06bc8eac14362db4f
', 
 289             'title
': 'Skinny brunette takes big cock down her anal hole
', 
 290             'uploader
': 'PornerBros HD
', 
 291             'uploader_id
': 'pornerbros
-hd
', 
 292             'upload_date
': '20130130', 
 293             'timestamp
': 1359527401, 
 300             'skip_download
': True, 
 303         'url
': 'https
://www
.pornerbros
.com
/embed
/181369', 
 304         'only_matching
': True, 
 306         'url
': 'https
://m
.pornerbros
.com
/videos
/skinny
-brunette
-takes
-big
-cock
-down
-her
-anal
-hole_181369
', 
 307         'only_matching
': True,