From f5ef144419182024fb03850f8fadf8a272b9c48f Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 19 Apr 2023 15:58:01 -0400 Subject: [PATCH] playlist: Create a Previous Playlist type This Playlist implements some special handling for the Previous Playlist. This includes adding a previous_tracks() function and properties to indicate if the Playlist has a previous or next track. Signed-off-by: Anna Schumaker --- emmental/playlist/previous.py | 39 ++++++++++++++ tests/playlist/test_previous.py | 92 +++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 emmental/playlist/previous.py create mode 100644 tests/playlist/test_previous.py diff --git a/emmental/playlist/previous.py b/emmental/playlist/previous.py new file mode 100644 index 0000000..b246546 --- /dev/null +++ b/emmental/playlist/previous.py @@ -0,0 +1,39 @@ +# Copyright 2023 (c) Anna Schumaker. +"""A Playlist model configured for the Previous Tracks playlist.""" +from gi.repository import GObject +from . import playlist +from .. import db + + +class Previous(playlist.Playlist): + """A Playlist configured for Previous Tracks.""" + + can_go_forward = GObject.Property(type=bool, default=False) + + def __init__(self, sql: db.Connection, playlist: db.playlist.Playlist): + """Initialize the Previous Playlist object.""" + super().__init__(sql=sql, playlist=playlist) + self.connect("notify::current-track", self.__notify_current_track) + + def __notify_current_track(self, plist: playlist.Playlist, param) -> None: + current = self.current_track + self.can_go_forward = current and current != self[0] + + def do_get_sort_key(self, trackid: int) -> float: + """Get the sort key for a given trackid.""" + track = self.sql.tracks.rows.get(trackid) + return 0 if track is None else -track.laststarted.timestamp() + + def on_trackid_added(self, set: db.tracks.TrackidSet, + trackid: int) -> None: + """Handle the TrackidSet::trackid-added signal.""" + super().on_trackid_added(set, trackid) + self.playlist.current_trackid = self.trackids[0] + self.notify("current-track") + + def previous_track(self) -> db.tracks.Track | None: + """Go backwards one Track.""" + if self.playlist is not None and len(self.trackids) > 0: + if (index := self.index(self.current_track)) > 0: + self.current_track = self[index - 1] + return self.current_track diff --git a/tests/playlist/test_previous.py b/tests/playlist/test_previous.py new file mode 100644 index 0000000..eb34d7e --- /dev/null +++ b/tests/playlist/test_previous.py @@ -0,0 +1,92 @@ +# Copyright 2023 (c) Anna Schumaker. +"""Tests our Previous Tracks Playlist model.""" +import pathlib +import tests.util +import emmental.playlist.previous + + +class TestPrevious(tests.util.TestCase): + """Tests the Previous Playlist Gio.ListModel.""" + + def setUp(self): + """Set up common variables.""" + super().setUp() + self.sql.playlists.load(now=True) + self.previous = emmental.playlist.previous.Previous( + self.sql, self.sql.playlists.previous) + + self.library = self.sql.libraries.create(pathlib.Path("/a/b")) + self.album = self.sql.albums.create("Test Album", "Artist", "2023") + self.medium = self.sql.media.create(self.album, "", number=1) + self.year = self.sql.years.create(2023) + + self.tracks = [self.sql.tracks.create(self.library, + pathlib.Path(f"/a/b/{i}.ogg"), + self.medium, self.year, number=i) + for i in range(1, 6)] + + def test_init(self): + """Test that the Previous Playlist was set up correctly.""" + self.assertIsInstance(self.previous, + emmental.playlist.playlist.Playlist) + self.assertEqual(self.previous.playlist, self.sql.playlists.previous) + + def test_can_go_forward(self): + """Test the can-go-forward property.""" + self.assertFalse(self.previous.can_go_forward) + + self.tracks[0].start() + self.assertFalse(self.previous.can_go_forward) + + self.tracks[1].start() + self.assertFalse(self.previous.can_go_forward) + + self.previous.next_track() + self.assertTrue(self.previous.can_go_forward) + + def test_start_tracks(self): + """Test that tracks added to the playlist are sorted correctly.""" + self.tracks[0].start() + self.assertEqual(self.previous[0], self.tracks[0]) + self.assertEqual(self.previous.current_track, self.tracks[0]) + + self.tracks[1].start() + self.assertEqual(self.previous[0], self.tracks[1]) + self.assertEqual(self.previous[1], self.tracks[0]) + + self.tracks[2].start() + self.assertEqual(self.previous[0], self.tracks[2]) + self.assertEqual(self.previous[1], self.tracks[1]) + self.assertEqual(self.previous[2], self.tracks[0]) + + def test_previous_track(self): + """Test the previous_track() function.""" + self.assertIsNone(self.previous.previous_track()) + + for track in self.tracks: + track.start() + + self.previous.current_track = self.tracks[0] + self.assertEqual(self.previous.previous_track(), self.tracks[1]) + self.assertEqual(self.previous.previous_track(), self.tracks[2]) + self.assertEqual(self.previous.previous_track(), self.tracks[3]) + self.assertEqual(self.previous.previous_track(), self.tracks[4]) + self.assertIsNone(self.previous.previous_track()) + + self.previous.playlist = None + self.assertIsNone(self.previous.previous_track()) + + def test_next_previous_track(self): + """Test mixing the next_track() and previous_track() functions.""" + for track in self.tracks: + track.start() + + self.assertEqual(self.previous.next_track(), self.tracks[3]) + self.assertEqual(self.previous.next_track(), self.tracks[2]) + self.assertEqual(self.previous.next_track(), self.tracks[1]) + self.assertEqual(self.previous.next_track(), self.tracks[0]) + self.assertIsNone(self.previous.next_track()) + + self.assertEqual(self.previous.previous_track(), self.tracks[1]) + self.assertEqual(self.previous.previous_track(), self.tracks[2]) + self.assertEqual(self.previous.previous_track(), self.tracks[3])