Skip to content

Commit

Permalink
implement content sharing (client)
Browse files Browse the repository at this point in the history
  • Loading branch information
alkazar committed Jul 2, 2024
1 parent db1d467 commit c75ea1f
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 16 deletions.
115 changes: 115 additions & 0 deletions chimera_app/platforms/chimera_remote.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import os
import subprocess
import requests
import tempfile
from typing import List, Dict
from chimera_app.utils import upsert_file
from chimera_app.config import CONTENT_DIR
from chimera_app.config import UPLOADS_DIR
from chimera_app.shortcuts import PlatformShortcutsFile
from chimera_app.platforms.store_platform import StorePlatform, dic
from chimera_app.config import PLATFORMS, RESOURCE_DIR, BANNER_DIR, BIN_PATH
from chimera_app.steam_config import status_to_collection_name



class ChimeraRemote(StorePlatform):
def __init__(self, platform_id, server_ip):
super().__init__()
self.platform_code = platform_id
self.__server_ip = server_ip
self.__host = f'http://{server_ip}:8844'

def is_authenticated(self):
return True


def _get_all_content(self):
applications = []
api_response = requests.get(f'{self.__host}/share/platforms/{self.platform_code}', timeout=20)
if api_response.status_code == requests.codes.ok:
installed_list = self.__get_installed_list()
for entry in api_response.json():
name = entry['name']
content_filename = entry['content_filename']
content_download_url = self.__host + entry['content_download_url']
banner = self.__host + entry['banner']
poster = self.__host + entry['poster']
background = self.__host + entry['background']
logo = self.__host + entry['logo']
icon = self.__host + entry['icon']
installed = False

if name in installed_list:
installed = True

applications.append(dic({"content_id": name,
"summary": '',
"name": name,
"content_filename": content_filename,
"content_download_url": content_download_url,
"installed_version": None,
"available_version": None,
"image_url": banner,
"banner": banner,
"poster": poster,
"background": background,
"logo": logo,
"icon": icon,
"installed": installed,
"operation": None,
"status": None,
"status_icon": None,
"notes": None,
"launch_options": None
}))

return applications

def __get_installed_list(self) -> List[str]:
shortcuts_file = PlatformShortcutsFile(self.platform_code)
installed = shortcuts_file.get_shortcuts_data()
return [ game['name'] for game in installed if 'deleted' not in game or game['deleted'] != True ]

def get_shortcut(self, content):
shortcut = {
'name': content.name,
'hidden': False,
'cmd': PLATFORMS[self.platform_code]['cmd'],
'dir': '"' + os.path.join(CONTENT_DIR, self.platform_code) + '"',
'tags': [PLATFORMS[self.platform_code]['name']],
'params': '"' + content.content_filename + '"',
}

for img_type in [ 'banner', 'poster', 'background', 'logo', 'icon' ]:
img_path = self.get_image_path(content, img_type)
if img_path:
shortcut[img_type] = img_path

return shortcut

def _install(self, content) -> subprocess:
if not os.path.exists(UPLOADS_DIR):
os.makedirs(UPLOADS_DIR)
(_, tmp_path) = tempfile.mkstemp(dir=UPLOADS_DIR)
self.__temp_download_path = tmp_path

return subprocess.Popen(['curl', '--progress-bar', content.content_download_url, '-o', tmp_path],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)

def _post_install(self, content_id):
content = self.get_content(content_id)
content_path = upsert_file(self.__temp_download_path,
CONTENT_DIR,
self.platform_code,
content.name,
content.content_filename)

def _uninstall(self, content_id) -> subprocess:
# uninstall handled through regular shortcut deletion i.e. server.py:shortcut_delete
pass

def _update(self, content_id) -> subprocess:
# no updates for regular shortcut files
pass
10 changes: 9 additions & 1 deletion chimera_app/platforms/store_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ def get_available_content(self, listAll=False) -> list:
def _get_all_content(self) -> list:
pass

def _post_install(self, content_id):
pass

def get_content(self, content_id):
app = None
apps = self.get_installed_content()
Expand Down Expand Up @@ -190,4 +193,9 @@ def _update_progress(self, sp: subprocess, content_id):
if "%" in value:
self.tasks[content_id].progress = int(value.replace("%", "").split('.')[0])
buf.truncate(0)
del self.tasks[content_id]

if self.tasks[content_id].operation == 'Installing':
time.sleep(2)
self._post_install(content_id)

del self.tasks[content_id]
84 changes: 71 additions & 13 deletions chimera_app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from chimera_app.platforms.epic_store import EpicStore
from chimera_app.platforms.flathub import Flathub
from chimera_app.platforms.gog import GOG
from chimera_app.platforms.chimera_remote import ChimeraRemote
from chimera_app.shortcuts import PlatformShortcutsFile
from chimera_app.shortcuts import get_bpmbanner_id
import chimera_app.power as power
Expand All @@ -67,6 +68,25 @@
"gog": GOG(),
}

REMOTE_HANDLERS = {}
def find_remote_chimera():
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
client.bind(("", 48844))
while True:
data, addr = client.recvfrom(1024)
if data != b'chimera service v1':
continue
for platform in PLATFORMS:
REMOTE_HANDLERS[platform] = ChimeraRemote(platform, addr[0])
break

scan_thread = threading.Thread(target=find_remote_chimera)
scan_thread.start()


def refresh_local_password():
password = ''.join((secrets.choice(string.ascii_letters + string.digits) for i in range(100)))
Expand Down Expand Up @@ -116,7 +136,8 @@ def platform_page(platform):
showAll=False,
isInstalledOverview=True,
platform=platform,
platformName=PLATFORMS[platform]['name']
platformName=PLATFORMS[platform]['name'],
remote=False,
)
else:
return template('custom_login',
Expand Down Expand Up @@ -145,7 +166,8 @@ def platform_page(platform):
return template('platform.tpl',
shortcuts=data,
platform=platform,
platformName=PLATFORMS[platform]['name'])
platformName=PLATFORMS[platform]['name'],
remoteConnected=bool(REMOTE_HANDLERS))


@route('/library/<platform>/authenticate', method='POST')
Expand All @@ -172,18 +194,30 @@ def banners(platform, filename):
@route('/library/<platform>/new')
@authenticate
def new(platform):
handler = None
showAll = False
remote = False
if platform in PLATFORM_HANDLERS:
handler = PLATFORM_HANDLERS[platform]
showAll = request.query.showAll
elif request.query.remote == 'true':
handler = REMOTE_HANDLERS[platform]
showAll = True
remote = True

if handler:
if not authenticate_platform(platform):
return

return template(
'custom',
app_list=PLATFORM_HANDLERS[platform].get_available_content(request.query.showAll),
showAll=request.query.showAll,
app_list=handler.get_available_content(showAll),
showAll=showAll,
isInstalledOverview=False,
isNew=True,
platform=platform,
platformName=PLATFORMS[platform]['name']
platformName=PLATFORMS[platform]['name'],
remote=remote,
)
return template('new.tpl',
isNew=True,
Expand All @@ -200,13 +234,22 @@ def new(platform):
@authenticate
def edit(platform, name):
remoteLaunchEnabled = SETTINGS_HANDLER.get_setting('enable_remote_launch')

handler = None
remote = False
if platform in PLATFORM_HANDLERS:
handler = PLATFORM_HANDLERS[platform]
elif request.query.remote == 'true':
handler = REMOTE_HANDLERS[platform]
remote = True

if handler:
if not authenticate_platform(platform):
return

content_id = name
content = PLATFORM_HANDLERS[platform].get_content(content_id)
shortcut = PLATFORM_HANDLERS[platform].get_shortcut(content)
content = handler.get_content(content_id)
shortcut = handler.get_shortcut(content)
if content:
return template(
'custom_edit',
Expand All @@ -215,6 +258,7 @@ def edit(platform, name):
platformName=PLATFORMS[platform]['name'],
name=content_id,
steamShortcutID=(get_bpmbanner_id(shortcut['cmd'], shortcut['name']) if remoteLaunchEnabled else None),
remote=remote,
)
else:
abort(404, 'Content not found')
Expand Down Expand Up @@ -452,18 +496,26 @@ def delete_file_upload():
@route('/<platform>/install/<content_id>')
@authenticate
def platform_install(platform, content_id):
content = PLATFORM_HANDLERS[platform].get_content(content_id)
handler = None
redirect_url = f'/library/{platform}/edit/{content_id}'
if platform in PLATFORM_HANDLERS:
handler = PLATFORM_HANDLERS[platform]
else:
handler = REMOTE_HANDLERS[platform]
redirect_url = f'/library/{platform}/edit/{content_id}?remote=true'

content = handler.get_content(content_id)
if not content:
abort(404, 'Content not found')

PLATFORM_HANDLERS[platform].install_content(content)
handler.install_content(content)

shortcuts = PlatformShortcutsFile(platform)
shortcut = PLATFORM_HANDLERS[platform].get_shortcut(content)
shortcut = handler.get_shortcut(content)
shortcuts.add_shortcut(shortcut)
shortcuts.save()

PLATFORM_HANDLERS[platform].download_images(content)
handler.download_images(content)

if ('compat_tool' in shortcut
and shortcut['compat_tool'] in OFFICIAL_COMPAT_TOOLS):
Expand All @@ -475,7 +527,7 @@ def platform_install(platform, content_id):
except Exception as e:
print(e)

redirect(f'/library/{platform}/edit/{content_id}')
redirect(redirect_url)


@route('/<platform>/uninstall/<content_id>')
Expand Down Expand Up @@ -509,7 +561,13 @@ def content_update(platform, content_id):
@route('/<platform>/progress/<content_id>')
@authenticate
def install_progress(platform, content_id):
content = PLATFORM_HANDLERS[platform].get_content(content_id)
handler = None
if platform in PLATFORM_HANDLERS:
handler = PLATFORM_HANDLERS[platform]
else:
handler = REMOTE_HANDLERS[platform]

content = handler.get_content(content_id)
if not content:
abort(404, '{} not found'.format(content_id))

Expand Down
Binary file added images/add-remote.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions views/custom.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
% if isInstalledOverview:
<div class="app-container">
<div class="img-container">
<a href="/library/{{ platform }}/new">
<a href="/library/{{ platform }}/new{{ '?remote=true' if remote == True else '' }}">
<img src="/images/add.png" alt="Add new shortcut" title="Add new shortcut">
</a>
</div>
Expand All @@ -15,7 +15,7 @@
% for app in app_list:
<div class="app-container">
<div class="img-container">
<a href="/library/{{ platform }}/edit/{{ app.content_id }}">
<a href="/library/{{ platform }}/edit/{{ app.content_id }}{{ '?remote=true' if remote == True else '' }}">
<img src="{{ app.image_url }}" alt="{{ app.name }}" title="{{ app.get('status_icon') }} {{ app.name }}">
</a>
</div>
Expand Down
2 changes: 2 additions & 0 deletions views/custom_edit.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@
<button>Launch</button>
</form>
% end
% if not remote:
<form action="/{{platform}}/uninstall/{{app.content_id}}">
<button class="delete">Uninstall</button>
</form>
% end
% else:
<form action="/{{ platform }}/install/{{app.content_id}}">
<button class="add">Install</button>
Expand Down
7 changes: 7 additions & 0 deletions views/platform.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
<img src="/images/add.png" alt="Add new shortcut" title="Add new shortcut">
</a>
</div>
% if remoteConnected:
<div class="img-container">
<a href="/library/{{platform}}/new?remote=true">
<img src="/images/add-remote.png" alt="Add shortcut from remote source" title="Add shortcut from remote source">
</a>
</div>
% end

% from urllib.parse import quote
% for s in shortcuts:
Expand Down

0 comments on commit c75ea1f

Please sign in to comment.