Improved state saving logic
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
import mpv
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gio, Gtk, GLib, Gdk, GObject
|
||||
from gi.repository import Gdk, Gio, GLib, Gtk
|
||||
|
||||
from .ui.main import MainWindow
|
||||
from .ui.configure_servers import ConfigureServersDialog
|
||||
@@ -33,6 +32,8 @@ class LibremsonicApp(Gtk.Application):
|
||||
'Specify a configuration file. Defaults to ~/.config/libremsonic/config.json',
|
||||
None)
|
||||
|
||||
self.connect('shutdown', lambda _: self.state.save())
|
||||
|
||||
self.player = mpv.MPV()
|
||||
|
||||
@self.player.property_observer('time-pos')
|
||||
@@ -118,7 +119,7 @@ class LibremsonicApp(Gtk.Application):
|
||||
|
||||
# Load the configuration and update the UI with the curent server, if
|
||||
# it exists.
|
||||
self.state.load_config()
|
||||
self.state.load()
|
||||
|
||||
# If there is no current server, show the dialog to select a server.
|
||||
if self.state.config.current_server is None:
|
||||
@@ -162,11 +163,11 @@ class LibremsonicApp(Gtk.Application):
|
||||
|
||||
def on_server_list_changed(self, action, servers):
|
||||
self.state.config.servers = servers
|
||||
self.state.save_config()
|
||||
self.state.save()
|
||||
|
||||
def on_connected_server_changed(self, action, current_server):
|
||||
self.state.config.current_server = current_server
|
||||
self.state.save_config()
|
||||
self.state.save()
|
||||
|
||||
# Reset the CacheManager.
|
||||
CacheManager.reset(
|
||||
|
@@ -1,9 +1,6 @@
|
||||
import os
|
||||
|
||||
from typing import Any, Dict, List
|
||||
import json
|
||||
|
||||
from libremsonic.from_json import from_json
|
||||
from typing import List
|
||||
|
||||
|
||||
class ServerConfiguration:
|
||||
@@ -16,16 +13,17 @@ class ServerConfiguration:
|
||||
browse_by_tags: bool
|
||||
sync_enabled: bool
|
||||
|
||||
def __init__(self,
|
||||
name='Default',
|
||||
server_address='http://yourhost',
|
||||
local_network_address='',
|
||||
local_network_ssid='',
|
||||
username='',
|
||||
password='',
|
||||
browse_by_tags=False,
|
||||
sync_enabled=True):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name='Default',
|
||||
server_address='http://yourhost',
|
||||
local_network_address='',
|
||||
local_network_ssid='',
|
||||
username='',
|
||||
password='',
|
||||
browse_by_tags=False,
|
||||
sync_enabled=True,
|
||||
):
|
||||
self.name = name
|
||||
self.server_address = server_address
|
||||
self.local_network_address = local_network_address
|
||||
@@ -67,21 +65,3 @@ class AppConfiguration:
|
||||
default_cache_location = (os.environ.get('XDG_DATA_HOME')
|
||||
or os.path.expanduser('~/.local/share'))
|
||||
return os.path.join(default_cache_location, 'libremsonic')
|
||||
|
||||
|
||||
def get_config(filename: str) -> AppConfiguration:
|
||||
if not os.path.exists(filename):
|
||||
return AppConfiguration.get_default_configuration()
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
try:
|
||||
return from_json(AppConfiguration, json.load(f))
|
||||
except json.decoder.JSONDecodeError:
|
||||
return AppConfiguration.get_default_configuration()
|
||||
|
||||
|
||||
def save_config(config: AppConfiguration, filename: str):
|
||||
# Make the necessary directories before writing the config.
|
||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||
with open(filename, 'w+') as f:
|
||||
f.write(json.dumps(config.to_json(), indent=2, sort_keys=True))
|
||||
|
@@ -1,11 +1,27 @@
|
||||
from typing import List, Any
|
||||
|
||||
from .config import get_config, save_config, AppConfiguration
|
||||
import os
|
||||
from pathlib import Path
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
from libremsonic.from_json import from_json
|
||||
from .config import AppConfiguration
|
||||
from .server.api_objects import Child
|
||||
|
||||
|
||||
class ApplicationState:
|
||||
"""
|
||||
Represents the state of the application. In general, there are two things
|
||||
that are stored here: configuration, and UI state.
|
||||
|
||||
Configuration is stored in ``config`` which is an ``AppConfiguration``
|
||||
object. UI state is stored as separate properties on this class.
|
||||
|
||||
Configuration is stored to disk in $XDG_CONFIG_HOME/libremsonic. State is
|
||||
stored in $XDG_CACHE_HOME. Nothing in state should be assumed to be
|
||||
permanent. State need not be saved, the ``to_json`` and ``from_json``
|
||||
functions define what part of the state will be saved across application
|
||||
loads.
|
||||
"""
|
||||
config: AppConfiguration = AppConfiguration.get_default_configuration()
|
||||
current_song: Child
|
||||
config_file: str
|
||||
@@ -14,10 +30,51 @@ class ApplicationState:
|
||||
volume: int = 100
|
||||
old_volume: int = 100
|
||||
|
||||
# TODO save these values that aren't necessarily config to disk
|
||||
def to_json(self):
|
||||
return {
|
||||
'current_song': getattr(self, 'current_song', None),
|
||||
'play_queue': getattr(self, 'play_queue', None),
|
||||
'volume': getattr(self, 'volume', None),
|
||||
}
|
||||
|
||||
def load_config(self):
|
||||
self.config = get_config(self.config_file)
|
||||
def load_from_json(self, json_object):
|
||||
self.current_song = json_object.get('current_song') or None
|
||||
self.play_queue = json_object.get('play_queue') or []
|
||||
self.volume = json_object.get('volume') or 100
|
||||
|
||||
def save_config(self):
|
||||
save_config(self.config, self.config_file)
|
||||
def load(self):
|
||||
self.config = self.get_config(self.config_file)
|
||||
|
||||
if os.path.exists(self.state_filename):
|
||||
with open(self.state_filename, 'r') as f:
|
||||
self.load_from_json(json.load(f))
|
||||
|
||||
def save(self):
|
||||
# Make the necessary directories before writing the config and state.
|
||||
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(self.state_filename), exist_ok=True)
|
||||
|
||||
# Save the config
|
||||
with open(self.config_file, 'w+') as f:
|
||||
f.write(json.dumps(self.config.to_json(), indent=2,
|
||||
sort_keys=True))
|
||||
|
||||
# Save the state
|
||||
with open(self.state_filename, 'w+') as f:
|
||||
f.write(json.dumps(self.to_json(), indent=2, sort_keys=True))
|
||||
|
||||
def get_config(self, filename: str) -> AppConfiguration:
|
||||
if not os.path.exists(filename):
|
||||
return AppConfiguration.get_default_configuration()
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
try:
|
||||
return from_json(AppConfiguration, json.load(f))
|
||||
except json.decoder.JSONDecodeError:
|
||||
return AppConfiguration.get_default_configuration()
|
||||
|
||||
@property
|
||||
def state_filename(self):
|
||||
state_filename = (os.environ.get('XDG_CACHE_HOME')
|
||||
or os.path.expanduser('~/.cache'))
|
||||
return os.path.join(state_filename, 'libremsonic/state.yaml')
|
||||
|
@@ -40,7 +40,8 @@ class PlayerControls(Gtk.ActionBar):
|
||||
self.play_button.get_child().set_from_icon_name(
|
||||
f"media-playback-{icon}-symbolic", Gtk.IconSize.LARGE_TOOLBAR)
|
||||
|
||||
has_current_song = hasattr(state, 'current_song')
|
||||
has_current_song = (hasattr(state, 'current_song')
|
||||
and state.current_song is not None)
|
||||
has_prev_song, has_next_song = False, False
|
||||
if has_current_song and state.current_song.id in state.play_queue:
|
||||
# TODO will need to change when repeat is implemented
|
||||
@@ -72,7 +73,7 @@ class PlayerControls(Gtk.ActionBar):
|
||||
self.volume_slider.set_value(state.volume)
|
||||
|
||||
if not has_current_song:
|
||||
# TODO should probably clear out something?
|
||||
# TODO should probably clear out the cover art display?
|
||||
return
|
||||
|
||||
self.update_cover_art(state.current_song.coverArt, size='70')
|
||||
|
Reference in New Issue
Block a user