From d19b35ec64e719539432c2d744d816e0536d2b27 Mon Sep 17 00:00:00 2001
From: beebls <102569435+beebls@users.noreply.github.com>
Date: Sun, 18 Jun 2023 14:06:27 -0600
Subject: [PATCH 1/4] fix up user vars and implement global dfl
---
audio_remoteinstall.py | 51 +++++++++++++++++++++---------------------
audio_utils.py | 31 ++++++++++++++++++++++++-
main.py | 15 ++++++-------
package-lock.json | 14 ++++++------
package.json | 2 +-
rollup.config.js | 8 ++-----
6 files changed, 72 insertions(+), 49 deletions(-)
diff --git a/audio_remoteinstall.py b/audio_remoteinstall.py
index 87eff7a..8e19bf7 100644
--- a/audio_remoteinstall.py
+++ b/audio_remoteinstall.py
@@ -1,7 +1,5 @@
-import asyncio, json, tempfile, os
-from audio_utils import Result, Log
-from audio_utils import AUDIO_LOADER_VERSION, DECKY_HOME
-import aiohttp
+import asyncio, tempfile, os, zipfile, aiohttp
+from audio_utils import Result, Log, get_pack_path, AUDIO_LOADER_VERSION
async def run(command : str) -> str:
proc = await asyncio.create_subprocess_shell(command,
@@ -20,41 +18,42 @@ async def install(id : str, base_url : str) -> Result:
url = f"{base_url}themes/{id}"
- try:
- async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
+ async with aiohttp.ClientSession(headers={"User-Agent": f"SDH-AudioLoader/{AUDIO_LOADER_VERSION}"}, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
+ try:
async with session.get(url) as resp:
if resp.status != 200:
raise Exception(f"Invalid status code {resp.status}")
data = await resp.json()
- except Exception as e:
- return Result(False, str(e))
-
- if (data["manifestVersion"] > AUDIO_LOADER_VERSION):
- raise Exception("Manifest version of entry is unsupported by this version of Audio Loader")
+ except Exception as e:
+ return Result(False, str(e))
- download_url = f"{base_url}blobs/{data['download']['id']}"
- tempDir = tempfile.TemporaryDirectory()
+ if (data["manifestVersion"] > AUDIO_LOADER_VERSION):
+ raise Exception("Manifest version of themedb entry is unsupported by this version of AudioLoader")
- Log(f"Downloading {download_url} to {tempDir.name}...")
- themeZipPath = os.path.join(tempDir.name, 'theme.zip')
- try:
- await run(f"curl \"{download_url}\" -L -o \"{themeZipPath}\"")
- except Exception as e:
- return Result(False, str(e))
+ download_url = f"{base_url}blobs/{data['download']['id']}"
+ tempDir = tempfile.TemporaryDirectory()
+
+ Log(f"Downloading {download_url} to {tempDir.name}...")
+ themeZipPath = os.path.join(tempDir.name, 'pack.zip')
+ try:
+ async with session.get(download_url) as resp:
+ if resp.status != 200:
+ raise Exception(f"Got {resp.status} code from '{download_url}'")
+
+ with open(themeZipPath, "wb") as out:
+ out.write(await resp.read())
+
+ except Exception as e:
+ return Result(False, str(e))
Log(f"Unzipping {themeZipPath}")
try:
- await run(f"unzip -o \"{themeZipPath}\" -d \"{DECKY_HOME}/sounds\"")
+ with zipfile.ZipFile(themeZipPath, 'r') as zip:
+ zip.extractall(get_pack_path())
except Exception as e:
return Result(False, str(e))
tempDir.cleanup()
- # for x in data["dependencies"]:
- # if x["name"] in local_themes:
- # continue
-
- # await install(x["id"], base_url, local_themes)
-
return Result(True)
\ No newline at end of file
diff --git a/audio_utils.py b/audio_utils.py
index 4c9cb93..5cf6c9f 100644
--- a/audio_utils.py
+++ b/audio_utils.py
@@ -1,14 +1,43 @@
import os
from logging import getLogger
+import platform
Logger = getLogger("AUDIO_LOADER")
AUDIO_LOADER_VERSION = 2
+HOME = os.getenv("HOME")
+if not HOME:
+ HOME = os.path.expanduser("~")
+PLATFORM_WIN = platform.system() == "Windows"
DECKY_HOME = os.environ["DECKY_HOME"] # /home/user/homebrew
DECKY_USER_HOME = os.environ["DECKY_USER_HOME"] # /home/user
+DECKY_USER = os.getenv("DECKY_USER")
def Log(text : str):
Logger.info(f"[AUDIO_LOADER] {text}")
+def get_user_home() -> str:
+ return HOME
+
+def get_pack_path() -> str:
+ return os.path.join(DECKY_HOME, "sounds")
+
+def get_steam_path() -> str:
+ if PLATFORM_WIN:
+ try:
+ import winreg
+ conn = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
+ key = winreg.OpenKey(conn, "SOFTWARE\\Wow6432Node\\Valve\\Steam")
+ val, type = winreg.QueryValueEx(key, "InstallPath")
+ if type != winreg.REG_SZ:
+ raise Exception(f"Expected type {winreg.REG_SZ}, got {type}")
+
+ Log(f"Got win steam install path: '{val}'")
+ return val
+ except Exception as e:
+ return "C:\\Program Files (x86)\\Steam" # Taking a guess here
+ else:
+ return f"{get_user_home()}/.local/share/Steam"
+
class Result:
def __init__(self, success : bool, message : str = "Success"):
self.success = success
@@ -25,7 +54,7 @@ def to_dict(self):
return {"success": self.success, "message": self.message}
def store_path() -> str:
- return os.path.join(DECKY_HOME, "sounds", "STORE")
+ return os.path.join(get_pack_path(), "STORE")
def store_reads() -> dict:
path = store_path()
diff --git a/main.py b/main.py
index 55aaf00..e352e4b 100644
--- a/main.py
+++ b/main.py
@@ -5,7 +5,7 @@
sys.path.append(os.path.dirname(__file__))
-from audio_utils import store_read as util_store_read, store_write as util_store_write, AUDIO_LOADER_VERSION, DECKY_HOME, DECKY_USER_HOME
+from audio_utils import store_read as util_store_read, store_write as util_store_write, get_steam_path, get_pack_path, AUDIO_LOADER_VERSION, DECKY_HOME, DECKY_USER_HOME
from audio_remoteinstall import install
starter_config_data = {
@@ -111,7 +111,7 @@ async def download_theme_from_url(self, id : str, url : str) -> dict:
return (await install(id, url)).to_dict()
async def get_config(self) -> object:
- configPath = f"{DECKY_HOME}/sounds/config.json"
+ configPath = os.path.join(get_pack_path(), 'config.json')
Log("Audio Loader - Fetching config file at {}".format(configPath))
if (os.path.exists(configPath)):
@@ -122,7 +122,7 @@ async def get_config(self) -> object:
return data
async def set_config(self, configObj: object):
- configPath = f"{DECKY_HOME}/sounds/config.json"
+ configPath = os.path.join(get_pack_path(), 'config.json')
Log("Audio Loader - Setting config file at {}".format(configPath))
if (os.path.exists(configPath)):
@@ -189,10 +189,9 @@ async def store_write(self, key : str, val : str) -> dict:
return Result(True).to_dict()
async def _load(self):
- packsPath = f"{DECKY_HOME}/sounds"
- symlinkPath = f"{DECKY_USER_HOME}/.local/share/Steam/steamui/sounds_custom"
+ packsPath = get_pack_path()
- configPath = f"{DECKY_HOME}/sounds/config.json"
+ configPath = os.path.join(get_pack_path(), 'config.json')
Log("Audio Loader - Finding sound packs...")
self.soundPacks = []
@@ -200,8 +199,8 @@ async def _load(self):
if (not os.path.exists(packsPath)):
await create_folder(packsPath)
- if (not os.path.exists(symlinkPath)):
- await create_symlink(packsPath, symlinkPath)
+ if (not os.path.exists(os.path.join(get_steam_path(), 'steamui', 'sounds_custom'))):
+ await create_symlink(get_pack_path(), os.path.join(get_steam_path(), 'steamui', 'sounds_custom'))
if (not os.path.exists(configPath)):
await create_config(configPath)
diff --git a/package-lock.json b/package-lock.json
index b066cb7..26885aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"license": "GPL-2.0-or-later",
"dependencies": {
"@types/lodash": "^4.14.191",
- "decky-frontend-lib": "^3.21.0",
+ "decky-frontend-lib": "^3.21.1",
"lodash": "^4.17.21",
"react-icons": "^4.4.0"
},
@@ -901,9 +901,9 @@
"dev": true
},
"node_modules/decky-frontend-lib": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-3.21.0.tgz",
- "integrity": "sha512-BZY+F9HKhjF0zthoBdQxbluZ/37UyjlzCCpcC6dQfOeAAPmjFDATs/FF/n9qo9B+NMepgWUXpStZR0becE9EOw=="
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-3.21.1.tgz",
+ "integrity": "sha512-30605ET9qqZ6St6I9WmMmLGgSrTIdMwo7xy85+lRaF1miUd2icOGEJjwnbVcZDdkal+1fJ3tNEDXlchVfG4TrA=="
},
"node_modules/decode-uri-component": {
"version": "0.2.2",
@@ -3494,9 +3494,9 @@
"dev": true
},
"decky-frontend-lib": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-3.21.0.tgz",
- "integrity": "sha512-BZY+F9HKhjF0zthoBdQxbluZ/37UyjlzCCpcC6dQfOeAAPmjFDATs/FF/n9qo9B+NMepgWUXpStZR0becE9EOw=="
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-3.21.1.tgz",
+ "integrity": "sha512-30605ET9qqZ6St6I9WmMmLGgSrTIdMwo7xy85+lRaF1miUd2icOGEJjwnbVcZDdkal+1fJ3tNEDXlchVfG4TrA=="
},
"decode-uri-component": {
"version": "0.2.2",
diff --git a/package.json b/package.json
index 3f3ad2c..2ae1e8f 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
},
"dependencies": {
"@types/lodash": "^4.14.191",
- "decky-frontend-lib": "^3.21.0",
+ "decky-frontend-lib": "^3.21.1",
"lodash": "^4.17.21",
"react-icons": "^4.4.0"
},
diff --git a/rollup.config.js b/rollup.config.js
index 41e68fe..68d7332 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -26,17 +26,13 @@ export default defineConfig({
styles(),
],
context: "window",
- external: [
- "react",
- "react-dom",
- // "decky-frontend-lib"
- ],
+ external: ["react", "react-dom", "decky-frontend-lib"],
output: {
file: "dist/index.js",
globals: {
react: "SP_REACT",
"react-dom": "SP_REACTDOM",
- // "decky-frontend-lib": "DFL",
+ "decky-frontend-lib": "DFL",
},
format: "iife",
exports: "default",
From dc227d760cf219d52cd0d1598aab67b7149f3b55 Mon Sep 17 00:00:00 2001
From: beebls <102569435+beebls@users.noreply.github.com>
Date: Tue, 20 Jun 2023 14:32:42 -0600
Subject: [PATCH 2/4] Extract menu music to a single function
Also now pauses/plays the music upon entering a game instead of killing and restarting
---
src/audioPlayers/changeMenuMusic.ts | 48 +++++++++
src/audioPlayers/index.ts | 1 +
src/index.tsx | 156 ++++++----------------------
3 files changed, 80 insertions(+), 125 deletions(-)
create mode 100644 src/audioPlayers/changeMenuMusic.ts
create mode 100644 src/audioPlayers/index.ts
diff --git a/src/audioPlayers/changeMenuMusic.ts b/src/audioPlayers/changeMenuMusic.ts
new file mode 100644
index 0000000..d1c9782
--- /dev/null
+++ b/src/audioPlayers/changeMenuMusic.ts
@@ -0,0 +1,48 @@
+import { Pack } from "../classes";
+
+export function changeMenuMusic(
+ newMusic: string,
+ menuMusic: HTMLAudioElement | null,
+ setGlobalState: (key: string, value: any) => void,
+ gamesRunning: any,
+ soundPacks: Pack[],
+ musicVolume: number
+) {
+ setGlobalState("selectedMusic", newMusic);
+ if (menuMusic !== null) {
+ menuMusic.pause();
+ menuMusic.currentTime = 0;
+ setGlobalState("menuMusic", null);
+ }
+ // This makes sure if you are in a game, music doesn't start playing
+ if (newMusic !== "None" && gamesRunning.length === 0) {
+ const currentPack = soundPacks.find((e) => e.name === newMusic);
+ let musicFileName = "menu_music.mp3";
+ if (Object.keys(currentPack?.mappings || {}).includes("menu_music.mp3")) {
+ const randIndex = Math.trunc(
+ Math.random() * currentPack?.mappings["menu_music.mp3"].length
+ );
+ musicFileName = currentPack?.mappings["menu_music.mp3"][randIndex];
+ }
+ const newMenuMusic = new Audio(
+ `/sounds_custom/${
+ currentPack?.truncatedPackPath || "error"
+ }/${musicFileName}`
+ );
+ newMenuMusic.play();
+ newMenuMusic.loop = true;
+ newMenuMusic.volume = musicVolume;
+ const setVolume = (value: number) => {
+ newMenuMusic.volume = value;
+ };
+ // Update menuMusic in globalState after every change so that it reflects the changes the next time it checks
+ // @ts-ignore
+ window.AUDIOLOADER_MENUMUSIC = {
+ play: newMenuMusic.play.bind(newMenuMusic),
+ pause: newMenuMusic.pause.bind(newMenuMusic),
+ origVolume: newMenuMusic.volume,
+ setVolume: setVolume.bind(this),
+ };
+ setGlobalState("menuMusic", newMenuMusic);
+ }
+}
diff --git a/src/audioPlayers/index.ts b/src/audioPlayers/index.ts
new file mode 100644
index 0000000..5be28e6
--- /dev/null
+++ b/src/audioPlayers/index.ts
@@ -0,0 +1 @@
+export * from "./changeMenuMusic";
diff --git a/src/index.tsx b/src/index.tsx
index a22eba2..4ee2d46 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -32,6 +32,7 @@ import {
GlobalStateContextProvider,
useGlobalState,
} from "./state/GlobalState";
+import { changeMenuMusic } from "./audioPlayers";
const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
const {
@@ -52,42 +53,14 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
}, []);
function restartMusicPlayer(newMusic: string) {
- if (menuMusic !== null) {
- menuMusic.pause();
- menuMusic.currentTime = 0;
- setGlobalState("menuMusic", null);
- }
- // This makes sure if you are in a game, music doesn't start playing
- if (newMusic !== "None" && gamesRunning.length === 0) {
- const currentPack = soundPacks.find((e) => e.name === newMusic);
- let musicFileName = "menu_music.mp3";
- if (Object.keys(currentPack?.mappings || {}).includes("menu_music.mp3")) {
- const randIndex = Math.trunc(
- Math.random() * currentPack?.mappings["menu_music.mp3"].length
- );
- musicFileName = currentPack?.mappings["menu_music.mp3"][randIndex];
- }
- const newMenuMusic = new Audio(
- `/sounds_custom/${
- currentPack?.truncatedPackPath || "error"
- }/${musicFileName}`
- );
- newMenuMusic.play();
- newMenuMusic.loop = true;
- newMenuMusic.volume = musicVolume;
- const setVolume = (value: number) => {
- newMenuMusic.volume = value;
- };
- // Update menuMusic in globalState after every change so that it reflects the changes the next time it checks
- // @ts-ignore
- window.AUDIOLOADER_MENUMUSIC = {
- play: newMenuMusic.play.bind(newMenuMusic),
- pause: newMenuMusic.pause.bind(newMenuMusic),
- origVolume: newMenuMusic.volume,
- setVolume: setVolume.bind(this),
- };
- setGlobalState("menuMusic", newMenuMusic);
- }
+ changeMenuMusic(
+ newMusic,
+ menuMusic,
+ (key, val) => setGlobalState(key, val),
+ gamesRunning,
+ soundPacks,
+ musicVolume
+ );
}
function refetchLocalPacks() {
@@ -202,8 +175,6 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
?.data ?? -1
}
onChange={async (option) => {
- setGlobalState("selectedMusic", option.label as string);
-
const configObj = {
selected_pack: activeSound,
selected_music: option.label,
@@ -225,7 +196,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
step={0.01}
onChange={(value) => {
setGlobalState("musicVolume", value);
- menuMusic.origVolume = value;
+ menuMusic.volume = value;
// @ts-ignore
window.AUDIOLOADER_MENUMUSIC.volume = value;
const configObj = {
@@ -325,7 +296,6 @@ export default definePlugin((serverApi: ServerAPI) => {
python.setStateClass(state);
api.setServer(serverApi);
api.setStateClass(state);
- let menuMusic: any = null;
python.resolve(python.storeRead("shortToken"), (token: string) => {
if (token) {
@@ -412,43 +382,17 @@ export default definePlugin((serverApi: ServerAPI) => {
setGlobalState("soundVolume", configSoundVolume);
setGlobalState("musicVolume", configMusicVolume);
+ const { soundPacks } = state.getPublicState();
+
// Plays menu music initially
- // TODO: Add check if game is currently running
- if (configSelectedMusic !== "None") {
- const { soundPacks } = state.getPublicState();
- const currentPack = soundPacks.find(
- (e) => e.name === configSelectedMusic
- );
- let musicFileName = "menu_music.mp3";
- if (
- Object.keys(currentPack?.mappings || {}).includes("menu_music.mp3")
- ) {
- const randIndex = Math.trunc(
- Math.random() * currentPack?.mappings["menu_music.mp3"].length
- );
- musicFileName = currentPack?.mappings["menu_music.mp3"][randIndex];
- }
- menuMusic = new Audio(
- `/sounds_custom/${
- currentPack?.truncatedPackPath || "error"
- }/${musicFileName}`
- );
- menuMusic.play();
- menuMusic.loop = true;
- menuMusic.volume = configMusicVolume;
- const setVolume = (value: number) => {
- menuMusic.volume = value;
- };
- // @ts-ignore
- window.AUDIOLOADER_MENUMUSIC = {
- play: menuMusic.play.bind(menuMusic),
- pause: menuMusic.pause.bind(menuMusic),
- origVolume: menuMusic.volume,
- setVolume: setVolume.bind(this),
- };
- console.log("play and loop ran", menuMusic);
- setGlobalState("menuMusic", menuMusic);
- }
+ changeMenuMusic(
+ configSelectedMusic,
+ null,
+ setGlobalState,
+ [],
+ soundPacks,
+ configMusicVolume
+ );
});
});
@@ -457,13 +401,8 @@ export default definePlugin((serverApi: ServerAPI) => {
// Refer to the SteamClient.d.ts or just console.log(SteamClient) to see all of it's methods
SteamClient.GameSessions.RegisterForAppLifetimeNotifications(
(update: AppState) => {
- const {
- soundPacks,
- menuMusic,
- selectedMusic,
- gamesRunning,
- musicVolume,
- } = state.getPublicState();
+ const { menuMusic, selectedMusic, gamesRunning } =
+ state.getPublicState();
const setGlobalState = state.setGlobalState.bind(state);
if (selectedMusic !== "None") {
if (update.bRunning) {
@@ -471,54 +410,21 @@ export default definePlugin((serverApi: ServerAPI) => {
setGlobalState("gamesRunning", [...gamesRunning, update.unAppID]);
if (menuMusic != null) {
menuMusic.pause();
- menuMusic.currentTime = 0;
- setGlobalState("menuMusic", null);
+ // menuMusic.currentTime = 0;
+ // setGlobalState("menuMusic", null);
}
} else {
- // This happens when an app is closed
- setGlobalState(
- "gamesRunning",
- gamesRunning.filter((e) => e !== update.unAppID)
+ const filteredGames = gamesRunning.filter(
+ (e) => e !== update.unAppID
);
+ // This happens when an app is closed
+ setGlobalState("gamesRunning", filteredGames);
// I'm re-using the filter here because I don't believe the getPublicState() method will update the values if they are changed
- if (gamesRunning.filter((e) => e !== update.unAppID).length === 0) {
- const currentMusic = soundPacks.find(
- (e) => e.name === selectedMusic
- );
- let musicFileName = "menu_music.mp3";
- if (
- Object.keys(currentMusic?.mappings || {}).includes(
- "menu_music.mp3"
- )
- ) {
- const randIndex = Math.trunc(
- Math.random() *
- currentMusic?.mappings["menu_music.mp3"].length
- );
- musicFileName =
- currentMusic?.mappings["menu_music.mp3"][randIndex];
+ if (filteredGames.length === 0) {
+ if (menuMusic !== null) {
+ menuMusic.play();
}
- const newMenuMusic = new Audio(
- `/sounds_custom/${
- currentMusic?.truncatedPackPath || "error"
- }/${musicFileName}`
- );
- newMenuMusic.play();
- newMenuMusic.loop = true;
- newMenuMusic.volume = musicVolume;
- const setVolume = (value: number) => {
- newMenuMusic.volume = value;
- };
- // Update menuMusic in globalState after every change so that it reflects the changes the next time it checks
- // @ts-ignore
- window.AUDIOLOADER_MENUMUSIC = {
- play: newMenuMusic.play.bind(newMenuMusic),
- pause: newMenuMusic.pause.bind(newMenuMusic),
- origVolume: newMenuMusic.volume,
- setVolume: setVolume.bind(this),
- };
- setGlobalState("menuMusic", newMenuMusic);
}
}
}
From 30bb9a651affcab5b70334f266e204818e6db682 Mon Sep 17 00:00:00 2001
From: beebls <102569435+beebls@users.noreply.github.com>
Date: Tue, 20 Jun 2023 16:19:15 -0600
Subject: [PATCH 3/4] Add working intros to music packs
music packs can now contain a `intro_music.mp3` or a mapped version of it, this intro will play once before the looping menu music.
Additionally, this bumps the targeted manifest version to 3
---
.prettierrc | 1 +
audio_utils.py | 2 +-
main.py | 6 ++
src/audioPlayers/changeMenuMusic.ts | 101 ++++++++++++++++++++--------
src/classes.ts | 5 +-
src/index.tsx | 90 +++++++++----------------
6 files changed, 116 insertions(+), 89 deletions(-)
diff --git a/.prettierrc b/.prettierrc
index 2d08c14..c6015ef 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,6 +1,7 @@
{
"singleQuote": false,
"tabWidth": 2,
+ "printWidth": 100,
"semi": true,
"trailingComma": "es5",
"bracketSameLine": false
diff --git a/audio_utils.py b/audio_utils.py
index 5cf6c9f..c805bb5 100644
--- a/audio_utils.py
+++ b/audio_utils.py
@@ -3,7 +3,7 @@
import platform
Logger = getLogger("AUDIO_LOADER")
-AUDIO_LOADER_VERSION = 2
+AUDIO_LOADER_VERSION = 3
HOME = os.getenv("HOME")
if not HOME:
HOME = os.path.expanduser("~")
diff --git a/main.py b/main.py
index e352e4b..f843fdb 100644
--- a/main.py
+++ b/main.py
@@ -68,6 +68,7 @@ def __init__(self, packPath : str, json : dict, truncatedPackPath: str):
self.mappings = json["mappings"] if ("mappings" in json) else {}
self.music = bool(json["music"]) if ("music" in json) else False
self.id = json["id"] if ("id" in json) else self.name
+
if (AUDIO_LOADER_VERSION < self.require):
raise Exception("Audio Loader - {} requires Audio Loader version {} but only version {} is installed".format(self.name, self.require, AUDIO_LOADER_VERSION))
@@ -75,6 +76,10 @@ def __init__(self, packPath : str, json : dict, truncatedPackPath: str):
self.packPath = packPath
self.truncatedPackPath = truncatedPackPath
+ self.has_intro = False
+ if (self.music == True):
+ self.has_intro = os.path.exists(os.path.join(packPath, self.mappings["intro_music.mp3"] if "intro_music.mp3" in self.mappings else "intro_music.mp3"))
+
async def delete(self) -> Result:
try:
shutil.rmtree(self.packPath)
@@ -92,6 +97,7 @@ def to_dict(self) -> dict:
"ignore": self.ignore,
"mappings": self.mappings,
"music": self.music,
+ "hasIntro": self.has_intro,
"packPath": self.packPath,
"truncatedPackPath": self.truncatedPackPath,
"id": self.id
diff --git a/src/audioPlayers/changeMenuMusic.ts b/src/audioPlayers/changeMenuMusic.ts
index d1c9782..1c77b0e 100644
--- a/src/audioPlayers/changeMenuMusic.ts
+++ b/src/audioPlayers/changeMenuMusic.ts
@@ -1,4 +1,16 @@
-import { Pack } from "../classes";
+import { Mappings, Pack } from "../classes";
+
+function findMapping(origFileName: string, mappings: Mappings | undefined): string {
+ if (mappings && Object.keys(mappings || {}).includes(origFileName)) {
+ const randIndex = Math.trunc(Math.random() * mappings[origFileName].length);
+ return mappings[origFileName][randIndex];
+ }
+ return origFileName;
+}
+
+function createFullPath(fileName: string, truncatedPackPath: string | undefined) {
+ return `/sounds_custom/${truncatedPackPath || "error"}/${fileName}`;
+}
export function changeMenuMusic(
newMusic: string,
@@ -9,40 +21,73 @@ export function changeMenuMusic(
musicVolume: number
) {
setGlobalState("selectedMusic", newMusic);
+
+ // Stops the old music
if (menuMusic !== null) {
menuMusic.pause();
menuMusic.currentTime = 0;
setGlobalState("menuMusic", null);
}
- // This makes sure if you are in a game, music doesn't start playing
+
+ // Start the new one, if the user selected a music at all
if (newMusic !== "None" && gamesRunning.length === 0) {
const currentPack = soundPacks.find((e) => e.name === newMusic);
- let musicFileName = "menu_music.mp3";
- if (Object.keys(currentPack?.mappings || {}).includes("menu_music.mp3")) {
- const randIndex = Math.trunc(
- Math.random() * currentPack?.mappings["menu_music.mp3"].length
- );
- musicFileName = currentPack?.mappings["menu_music.mp3"][randIndex];
- }
- const newMenuMusic = new Audio(
- `/sounds_custom/${
- currentPack?.truncatedPackPath || "error"
- }/${musicFileName}`
+
+ const musicFilePath = createFullPath(
+ findMapping("menu_music.mp3", currentPack?.mappings),
+ currentPack?.truncatedPackPath
);
- newMenuMusic.play();
- newMenuMusic.loop = true;
- newMenuMusic.volume = musicVolume;
- const setVolume = (value: number) => {
- newMenuMusic.volume = value;
- };
- // Update menuMusic in globalState after every change so that it reflects the changes the next time it checks
- // @ts-ignore
- window.AUDIOLOADER_MENUMUSIC = {
- play: newMenuMusic.play.bind(newMenuMusic),
- pause: newMenuMusic.pause.bind(newMenuMusic),
- origVolume: newMenuMusic.volume,
- setVolume: setVolume.bind(this),
- };
- setGlobalState("menuMusic", newMenuMusic);
+ const introFilePath = createFullPath(
+ findMapping("intro_music.mp3", currentPack?.mappings),
+ currentPack?.truncatedPackPath
+ );
+
+ let newMenuMusic: HTMLAudioElement;
+
+ // If there is an intro, it must play that, and add an onended listener to change to the normal music
+ // If there's no intro, it can just go straight to playAndLoopMenuMusic()
+ if (currentPack?.hasIntro) {
+ function handleIntroEnd() {
+ newMenuMusic.currentTime = 0;
+ newMenuMusic.src = musicFilePath;
+ newMenuMusic.onended = null;
+ playAndLoopMenuMusic();
+ }
+ newMenuMusic = new Audio(introFilePath);
+ newMenuMusic.onended = handleIntroEnd;
+ newMenuMusic.volume = musicVolume;
+ newMenuMusic.play();
+ createWindowObject(newMenuMusic);
+ } else {
+ newMenuMusic = new Audio(musicFilePath);
+ playAndLoopMenuMusic();
+ }
+
+ function playAndLoopMenuMusic(wasFromIntro: boolean = false) {
+ newMenuMusic.play();
+ newMenuMusic.loop = true;
+ // If someone has changed the volume before the intro ended, this would overwrite it with the original as this function does not have up to date data
+ if (!wasFromIntro) {
+ newMenuMusic.volume = musicVolume;
+ }
+ createWindowObject(newMenuMusic);
+ }
+
+ // Self explanatory, just extracted it to a function so that I can run it once on the intro, and once on the menu music
+ // TODO: Not actually sure if it needs to be set the 2nd time
+ function createWindowObject(menuMusic: HTMLAudioElement) {
+ const setVolume = (value: number) => {
+ menuMusic.volume = value;
+ };
+ // @ts-ignore
+ window.AUDIOLOADER_MENUMUSIC = {
+ play: menuMusic.play.bind(menuMusic),
+ pause: menuMusic.pause.bind(menuMusic),
+ origVolume: menuMusic.volume,
+ // @ts-ignore
+ setVolume: setVolume.bind(this),
+ };
+ setGlobalState("menuMusic", menuMusic);
+ }
}
}
diff --git a/src/classes.ts b/src/classes.ts
index c6ed2cb..be11fbd 100644
--- a/src/classes.ts
+++ b/src/classes.ts
@@ -8,6 +8,7 @@ export interface Pack {
packPath: string; // This contains the full path from root to the pack
truncatedPackPath: string; // This is the relative path from ~/homebrew/sounds
music: boolean;
+ hasIntro: boolean;
id: string;
}
@@ -58,7 +59,9 @@ export type DeckSound =
| "steam_chatroom_notification.m4a"
| "ui_steam_message_old_smooth.m4a"
| "ui_steam_smoother_friend_join.m4a"
- | "ui_steam_smoother_friend_online.m4a";
+ | "ui_steam_smoother_friend_online.m4a"
+ | "menu_music.mp3"
+ | "intro_music.mp3";
export type Mappings =
| {
diff --git a/src/index.tsx b/src/index.tsx
index 4ee2d46..cb01f74 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -27,11 +27,7 @@ import {
} from "./pack-manager";
import * as python from "./python";
import * as api from "./api";
-import {
- GlobalState,
- GlobalStateContextProvider,
- useGlobalState,
-} from "./state/GlobalState";
+import { GlobalState, GlobalStateContextProvider, useGlobalState } from "./state/GlobalState";
import { changeMenuMusic } from "./audioPlayers";
const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
@@ -95,8 +91,8 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
return (
- AudioLoader failed to initialize, try reloading, and if that doesn't
- work, try restarting your deck.
+ AudioLoader failed to initialize, try reloading, and if that doesn't work, try restarting
+ your deck.
);
@@ -122,8 +118,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
// activeSound now stores a string, this just finds the corresponding option for the label
// the "?? -2" is there incase find returns undefined (the currently selected theme was deleted or something)
// it NEEDS to be a nullish coalescing operator because 0 needs to be treated as true
- SoundPackDropdownOptions.find((e) => e.label === activeSound)
- ?.data ?? -1
+ SoundPackDropdownOptions.find((e) => e.label === activeSound)?.data ?? -1
}
onChange={async (option) => {
setGlobalState("activeSound", option.label as string);
@@ -147,10 +142,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
max={1}
step={0.01}
onChange={(value) => {
- gainNode.gain.setValueAtTime(
- value,
- gainNode.context.currentTime + 0.01
- );
+ gainNode.gain.setValueAtTime(value, gainNode.context.currentTime + 0.01);
// gainNode.gain.value = value;
setGlobalState("soundVolume", value);
const configObj = {
@@ -171,8 +163,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
menuLabel="Music Pack"
rgOptions={MusicPackDropdownOptions}
selectedOption={
- MusicPackDropdownOptions.find((e) => e.label === selectedMusic)
- ?.data ?? -1
+ MusicPackDropdownOptions.find((e) => e.label === selectedMusic)?.data ?? -1
}
onChange={async (option) => {
const configObj = {
@@ -207,11 +198,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
};
python.setConfig(configObj);
}}
- icon={
-
- }
+ icon={}
/>
@@ -348,9 +335,7 @@ export default definePlugin((serverApi: ServerAPI) => {
}
// Mapping check
if (Object.keys(currentPack?.mappings || {}).includes(soundName)) {
- const randIndex = Math.trunc(
- Math.random() * currentPack?.mappings[soundName].length
- );
+ const randIndex = Math.trunc(Math.random() * currentPack?.mappings[soundName].length);
const mappedFileName = currentPack?.mappings[soundName][randIndex];
newSoundURL = `/sounds_custom/${
currentPack?.truncatedPackPath || "/error"
@@ -385,51 +370,39 @@ export default definePlugin((serverApi: ServerAPI) => {
const { soundPacks } = state.getPublicState();
// Plays menu music initially
- changeMenuMusic(
- configSelectedMusic,
- null,
- setGlobalState,
- [],
- soundPacks,
- configMusicVolume
- );
+ changeMenuMusic(configSelectedMusic, null, setGlobalState, [], soundPacks, configMusicVolume);
});
});
const AppStateRegistrar =
// SteamClient is something exposed by the SP tab of SteamUI, it's not a decky-frontend-lib thingy, but you can still call it normally
// Refer to the SteamClient.d.ts or just console.log(SteamClient) to see all of it's methods
- SteamClient.GameSessions.RegisterForAppLifetimeNotifications(
- (update: AppState) => {
- const { menuMusic, selectedMusic, gamesRunning } =
- state.getPublicState();
- const setGlobalState = state.setGlobalState.bind(state);
- if (selectedMusic !== "None") {
- if (update.bRunning) {
- // Because gamesRunning is in globalState, array methods like push and splice don't work
- setGlobalState("gamesRunning", [...gamesRunning, update.unAppID]);
- if (menuMusic != null) {
- menuMusic.pause();
- // menuMusic.currentTime = 0;
- // setGlobalState("menuMusic", null);
- }
- } else {
- const filteredGames = gamesRunning.filter(
- (e) => e !== update.unAppID
- );
- // This happens when an app is closed
- setGlobalState("gamesRunning", filteredGames);
+ SteamClient.GameSessions.RegisterForAppLifetimeNotifications((update: AppState) => {
+ const { menuMusic, selectedMusic, gamesRunning } = state.getPublicState();
+ const setGlobalState = state.setGlobalState.bind(state);
+ if (selectedMusic !== "None") {
+ if (update.bRunning) {
+ // Because gamesRunning is in globalState, array methods like push and splice don't work
+ setGlobalState("gamesRunning", [...gamesRunning, update.unAppID]);
+ if (menuMusic != null) {
+ menuMusic.pause();
+ // menuMusic.currentTime = 0;
+ // setGlobalState("menuMusic", null);
+ }
+ } else {
+ const filteredGames = gamesRunning.filter((e) => e !== update.unAppID);
+ // This happens when an app is closed
+ setGlobalState("gamesRunning", filteredGames);
- // I'm re-using the filter here because I don't believe the getPublicState() method will update the values if they are changed
- if (filteredGames.length === 0) {
- if (menuMusic !== null) {
- menuMusic.play();
- }
+ // I'm re-using the filter here because I don't believe the getPublicState() method will update the values if they are changed
+ if (filteredGames.length === 0) {
+ if (menuMusic !== null) {
+ menuMusic.play();
}
}
}
}
- );
+ });
serverApi.routerHook.addRoute("/audiopack-manager", () => (
@@ -452,8 +425,7 @@ export default definePlugin((serverApi: ServerAPI) => {
),
icon: ,
onDismount: () => {
- const { menuMusic, soundPatchInstance, volumePatchInstance } =
- state.getPublicState();
+ const { menuMusic, soundPatchInstance, volumePatchInstance } = state.getPublicState();
const setGlobalState = state.setGlobalState.bind(state);
if (menuMusic != null) {
menuMusic.pause();
From 176d81e58c13352ad8489a7e3da28f3422128b07 Mon Sep 17 00:00:00 2001
From: beebls <102569435+beebls@users.noreply.github.com>
Date: Thu, 22 Jun 2023 14:02:12 -0600
Subject: [PATCH 4/4] fix mappings with intro music
---
main.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/main.py b/main.py
index f843fdb..f60b020 100644
--- a/main.py
+++ b/main.py
@@ -78,7 +78,7 @@ def __init__(self, packPath : str, json : dict, truncatedPackPath: str):
self.has_intro = False
if (self.music == True):
- self.has_intro = os.path.exists(os.path.join(packPath, self.mappings["intro_music.mp3"] if "intro_music.mp3" in self.mappings else "intro_music.mp3"))
+ self.has_intro = "intro_music.mp3" in self.mappings or os.path.exists(os.path.join(packPath, "intro_music.mp3"))
async def delete(self) -> Result:
try: