diff --git a/clients/desktop/PlayPalace.spec b/clients/desktop/PlayPalace.spec new file mode 100644 index 00000000..f9ee271a --- /dev/null +++ b/clients/desktop/PlayPalace.spec @@ -0,0 +1,49 @@ +# -*- mode: python ; coding: utf-8 -*- + +a = Analysis( + ['client.py'], + pathex=[], + binaries=[], + datas=[('sounds', 'sounds')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='PlayPalace', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='PlayPalace', +) +app = BUNDLE( + coll, + name='PlayPalace.app', + icon=None, + bundle_identifier=None, +) diff --git a/clients/desktop/sound_manager.py b/clients/desktop/sound_manager.py index 36373c60..af9b9016 100644 --- a/clients/desktop/sound_manager.py +++ b/clients/desktop/sound_manager.py @@ -2,13 +2,27 @@ import logging import os +import sys import threading import time +from pathlib import Path from sound_cacher import SoundCacher LOG = logging.getLogger(__name__) +def _default_sounds_folder() -> str: + """Return the bundled sounds folder for source and PyInstaller runs.""" + if getattr(sys, "frozen", False): + bundle_root = getattr(sys, "_MEIPASS", None) + if bundle_root: + candidate = Path(bundle_root) / "sounds" + if candidate.is_dir(): + return str(candidate) + return str(Path(sys.executable).resolve().parent / "sounds") + return str(Path(__file__).resolve().parent / "sounds") + + class AudioPlaylist: """Represents a playlist that can play either sounds or music tracks.""" @@ -326,7 +340,7 @@ def __init__(self): self.current_music = None self.current_music_name = None self.music_volume = 0.2 - self.sounds_folder = "sounds" + self.sounds_folder = _default_sounds_folder() # Configurable menu sounds (can be changed by server) self.menuclick_sound = "menuclick.ogg" diff --git a/clients/desktop/tests/test_sound_manager.py b/clients/desktop/tests/test_sound_manager.py index 30e67349..bb0c8995 100644 --- a/clients/desktop/tests/test_sound_manager.py +++ b/clients/desktop/tests/test_sound_manager.py @@ -1,3 +1,5 @@ +import os + import pytest import sound_manager as sm_mod @@ -41,6 +43,51 @@ def make_manager(tmp_path): return manager +def test_default_sounds_folder_is_absolute(): + manager = SoundManager() + + assert manager.sounds_folder.endswith("sounds") + assert os.path.isabs(manager.sounds_folder) + + +def test_default_sounds_folder_uses_pyinstaller_meipass(monkeypatch, tmp_path): + bundle_root = tmp_path / "bundle" + (bundle_root / "sounds").mkdir(parents=True) + executable = tmp_path / "PlayPalace.app" / "Contents" / "MacOS" / "PlayPalace" + + monkeypatch.setattr(sm_mod.sys, "frozen", True, raising=False) + monkeypatch.setattr(sm_mod.sys, "_MEIPASS", str(bundle_root), raising=False) + monkeypatch.setattr(sm_mod.sys, "executable", str(executable)) + + assert sm_mod._default_sounds_folder() == str(bundle_root / "sounds") + + +def test_default_sounds_folder_falls_back_when_meipass_sounds_missing( + monkeypatch, tmp_path +): + bundle_root = tmp_path / "_internal" + bundle_root.mkdir() + executable = tmp_path / "PlayPalace" / "PlayPalace.exe" + + monkeypatch.setattr(sm_mod.sys, "frozen", True, raising=False) + monkeypatch.setattr(sm_mod.sys, "_MEIPASS", str(bundle_root), raising=False) + monkeypatch.setattr(sm_mod.sys, "executable", str(executable)) + + assert sm_mod._default_sounds_folder() == str(executable.parent / "sounds") + + +def test_default_sounds_folder_uses_frozen_executable_without_meipass( + monkeypatch, tmp_path +): + executable = tmp_path / "PlayPalace.app" / "Contents" / "MacOS" / "PlayPalace" + + monkeypatch.setattr(sm_mod.sys, "frozen", True, raising=False) + monkeypatch.delattr(sm_mod.sys, "_MEIPASS", raising=False) + monkeypatch.setattr(sm_mod.sys, "executable", str(executable)) + + assert sm_mod._default_sounds_folder() == str(executable.parent / "sounds") + + def test_play_passes_full_path(tmp_path): (tmp_path / "click.ogg").write_bytes(b"123") manager = make_manager(tmp_path)