Commit Graph

295 Commits

Author SHA1 Message Date
Anna Schumaker 2b5cdaa197 action: Add an ActionEntry class
This is inspried by the Gio.ActionEntry struct, which I can't figure out
how to get working in Python. I add on a few extra helpful features,
such as:

  - Automatically creating a Gio.SimpleAction
  - Tracking the desired accelerator keys
  - Binding the "enabled" state to a specificed property at construction

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-09 09:45:58 -04:00
Anna Schumaker c5f9608c49 tracklist: Commit the database after adding, moving, or removing tracks
Otherwise we could lose the changes if the app crashes.

Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-07 14:58:19 -04:00
Anna Schumaker cae93cae11 sidebar: Commit the database after creating a new playlist
Otherwise we could have data loss if the application crashes.

Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-07 14:58:19 -04:00
Anna Schumaker 01a37dbbc1 db: Commit the database after deleting a Table Row
Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-07 14:54:32 -04:00
Anna Schumaker 14c487c295 db: Commit the database when a Track has been started or stopped
Leaving the database in a dirty state could cause unintentional data
loss if the app crashes.

Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-07 13:26:55 -04:00
Anna Schumaker 57dd2c280e db: Don't notify if the active-playlist doesn't change
This lets us skip a handful of database calls if we detect that we're
setting the active playlist to the same value.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-06 09:29:16 -04:00
Anna Schumaker 59fb7d12f3 db: Set the Queued Tracks playlist as active when adding tracks
I do this unconditionally, so even if the track is already on the
playlist we set it as active. I think this is what the user would
expect, but I'm open to revising this approach later.

Fixes: #62 ("Queueing tracks doesn't activate the Queued Tracks playlist")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-06 09:22:22 -04:00
Anna Schumaker 8afd1a6240 audio: Replace the playbin audio-filter with the new filter
And wire up the bg-enabled and bg-volume properties from the header to
the playbin properties with the same name.

Implements: #50 ("Background Music Mode")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-01 11:13:48 -04:00
Anna Schumaker 84fbd94aa1 audio: Create a new audio filter for ReplayGain and Background Mode
This links together our ReplayGain filter with a volume element that is
set to the user configured background volume when background listening
mode is enabled, and 100% when background listening mode is disabled.

Implements: #50 ("Background Music Mode")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-01 11:13:46 -04:00
Anna Schumaker 7155fa9db5 header: Add an icon for background mode to the Header
I also update the button tooltip to display the background listening
status along with the current volume level.

Implements: #50 ("Background Music Mode")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-01 11:13:44 -04:00
Anna Schumaker 0e40e6a4e8 header: Add a BackgroundRow to the popover menu
And wire up the properties so we can save the user's current setting.

Implements: #50 ("Background Music Mode")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-01 11:13:42 -04:00
Anna Schumaker deea9caa37 header: Create a BackgroundRow ListBox row
This widget will be added to the Volume popover and allow configuring a
lower volume for background listening while focusing on tasks.

Implements: #50 ("Background Music Mode")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-06-01 11:13:39 -04:00
Anna Schumaker a626a1f3c4 header: Convert the volume button box to a Gtk.ListBox
And give it the boxed-list style class so it looks nice.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-31 16:31:31 -04:00
Anna Schumaker dae588bfaf header: Convert the ReplayGain selector into an Adw.ExpanderRow
This will be added to a ListBox with the volume controls. Expanding the
row will enable ReplayGain and give the user a menu to select ReplayGain
mode.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-31 16:31:31 -04:00
Anna Schumaker 03e5b9ad1b header: Convert the volume controls into a ListBoxRow
The plan is to convert the volume control panel into a Gtk.ListBox for a
more modern appearance.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-31 16:31:30 -04:00
Anna Schumaker 5b0a0f54e4 header: Add tooltips to the Header widgets
This is nice to have so users know what to expect from each button. I do
take some liberties, such as putting version numbers of our dependencies
as the tooltip for the Title widget. I also display the current volume
level in the volume button tooltip.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-30 13:54:21 -04:00
Anna Schumaker dd9d6268ff gsetup: Rework version string handling
I split the function into two. Calling env_string() will return the
string we generate with the versions of the various modules we depend
on. Calling print_env() will print this string.

I need env_string() so we can set the environment as a tooltip in the
header.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-30 13:19:51 -04:00
Anna Schumaker a86ce6165d Emmental: Add a MICRO_VERSION
And set it to '1' to start, since we have a few extra commits on top of
3.0 already.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-25 10:19:51 -04:00
Anna Schumaker 55d7eb3d45 db: Raise an exception if the user_version is too new
Future proof. If we update the database schema, then we'll bump the
user_version field. If the user then tries to open the new database with
an old Emmental version then there could be a lot of issues. Let's
detect this and raise an error with a description of the problem.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-23 09:16:52 -04:00
Anna Schumaker 78ea2904a1 tracklist: Add Move Down and Move Up buttons to the OSD
These are used to manually rearrange the Tracks in the Playlist. The
buttons are only marked sensitive if one item is selected.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker a6f59d9378 tracklist: Add an Add Tracks button to the TrackView OSD
The Add Tracks button is a popover button configured to display a list
of playlists that tracks could be added to. I take some extra care to
make sure we only display playlists that have their user-tracks property
set to True, and to hide the currently visible playlist from the list.

Additionally, I create a horizontal size group so the Add and Remove
buttons are the same size.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker ff9724a274 tracklist: Add a Remove Tracks button to the TrackView
The button is placed inside a Gtk.Overlay, and is hidden by default. The
button will be shown when tracks are selected if the current Playlist
has its "user-tracks" property set to True.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 911aeb84a1 tracklist: Add a Footer widget
This widget displays the number of visible tracks, number of selected
tracks, and runtime of the current playlist. I wire it up to these
properties from the trackview.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker ee1152bcc4 tracklist: Add a SortButton
This is a Popover Button containing a Gtk.ListView to display the Sort
Order Model for the currently visible Playlist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker ed4a484a31 tracklist: Add a SortFieldWidget
This Widget is intended to be used as the child widget in a Gtk.ListView
to display and change the sort order of the current playlist. I use
arrow buttons from the icon library to represent sort order

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 83355f7e96 tracklist: Add a SortOrderModel
This is a Gio.ListStore with some extra functions for enabling,
disabling, rearranging, and reversing sort fields. It also has a
sort-string property for getting the current sort order to save
to the database.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker b326320156 tracklist: Add a SortField object
The SortField object is used to represent a single column in the
Tracklist that the user could sort by.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
Anna Schumaker 0c77e509c3 tracklist: Add a ShuffleButton
This is an ImageToggle button that adjusts its opacity based on if it is
active or not.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:42:27 -04:00
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
Anna Schumaker 1b9458c278 db: Add Track support to Playlist Objects
Playlists use a tracks.TrackidSet to manage a set of trackids
representing the Tracks in this Playlist.

I have two functions for loading tracks: load_tracks() and
reload_tracks(). Calling load_tracks() checks if the tracks have been
loaded first before doing any work, but calling reload_tracks() will
force the Playlist to go to the database to load the latest tracks.

Finally, I add a have-next-track property to the main database
connection. This is set to True whenever the active playlist has one or
more tracks.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker 11560d781e db: Add a TrackidSet
The TrackidSet is intened to be used by Playlists to keep track of which
Tracks have been added without much overhead.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00
Anna Schumaker ff835832c8 db: Save Track data when deleting
This includes the favorite status, playcount, last played timestamp, and
last started timestamp. These values will be restored if a Track with
the same mbid is created at a later time.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:32 -04:00
Anna Schumaker 8a16b4e05f db: Add favorite track accounting
This patch adds extra handling for changing the value of the
Track:favorite property, if the Track is the current track. This lets us
have the Now Playing card bind to the current-favorite property to
update the UI (and have the UI update the Track).

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 14:42:58 -04:00
Anna Schumaker dc8ccff311 db: Add current track accounting
Tracks now have start(), stop(), and restart() functions that can be
used by the application to update the laststarted, lastplayed,
playcount, active, and restarted properties.

The track Table implements their half of these functions in addition to
a mark_path_active() function so opening Emmental with a filepath can
update the current track before the database is loaded. The Table also
adjusts the necessary system playlists when tracks are marked as played.

Finally, the Table now has have-current-track and current-track
properties that can be wired up to the Now Playing card.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 14:42:58 -04:00
Anna Schumaker 08687882a3 db: Add a Track Table
The Track Table does all the work for saving, loading, and managing
Track objects. I also create a SQLite View to link tracks to their
associated artists, albums, and mediums.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 14:42:57 -04:00
Anna Schumaker 24cb87d298 db: Add a Track object
The Track object represents a single track in the Library along with all
their corresponding metadata.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:09:08 -04:00
Anna Schumaker afb599dcf4 audio: Add Track support to the tagger
I extract the artist, length, mbid, mtime, tracknumber, and title from
the tags to use when constructing Tracks.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:09:08 -04:00
Anna Schumaker 2c629c887c audio: Notify when playback is almost done
The application can use this to pre-load the next track for gapless
playback.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:09:08 -04:00
Anna Schumaker 4be92c7326 audio: Calculate the play time for the current track
I am going to use this to determine if a track has been played or not.
Gstreamer resets the clock when seeking, so I do some extra work to save
the play time just before seeking and add it back later.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:09:08 -04:00
Anna Schumaker 1c0712e673 sidebar: Add the Library section to the sidebar
And show the directory chooser if the application is started without any
library paths.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:08:10 -04:00
Anna Schumaker 0dcdcd4a68 sidebar: Create a Section for Libraries
This section shows a list of library path playlists. I also add an extra
widget that opens a Gtk.FileDialogso users can add music to their
collections.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:07:07 -04:00
Anna Schumaker 0de8089d59 sidebar: Add the Decade section to the sidebar
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:07:02 -04:00
Anna Schumaker 3ba46db064 sidebar: Create a Section for Decades
This section shows a tree of Decade and Year playlists. I use the
year-alt icon from the gnome icon-library as the section header.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:06:58 -04:00
Anna Schumaker f8494cf47b sidebar: Add the Genre section to the sidebar
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:06:53 -04:00
Anna Schumaker 0f2a5aee9d sidebar: Create a Section for Genres
This section uses the default Row for displaying genre playlists. I use
the theater icon from the gnome icon-library as the section header.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:06:49 -04:00
Anna Schumaker c396839316 sidebar: Add the Artist section to the sidebar
I make sure to save the "show-all" property to the settings so it can be
preserved across sessions.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:06:43 -04:00
Anna Schumaker 2f239bd94d sidebar: Create a Section for Artists
This section shows a tree of Artist and Album playlists. I use the
library-artists icon from the gnome icon-library as the section header,
and the music-artist / music-artist2 icons for the "show all" button to
indicate state..

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:05:18 -04:00
Anna Schumaker 711c04cb29 sidebar: Add the Playlist section to the Sidebar
I created a section Group with this section as the only member for now, and
bind the "selected-playlist" property to the sidebar.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 15:05:17 -04:00
Anna Schumaker d49b033b0d sidebar: Create a Section for Playlists
This section creates PlaylistRows for displaying user and system
playlists.  We also hook into the database playlist table to provide a
way to create new playlists. I add several new icons from the
icon-library to use for the section header and playlists.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker eb0b005c75 sidebar: Create a Section Group
This group manages a list of sections so only one is active at a time.
Additionally, it adjusts the animation property of each section to match
the direction the header moves when activated.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker b316184bf1 sidebar: Create a Section class
This class combines a header with an initially hidden ListView that can
be configured to list our playlists. It also implements
"playlist-activated" and "playlist-selected" signals to signal user
interaction.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 976f465cec sidebar: Create a TreeRow
The TreeRow is a factory.TreeRow configured for binding playlists to a
child widget. Individual sections are expected to inherit from this to
set up their section-specific widgets and bind any extra properties.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 70799fa50f sidebar: Create a LibraryRow
This is a preconfigured row for displaying library paths. It includes a
switch to enable & disable the path, buttons for removing and updating a
path, and a progress bar for displaying scan progress.

I use the "update-symbolic" and "stop-sign-large-symbolic" icons from
the gnome icon library for this widget.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker dcd63015b8 sidebar: Create a PlaylistRow
This row is configured for showing user and system playlists. This means
we use a SettableIcon, and EditableTitle, and an extra button for
deleting playlists.  I use the big-x-symbolic icon from the gnome icon
library for deleting playlists.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 0adb0d472b sidebar: Create a Row widget
This is a basic Row widget with an Icon as a prefix widget, and no
postfix widget. You can set the "year" property to set a year value to
the icon text.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker bf4fa68991 sidebar: Create a BaseRow widget
This is intended to be used as a base class for our playlist Row
widgets, and sets up some common variables needed by both.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker b25ca24dc3 sidebar: Create a Header class
This will be used to display different types of playlists in the
sidebar, such as artist or genre. It also has a revealer that shows its
child when the header is active.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 2542a6cbd7 sidebar: Create a Settable Icon widget
This widget allows users to change the displayed image by selecting a
new one from the filesystem.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 0584a2398a sidebar: Create an Icon widget
This is mostly a wrapper around an Adw.Avatar to make it easier to load
images from a file.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 673c6910e9 sidebar: Create an EditableTitle widget
This widget gives users the ability to change the title of the displayed
playlist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker cdae9541e9 sidebar: Create a PlaylistTitle widget
This widget sets the Title::subtitle property to a nicely formatted
string based on the number set to the 'count' property.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker cd4caf7df8 sidebar: Create row Title widgets
This widget displays a title and subtitle line, with some added styling.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 710f3fba80 factory: Give ListRows an "active" property
And style them so the row is highlighted and the text is bold.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 9d7763a730 factory: Create a TreeRow
The TreeRow is used to display rows from a Gtk.TreeListModel. I adjust
the TreeRow "item" and "child" properties so they still access the
underlying item or child widget instead of the Gtk.TreeRow or
Gtk.TreeExpander.

I also give the TreeRow widget "n-children" and "have-children" properties
which are used to dynamically show or hide the expander arrow when there
aren't any children.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 40c463da81 sidebar: Create a basic Sidebar widget
It only contains a FilterEntry for filtering future playlists. The
application will save its size when resized.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:43 -04:00
Anna Schumaker 5545cb106d db: Give Libraries an idle Tagger
This uses a combination of an Idle Queue, ReaddirThread, and tagger
Thread to scan a directory path and tag the audio files found within. I
do this by adding a scan() function to each Library object to begin
scanning (if not already running).

Libraries also have a stop() function to cancel any pending idle tasks
and stop any running threads. The Library table makes sure to stop each
Library object during shutdown so we don't leave any hanging threads.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:17 -04:00
Anna Schumaker 6131640e25 db: Create a tagger Thread
This Thread uses the audio.tagger.tag_file() function to find the tags
for a specific file without hanging the UI. There may be cases where we
have an Artist MBID but not the matching Artist name. When this happens,
I do my best to first check the database and then query the musicbrainz
server.

I take some care to only connect to the database once, and to close the
connection when the thread exits.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 14:18:16 -04:00
Anna Schumaker 300ee18569 db: Add a Tagger tool
This tool wraps around a mutagen.File to read tags and translate them
into our database playlists.

Implements: #41 ("Check for new or modified tags during startup")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 24c1a31367 db: Add a Library Table
This table allows us to work with Library playlists that are represented
by a filesystem path. The user can manually enable or disable library
paths to prevent their tracks from showing up in the Collection
playlist. Additionally, library paths have an online property to
determine if the library still exists in the filesystem to prevent us
from removing tracks due to a broken NFS mount or symlink.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker b7f1a05967 db: Give Decades knowledge about their Years
Similar to the Artists tree structure. I create a filter on the Year
table for each Decade object and adjust filtering so a Decade remains
visible if one or more years match the query.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker c0edbd9bff db: Add a Year Table
This table allows us to work with Year playlists that are represented
only by the year.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 0cf5f80eb4 db: Add a Decade Table
This table allows us to work with Decade playlists that can be created
or looked up by an individual year in that decade. I also add a few
custom functions to SQLite to make working with decades easier.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 2dc5d9ed0a db: Add a Genre Table
This table allows us to work with Genre playlists.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker aeeee1417a db: Give Artists knowledge about their Media
I also adjust how filtering Artists works so an Artist remains visible
if one of its Media matches the query.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 87606f8fac db: Give Albums knowledge about their Media
We create a filter on the Media table for each Album object, but
only match Media that have a name set. I also adjust filtering to
display Albums that have a matching Medium.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 51a13a8a04 db: Add a Media Table
This table represents an individual medium in an album (such as a single
CD). Each medium has an associated album, number, type, and (optional)
title. This means we can have multiple media for a given album as long
as they each have a different number or type.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 1730b7e92c db: Create a link between Artists and Albums
I use a sql link table to accomplish this so a single album can be added
to multiple album-artists. Additionally, I set up a view on Artists and
Albums to make filtering easier without needing to use a complicated
join every time.

Additionally, I use the Playlist.add_children() function to set up a
filter on the Album list model for each Artist's albums.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 1b38c4d6ec db: Add an Album Table
This table allows us to work with Album playlists that have a name,
album artist, release date, (optional) mbid, and (optional) cover.
Note that we can insert multiple albums with the same name as long as
their mbid, artist, or release date is different.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker d3bdaaa063 db: Add an Artist Table
This table allows us to work with Artist playlists that have a name and
(optional) mbid. Note that we can insert multiple artists with the same
name into the database as long as they have different mbids.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker d57509425b db: Add a Playlists Table
This inherits from our base playlist Table class, and implements
functions for creating and renaming playlists. Additionally, the
Playlist object allows for setting a custom image to display as an icon
in the sidebar.

Finally, I add in a custom sqlite3 adapter and converter to support
pathlib.Path types in the database.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 8a9c90a7ff db: Add a base class for Playlist Tables
This is an implementation of an emmental.db.table.Table that adds
support for creating Playlists, updating playlist properties, sorting
based on playlist name, and displaying playlists with their child
playlists in a tree structure.

Additionally, I create a playlist_properties table in the database to store
properties that all playlists have.

Implements: #17 ("Save currently selected playlist")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker d22b3c0ce2 db: Add a base class for Playlist Objects
This object inherits from the table.Row base class. It adds in
properties for name, active state, and propertyid and a rename()
function for updating the Table sort order during a rename.

Additionally, child playlists can be enabled by calling add_children().
This will set up a Gtk.FilterListModel using the provide child Table and
Filter.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:34 -04:00
Anna Schumaker 9944d07bba db: Give Tables an idle Queue
Tables use the idle queue to load their data or filter rows in the
background. Tables will create a new queue by default, but can accept a
pre-constructed queue through the queue= parameter.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:44:27 -04:00
Anna Schumaker e502a7e8cb db: Add an idle Queue
This is intended to be used as a base class for other task-specific
Queues, and runs each task under a single transaction. Queue
implementations should override the do_run_task() function for their
implementation-specific work.

The push_many() function can be used to efficiently add several tasks to
the Queue at the same time.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:42:14 -04:00
Anna Schumaker 1832a56786 audio: Implement a file tagger
This class reads the tags in an audio file and parses them into a format
we can use later to build our database playlist objects.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:42:14 -04:00
Anna Schumaker ad3d4840e8 path: Add a readdir_async() function
This function creates a ReaddirThread object to read the directory in
a secondary thread. This will let us poll for results without hanging the
UI in the case where a music library is on very slow storage (such as
NFS).

The ReaddirThread will accumulate a list of files that can be polled
using the poll_result() function, which will return the files found since
the last call to poll_result().

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:42:14 -04:00
Anna Schumaker ec5c4ddd2c format: Add a function for formatting sort keys
This function casefolds the input string and makes a series of
substitutions before splitting the string into a tuple of strings that
can be compared against.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:42:14 -04:00
Anna Schumaker 6cade5d779 nowplaying: Add a Jump button
This button will be used to scroll the displayed playlist to the
currently playing track.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 00f6ee9238 nowplaying: Add a Favorite button
This button is an ImageToggle configured to show a filled-in heart when
active and an outline when inactive. I added some icons from the Gnome
icon-library to represent the different states.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 011dbd114b buttons: Create an ImageToggle button
This button is inspired by the Gtk.ToggleButton button, except it
changes the displayed icon when active.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker f85cdb5b49 nowplaying: Add an Artwork widget to the Now Playing card
Implements: #45 (Create a new NowPlaying widget)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker b3d04805d7 nowplaying: Add an Album Art class
This displays the current track's album art with a fancy frame drawn
around it. Clicking the image opens a popover showing the artwork at
full size.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker ee8db58fb2 audio: Add an 'artwork' property
And use it to track the existence of artwork for the current file. This
could either be a cover.jpg file in the same directory as the currently
playing track or embedded artwork found in the Gst.TagList.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 7ff1a3d60c emmental: Add support for an application-specific temporary directory
And include a function to help extract embedded cover.jpg files.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 000dbd7018 nowplaying: Add a Seeker to the Now Playing card
And wire it up to the Player through the Application.

Implements: #45 (Create a new NowPlaying widget)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 2ff03bba18 nowplaying: Create a Seeker
This is a Gtk.Scale configured to be used to display track progress and
seek inside the track.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker e0becbb059 audio: Add a 'position' property to the player
And schedule a repeating callback to update the UI.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker a5db116d42 audio: Add a 'duration' property to the player
And make sure we are able to watch for changes when tracks are loaded. I
also export it as mpris' mpris:length metadata field.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 2ed34d3465 nowplaying: Add Controls to the Now Playing card
And wire up signals between the Now Playing card and the player.

Implements: #45 (Create a new NowPlaying widget)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 3157c53423 nowplaying: Add Control buttons
Complete with signals so we'll know when the user wants us to do
something. I also clear the autopause property when the user manually
pauses the player. I use large versions of the play and pause icons from
the Gnome Icon Library for the buttons.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker bf8d7fac1b nowplaying: Create an Autopause Button
This is a PopoverButton that has an autopause.Entry set as the child. I
also override the displayed icon to show the current autopause count.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00