123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import functools
- import json
- from .common import InfoExtractor
- from ..utils import (
- ExtractorError,
- OnDemandPagedList,
- determine_ext,
- int_or_none,
- try_get,
- )
- class MurrtubeIE(InfoExtractor):
- _VALID_URL = r'''(?x)
- (?:
- murrtube:|
- https?://murrtube\.net/videos/(?P<slug>[a-z0-9\-]+)\-
- )
- (?P<id>[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12})
- '''
- _TEST = {
- 'url': 'https://murrtube.net/videos/inferno-x-skyler-148b6f2a-fdcc-4902-affe-9c0f41aaaca0',
- 'md5': '169f494812d9a90914b42978e73aa690',
- 'info_dict': {
- 'id': '148b6f2a-fdcc-4902-affe-9c0f41aaaca0',
- 'ext': 'mp4',
- 'title': 'Inferno X Skyler',
- 'description': 'Humping a very good slutty sheppy (roomate)',
- 'thumbnail': r're:^https?://.*\.jpg$',
- 'duration': 284,
- 'uploader': 'Inferno Wolf',
- 'age_limit': 18,
- 'comment_count': int,
- 'view_count': int,
- 'like_count': int,
- 'tags': ['hump', 'breed', 'Fursuit', 'murrsuit', 'bareback'],
- }
- }
- def _download_gql(self, video_id, op, note=None, fatal=True):
- result = self._download_json(
- 'https://murrtube.net/graphql',
- video_id, note, data=json.dumps(op).encode(), fatal=fatal,
- headers={'Content-Type': 'application/json'})
- return result['data']
- def _real_extract(self, url):
- video_id = self._match_id(url)
- data = self._download_gql(video_id, {
- 'operationName': 'Medium',
- 'variables': {
- 'id': video_id,
- },
- 'query': '''\
- query Medium($id: ID!) {
- medium(id: $id) {
- title
- description
- key
- duration
- commentsCount
- likesCount
- viewsCount
- thumbnailKey
- tagList
- user {
- name
- __typename
- }
- __typename
- }
- }'''})
- meta = data['medium']
- storage_url = 'https://storage.murrtube.net/murrtube/'
- format_url = storage_url + meta.get('key', '')
- thumbnail = storage_url + meta.get('thumbnailKey', '')
- if determine_ext(format_url) == 'm3u8':
- formats = self._extract_m3u8_formats(
- format_url, video_id, 'mp4', entry_protocol='m3u8_native', fatal=False)
- else:
- formats = [{'url': format_url}]
- return {
- 'id': video_id,
- 'title': meta.get('title'),
- 'description': meta.get('description'),
- 'formats': formats,
- 'thumbnail': thumbnail,
- 'duration': int_or_none(meta.get('duration')),
- 'uploader': try_get(meta, lambda x: x['user']['name']),
- 'view_count': meta.get('viewsCount'),
- 'like_count': meta.get('likesCount'),
- 'comment_count': meta.get('commentsCount'),
- 'tags': meta.get('tagList'),
- 'age_limit': 18,
- }
- class MurrtubeUserIE(MurrtubeIE): # XXX: Do not subclass from concrete IE
- IE_DESC = 'Murrtube user profile'
- _VALID_URL = r'https?://murrtube\.net/(?P<id>[^/]+)$'
- _TEST = {
- 'url': 'https://murrtube.net/stormy',
- 'info_dict': {
- 'id': 'stormy',
- },
- 'playlist_mincount': 27,
- }
- _PAGE_SIZE = 10
- def _fetch_page(self, username, user_id, page):
- data = self._download_gql(username, {
- 'operationName': 'Media',
- 'variables': {
- 'limit': self._PAGE_SIZE,
- 'offset': page * self._PAGE_SIZE,
- 'sort': 'latest',
- 'userId': user_id,
- },
- 'query': '''\
- query Media($q: String, $sort: String, $userId: ID, $offset: Int!, $limit: Int!) {
- media(q: $q, sort: $sort, userId: $userId, offset: $offset, limit: $limit) {
- id
- __typename
- }
- }'''},
- 'Downloading page {0}'.format(page + 1))
- if data is None:
- raise ExtractorError(f'Failed to retrieve video list for page {page + 1}')
- media = data['media']
- for entry in media:
- yield self.url_result('murrtube:{0}'.format(entry['id']), MurrtubeIE.ie_key())
- def _real_extract(self, url):
- username = self._match_id(url)
- data = self._download_gql(username, {
- 'operationName': 'User',
- 'variables': {
- 'id': username,
- },
- 'query': '''\
- query User($id: ID!) {
- user(id: $id) {
- id
- __typename
- }
- }'''},
- 'Downloading user info')
- if data is None:
- raise ExtractorError('Failed to fetch user info')
- user = data['user']
- entries = OnDemandPagedList(functools.partial(
- self._fetch_page, username, user.get('id')), self._PAGE_SIZE)
- return self.playlist_result(entries, username)
|