diff options
| author | Victor Borges <victor1882@outlook.com> | 2025-05-03 15:19:10 -0300 |
|---|---|---|
| committer | Victor Borges <victor1882@outlook.com> | 2025-05-03 15:19:10 -0300 |
| commit | d97d5f11e7807906cff2cf41cad815fb05b0096c (patch) | |
| tree | f5782c1785fa35586be12d871cfc00a35f0569a5 | |
| parent | ba7a86099b321f378b9d18ea025af9730642d8bf (diff) | |
| download | decky-bazzite-buddy-d97d5f11e7807906cff2cf41cad815fb05b0096c.tar.gz decky-bazzite-buddy-d97d5f11e7807906cff2cf41cad815fb05b0096c.zip | |
gets the correct branch now
| -rw-r--r-- | .editorconfig | 7 | ||||
| -rw-r--r-- | decky.pyi | 184 | ||||
| -rw-r--r-- | main.py | 9 | ||||
| -rw-r--r-- | src/FetchReleases.ts | 47 | ||||
| -rw-r--r-- | src/PartnerEventStorePatch.tsx | 62 | ||||
| -rwxr-xr-x | src/index.tsx | 21 |
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. + """ + @@ -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(); |
