4 from .common
import InfoExtractor
14 class MurrtubeIE(InfoExtractor
):
19 https?://murrtube\.net/videos/(?P<slug>[a-z0-9\-]+)\-
21 (?P<id>[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12})
24 'url': 'https://murrtube.net/videos/inferno-x-skyler-148b6f2a-fdcc-4902-affe-9c0f41aaaca0',
25 'md5': '169f494812d9a90914b42978e73aa690',
27 'id': '148b6f2a-fdcc-4902-affe-9c0f41aaaca0',
29 'title': 'Inferno X Skyler',
30 'description': 'Humping a very good slutty sheppy (roomate)',
31 'thumbnail': r
're:^https?://.*\.jpg$',
33 'uploader': 'Inferno Wolf',
38 'tags': ['hump', 'breed', 'Fursuit', 'murrsuit', 'bareback'],
42 def _download_gql(self
, video_id
, op
, note
=None, fatal
=True):
43 result
= self
._download
_json
(
44 'https://murrtube.net/graphql',
45 video_id
, note
, data
=json
.dumps(op
).encode(), fatal
=fatal
,
46 headers
={'Content-Type': 'application/json'}
)
49 def _real_extract(self
, url
):
50 video_id
= self
._match
_id
(url
)
51 data
= self
._download
_gql
(video_id
, {
52 'operationName': 'Medium',
57 query Medium($id: ID!) {
77 storage_url
= 'https://storage.murrtube.net/murrtube/'
78 format_url
= storage_url
+ meta
.get('key', '')
79 thumbnail
= storage_url
+ meta
.get('thumbnailKey', '')
81 if determine_ext(format_url
) == 'm3u8':
82 formats
= self
._extract
_m
3u8_formats
(
83 format_url
, video_id
, 'mp4', entry_protocol
='m3u8_native', fatal
=False)
85 formats
= [{'url': format_url}
]
89 'title': meta
.get('title'),
90 'description': meta
.get('description'),
92 'thumbnail': thumbnail
,
93 'duration': int_or_none(meta
.get('duration')),
94 'uploader': try_get(meta
, lambda x
: x
['user']['name']),
95 'view_count': meta
.get('viewsCount'),
96 'like_count': meta
.get('likesCount'),
97 'comment_count': meta
.get('commentsCount'),
98 'tags': meta
.get('tagList'),
103 class MurrtubeUserIE(MurrtubeIE
): # XXX: Do not subclass from concrete IE
105 IE_DESC
= 'Murrtube user profile'
106 _VALID_URL
= r
'https?://murrtube\.net/(?P<id>[^/]+)$'
108 'url': 'https://murrtube.net/stormy',
112 'playlist_mincount': 27,
116 def _fetch_page(self
, username
, user_id
, page
):
117 data
= self
._download
_gql
(username
, {
118 'operationName': 'Media',
120 'limit': self
._PAGE
_SIZE
,
121 'offset': page
* self
._PAGE
_SIZE
,
126 query Media($q: String, $sort: String, $userId: ID, $offset: Int!, $limit: Int!) {
127 media(q: $q, sort: $sort, userId: $userId, offset: $offset, limit: $limit) {
132 'Downloading page {0}'.format(page
+ 1))
134 raise ExtractorError(f
'Failed to retrieve video list for page {page + 1}')
136 media
= data
['media']
139 yield self
.url_result('murrtube:{0}'.format(entry
['id']), MurrtubeIE
.ie_key())
141 def _real_extract(self
, url
):
142 username
= self
._match
_id
(url
)
143 data
= self
._download
_gql
(username
, {
144 'operationName': 'User',
149 query User($id: ID!) {
155 'Downloading user info')
157 raise ExtractorError('Failed to fetch user info')
161 entries
= OnDemandPagedList(functools
.partial(
162 self
._fetch
_page
, username
, user
.get('id')), self
._PAGE
_SIZE
)
164 return self
.playlist_result(entries
, username
)