Closes #175: cached a bunch of calls that hit the database a lot from the DBus Manager

This commit is contained in:
Sumner Evans
2020-05-10 09:32:13 -06:00
parent 60063a12a9
commit 65aafd111d
9 changed files with 73 additions and 65 deletions

6
Pipfile.lock generated
View File

@@ -157,10 +157,10 @@
}, },
"marshmallow": { "marshmallow": {
"hashes": [ "hashes": [
"sha256:56663fa1d5385c14c6a1236badd166d6dee987a5f64d2b6cc099dadf96eb4f09", "sha256:c2673233aa21dde264b84349dc2fd1dce5f30ed724a0a00e75426734de5b84ab",
"sha256:f12203bf8d94c410ab4b8d66edfde4f8a364892bde1f6747179765559f93d62a" "sha256:f88fe96434b1f0f476d54224d59333eba8ca1a203a2695683c1855675c4049a7"
], ],
"version": "==3.5.2" "version": "==3.6.0"
}, },
"marshmallow-enum": { "marshmallow-enum": {
"hashes": [ "hashes": [

View File

@@ -8,7 +8,6 @@ from termcolor import cprint
todo_re = re.compile(r"\s*#\s*TODO:?\s*") todo_re = re.compile(r"\s*#\s*TODO:?\s*")
accounted_for_todo = re.compile(r"\s*#\s*TODO:?\s*\((#\d+)\)") accounted_for_todo = re.compile(r"\s*#\s*TODO:?\s*\((#\d+)\)")
print_re = re.compile(r"\s+print\(.*\)")
def noqa_re(error_id: str = ""): def noqa_re(error_id: str = ""):
@@ -29,10 +28,6 @@ def check_file(path: Path) -> bool:
eprint(f"{i}: {line}") eprint(f"{i}: {line}")
valid = False valid = False
if print_re.search(line) and not noqa_re("T001").search(line):
eprint(f"{i}: {line}")
valid = False
file.close() file.close()
return valid return valid

View File

@@ -1,8 +1,7 @@
[flake8] [flake8]
select = C,E,F,W,B,B950 extend-ignore = E203, E402, E722, W503, ANN002, ANN003, ANN101, ANN102, ANN204
ignore = E203, E402, E501, W503, ANN002, ANN003, ANN101, ANN102, ANN204
exclude = .git,__pycache__,build,dist,flatpak exclude = .git,__pycache__,build,dist,flatpak
max-line-length = 80 max-line-length = 88
suppress-none-returning = True suppress-none-returning = True
suppress-dummy-args = True suppress-dummy-args = True
application-import-names = sublime application-import-names = sublime
@@ -47,15 +46,6 @@ ignore_missing_imports = True
[mypy-peewee] [mypy-peewee]
ignore_missing_imports = True ignore_missing_imports = True
[yapf]
based_on_style = pep8
split_before_bitwise_operator = true
split_before_arithmetic_operator = true
split_before_dot = true
split_before_logical_operator = true
split_complex_comprehension = true
split_before_first_argument = true
[tool:pytest] [tool:pytest]
python_files = tests/**/*.py tests/*.py python_files = tests/**/*.py tests/*.py
python_functions = test_* *_test python_functions = test_* *_test

View File

@@ -383,6 +383,7 @@ class AdapterManager:
def get_playlists( def get_playlists(
before_download: Callable[[], None] = lambda: None, before_download: Callable[[], None] = lambda: None,
force: bool = False, # TODO: rename to use_ground_truth_adapter? force: bool = False, # TODO: rename to use_ground_truth_adapter?
allow_download: bool = True,
) -> Result[Sequence[Playlist]]: ) -> Result[Sequence[Playlist]]:
assert AdapterManager._instance assert AdapterManager._instance
partial_playlists_data = None partial_playlists_data = None
@@ -396,6 +397,9 @@ class AdapterManager:
except Exception: except Exception:
logging.exception(f'Error on {"get_playlists"} retrieving from cache.') logging.exception(f'Error on {"get_playlists"} retrieving from cache.')
if not allow_download:
raise CacheMissError(partial_data=partial_playlist_data)
if AdapterManager._instance.caching_adapter and force: if AdapterManager._instance.caching_adapter and force:
AdapterManager._instance.caching_adapter.invalidate_data( AdapterManager._instance.caching_adapter.invalidate_data(
CachingAdapter.CachedDataKey.PLAYLISTS, () CachingAdapter.CachedDataKey.PLAYLISTS, ()

View File

@@ -348,7 +348,7 @@ class SublimeMusicApp(Gtk.Application):
self.on_song_clicked( self.on_song_clicked(
None, None,
song_idx, song_idx,
[s.id for s in playlist.songs], tuple(s.id for s in playlist.songs),
{"active_playlist_id": playlist_id}, {"active_playlist_id": playlist_id},
) )
@@ -598,7 +598,7 @@ class SublimeMusicApp(Gtk.Application):
self.update_window() self.update_window()
@dbus_propagate() @dbus_propagate()
def on_play_next(self, action: Any, song_ids: List[str]): def on_play_next(self, action: Any, song_ids: Tuple[str, ...]):
if self.app_config.state.current_song is None: if self.app_config.state.current_song is None:
insert_at = 0 insert_at = 0
else: else:
@@ -606,16 +606,18 @@ class SublimeMusicApp(Gtk.Application):
self.app_config.state.play_queue = ( self.app_config.state.play_queue = (
self.app_config.state.play_queue[:insert_at] self.app_config.state.play_queue[:insert_at]
+ list(song_ids) + song_ids
+ self.app_config.state.play_queue[insert_at:] + self.app_config.state.play_queue[insert_at:]
) )
self.app_config.state.old_play_queue.extend(song_ids) self.app_config.state.old_play_queue += song_ids
self.update_window() self.update_window()
@dbus_propagate() @dbus_propagate()
def on_add_to_queue(self, action: Any, song_ids: GLib.Variant): def on_add_to_queue(self, action: Any, song_ids: GLib.Variant):
self.app_config.state.play_queue.extend(song_ids) print(song_ids)
self.app_config.state.old_play_queue.extend(song_ids) print(type(song_ids))
self.app_config.state.play_queue += tuple(song_ids)
self.app_config.state.old_play_queue += tuple(song_ids)
self.update_window() self.update_window()
def on_go_to_album(self, action: Any, album_id: GLib.Variant): def on_go_to_album(self, action: Any, album_id: GLib.Variant):
@@ -714,12 +716,14 @@ class SublimeMusicApp(Gtk.Application):
self, self,
win: Any, win: Any,
song_index: int, song_index: int,
song_queue: List[str], song_queue: Tuple[str, ...],
metadata: Dict[str, Any], metadata: Dict[str, Any],
): ):
print(type(song_queue), song_queue)
song_queue = tuple(song_queue)
# Reset the play queue so that we don't ever revert back to the # Reset the play queue so that we don't ever revert back to the
# previous one. # previous one.
old_play_queue = song_queue.copy() old_play_queue = song_queue
if (force_shuffle := metadata.get("force_shuffle_state")) is not None: if (force_shuffle := metadata.get("force_shuffle_state")) is not None:
self.app_config.state.shuffle_on = force_shuffle self.app_config.state.shuffle_on = force_shuffle
@@ -729,10 +733,11 @@ class SublimeMusicApp(Gtk.Application):
# If shuffle is enabled, then shuffle the playlist. # If shuffle is enabled, then shuffle the playlist.
if self.app_config.state.shuffle_on and not metadata.get("no_reshuffle"): if self.app_config.state.shuffle_on and not metadata.get("no_reshuffle"):
song_id = song_queue[song_index] song_id = song_queue[song_index]
song_queue_list = list(
del song_queue[song_index] song_queue[:song_index] + song_queue[song_index + 1 :]
random.shuffle(song_queue) )
song_queue = [song_id] + song_queue random.shuffle(song_queue_list)
song_queue = tuple(song_id, *song_queue_list)
song_index = 0 song_index = 0
self.play_song( self.play_song(
@@ -743,11 +748,11 @@ class SublimeMusicApp(Gtk.Application):
) )
def on_songs_removed(self, win: Any, song_indexes_to_remove: List[int]): def on_songs_removed(self, win: Any, song_indexes_to_remove: List[int]):
self.app_config.state.play_queue = [ self.app_config.state.play_queue = tuple(
song_id song_id
for i, song_id in enumerate(self.app_config.state.play_queue) for i, song_id in enumerate(self.app_config.state.play_queue)
if i not in song_indexes_to_remove if i not in song_indexes_to_remove
] )
# Determine how many songs before the currently playing one were also # Determine how many songs before the currently playing one were also
# deleted. # deleted.
@@ -902,7 +907,7 @@ class SublimeMusicApp(Gtk.Application):
def do_update(f: Future): def do_update(f: Future):
play_queue = f.result() play_queue = f.result()
new_play_queue = [s.id for s in play_queue.entry] new_play_queue = tuple(s.id for s in play_queue.entry)
new_current_song_id = str(play_queue.current) new_current_song_id = str(play_queue.current)
new_song_progress = play_queue.position / 1000 new_song_progress = play_queue.position / 1000
@@ -965,8 +970,8 @@ class SublimeMusicApp(Gtk.Application):
self, self,
song_index: int, song_index: int,
reset: bool = False, reset: bool = False,
old_play_queue: List[str] = None, old_play_queue: Tuple[str, ...] = None,
play_queue: List[str] = None, play_queue: Tuple[str, ...] = None,
): ):
# Do this the old fashioned way so that we can have access to ``reset`` # Do this the old fashioned way so that we can have access to ``reset``
# in the callback. # in the callback.

View File

@@ -137,6 +137,7 @@ class AppConfiguration:
return self._state return self._state
def load_state(self): def load_state(self):
self._state = UIState()
if not self.server: if not self.server:
return return

View File

@@ -170,27 +170,9 @@ class DBusManager:
elif has_current_song: elif has_current_song:
has_next_song = state.current_song_index < len(state.play_queue) - 1 has_next_song = state.current_song_index < len(state.play_queue) - 1
active_playlist = (False, GLib.Variant("(oss)", ("/", "", ""))) active_playlist = self.get_active_playlist(state.active_playlist_id)
if state.active_playlist_id and AdapterManager.can_get_playlist_details():
try:
playlist = AdapterManager.get_playlist_details(
state.active_playlist_id, allow_download=False
).result()
cover_art = AdapterManager.get_cover_art_filename( get_playlists_result = AdapterManager.get_playlists(allow_download=False)
playlist.cover_art, allow_download=False
).result()
active_playlist = (
True,
GLib.Variant(
"(oss)", ("/playlist/" + playlist.id, playlist.name, cover_art)
),
)
except CacheMissError:
pass
get_playlists_result = AdapterManager.get_playlists()
if get_playlists_result.data_is_available: if get_playlists_result.data_is_available:
playlist_count = len(get_playlists_result.result()) playlist_count = len(get_playlists_result.result())
else: else:
@@ -249,7 +231,38 @@ class DBusManager:
}, },
} }
def get_mpris_metadata(self, idx: int, play_queue: List[str],) -> Dict[str, Any]: @functools.lru_cache(maxsize=10)
def get_active_playlist(
self, active_playlist_id: Optional[str]
) -> Tuple[bool, GLib.Variant]:
if not active_playlist_id or not AdapterManager.can_get_playlist_details():
return (False, GLib.Variant("(oss)", ("/", "", "")))
try:
playlist = AdapterManager.get_playlist_details(
active_playlist_id, allow_download=False
).result()
try:
cover_art = AdapterManager.get_cover_art_filename(
playlist.cover_art, allow_download=False
).result()
except CacheMissError:
cover_art = ""
return (
True,
GLib.Variant(
"(oss)", ("/playlist/" + playlist.id, playlist.name, cover_art)
),
)
except CacheMissError:
return (False, GLib.Variant("(oss)", ("/", "", "")))
@functools.lru_cache(maxsize=10)
def get_mpris_metadata(
self, idx: int, play_queue: Tuple[str, ...]
) -> Dict[str, Any]:
try: try:
song = AdapterManager.get_song_details( song = AdapterManager.get_song_details(
play_queue[idx], allow_download=False play_queue[idx], allow_download=False
@@ -283,7 +296,8 @@ class DBusManager:
"xesam:title": song.title, "xesam:title": song.title,
} }
def get_dbus_playlist(self, play_queue: List[str]) -> List[str]: @functools.lru_cache(maxsize=10)
def get_dbus_playlist(self, play_queue: Tuple[str, ...]) -> List[str]:
seen_counts: DefaultDict[str, int] = defaultdict(int) seen_counts: DefaultDict[str, int] = defaultdict(int)
tracks = [] tracks = []
for song_id in play_queue: for song_id in play_queue:

View File

@@ -2,7 +2,7 @@ import math
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Any, Callable, List, Optional from typing import Any, Callable, List, Optional, Tuple
import gi import gi
@@ -46,7 +46,7 @@ class PlayerControls(Gtk.ActionBar):
reordering_play_queue_song_list: bool = False reordering_play_queue_song_list: bool = False
current_song = None current_song = None
current_device = None current_device = None
current_play_queue: List[str] = [] current_play_queue: Tuple[str, ...] = ()
chromecasts: List[ChromecastPlayer] = [] chromecasts: List[ChromecastPlayer] = []
cover_art_update_order_token = 0 cover_art_update_order_token = 0
play_queue_update_order_token = 0 play_queue_update_order_token = 0
@@ -154,7 +154,6 @@ class PlayerControls(Gtk.ActionBar):
self.update_device_list() self.update_device_list()
# Short circuit if no changes to the play queue # Short circuit if no changes to the play queue
print(self.current_play_queue, app_config.state.play_queue)
if self.current_play_queue == app_config.state.play_queue: if self.current_play_queue == app_config.state.play_queue:
return return
self.current_play_queue = app_config.state.play_queue self.current_play_queue = app_config.state.play_queue

View File

@@ -1,6 +1,6 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Dict, List, Optional from typing import Dict, Optional, Tuple
from sublime.adapters.api_objects import Song from sublime.adapters.api_objects import Song
@@ -36,8 +36,8 @@ class UIState:
version: int = 1 version: int = 1
playing: bool = False playing: bool = False
current_song_index: int = -1 current_song_index: int = -1
play_queue: List[str] = field(default_factory=list) play_queue: Tuple[str, ...] = field(default_factory=tuple)
old_play_queue: List[str] = field(default_factory=list) old_play_queue: Tuple[str, ...] = field(default_factory=tuple)
_volume: Dict[str, float] = field(default_factory=lambda: {"this device": 100.0}) _volume: Dict[str, float] = field(default_factory=lambda: {"this device": 100.0})
is_muted: bool = False is_muted: bool = False
repeat_type: RepeatType = RepeatType.NO_REPEAT repeat_type: RepeatType = RepeatType.NO_REPEAT