mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-30 15:09:13 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e6b3834da | |||
| 6749c78ed7 | |||
| 4ad15568cd | |||
| 58849b3002 | |||
| 6346da6fe5 | |||
| af51a29055 | |||
| c546a818f1 | |||
| 0226bd2bf8 |
@@ -48,7 +48,7 @@ jobs:
|
||||
node-version: 18
|
||||
|
||||
- name: Set up Python 3.10 🐍
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -128,7 +128,7 @@
|
||||
"isDefault": true
|
||||
},
|
||||
"dependsOn": [
|
||||
"buildall",
|
||||
"buildfrontend",
|
||||
"deploy",
|
||||
"runpydeck"
|
||||
],
|
||||
|
||||
+13
-5
@@ -57,7 +57,7 @@ class PluginBrowser:
|
||||
if access(packageJsonPath, R_OK):
|
||||
with open(packageJsonPath, 'r') as f:
|
||||
packageJson = json.load(f)
|
||||
if len(packageJson["remote_binary"]) > 0:
|
||||
if "remote_binary" in packageJson and len(packageJson["remote_binary"]) > 0:
|
||||
# create bin directory if needed.
|
||||
rc=call(["chmod", "-R", "777", pluginBasePath])
|
||||
if access(pluginBasePath, W_OK):
|
||||
@@ -97,7 +97,7 @@ class PluginBrowser:
|
||||
plugin = json.load(f)
|
||||
|
||||
if plugin['name'] == name:
|
||||
return path.join(self.plugin_path, folder)
|
||||
return str(path.join(self.plugin_path, folder))
|
||||
except:
|
||||
logger.debug(f"skipping {folder}")
|
||||
|
||||
@@ -124,12 +124,15 @@ class PluginBrowser:
|
||||
self.loader.watcher.disabled = False
|
||||
|
||||
async def _install(self, artifact, name, version, hash):
|
||||
isInstalled = False
|
||||
if self.loader.watcher:
|
||||
self.loader.watcher.disabled = True
|
||||
try:
|
||||
await self.uninstall_plugin(name)
|
||||
pluginFolderPath = self.find_plugin_folder(name)
|
||||
if pluginFolderPath:
|
||||
isInstalled = True
|
||||
except:
|
||||
logger.error(f"Plugin {name} not installed, skipping uninstallation")
|
||||
logger.error(f"Failed to determine if {name} is already installed, continuing anyway.")
|
||||
logger.info(f"Installing {name} (Version: {version})")
|
||||
async with ClientSession() as client:
|
||||
logger.debug(f"Fetching {artifact}")
|
||||
@@ -139,6 +142,12 @@ class PluginBrowser:
|
||||
data = await res.read()
|
||||
logger.debug(f"Read {len(data)} bytes")
|
||||
res_zip = BytesIO(data)
|
||||
if isInstalled:
|
||||
try:
|
||||
logger.debug("Uninstalling existing plugin...")
|
||||
await self.uninstall_plugin(name)
|
||||
except:
|
||||
logger.error(f"Plugin {name} could not be uninstalled.")
|
||||
logger.debug("Unzipping...")
|
||||
ret = self._unzip_to_plugin_dir(res_zip, name, hash)
|
||||
if ret:
|
||||
@@ -151,7 +160,6 @@ class PluginBrowser:
|
||||
self.loader.plugins.pop(name, None)
|
||||
await sleep(1)
|
||||
self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_dir)
|
||||
# await inject_to_tab("SP", "window.syncDeckyPlugins()")
|
||||
else:
|
||||
logger.fatal(f"Failed Downloading Remote Binaries")
|
||||
else:
|
||||
|
||||
@@ -71,6 +71,15 @@ class PluginWrapper:
|
||||
self.log.error("Failed to start " + self.name + "!\n" + format_exc())
|
||||
exit(0)
|
||||
|
||||
async def _unload(self):
|
||||
try:
|
||||
self.log.info("Attempting to unload " + self.name + "\n")
|
||||
if hasattr(self.Plugin, "_unload"):
|
||||
await self.Plugin._unload(self.Plugin)
|
||||
except:
|
||||
self.log.error("Failed to unload " + self.name + "!\n" + format_exc())
|
||||
exit(0)
|
||||
|
||||
async def _setup_socket(self):
|
||||
self.socket = await start_unix_server(self._listen_for_method_call, path=self.socket_addr, limit=BUFFER_LIMIT)
|
||||
|
||||
@@ -90,6 +99,7 @@ class PluginWrapper:
|
||||
break
|
||||
data = loads(line.decode("utf-8"))
|
||||
if "stop" in data:
|
||||
await self._unload()
|
||||
get_event_loop().stop()
|
||||
while get_event_loop().is_running():
|
||||
await sleep(0)
|
||||
|
||||
@@ -104,14 +104,9 @@ class Updater:
|
||||
elif selectedBranch == 1:
|
||||
logger.debug("release type: pre-release")
|
||||
self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("-pre"), remoteVersions), None)
|
||||
# elif selectedBranch == 2:
|
||||
# logger.debug("release type: nightly")
|
||||
# self.remoteVer = next(filter(lambda ver: ver["prerelease"] and ver["tag_name"].startswith("v") and ver["tag_name"].find("nightly"), remoteVersions), None)
|
||||
else:
|
||||
logger.error("release type: NOT FOUND")
|
||||
raise ValueError("no valid branch found")
|
||||
# doesn't make it to this line below or farther
|
||||
# logger.debug("Remote Version: %s" % self.remoteVer.find("name"))
|
||||
logger.info("Updated remote version information")
|
||||
tab = await get_tab("SP")
|
||||
await tab.evaluate_js(f"window.DeckyPluginLoader.notifyUpdates()", False, True, False)
|
||||
|
||||
@@ -15,18 +15,8 @@ export default function GeneralSettings({
|
||||
setIsDeveloper: (val: boolean) => void;
|
||||
}) {
|
||||
const [pluginURL, setPluginURL] = useState('');
|
||||
// const [checked, setChecked] = useState(false); // store these in some kind of State instead
|
||||
return (
|
||||
<div>
|
||||
{/* <Field
|
||||
label="A Toggle with an icon"
|
||||
icon={<FaShapes style={{ display: 'block' }} />}
|
||||
>
|
||||
<Toggle
|
||||
value={checked}
|
||||
onChange={(e) => setChecked(e)}
|
||||
/>
|
||||
</Field> */}
|
||||
<UpdaterSettings />
|
||||
<BranchSelect />
|
||||
<RemoteDebuggingSettings />
|
||||
|
||||
@@ -11,17 +11,10 @@ import {
|
||||
} from 'decky-frontend-lib';
|
||||
import { FC, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
LegacyStorePlugin,
|
||||
StorePlugin,
|
||||
StorePluginVersion,
|
||||
isLegacyPlugin,
|
||||
requestLegacyPluginInstall,
|
||||
requestPluginInstall,
|
||||
} from '../../store';
|
||||
import { StorePlugin, StorePluginVersion, requestPluginInstall } from '../../store';
|
||||
|
||||
interface PluginCardProps {
|
||||
plugin: StorePlugin | LegacyStorePlugin;
|
||||
plugin: StorePlugin;
|
||||
}
|
||||
|
||||
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
@@ -63,22 +56,13 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
}}
|
||||
>
|
||||
<div className="deckyStoreCardHeader" style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<a
|
||||
<div
|
||||
style={{ fontSize: '18pt', padding: '10px' }}
|
||||
className={joinClassNames(staticClasses.Text)}
|
||||
// onClick={() => Router.NavigateToExternalWeb('https://github.com/' + plugin.artifact)}
|
||||
>
|
||||
{isLegacyPlugin(plugin) ? (
|
||||
<div className="deckyStoreCardNameContainer">
|
||||
<span className="deckyStoreCardLegacyRepoOwner" style={{ color: 'grey' }}>
|
||||
{plugin.artifact.split('/')[0]}/
|
||||
</span>
|
||||
{plugin.artifact.split('/')[1]}
|
||||
</div>
|
||||
) : (
|
||||
plugin.name
|
||||
)}
|
||||
</a>
|
||||
{plugin.name}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@@ -94,17 +78,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
width: 'auto',
|
||||
height: '160px',
|
||||
}}
|
||||
src={
|
||||
isLegacyPlugin(plugin)
|
||||
? `https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/artifact_images/${plugin.artifact.replace(
|
||||
'/',
|
||||
'_',
|
||||
)}.png`
|
||||
: `https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/artifact_images/${plugin.name.replace(
|
||||
'/',
|
||||
'_',
|
||||
)}.png`
|
||||
}
|
||||
src={plugin.image_url}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
@@ -152,19 +126,6 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
{tag == 'root' ? 'Requires root' : tag}
|
||||
</span>
|
||||
))}
|
||||
{isLegacyPlugin(plugin) && (
|
||||
<span
|
||||
className="deckyStoreCardTag deckyStoreCardLegacyTag"
|
||||
style={{
|
||||
color: '#232120',
|
||||
padding: '5px',
|
||||
borderRadius: '5px',
|
||||
background: '#EDE841',
|
||||
}}
|
||||
>
|
||||
legacy
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -194,11 +155,7 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
<DialogButton
|
||||
className="deckyStoreCardInstallButton"
|
||||
ref={buttonRef}
|
||||
onClick={() =>
|
||||
isLegacyPlugin(plugin)
|
||||
? requestLegacyPluginInstall(plugin, Object.keys(plugin.versions)[selectedOption])
|
||||
: requestPluginInstall(plugin.name, plugin.versions[selectedOption])
|
||||
}
|
||||
onClick={() => requestPluginInstall(plugin.name, plugin.versions[selectedOption])}
|
||||
>
|
||||
Install
|
||||
</DialogButton>
|
||||
@@ -211,15 +168,10 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||
>
|
||||
<Dropdown
|
||||
rgOptions={
|
||||
(isLegacyPlugin(plugin)
|
||||
? Object.keys(plugin.versions).map((v, k) => ({
|
||||
data: k,
|
||||
label: v,
|
||||
}))
|
||||
: plugin.versions.map((version: StorePluginVersion, index) => ({
|
||||
data: index,
|
||||
label: version.name,
|
||||
}))) as SingleDropdownOption[]
|
||||
plugin.versions.map((version: StorePluginVersion, index) => ({
|
||||
data: index,
|
||||
label: version.name,
|
||||
})) as SingleDropdownOption[]
|
||||
}
|
||||
strDefaultLabel={'Select a version'}
|
||||
selectedOption={selectedOption}
|
||||
|
||||
@@ -2,14 +2,13 @@ import { SteamSpinner } from 'decky-frontend-lib';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
|
||||
import Logger from '../../logger';
|
||||
import { LegacyStorePlugin, StorePlugin, getLegacyPluginList, getPluginList } from '../../store';
|
||||
import { StorePlugin, getPluginList } from '../../store';
|
||||
import PluginCard from './PluginCard';
|
||||
|
||||
const logger = new Logger('FilePicker');
|
||||
|
||||
const StorePage: FC<{}> = () => {
|
||||
const [data, setData] = useState<StorePlugin[] | null>(null);
|
||||
const [legacyData, setLegacyData] = useState<LegacyStorePlugin[] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -17,11 +16,6 @@ const StorePage: FC<{}> = () => {
|
||||
logger.log('got data!', res);
|
||||
setData(res);
|
||||
})();
|
||||
(async () => {
|
||||
const res = await getLegacyPluginList();
|
||||
logger.log('got legacy data!', res);
|
||||
setLegacyData(res);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -49,11 +43,6 @@ const StorePage: FC<{}> = () => {
|
||||
{data.map((plugin: StorePlugin) => (
|
||||
<PluginCard plugin={plugin} />
|
||||
))}
|
||||
{!legacyData ? (
|
||||
<SteamSpinner />
|
||||
) : (
|
||||
legacyData.map((plugin: LegacyStorePlugin) => <PluginCard plugin={plugin} />)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+7
-49
@@ -1,5 +1,3 @@
|
||||
import { ConfirmModal, showModal, staticClasses } from 'decky-frontend-lib';
|
||||
|
||||
import { Plugin } from './plugin';
|
||||
|
||||
export interface StorePluginVersion {
|
||||
@@ -14,30 +12,19 @@ export interface StorePlugin {
|
||||
author: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export interface LegacyStorePlugin {
|
||||
artifact: string;
|
||||
versions: {
|
||||
[version: string]: string;
|
||||
};
|
||||
author: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
image_url: string;
|
||||
}
|
||||
|
||||
// name: version
|
||||
export type PluginUpdateMapping = Map<string, StorePluginVersion>;
|
||||
|
||||
export function getPluginList(): Promise<StorePlugin[]> {
|
||||
return fetch('https://beta.deckbrew.xyz/plugins', {
|
||||
method: 'GET',
|
||||
}).then((r) => r.json());
|
||||
}
|
||||
|
||||
export function getLegacyPluginList(): Promise<LegacyStorePlugin[]> {
|
||||
return fetch('https://plugins.deckbrew.xyz/get_plugins', {
|
||||
export async function getPluginList(): Promise<StorePlugin[]> {
|
||||
let version = await window.DeckyPluginLoader.updateVersion();
|
||||
return fetch('https://plugins.deckbrew.xyz/plugins', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Decky-Version': version.current,
|
||||
},
|
||||
}).then((r) => r.json());
|
||||
}
|
||||
|
||||
@@ -49,31 +36,6 @@ export async function installFromURL(url: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export function requestLegacyPluginInstall(plugin: LegacyStorePlugin, selectedVer: string) {
|
||||
showModal(
|
||||
<ConfirmModal
|
||||
onOK={() => {
|
||||
window.DeckyPluginLoader.callServerMethod('install_plugin', {
|
||||
name: plugin.artifact,
|
||||
artifact: `https://github.com/${plugin.artifact}/archive/refs/tags/${selectedVer}.zip`,
|
||||
version: selectedVer,
|
||||
hash: plugin.versions[selectedVer],
|
||||
});
|
||||
}}
|
||||
onCancel={() => {
|
||||
// do nothing
|
||||
}}
|
||||
>
|
||||
<div className={staticClasses.Title} style={{ flexDirection: 'column', boxShadow: 'unset' }}>
|
||||
Using legacy plugins
|
||||
</div>
|
||||
You are currently installing a <b>legacy</b> plugin. Legacy plugins are no longer supported and may have issues.
|
||||
Legacy plugins do not support gamepad input. To interact with a legacy plugin, you will need to use the
|
||||
touchscreen.
|
||||
</ConfirmModal>,
|
||||
);
|
||||
}
|
||||
|
||||
export async function requestPluginInstall(plugin: string, selectedVer: StorePluginVersion) {
|
||||
await window.DeckyPluginLoader.callServerMethod('install_plugin', {
|
||||
name: plugin,
|
||||
@@ -94,7 +56,3 @@ export async function checkForUpdates(plugins: Plugin[]): Promise<PluginUpdateMa
|
||||
}
|
||||
return updateMap;
|
||||
}
|
||||
|
||||
export function isLegacyPlugin(plugin: LegacyStorePlugin | StorePlugin): plugin is LegacyStorePlugin {
|
||||
return 'artifact' in plugin;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user