4 from .common
import InfoExtractor
11 get_element_html_by_id
,
19 class MurrtubeIE(InfoExtractor
):
23 https?://murrtube\.net/(?:v/|videos/(?P<slug>[a-z0-9-]+?)-)
25 (?P<id>[A-Z0-9]{4}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})
28 'url': 'https://murrtube.net/videos/inferno-x-skyler-148b6f2a-fdcc-4902-affe-9c0f41aaaca0',
29 'md5': '70380878a77e8565d4aea7f68b8bbb35',
31 'id': 'ca885d8456b95de529b6723b158032e11115d',
33 'title': 'Inferno X Skyler',
34 'description': 'Humping a very good slutty sheppy (roomate)',
35 'uploader': 'Inferno Wolf',
37 'thumbnail': 'https://storage.murrtube.net/murrtube-production/ekbs3zcfvuynnqfx72nn2tkokvsd',
43 'url': 'https://murrtube.net/v/0J2Q',
44 'md5': '31262f6ac56f0ca75e5a54a0f3fefcb6',
46 'id': '8442998c52134968d9caa36e473e1a6bac6ca',
49 'title': 'Who\'s in charge now?',
50 'description': 'md5:795791e97e5b0f1805ea84573f02a997',
52 'thumbnail': 'https://storage.murrtube.net/murrtube-production/fb1ojjwiucufp34ya6hxu5vfqi5s',
59 def _extract_count(self
, name
, html
):
60 return parse_count(self
._search
_regex
(
61 rf
'([\d,]+)\s+<span[^>]*>{name}</span>', html
, name
, default
=None))
63 def _real_initialize(self
):
64 homepage
= self
._download
_webpage
(
65 'https://murrtube.net', None, note
='Getting session token')
66 self
._request
_webpage
(
67 'https://murrtube.net/accept_age_check', None, 'Setting age cookie',
68 data
=urlencode_postdata(self
._hidden
_inputs
(homepage
)))
70 def _real_extract(self
, url
):
71 video_id
= self
._match
_id
(url
)
72 if video_id
.startswith('murrtube:'):
73 raise ExtractorError('Support for murrtube: prefix URLs is broken')
74 video_page
= self
._download
_webpage
(url
, video_id
)
75 video_attrs
= extract_attributes(get_element_html_by_id('video', video_page
))
76 playlist
= update_url(video_attrs
['data-url'], query
=None)
77 video_id
= self
._search
_regex
(r
'/([\da-f]+)/index.m3u8', playlist
, 'video id')
81 'title': remove_end(self
._og
_search
_title
(video_page
), ' - Murrtube'),
83 'formats': self
._extract
_m
3u8_formats
(playlist
, video_id
, 'mp4'),
84 'description': self
._og
_search
_description
(video_page
),
85 'thumbnail': update_url(self
._og
_search
_thumbnail
(video_page
, default
=''), query
=None) or None,
86 'uploader': clean_html(get_element_by_class('pl-1 is-size-6 has-text-lighter', video_page
)),
87 'view_count': self
._extract
_count
('Views', video_page
),
88 'like_count': self
._extract
_count
('Likes', video_page
),
89 'comment_count': self
._extract
_count
('Comments', video_page
),
93 class MurrtubeUserIE(InfoExtractor
):
95 IE_DESC
= 'Murrtube user profile'
96 _VALID_URL
= r
'https?://murrtube\.net/(?P<id>[^/]+)$'
98 'url': 'https://murrtube.net/stormy',
102 'playlist_mincount': 27,
106 def _download_gql(self
, video_id
, op
, note
=None, fatal
=True):
107 result
= self
._download
_json
(
108 'https://murrtube.net/graphql',
109 video_id
, note
, data
=json
.dumps(op
).encode(), fatal
=fatal
,
110 headers
={'Content-Type': 'application/json'})
111 return result
['data']
113 def _fetch_page(self
, username
, user_id
, page
):
114 data
= self
._download
_gql
(username
, {
115 'operationName': 'Media',
117 'limit': self
._PAGE
_SIZE
,
118 'offset': page
* self
._PAGE
_SIZE
,
123 query Media($q: String, $sort: String, $userId: ID, $offset: Int!, $limit: Int!) {
124 media(q: $q, sort: $sort, userId: $userId, offset: $offset, limit: $limit) {
129 f
'Downloading page {page + 1}')
131 raise ExtractorError(f
'Failed to retrieve video list for page {page + 1}')
133 media
= data
['media']
136 yield self
.url_result('murrtube:{}'.format(entry
['id']), MurrtubeIE
.ie_key())
138 def _real_extract(self
, url
):
139 username
= self
._match
_id
(url
)
140 data
= self
._download
_gql
(username
, {
141 'operationName': 'User',
146 query User($id: ID!) {
152 'Downloading user info')
154 raise ExtractorError('Failed to fetch user info')
158 entries
= OnDemandPagedList(functools
.partial(
159 self
._fetch
_page
, username
, user
.get('id')), self
._PAGE
_SIZE
)
161 return self
.playlist_result(entries
, username
)