Commit Graph

1334 Commits

Author SHA1 Message Date
Anna Schumaker ed1d990e74 tracklist: Add a LoopButton
This is an ImageToggle button that has been configured to cycle between
3 states corresponding to no looping, playlist looping, and track
looping.

I also update the Tracklist to look for changes in the visible Playlist
to update the Loop button.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 2d19d78eb6 tracklist: Add an Unselect All button
This button will unselect any selected tracks when clicked.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker f1e18549ff tracklist: Add a VisibleColumns button
This button shows a popover menu to set the visibility of our columns.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker ff1d772a05 tracklist: Add a Selection OSD to the TrackView
The OSD will eventually contain buttons for modifying playlists, but for
now it just has functions and properties for mananging the current track
selection.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker a485a3806b tracklist: Scroll to the requested Track
And wire this up to not only the Now Playing "jump" signal, but also the
next track pickers so we scroll when tracks are changed.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 481c4856c7 tracklist: Request a Track when it is activated
And wire up a handler for the factory track-requested signal in the main
application code.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 61dfc2a586 tracklist: Create a MediumName TrackRow
The MediumName TrackRow is used to combine the album and medium names
into a single string. This means we won't need to have a separate medium
name column that is empty for most tracks.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 97bf9d48db tracklist: Create an AlbumCover TrackRow
The AlbumCover shows a cover.jpg image for a specific Album in a column.
I also need to do some special handling so generate a tooltip to show a
larger version of the image.

I try to cache the AlbumCover Texture to cut down on disk accesses,
since we'll usually end up loading the same image several times for each
track in an album.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker b3dcd3c0b9 tracklist: Create an AlbumString TrackRow
This is an InscriptionRow that binds a Track's Album's property to the
Gtk.Inscription. I use it to display album artist and release information
in columns.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 1ffc300eda tracklist: Create a TracknoString TrackRow
This is an InscriptionRow that combines a Track's number with its
Medium's number.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker bed518cd77 tracklist: Create a FavoriteButton TrackRow
This button shows an ImageToggle button connected to the "favorite"
property of the Track.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 915a59a46b tracklist: Create LengthString, PlayCountString, TimestampString, and PathString TrackRows
These are specially configured TrackRows that take a non-string Track
property and convert it into a string displayed in the Inscription. I
use them to add Length, Play Count, Last Started, Last Played, and
Filepath columns to the TrackView.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 9edfc4a5b0 tracklist: Create TrackRow, InscriptionRow, and TrackString widgets
The TrackRow widget is used to bind Tracks to a generic Widget. The
InscriptionRow builds on this to create a Gtk.Inscription that can be
used in derived classes. Finally, the TrackString widget implements
binding a string Track property directly to the Inscription.

I use these widgets to create a Title and Artist column in the
TrackView.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 999a3eb523 tracklist: Create a TrackView
The TrackView sets up a scrollable Gtk.ColumnView inside a nice looking
frame. It also creates a FilterListModel for filtering tracks in a
separate layer from the Playlist so we don't affect choosing the next
track.

Finally, I add the TrackView to the TrackList Card.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 2c2462c3d6 tracklist: Create a basic Tracklist widget
For now it only has the Gtk.CenterBox child with an entry.Filter widget,
but this will be expanded on in future patches. I also take the chance
to bind the factory:visible-playlist property to the playlist displayed
in the tracklist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 0d27a09233 emmental: Wire up the Next and Previous buttons
And connect to the Player EOS and about-to-finish signals so we can
select the next track when the current one finishes (or slightly before
for gapless playback).

Implements: #7 (Add MPRIS2 Support)
Implements: #48 (Implement Intelligent ReplayGain)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker a687b564a9 playlist: Give the Factory a track-requested signal
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 6bbc423193 playlist: Give the Factory a next_track() function
It takes an additional user= parameter to indicate if the user is asking
for the next track or if it is coming from the audio player (such as an
EOS / about-to-finish signal). This lets us handle the next_track() call
slightly differently for the user case.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker cf056d6ec5 playlist: Give the Factory a previous_track() function
And also a can-go-previous property to set the correct sensitivity on
the "Previous" button and for notifying MPRIS.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker fd584e516a playlist: Have the Factory create a previous Playlist
And add some special handling so a previous.Previous() playlist type is
created for the db_previous playlist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 820eda4c46 playlist: Have the Factory create an active Playlist
And add some special handling for setting the active playlist and
visible playlist to the same db playlist. I also add active-loop and
active-shuffle properties that are wired up to the MPRIS2 "Shuffle" and
"LoopStatus" player properties.

Implements: #7 (Add MPRIS2 Support)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker b0734a41d0 playlist: Have the Factory create a visible Playlist
I either create or reuse an existing Playlist object when the
db-visible property changes to a new value.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker f5ef144419 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 <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 55486c20c3 playlist: Give Playlists a track-requested signal
We need to do some bookkeeping inside the Playlist before notifying the
Factory that a track has been requested.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 14bcef6e52 playlist: Give Playlists a can-go-next property
This property is updated whenever the items in the Playlist change or if
the current-track property is changed. It can be used to know in advance
if calling next_track() can be expected to return a valid Track
instance.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 6592b97cbd playlist: Give the next_track() function shuffle support
And add a "shuffle" property to the Playlist class. I use a TrackidSet
instance to keep track of which trackids have already been picked, and
to select a random trackid from the remaining options.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 2d18ce422e playlist: Give Playlists a sequential next_track() function
I add a "loop" property to the Playlist class that can be set to "None",
"Playlist", or "Track" (to match the MPRIS2 loop property). I can then
tune the behavior of next_track() based on how loop is configured.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 58934d9b46 playlist: Give playlists a current_track property
This sets the current_trackid field on the database playlist object to
the trackid of the provided track.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 45ddb22cc7 playlist: Give playlists functions for moving individual tracks
The move_track_up() and move_track_down() functions are used to manually
arrange the tracks in a playlist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker edaa275ba5 playlist: Give the Playlist model a sort-order property
And make sure we re-sort the tracks when it changes to match the new
order.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 298b58a54e playlist: Add a Playlist model
This model builds on the TrackidModel to make it more Playlist and Track
focused instead of trackid centric.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 4ce571ebf8 playlist: Create a TrackidModel Gio.ListModel
The TrackidModel takes a TrackidSet and presents it as a Gio.ListModel
that maps trackids into Track objects. Tracks can be found by value
using the bisect() function, which sorts the trackids by number by
default (this can be changed by overriding the do_get_sort_key()
function).

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker fdfc12fbd2 playlist: Create a Playlist Factory object
The playlist factory has properties for the currently selected,
currently active, and previous playlists. It will eventually create
Gio.ListModel instances representing the tracks in each of these playlists,
in sorted order.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 7d26d89405 audio: Give the player a pause-on-load property
Setting this to True will cause the Player to change state to PAUSED
during the next emission of the "file-loaded" signal.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:17 -04:00
Anna Schumaker 9a3d095081 emmental: Wire up the current track properties
I attach these to the Now Playing card so the "add to favorites" button
can be correctly marked as sensitive and enabled when a Track is
selected for playback.

Additionally, I look for a notification from the Track table to say it
has been loaded. This lets me set the Player to load up the current_track
if one is set.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 1aec9df0a8 db: Reload Playlists before deleting a Library path
This is much faster than removing tracks from their playlists one at a
time. I also clear and reload the Tracks table to clear out pointers to
old Tracks.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker eea763f133 db: Detect deleted Tracks during Library scanning
And use the tagger.untag_track() function to clean up and remove them
from the database.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 3cda4caa76 db: Give the Tagger a function for untagging Tracks
This is used to remove a Track from each of its Playlists before
deleting.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker e0e7b556be db: Add Track support to the Tagger class
This includes both creating new Tracks and updating existing Tracks when
their tags have changed. New tracks are added to playlists using
idle=True so Gtk can spread out UI updates for each playlist so we don't
slow things down too much for the user.

This patch also adds a library argument to the Tagger thread
get_result() function which we pass to the Tagger class to be used by
Tracks.creat(). I also add an mtime argument to the Tagger thread
tag_file() function to pass down to the audio.tagger layer so we can
skip updating tracks that have not changed since the last scan.

Implements: #41 (Check for new or modified tags during startup)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 7920b3d5a8 db: Give Libraries knowledge about their Tracks & Properties
I expand on the libraries_view to include additional playlist
properties, and configure the default sort order to sort by filepath.

I then set up the library_tracks_view to make it easy to select tracks
that belong to a specific Library.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker e39d128488 db: Give Years knowledge about their Tracks & Properties
I expand on the years_view to include additional playlist properties,
and configure the default sort order to stort by release date first.

I then set up the year_tracks_view to make it easy to select tracks that
belong to a specific year.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 99eb4abee3 db: Give Decades knowledge about their Tracks & Properties
I expand on the decades_view to include additional playlist properties,
and configure the default sort order to sort by year first.

I then set up the decade_tracks_view to make it easy to select tracks
that belong to a specific decade.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 0524085602 db: Give Genres knowledge about their Tracks & Properties
I expand on the genres_view to include additional playlist properties,
and configure the default sort order to sort by artist, album, and track
number.

I then configure the Genres table to use the system_tracks table to
manage each genre's associated tracks and set up the genre_tracks_view
to make it easy for Tracks to find their Genres.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 36ca0b9818 db: Give Media knowledge about their Tracks & Properties
I expand on the media_view to include additional playlist properties,
and configure the default sort order to sort by track number.

I then set up the medium_tracks_view to make it easy to select tracks
that belong to a specific medium.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 8e55de26d1 db: Give Albums knowledge about their Tracks & Properties
I expand on the albums_view to include additional playlist properties,
and configure the default sort order to sort by track numbers in an
intuitive way.

I then set up the album_tracks_view to make it easy to select tracks
that belong to a specific album.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker b4d8a7cfaa db: Give Artists knowledge about their Tracks & Properties
I expand on the artists_view to include additional playlist properties,
and configure the default sort_order to sort albums in an intuitive way.

I then configure the Artists table to us the system_tracks table to
manage each artist's associated tracks and set up the artist_tracks_view
to make it easy for Tracks to find their Artists.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 6762916899 db: Give System Playlists knowledge about their Tracks
I need to do something slightly different for each Playlist.

* Collection: I load tracks from the collection_view, which filters
      tracks to those where the library is enabled but not deleting.
* Favorite Tracks: I load tracks from the favorites_view, which filters
      tracks based on the tracks.favorite and library.deleting column.
* Most Played Tracks: I load tracks with a playcount greater than the
      average playcount of all tracks (rounded up to the nearest integer).
* New Tracks: I load tracks that have been added within the last week.
* Previous Tracks: I load tracks that have been played since startup
      using the system_tracks table.  I take care to clear these entries
      in the table during startup.
* Queued Tracks: Load tracks from the user_tracks table.
* Unplayed Tracks: I load tracks with a playcount equal to 0 and remove
      when they are played.
* User-Defined Playlists: Load tracks from the track_playlist_link
      table.

Additionally, I implement move_track_up() and move_track_down() support
for user playlists and queued tracks.

Finally, I update the have-next-track property to take into account if
the Collection has tracks too.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 99496ca8bf db: Give System Playlists special property handling
I need to do something slightly different for each system Playlist:

* Collection: I disallow setting loop to "None".
* Favorite Tracks: I set the user-tracks property to "True"
* Most Played Tracks: I add playcount as the first sort field.
* Previous Tracks: I disallow changing loop, shuffle, and sort-order.
* Queued Tracks: I set the user-tracks and tracks-movable properties to "True"

User created playlists also set the user-tracks and tracks-movable
properties to "True".  I also disable autodelete on the Table so
playlists aren't deleted unexpectedly.

New Tracks and Unplayed Tracks have no special properties set.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 85c42216ab db: Give Playlists extra properties
* loop is based on the mpris loop property, and can be set to "None",
      "Track", or "Playlist".
* shuffle is a boolean True / False value.
* sort-order saves a user-configured sort order for each playlist.
* current-trackid is an integer referring to the trackid of the currently
      playing track.
* user-tracks: is a boolean representing if the user should be allowed
      to manually add and remove tracks.

I also use the sort-order property to implement a get_track_order()
function to get the sort keys for tracks in a Playlist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 6eec4dbfc3 db: Add Track support to Playlist Tables
I implement add_track(), get_trackids(), and remove_track() functions that
either modify the 'system_playlist_tracks' table or call a virtual function
depending on the value of the 'system-tracks' property.

I also add the "autodelete" property. When set to True, Playlists will
be deleted when they have 0 tracks remaining.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00