Improved state saving logic

This commit is contained in:
Sumner Evans
2019-06-28 20:04:08 -06:00
parent ee47d63cb4
commit 2168b47585
4 changed files with 86 additions and 47 deletions

View File

@@ -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(

View File

@@ -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))

View File

@@ -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')

View File

@@ -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')