]>
Commit | Line | Data |
---|---|---|
81228319 | 1 | # coding: utf-8 |
2 | from __future__ import unicode_literals | |
3 | ||
4 | import functools | |
5 | import json | |
6 | ||
7 | from .common import InfoExtractor | |
8 | from ..utils import ( | |
9 | ExtractorError, | |
10 | OnDemandPagedList, | |
11 | determine_ext, | |
12 | int_or_none, | |
13 | try_get, | |
14 | ) | |
15 | ||
16 | ||
17 | class MurrtubeIE(InfoExtractor): | |
18 | _VALID_URL = r'''(?x) | |
19 | (?: | |
20 | murrtube:| | |
21 | https?://murrtube\.net/videos/(?P<slug>[a-z0-9\-]+)\- | |
22 | ) | |
23 | (?P<id>[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}) | |
24 | ''' | |
25 | _TEST = { | |
26 | 'url': 'https://murrtube.net/videos/inferno-x-skyler-148b6f2a-fdcc-4902-affe-9c0f41aaaca0', | |
27 | 'md5': '169f494812d9a90914b42978e73aa690', | |
28 | 'info_dict': { | |
29 | 'id': '148b6f2a-fdcc-4902-affe-9c0f41aaaca0', | |
30 | 'ext': 'mp4', | |
31 | 'title': 'Inferno X Skyler', | |
32 | 'description': 'Humping a very good slutty sheppy (roomate)', | |
33 | 'thumbnail': r're:^https?://.*\.jpg$', | |
34 | 'duration': 284, | |
35 | 'uploader': 'Inferno Wolf', | |
36 | 'age_limit': 18, | |
37 | 'comment_count': int, | |
38 | 'view_count': int, | |
39 | 'like_count': int, | |
40 | 'tags': ['hump', 'breed', 'Fursuit', 'murrsuit', 'bareback'], | |
41 | } | |
42 | } | |
43 | ||
44 | def _download_gql(self, video_id, op, note=None, fatal=True): | |
45 | result = self._download_json( | |
46 | 'https://murrtube.net/graphql', | |
47 | video_id, note, data=json.dumps(op).encode(), fatal=fatal, | |
48 | headers={'Content-Type': 'application/json'}) | |
49 | return result['data'] | |
50 | ||
51 | def _real_extract(self, url): | |
52 | video_id = self._match_id(url) | |
53 | data = self._download_gql(video_id, { | |
54 | 'operationName': 'Medium', | |
55 | 'variables': { | |
56 | 'id': video_id, | |
57 | }, | |
58 | 'query': '''\ | |
59 | query Medium($id: ID!) { | |
60 | medium(id: $id) { | |
61 | title | |
62 | description | |
63 | key | |
64 | duration | |
65 | commentsCount | |
66 | likesCount | |
67 | viewsCount | |
68 | thumbnailKey | |
69 | tagList | |
70 | user { | |
71 | name | |
72 | __typename | |
73 | } | |
74 | __typename | |
75 | } | |
76 | }'''}) | |
77 | meta = data['medium'] | |
78 | ||
79 | storage_url = 'https://storage.murrtube.net/murrtube/' | |
80 | format_url = storage_url + meta.get('key', '') | |
81 | thumbnail = storage_url + meta.get('thumbnailKey', '') | |
82 | ||
83 | if determine_ext(format_url) == 'm3u8': | |
84 | formats = self._extract_m3u8_formats( | |
85 | format_url, video_id, 'mp4', entry_protocol='m3u8_native', fatal=False) | |
86 | else: | |
87 | formats = [{'url': format_url}] | |
88 | ||
89 | return { | |
90 | 'id': video_id, | |
91 | 'title': meta.get('title'), | |
92 | 'description': meta.get('description'), | |
93 | 'formats': formats, | |
94 | 'thumbnail': thumbnail, | |
95 | 'duration': int_or_none(meta.get('duration')), | |
96 | 'uploader': try_get(meta, lambda x: x['user']['name']), | |
97 | 'view_count': meta.get('viewsCount'), | |
98 | 'like_count': meta.get('likesCount'), | |
99 | 'comment_count': meta.get('commentsCount'), | |
100 | 'tags': meta.get('tagList'), | |
101 | 'age_limit': 18, | |
102 | } | |
103 | ||
104 | ||
105 | class MurrtubeUserIE(MurrtubeIE): | |
106 | IE_DESC = 'Murrtube user profile' | |
107 | _VALID_URL = r'https?://murrtube\.net/(?P<id>[^/]+)$' | |
108 | _TEST = { | |
109 | 'url': 'https://murrtube.net/stormy', | |
110 | 'info_dict': { | |
111 | 'id': 'stormy', | |
112 | }, | |
113 | 'playlist_mincount': 27, | |
114 | } | |
115 | _PAGE_SIZE = 10 | |
116 | ||
117 | def _fetch_page(self, username, user_id, page): | |
118 | data = self._download_gql(username, { | |
119 | 'operationName': 'Media', | |
120 | 'variables': { | |
121 | 'limit': self._PAGE_SIZE, | |
122 | 'offset': page * self._PAGE_SIZE, | |
123 | 'sort': 'latest', | |
124 | 'userId': user_id, | |
125 | }, | |
126 | 'query': '''\ | |
127 | query Media($q: String, $sort: String, $userId: ID, $offset: Int!, $limit: Int!) { | |
128 | media(q: $q, sort: $sort, userId: $userId, offset: $offset, limit: $limit) { | |
129 | id | |
130 | __typename | |
131 | } | |
132 | }'''}, | |
133 | 'Downloading page {0}'.format(page + 1)) | |
134 | if data is None: | |
135 | raise ExtractorError(f'Failed to retrieve video list for page {page + 1}') | |
136 | ||
137 | media = data['media'] | |
138 | ||
139 | for entry in media: | |
140 | yield self.url_result('murrtube:{0}'.format(entry['id']), MurrtubeIE.ie_key()) | |
141 | ||
142 | def _real_extract(self, url): | |
143 | username = self._match_id(url) | |
144 | data = self._download_gql(username, { | |
145 | 'operationName': 'User', | |
146 | 'variables': { | |
147 | 'id': username, | |
148 | }, | |
149 | 'query': '''\ | |
150 | query User($id: ID!) { | |
151 | user(id: $id) { | |
152 | id | |
153 | __typename | |
154 | } | |
155 | }'''}, | |
156 | 'Downloading user info') | |
157 | if data is None: | |
158 | raise ExtractorError('Failed to fetch user info') | |
159 | ||
160 | user = data['user'] | |
161 | ||
162 | entries = OnDemandPagedList(functools.partial( | |
163 | self._fetch_page, username, user.get('id')), self._PAGE_SIZE) | |
164 | ||
165 | return self.playlist_result(entries, username) |