summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Borges <victor1882@outlook.com>2025-05-03 15:19:10 -0300
committerVictor Borges <victor1882@outlook.com>2025-05-03 15:19:10 -0300
commitd97d5f11e7807906cff2cf41cad815fb05b0096c (patch)
treef5782c1785fa35586be12d871cfc00a35f0569a5
parentba7a86099b321f378b9d18ea025af9730642d8bf (diff)
downloaddecky-bazzite-buddy-d97d5f11e7807906cff2cf41cad815fb05b0096c.tar.gz
decky-bazzite-buddy-d97d5f11e7807906cff2cf41cad815fb05b0096c.zip
gets the correct branch now
-rw-r--r--.editorconfig7
-rw-r--r--decky.pyi184
-rw-r--r--main.py9
-rw-r--r--src/FetchReleases.ts47
-rw-r--r--src/PartnerEventStorePatch.tsx62
-rwxr-xr-xsrc/index.tsx21
6 files changed, 264 insertions, 66 deletions
diff --git a/.editorconfig b/.editorconfig
index f666140..a1c77bf 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,7 +1,12 @@
[*]
charset = utf-8
end_of_line = lf
-indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 120
+
+[{*.ts,*.tsx}]
+indent_size = 2
+
+[{*.py,*.pyi}]
+indent_size = 4
diff --git a/decky.pyi b/decky.pyi
new file mode 100644
index 0000000..066f085
--- /dev/null
+++ b/decky.pyi
@@ -0,0 +1,184 @@
+"""
+This module exposes various constants and helpers useful for decky plugins.
+
+* Plugin's settings and configurations should be stored under `DECKY_PLUGIN_SETTINGS_DIR`.
+* Plugin's runtime data should be stored under `DECKY_PLUGIN_RUNTIME_DIR`.
+* Plugin's persistent log files should be stored under `DECKY_PLUGIN_LOG_DIR`.
+
+Avoid writing outside of `DECKY_HOME`, storing under the suggested paths is strongly recommended.
+
+Some basic migration helpers are available: `migrate_any`, `migrate_settings`, `migrate_runtime`, `migrate_logs`.
+
+A logging facility `logger` is available which writes to the recommended location.
+"""
+
+__version__ = '1.0.0'
+
+import logging
+
+from typing import Any
+
+"""
+Constants
+"""
+
+HOME: str
+"""
+The home directory of the effective user running the process.
+Environment variable: `HOME`.
+If `root` was specified in the plugin's flags it will be `/root` otherwise the user whose home decky resides in.
+e.g.: `/home/deck`
+"""
+
+USER: str
+"""
+The effective username running the process.
+Environment variable: `USER`.
+It would be `root` if `root` was specified in the plugin's flags otherwise the user whose home decky resides in.
+e.g.: `deck`
+"""
+
+DECKY_VERSION: str
+"""
+The version of the decky loader.
+Environment variable: `DECKY_VERSION`.
+e.g.: `v2.5.0-pre1`
+"""
+
+DECKY_USER: str
+"""
+The user whose home decky resides in.
+Environment variable: `DECKY_USER`.
+e.g.: `deck`
+"""
+
+DECKY_USER_HOME: str
+"""
+The home of the user where decky resides in.
+Environment variable: `DECKY_USER_HOME`.
+e.g.: `/home/deck`
+"""
+
+DECKY_HOME: str
+"""
+The root of the decky folder.
+Environment variable: `DECKY_HOME`.
+e.g.: `/home/deck/homebrew`
+"""
+
+DECKY_PLUGIN_SETTINGS_DIR: str
+"""
+The recommended path in which to store configuration files (created automatically).
+Environment variable: `DECKY_PLUGIN_SETTINGS_DIR`.
+e.g.: `/home/deck/homebrew/settings/decky-plugin-template`
+"""
+
+DECKY_PLUGIN_RUNTIME_DIR: str
+"""
+The recommended path in which to store runtime data (created automatically).
+Environment variable: `DECKY_PLUGIN_RUNTIME_DIR`.
+e.g.: `/home/deck/homebrew/data/decky-plugin-template`
+"""
+
+DECKY_PLUGIN_LOG_DIR: str
+"""
+The recommended path in which to store persistent logs (created automatically).
+Environment variable: `DECKY_PLUGIN_LOG_DIR`.
+e.g.: `/home/deck/homebrew/logs/decky-plugin-template`
+"""
+
+DECKY_PLUGIN_DIR: str
+"""
+The root of the plugin's directory.
+Environment variable: `DECKY_PLUGIN_DIR`.
+e.g.: `/home/deck/homebrew/plugins/decky-plugin-template`
+"""
+
+DECKY_PLUGIN_NAME: str
+"""
+The name of the plugin as specified in the 'plugin.json'.
+Environment variable: `DECKY_PLUGIN_NAME`.
+e.g.: `Example Plugin`
+"""
+
+DECKY_PLUGIN_VERSION: str
+"""
+The version of the plugin as specified in the 'package.json'.
+Environment variable: `DECKY_PLUGIN_VERSION`.
+e.g.: `0.0.1`
+"""
+
+DECKY_PLUGIN_AUTHOR: str
+"""
+The author of the plugin as specified in the 'plugin.json'.
+Environment variable: `DECKY_PLUGIN_AUTHOR`.
+e.g.: `John Doe`
+"""
+
+DECKY_PLUGIN_LOG: str
+"""
+The path to the plugin's main logfile.
+Environment variable: `DECKY_PLUGIN_LOG`.
+e.g.: `/home/deck/homebrew/logs/decky-plugin-template/plugin.log`
+"""
+
+"""
+Migration helpers
+"""
+
+
+def migrate_any(target_dir: str, *files_or_directories: str) -> dict[str, str]:
+ """
+ Migrate files and directories to a new location and remove old locations.
+ Specified files will be migrated to `target_dir`.
+ Specified directories will have their contents recursively migrated to `target_dir`.
+
+ Returns the mapping of old -> new location.
+ """
+
+
+def migrate_settings(*files_or_directories: str) -> dict[str, str]:
+ """
+ Migrate files and directories relating to plugin settings to the recommended location and remove old locations.
+ Specified files will be migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
+ Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
+
+ Returns the mapping of old -> new location.
+ """
+
+
+def migrate_runtime(*files_or_directories: str) -> dict[str, str]:
+ """
+ Migrate files and directories relating to plugin runtime data to the recommended location and remove old locations
+ Specified files will be migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
+ Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
+
+ Returns the mapping of old -> new location.
+ """
+
+
+def migrate_logs(*files_or_directories: str) -> dict[str, str]:
+ """
+ Migrate files and directories relating to plugin logs to the recommended location and remove old locations.
+ Specified files will be migrated to `DECKY_PLUGIN_LOG_DIR`.
+ Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_LOG_DIR`.
+
+ Returns the mapping of old -> new location.
+ """
+
+
+"""
+Logging
+"""
+
+logger: logging.Logger
+"""The main plugin logger writing to `DECKY_PLUGIN_LOG`."""
+
+"""
+Event handling
+"""
+async def emit(event: str, *args: Any) -> None:
+ """
+ Send an event to the frontend.
+ """
+
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..a1690fb
--- /dev/null
+++ b/main.py
@@ -0,0 +1,9 @@
+class Plugin:
+ # noinspection PyMethodMayBeStatic
+ async def get_bazzite_branch(self) -> str | None:
+ try:
+ file = open("/etc/bazzite/image_branch")
+ branch = file.read()
+ return branch
+ finally:
+ return "stable"
diff --git a/src/FetchReleases.ts b/src/FetchReleases.ts
new file mode 100644
index 0000000..04f42d6
--- /dev/null
+++ b/src/FetchReleases.ts
@@ -0,0 +1,47 @@
+import {callable} from "@decky/api";
+
+const getBazziteBranch = callable<[], string>("get_bazzite_branch");
+
+export async function isBazziteBranchTesting() {
+ const branch = await getBazziteBranch();
+ return branch === "testing";
+}
+
+export async function* fetchReleases(signal?: AbortSignal) {
+ const branch = await getBazziteBranch();
+ const testing = branch === "stable";
+ let currentPage = 1;
+ let done = false;
+
+ while (!done) {
+ let response: Response;
+ let responseJson: any;
+
+ try {
+ response = await fetch(
+ `https://api.github.com/repos/ublue-os/bazzite/releases?page=${currentPage++}&per_page=10`,
+ { signal });
+
+ if (response.ok) {
+ responseJson = await response.json();
+ } else {
+ responseJson = [];
+ }
+ } catch {
+ responseJson = [];
+ }
+
+ if (!Array.isArray(responseJson) || responseJson.length == 0) {
+ done = true;
+ } else {
+ responseJson.sort((a, b) => (new Date(b.created_at)).getTime() - (new Date(a.created_at)).getTime());
+
+ for (let release of responseJson) {
+ if (release && ((testing && release.prerelease) || (!testing && !release.prerelease)))
+ yield release;
+ }
+ }
+ }
+
+ return undefined;
+}
diff --git a/src/PartnerEventStorePatch.tsx b/src/PartnerEventStorePatch.tsx
index 75b8ed2..f7df4b3 100644
--- a/src/PartnerEventStorePatch.tsx
+++ b/src/PartnerEventStorePatch.tsx
@@ -6,6 +6,7 @@ import remarkGfm from "remark-gfm"
import {unified} from "unified"
import html2bbcode from "./html2bbcode";
import {Mutex} from 'async-mutex';
+import {fetchReleases, isBazziteBranchTesting} from "./FetchReleases";
const PartnerEventStore = findModuleExport(
(e) => e?.prototype?.InternalLoadAdjacentPartnerEvents
@@ -31,15 +32,14 @@ const SteamID = findModuleExport(
const steamClanSteamID = "103582791470414830";
const steamClanID = "40893422";
const steamOSAppId = 1675200;
-const githubReleasesURI = "https://api.github.com/repos/ublue-os/bazzite/releases";
let generator: AsyncGenerator<any, undefined, unknown>;
const mutex = new Mutex();
const cachedGithubReleases: { gid: string, release: any }[] = [];
enum SteamEventType {
- SmallUpdate = 12,
+ // SmallUpdate = 12,
Update = 13,
- BigUpdate = 14,
+ // BigUpdate = 14,
}
type SteamTags = {
@@ -49,7 +49,7 @@ type SteamTags = {
enum SteamOSChannel {
Stable = "stablechannel",
Beta = "betachannel",
- Preview = "previewchannel",
+ // Preview = "previewchannel",
}
export function patchPartnerEventStore(): Patch[] {
@@ -138,7 +138,7 @@ async function LoadBazziteReleasesAsPartnerEvents(module: any, gid: any, tags: S
}
if (cachedGithubReleases.length === 0) {
- await fetchMoreReleases(countAfter, isBetaOrPreviewChannel(tags));
+ await fetchMoreReleases(countAfter);
}
const releaseIndex = gid ? cachedGithubReleases.findIndex((e: any) => e.gid === gid) : -1;
@@ -149,7 +149,7 @@ async function LoadBazziteReleasesAsPartnerEvents(module: any, gid: any, tags: S
} else {
if (releaseIndex + countAfter + 1 > cachedGithubReleases.length) {
const toFetch = releaseIndex + countAfter + 1 - cachedGithubReleases.length;
- await fetchMoreReleases(toFetch, isBetaOrPreviewChannel(tags));
+ await fetchMoreReleases(toFetch);
}
releases = cachedGithubReleases.slice(Math.max(releaseIndex - countBefore + 1, 0), releaseIndex + countAfter + 1);
@@ -195,7 +195,7 @@ async function LoadBazziteReleasesAsPartnerEvents(module: any, gid: any, tags: S
"commentcount": 0,
"tags": [
"patchnotes",
- isBetaOrPreviewChannel(tags) ? SteamOSChannel.Beta : SteamOSChannel.Stable,
+ (await isBazziteBranchTesting()) ? SteamOSChannel.Beta : SteamOSChannel.Stable,
],
"language": 0,
"hidden": 0,
@@ -241,7 +241,7 @@ async function LoadBazziteReleasesAsPartnerEvents(module: any, gid: any, tags: S
return ret;
}
-async function fetchMoreReleases(count: number, beta: boolean) {
+async function fetchMoreReleases(count: number) {
const releases = [];
if (!generator && cachedGithubReleases.length === 0)
@@ -252,54 +252,10 @@ async function fetchMoreReleases(count: number, beta: boolean) {
do {
iterator = await generator.next();
const release = iterator.value;
-
- if (release && ((beta && release.prerelease) || (!beta && !release.prerelease)))
- releases.push(release);
+ releases.push(release);
} while (releases.length < count && !iterator.done)
for (const release of releases) {
cachedGithubReleases.push({gid: String(release.id), release});
}
}
-
-async function* fetchReleases() {
- let currentPage = 1;
- let done = false;
-
- while (!done) {
- let response: Response;
- let responseJson: any;
-
- try {
- response = await fetch(githubReleasesURI + `?page=${currentPage++}&per_page=10`);
-
- if (response.ok) {
- responseJson = await response.json();
- } else {
- responseJson = [];
- }
- } catch {
- responseJson = [];
- }
-
- if (!Array.isArray(responseJson) || responseJson.length == 0) {
- done = true;
- } else {
- responseJson.sort((a, b) => (new Date(b.created_at)).getTime() - (new Date(a.created_at)).getTime());
-
- for (let release of responseJson) {
- yield release;
- }
- }
- }
-
- return undefined;
-}
-
-function isBetaOrPreviewChannel(tags: SteamTags): boolean {
- if (!tags?.require_tags)
- return false;
-
- return tags.require_tags.includes(SteamOSChannel.Beta)
- || tags.require_tags.includes(SteamOSChannel.Preview);
-}
diff --git a/src/index.tsx b/src/index.tsx
index 74af918..ed3327f 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,12 +1,13 @@
import {useEffect, useState} from "react";
import {FaClipboardList} from "react-icons/fa";
-import {definePlugin} from "decky-frontend-lib";
import remarkHtml from "remark-html"
import remarkParse from "remark-parse"
import remarkGfm from "remark-gfm"
import {unified} from "unified"
import {patchPartnerEventStore} from "./PartnerEventStorePatch";
import {staticClasses} from "@decky/ui";
+import {definePlugin} from "@decky/api"
+import {fetchReleases} from "./FetchReleases";
function Content() {
const [changelogHtml, setChangelogHtml] = useState<string | null>(null);
@@ -14,25 +15,20 @@ function Content() {
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
const fetchChangelog = async (signal?: AbortSignal) => {
- const url = "https://api.github.com/repos/ublue-os/bazzite/releases/latest";
try {
- const response = await fetch(url, {
- headers: {
- Accept: "application/vnd.github.v3+json",
- },
- signal,
- });
+ const generator = fetchReleases(signal);
+ const iterator = await generator.next();
- if (!response.ok) {
- throw new Error(`Failed to fetch: ${response.statusText}`);
+ if (!iterator || iterator.done) {
+ setError("An unknown error occurred while fetching the changelog.");
+ return;
}
- const data = await response.json();
const html = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkHtml)
- .process(data.body)
+ .process(iterator.value.body);
setChangelogHtml(html.value as string);
setError(null);
@@ -181,6 +177,7 @@ function Content() {
);
}
+// noinspection JSUnusedGlobalSymbols
export default definePlugin(() => {
const patches = patchPartnerEventStore();