aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEddy Pedroni <epedroni@pm.me>2025-07-15 22:54:55 +0200
committerEddy Pedroni <epedroni@pm.me>2025-07-15 22:54:55 +0200
commit1d42bfe5f7b3c671fc5b50e716c5e8aa68728fb3 (patch)
tree0645cba6faaf0e20926a03c3d5e2dd4d878c1d7b
parentfb63f6b5912171595065ea7498d1d770a175d1d9 (diff)
Refactor session manager to be independent of the underlying storage
-rw-r--r--solo-tool-project/src/solo_tool/session_manager.py55
-rw-r--r--solo-tool-project/test/fixtures.py11
-rw-r--r--solo-tool-project/test/session_manager_unittest.py39
-rw-r--r--web-project/src/solo_tool_web.py46
4 files changed, 87 insertions, 64 deletions
diff --git a/solo-tool-project/src/solo_tool/session_manager.py b/solo-tool-project/src/solo_tool/session_manager.py
index cd5ebf7..4fae19f 100644
--- a/solo-tool-project/src/solo_tool/session_manager.py
+++ b/solo-tool-project/src/solo_tool/session_manager.py
@@ -1,30 +1,43 @@
+from glob import glob
import json
+from pathlib import Path
from . import SoloTool
-def loadSession(file: str, songPool: str, player=None) -> SoloTool:
- with open(file, "r") as f:
- session = json.load(f)
+class FileSystemSessionManager:
+ def __init__(self, songPool: str, sessionPath: str):
+ self._songPool = Path(songPool)
+ self._sessionPath = Path(sessionPath)
- st = SoloTool(songPool, player=player)
+ def getSessions(self) -> list[str]:
+ return [Path(f).stem for f in glob(f"{self._sessionPath}/*.json")]
- for i, entry in enumerate(session):
- songPath = entry["path"]
- keyPoints = entry["key_points"]
+ def loadSession(self, key: str, player=None) -> SoloTool:
+ with open(self._sessionPath / f"{key}.json", "r") as f:
+ session = json.load(f)
- st.addSong(songPath)
- st._keyPoints[i] = keyPoints
+ st = SoloTool(self._songPool, player=player)
- return st
-
-def saveSession(soloTool: SoloTool, file: str) -> None:
- session = []
+ for i, entry in enumerate(session):
+ songPath = entry["path"]
+ keyPoints = entry["key_points"]
- for i, song in enumerate(soloTool.songs):
- entry = {
- "path": song,
- "key_points" : soloTool._keyPoints[i]
- }
- session.append(entry)
+ st.addSong(songPath)
+ st._keyPoints[i] = keyPoints
- with open(file, "w") as f:
- json.dump(session, f)
+ return st
+
+ def saveSession(self, soloTool: SoloTool, key: str) -> None:
+ session = []
+
+ for i, song in enumerate(soloTool.songs):
+ entry = {
+ "path": song,
+ "key_points" : soloTool._keyPoints[i]
+ }
+ session.append(entry)
+
+ with open(self._sessionPath / f"{key}.json", "w") as f:
+ json.dump(session, f)
+
+def getSessionManager(songPool: str, sessionPath: str) -> FileSystemSessionManager:
+ return FileSystemSessionManager(songPool, sessionPath)
diff --git a/solo-tool-project/test/fixtures.py b/solo-tool-project/test/fixtures.py
index f70901b..e63d363 100644
--- a/solo-tool-project/test/fixtures.py
+++ b/solo-tool-project/test/fixtures.py
@@ -11,7 +11,15 @@ def mockPlayer():
@pytest.fixture
def songPool(tmp_path):
- return tmp_path / "songs"
+ path = tmp_path / "songs"
+ os.mkdir(path)
+ return path
+
+@pytest.fixture
+def sessionPath(tmp_path):
+ path = tmp_path / "sessions"
+ os.mkdir(path)
+ return path
@pytest.fixture
def soloTool(mockPlayer, songPool):
@@ -25,7 +33,6 @@ def testSongs(songPool):
songPool / "test.mp4"
]
- os.mkdir(songPool)
for song in songs:
song.touch()
return songs
diff --git a/solo-tool-project/test/session_manager_unittest.py b/solo-tool-project/test/session_manager_unittest.py
index cff3b35..bd6fbb6 100644
--- a/solo-tool-project/test/session_manager_unittest.py
+++ b/solo-tool-project/test/session_manager_unittest.py
@@ -2,11 +2,11 @@ import pytest
from json import loads
import os
-from solo_tool.session_manager import loadSession, saveSession
-from fixtures import songPool, soloTool, mockPlayer, testSongs
+from solo_tool.session_manager import getSessionManager
+from fixtures import songPool, soloTool, mockPlayer, testSongs, sessionPath
@pytest.fixture
-def testSessionFile(tmp_path, testSongs):
+def testSessionFile(sessionPath, testSongs):
contents = """[
{
"path" : "test.flac",
@@ -17,19 +17,20 @@ def testSessionFile(tmp_path, testSongs):
"key_points" : [0.1, 0.3]
}
]"""
-
- basePath = tmp_path / "sessions"
- sessionFile = basePath / "test-session.json"
-
- os.mkdir(basePath)
+ sessionFile = sessionPath / "test-session.json"
with open(sessionFile, "w") as f:
f.write(contents)
-
return sessionFile
-def test_loadSession(songPool, testSessionFile, mockPlayer):
- soloTool = loadSession(testSessionFile, songPool, player=mockPlayer)
+@pytest.fixture
+def sessionManager(sessionPath, songPool):
+ return getSessionManager(songPool, sessionPath)
+
+def test_loadSession(sessionManager, mockPlayer, testSessionFile):
+ sessions = sessionManager.getSessions()
+ assert sessions == [testSessionFile.stem]
+ soloTool = sessionManager.loadSession(sessions[0], player=mockPlayer)
assert soloTool.songs == ["test.flac", "test.mp3"]
soloTool.song = 0
@@ -38,16 +39,16 @@ def test_loadSession(songPool, testSessionFile, mockPlayer):
soloTool.song = 1
assert soloTool.keyPoints == [0.1, 0.3]
-def test_saveSession(soloTool, testSessionFile, tmp_path):
+def test_saveSession(sessionManager, soloTool, testSessionFile, sessionPath):
soloTool.addSong("test.flac")
soloTool.addSong("test.mp3")
soloTool.song = 1
soloTool.keyPoints = [0.1, 0.3]
- testFile = tmp_path / "test_session_saved.json"
- saveSession(soloTool, testFile)
+ sessionKey = "test_session_saved"
+ sessionManager.saveSession(soloTool, sessionKey)
- with open(testFile, "r") as f:
+ with open(sessionPath / f"{sessionKey}.json", "r") as f:
savedSession = loads(f.read())
with open(testSessionFile, "r") as f:
@@ -55,11 +56,11 @@ def test_saveSession(soloTool, testSessionFile, tmp_path):
assert savedSession == testSession
-def test_loadAndSaveEmptySession(songPool, soloTool, tmp_path):
- emptyFile = tmp_path / "empty_session.json"
+def test_loadAndSaveEmptySession(sessionManager, sessionPath, soloTool, tmp_path):
+ emptySession = "empty_session"
- saveSession(soloTool, emptyFile)
- reloadedTool = loadSession(emptyFile, songPool)
+ sessionManager.saveSession(soloTool, emptySession)
+ reloadedTool = sessionManager.loadSession(emptySession)
assert reloadedTool.songs == []
diff --git a/web-project/src/solo_tool_web.py b/web-project/src/solo_tool_web.py
index 20d2d11..5512408 100644
--- a/web-project/src/solo_tool_web.py
+++ b/web-project/src/solo_tool_web.py
@@ -1,4 +1,3 @@
-from glob import glob
import sys
from os import getenv
from os.path import basename, splitext
@@ -8,12 +7,9 @@ from starlette.formparsers import MultiPartParser
import click
from solo_tool import SoloTool
-from solo_tool.session_manager import loadSession, saveSession
+from solo_tool.session_manager import getSessionManager
from solo_tool import handlers
-SESSION_DIR = getenv("SESSION_DIR", "/home/eddy/music/sessions")
-SONG_POOL = getenv("SONG_POOL", "/home/eddy/music/songs")
-
def fileName(path: str) -> str:
return basename(splitext(path)[0])
@@ -31,13 +27,7 @@ def songList(st: SoloTool, songDrawer) -> None:
ui.item(fileName(path), on_click=handlers.songAbsolute(st, i, lambda: songDrawer.hide())).props('clickable v-ripple')
sessions = {}
-for f in glob(f"{SESSION_DIR}/*.json"):
- sessionName = fileName(f)
- songTool = loadSession(f, SONG_POOL)
- songTool.registerKeyPointListCallback(lambda new: keyPointList.refresh())
- songTool.registerSongSelectionCallback(lambda new: keyPointList.refresh())
- songTool.registerSongListCallback(lambda new: songList.refresh())
- sessions[sessionName] = songTool
+sessionManager = None
@ui.page('/{sessionName}')
def sessionPage(sessionName: str):
@@ -56,11 +46,12 @@ def sessionPage(sessionName: str):
# Upload song dialog
def handleFileUpload(e):
- from shutil import copyfileobj
- newSong = f"{SONG_POOL}/{e.name}"
- with open(newSong, "wb") as f:
- copyfileobj(e.content, f)
- st.addSong(newSong)
+ if False:
+ from shutil import copyfileobj
+ newSong = f"{SONG_POOL}/{e.name}"
+ with open(newSong, "wb") as f:
+ copyfileobj(e.content, f)
+ st.addSong(newSong)
with ui.dialog() as uploadSongDialog:
ui.upload(label="Upload songs", auto_upload=True, on_upload=handleFileUpload).classes('max-w-full')
@@ -72,7 +63,7 @@ def sessionPage(sessionName: str):
ui.label().bind_text_from(st, 'song', lambda index: fileName(st.songs[index]) if index is not None else "Select a song").classes('text-lg')
with ui.row().classes('items-center justify-start'):
ui.button(icon='home', on_click=lambda: ui.navigate.to("/")).props('flat dense round color=white')
- def save(): saveSession(st, f"{SESSION_DIR}/{sessionName}.json")
+ def save(): sessionManager.saveSession(st, f"{sessionName}.json")
ui.button(icon='save', on_click=save).props('flat dense round color=white')
ui.button(icon='fullscreen', on_click=fullscreen.toggle).props('flat dense round color=white')
@@ -129,7 +120,16 @@ def landingPage():
for name, soloTool in sessions.items():
ui.button(name, on_click=partial(ui.navigate.to, f"/{name}"))
-def start(port, refresh, reload):
+def start(port, refresh, reload, session_path, song_path):
+ sessionManager = getSessionManager(song_path, session_path)
+
+ for key in sessionManager.getSessions():
+ songTool = sessionManager.loadSession(key)
+ songTool.registerKeyPointListCallback(lambda new: keyPointList.refresh())
+ songTool.registerSongSelectionCallback(lambda new: keyPointList.refresh())
+ songTool.registerSongListCallback(lambda new: songList.refresh())
+ sessions[fileName(key)] = songTool
+
try:
ui.run(reload=reload, binding_refresh_interval=refresh, port=port)
except KeyboardInterrupt:
@@ -139,9 +139,11 @@ def start(port, refresh, reload):
@click.option("--port", type=int, default=8080, help="Port on which to bind.")
@click.option("--refresh", type=float, default=0.5, help="Refresh interval in seconds.")
@click.option("--reload/--no-reload", default=True, help="Auto-reload when files change.")
-def main(port, refresh, reload):
- start(port, refresh, reload)
+@click.option("--session_path", default="/home/eddy/music/sessions", help="Look for sessions in this location.")
+@click.option("--song_path", default="/home/eddy/music/songs", help="Look for songs in this location.")
+def main(port, refresh, reload, session_path, song_path):
+ start(port, refresh, reload, session_path, song_path)
# Hardcoded dev settings
if __name__ in {"__main__", "__mp_main__"}:
- start(8080, 0.5, True)
+ start(8080, 0.5, True, "/home/eddy/music/sessions", "/home/eddy/music/songs")