Commit Graph

53 Commits

Author SHA1 Message Date
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 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 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 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 b1cd1706ed db/settings: Create a Settings table
This creates a new class to dynamically create GObject Properties, save
them to the database, and make it easy to bind application properties to
specific settings properties.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:42 -04:00
Anna Schumaker 788ca374a8 db: Create a Table base class
This is a Gtk.FilterListModel containing a store.SortedList to store
individual rows in sorted order. I also implemented some convenience
functions to make it easier to add, remove, look up, and filter rows.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:41 -04:00
Anna Schumaker 651f24672b db: Create a database row Filter
This filter takes a set of primary keys for rows that should be visible
during filtering. Passing None as a value means that all rows are shown.
It also has functions for adding or removing individual rows from the
filter.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 09:26:27 -04:00