1
0
Fork 0
mirror of https://github.com/yt-dlp/yt-dlp.git synced 2025-03-09 12:50:23 -05:00
This commit is contained in:
florty2 2025-03-09 18:12:21 +11:00 committed by GitHub
commit f320578306
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 188 additions and 0 deletions

View file

@ -1219,6 +1219,7 @@
MxplayerIE,
MxplayerShowIE,
)
from .myfreecams import MyFreeCamsIE
from .myspace import (
MySpaceAlbumIE,
MySpaceIE,

View file

@ -0,0 +1,187 @@
import random
import re
import urllib.parse
from .common import InfoExtractor
from ..dependencies import websockets
from ..utils import ExtractorError
class MyFreeCamsIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?myfreecams\.com/#(?P<id>[^/?&#]+)'
_TESTS = [{
'url': 'https://www.myfreecams.com/#Elise_wood',
'md5': 'TODO: md5 sum of the first 10241 bytes of the video file (use --test)',
'info_dict': {
'id': 'Elise_wood',
'ext': 'mp4',
'title': r're:Elise_wood',
'description': r're:MyFreeCams',
'age_limit': 18,
'is_live': True,
'live_status': str,
},
'params': {
'skip_download': True,
},
'skip': 'Model is currently offline',
}]
JS_SERVER_URL = 'https://www.myfreecams.com/_js/serverconfig.js'
_dict_re = re.compile(r'''(?P<data>{.*})''')
_socket_re = re.compile(r'''(\w+) (\w+) (\w+) (\w+) (\w+)''')
def _get_servers(self):
return self._download_json(
self.JS_SERVER_URL, self.video_id,
headers={
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
}, fatal=False, impersonate=False) or {}
def _websocket_data(self, username, chat_servers):
try_to_connect = 0
xchat = None
host = None
while try_to_connect < 5:
try:
xchat = str(random.choice(chat_servers))
host = f'wss://{xchat}.myfreecams.com/fcsl'
ws = websockets.sync.client.connect(host)
ws.send('fcsws_20180422\n\0')
ws.send('1 0 0 20071025 0 1/guest:guest\n\0')
self.write_debug(f'Websocket server {xchat} connected')
self.write_debug(f'Websocket URL: {host}')
try_to_connect = 5
except websockets.exceptions.WebSocketException:
try_to_connect += 1
self.report_warning(f'Failed to connect to WS server: {xchat} - try {try_to_connect}')
if try_to_connect == 5:
error = f'Failed to connect to WS server: {host}'
raise ExtractorError(error)
buff = ''
php_message = ''
ws_close = 0
while ws_close == 0:
socket_buffer = ws.recv()
socket_buffer = buff + socket_buffer
buff = ''
while True:
ws_answer = self._socket_re.search(socket_buffer)
if bool(ws_answer) == 0:
break
FC = ws_answer.group(1)
FCTYPE = int(FC[6:])
message_length = int(FC[0:6])
message = socket_buffer[6:6 + message_length]
if len(message) < message_length:
buff = ''.join(socket_buffer)
break
message = urllib.parse.unquote(message)
if FCTYPE == 1 and username:
ws.send(f'10 0 0 20 0 {username}\n')
elif FCTYPE == 81:
php_message = message
if username is None:
ws_close = 1
elif FCTYPE == 10:
ws_close = 1
socket_buffer = socket_buffer[6 + message_length:]
if len(socket_buffer) == 0:
break
ws.send('99 0 0 0 0')
ws.close()
return message, php_message
def _get_camserver(self, servers, key):
server_types = ['h5video_servers', 'wzobs_servers', 'ngvideo_servers']
for server_type in server_types:
if servers[server_type].get(str(key)):
return servers[server_type][str(key)], server_type
return None, None
def _get_streams(self, url):
self.video_id = self._match_id(url)
webpage = self._download_webpage(url, self.video_id)
servers = self._get_servers()
chat_servers = servers['chat_servers']
message, php_message = self._websocket_data(self.video_id, chat_servers)
self.write_debug('Attempting to use WebSocket data')
data = self._search_json(r'(\w+) (\w+) (\w+) (\w+) (\w+)', message, name='ws_data', video_id=self.video_id)
if not data:
raise ExtractorError('Could not find data in WebSocket message')
try:
vs = data['vs']
ok_vs = [0, 90]
if vs not in ok_vs:
error_messages = {
2: 'Model is currently away',
12: 'Model is currently in a private show',
13: 'Model is currently in a group show',
14: 'Model is currently in a club show',
127: 'Model is currently offline',
}
error = error_messages.get(vs, f'Stream status: {vs}')
raise ExtractorError(error, expected=True)
self.write_debug(f'VS: {vs}')
nm = data['nm']
uid = data['uid']
uid_video = uid + 100000000
camserver = data['u']['camserv']
except KeyError:
raise ExtractorError('Could not find required data in WebSocket message')
server, server_type = self._get_camserver(servers, camserver)
self.write_debug(f'Username: {nm}')
self.write_debug(f'User ID: {uid}')
if not server:
raise ExtractorError('Missing video server')
self.write_debug(f'Video server: {server}')
self.write_debug(f'Video server_type: {server_type}')
if server_type == 'h5video_servers':
# DASH_VIDEO_URL = f'https://{server}.myfreecams.com/NxServer/ngrp:mfc_{uid_video}.f4v_desktop/manifest.mpd'
HLS_VIDEO_URL = f'https://{server}.myfreecams.com/NxServer/ngrp:mfc_{uid_video}.f4v_mobile/playlist.m3u8'
elif server_type == 'wzobs_servers':
# DASH_VIDEO_URL = ''
HLS_VIDEO_URL = f'https://{server}.myfreecams.com/NxServer/ngrp:mfc_a_{uid_video}.f4v_mobile/playlist.m3u8'
elif server_type == 'ngvideo_servers':
raise ExtractorError('ngvideo_servers are not supported.')
else:
raise ExtractorError('Unknow server type.')
self.write_debug(f'HLS URL: {HLS_VIDEO_URL}')
return {
'id': self.video_id,
'title': self._html_extract_title(html=webpage, default=self.video_id),
'description': self._html_search_meta('description', webpage, default=None),
'formats': self._extract_m3u8_formats(HLS_VIDEO_URL, self.video_id, ext='mp4', live=True),
'age_limit': self._rta_search(webpage),
'is_live': True,
}
def _real_extract(self, url):
if not websockets:
raise ImportError('This extractor needs websockets installed')
self.write_debug(self._get_streams(url=url))
return self._get_streams(url=url)