Skip to content

Commit

Permalink
Extend playlist model with status flags
Browse files Browse the repository at this point in the history
Refs: #177
  • Loading branch information
orontee committed Sep 14, 2024
1 parent 1858039 commit 92a8119
Show file tree
Hide file tree
Showing 12 changed files with 58 additions and 37 deletions.
7 changes: 4 additions & 3 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ Added
Changed
-------

- Check whether Mopidy server is reachable: Relying only on network
availability lead to UI being unresponsive when Argos runs on the
same host as the server but network becomes unavailable `#181
- Support playlists without date of last modification `#177
<https://github.com/orontee/argos/issues/177>`_

- Check whether Mopidy server is reachable `#181
<https://github.com/orontee/argos/issues/181>`_

Removed
Expand Down
12 changes: 6 additions & 6 deletions argos/controllers/playlists.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import gettext
import logging
import math
import time
from operator import itemgetter
from typing import TYPE_CHECKING, Dict, List, Optional
Expand All @@ -15,7 +16,7 @@
from argos.controllers.visitors import PlaylistTrackNameFix
from argos.dto import PlaylistDTO
from argos.message import Message, MessageType, consume
from argos.model import PlaylistModel, TrackModel
from argos.model import ModelFlag, PlaylistModel, TrackModel

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -142,8 +143,7 @@ async def _save_playlist(
if playlist is None:
return None

if playlist.last_modified == -1:
# The playlist hasn't been loaded
if not (ModelFlag.TRACKS_COMPLETED & ModelFlag(playlist.props.flags)):
playlist_dto = await self._http.lookup_playlist(playlist_uri)
if playlist_dto is not None:
await self._complete_playlist_from(
Expand Down Expand Up @@ -244,7 +244,7 @@ async def __complete_history_playlist(self) -> None:
if not self._history_playlist:
return

LOGGER.info("Begin of history playlist completion")
LOGGER.debug("Begin of history playlist completion")
history = await self._http.get_history()
if history is None:
return
Expand Down Expand Up @@ -306,9 +306,9 @@ async def __complete_history_playlist(self) -> None:
self._history_playlist.uri,
name=self._history_playlist.name,
tracks=parsed_history_tracks_with_duplicates,
last_modified=time.time(),
last_modified=math.floor(time.time()),
)
LOGGER.info("End of history playlist completion")
LOGGER.debug("End of history playlist completion")

def _on_playlist_settings_changed(
self,
Expand Down
6 changes: 3 additions & 3 deletions argos/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ class PlaylistDTO:

uri: str
name: str
last_modified: int
last_modified: Optional[int]

tracks: List[TrackDTO] = field(default_factory=list)

Expand All @@ -257,10 +257,10 @@ def factory(data: Any) -> Optional["PlaylistDTO"]:

uri = data.get("uri")
name = data.get("name")
last_modified = data.get("last_modified")
if uri is None or name is None or last_modified is None:
if uri is None or name is None:
return None

last_modified = data.get("last_modified")
dto = PlaylistDTO(uri, name, last_modified)
for track_data in data.get("tracks", []):
track_dto = TrackDTO.factory(track_data)
Expand Down
2 changes: 1 addition & 1 deletion argos/io.github.orontee.Argos.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<file preprocess="xml-stripblanks">ui/library_window.ui</file>
<file preprocess="xml-stripblanks">ui/playing_box.ui</file>
<file preprocess="xml-stripblanks">ui/playing_box_empty_tracklist_box.ui</file>
<file preprocess="xml-stripblanks">ui/playlist_empty_tracks_box.ui</file>
<file preprocess="xml-stripblanks">ui/playlist_loading_tracks_box.ui</file>
<file preprocess="xml-stripblanks">ui/playlist_creation_dialog.ui</file>
<file preprocess="xml-stripblanks">ui/playlist_selection_dialog.ui</file>
<file preprocess="xml-stripblanks">ui/playlist_track_box.ui</file>
Expand Down
6 changes: 4 additions & 2 deletions argos/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
RandomTracksChoice,
RandomTracksChoiceState,
)
from argos.model.status import ModelFlag
from argos.model.track import TrackModel
from argos.model.tracklist import TracklistModel, TracklistTrackModel
from argos.model.utils import PlaybackState
Expand All @@ -21,14 +22,15 @@
"LibraryModel",
"MixerModel",
"Model",
"ModelFlag",
"MopidyBackend",
"PlaybackModel",
"PlaybackState",
"PlaylistModel",
"RANDOM_TRACKS_CHOICE_STRATEGY",
"RandomTracksChoice",
"RandomTracksChoiceState",
"RANDOM_TRACKS_CHOICE_STRATEGY",
"TrackModel",
"TracklistModel",
"TracklistTrackModel",
"TrackModel",
)
18 changes: 15 additions & 3 deletions argos/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from argos.model.playback import PlaybackModel
from argos.model.playlist import PlaylistModel, compare_playlists_func
from argos.model.random import RandomTracksChoice, choose_random_tracks
from argos.model.status import ModelFlag
from argos.model.track import (
TrackModel,
compare_tracks_by_name_func,
Expand Down Expand Up @@ -401,7 +402,7 @@ def complete_playlist_description(
*,
name: str,
tracks: Sequence[TrackModel],
last_modified: float,
last_modified: Optional[int],
wait_for_model_update: bool = False,
) -> None:
event: Optional[threading.Event]
Expand All @@ -426,17 +427,28 @@ def _complete_playlist_description() -> None:
LOGGER.debug(f"Insertion of playlist with URI {playlist.uri!r}")
self.playlists.insert_sorted(playlist, compare_playlists_func, None)
else:
if playlist.last_modified == last_modified:
if (
last_modified is not None
and playlist.last_modified == last_modified
):
LOGGER.debug(f"Playlist with URI {playlist_uri!r} is up-to-date")
if event is not None:
event.set()
return

playlist.name = name
playlist.tracks.remove_all()

playlist.last_modified = last_modified
if last_modified is not None:
playlist.last_modified = last_modified

for track in tracks:
playlist.tracks.append(track)

playlist.set_property(
"flags", playlist.flags | ModelFlag.TRACKS_COMPLETED.value
)

if event is not None:
event.set()

Expand Down
3 changes: 2 additions & 1 deletion argos/model/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from gi.repository import Gio, GObject

from argos.model.status import ModelFlag
from argos.model.track import TrackModel


Expand Down Expand Up @@ -33,9 +34,9 @@ class PlaylistModel(GObject.Object):
"""Model for a playlist."""

uri = GObject.Property(type=str)
flags = GObject.Property(type=GObject.TYPE_INT, default=ModelFlag.NO_FLAG.value)
name = GObject.Property(type=str)
last_modified = GObject.Property(type=GObject.TYPE_DOUBLE, default=-1)

tracks: Gio.ListStore

def __init__(self, **kwargs):
Expand Down
6 changes: 6 additions & 0 deletions argos/model/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Flag, auto


class ModelFlag(Flag):
NO_FLAG = auto()
TRACKS_COMPLETED = auto()
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<template class="PlaylistEmptyTracksBox" parent="GtkBox">
<template class="PlaylistLoadingTracksBox" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
LOGGER = logging.getLogger(__name__)


@Gtk.Template(resource_path="/io/github/orontee/Argos/ui/playlist_empty_tracks_box.ui")
class PlaylistEmptyTracksBox(Gtk.Box):
"""Box to use as a placeholder for empty playlist tracks box.
@Gtk.Template(
resource_path="/io/github/orontee/Argos/ui/playlist_loading_tracks_box.ui"
)
class PlaylistLoadingTracksBox(Gtk.Box):
"""Box to use as a placeholder for playlist with loading tracks.
The box has vertical orientation and has two children boxes: An
active spinner and a label.
"""

__gtype_name__ = "PlaylistEmptyTracksBox"
__gtype_name__ = "PlaylistLoadingTracksBox"

progress_label: Gtk.Label = Gtk.Template.Child()
progress_spinner: Gtk.Spinner = Gtk.Template.Child()
Expand Down
21 changes: 9 additions & 12 deletions argos/widgets/playlistsbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from gi.repository import Gio, GLib, GObject, Gtk

from argos.message import MessageType
from argos.model import PlaylistModel, TrackModel
from argos.model import ModelFlag, PlaylistModel, TrackModel
from argos.utils import ms_to_text
from argos.widgets.condensedplayingbox import CondensedPlayingBox
from argos.widgets.playlistemptytracksbox import PlaylistEmptyTracksBox
from argos.widgets.playlistlabel import PlaylistLabel
from argos.widgets.playlistloadingtracksbox import PlaylistLoadingTracksBox
from argos.widgets.playlisttrackbox import PlaylistTrackBox
from argos.widgets.streamuridialog import StreamUriDialog
from argos.widgets.utils import set_list_box_header_with_date_separator, tracks_length
Expand Down Expand Up @@ -70,7 +70,7 @@ def __init__(self, application: Gtk.Application):
self._app = application
self._model = application.model
self._disable_tooltips = application.props.disable_tooltips
self._empty_tracks_placeholder = PlaylistEmptyTracksBox(application)
self._loading_tracks_placeholder = PlaylistLoadingTracksBox(application)

self.add(CondensedPlayingBox(application))

Expand All @@ -87,7 +87,7 @@ def __init__(self, application: Gtk.Application):
)

self.tracks_box.set_header_func(set_list_box_header_with_date_separator)
self.tracks_box.set_placeholder(self._empty_tracks_placeholder)
self.tracks_box.set_placeholder(self._loading_tracks_placeholder)

edition_menu = Gio.Menu()
edition_menu.append(_("Add stream to playlist…"), "win.add-stream-to-playlist")
Expand Down Expand Up @@ -133,22 +133,19 @@ def bind_model_to_playlist_tracks(self, uri: str) -> None:
self._create_track_box if tracks is not None else None,
)

# Since playlist model missed a "loaded" property, the tracks
# emptyness must be completed by a check to last_modified
# value...
self._empty_tracks_placeholder.props.loading = (
playlist is not None and playlist.props.last_modified == -1
self._loading_tracks_placeholder.props.loading = playlist is not None and not (
ModelFlag.TRACKS_COMPLETED & ModelFlag(playlist.props.flags)
)

if playlist is not None:

def update_placeholder_loading_prop(
playlist: GObject.GObject, _2: GObject.GParamSpec
) -> None:
if playlist.props.last_modified != -1:
self._empty_tracks_placeholder.props.loading = False
if ModelFlag.TRACKS_COMPLETED & ModelFlag(playlist.props.flags):
self._loading_tracks_placeholder.props.loading = False

playlist.connect("notify::last-modified", update_placeholder_loading_prop)
playlist.connect("notify::flags", update_placeholder_loading_prop)

self.play_button.set_sensitive(playlist is not None)
self.add_button.set_sensitive(playlist is not None)
Expand Down
2 changes: 1 addition & 1 deletion po/POTFILES.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ argos/utils.py
argos/widgets/albumdetailsbox.py
argos/widgets/playingboxemptytracklistbox.py
argos/widgets/playlistcreationdialog.py
argos/widgets/playlistemptytracksbox.py
argos/widgets/playlistloadingtracksbox.py
argos/widgets/playlistsbox.py
argos/widgets/playlistselectiondialog.py
argos/widgets/preferences.py
Expand Down

0 comments on commit 92a8119

Please sign in to comment.