Commit Graph

1198 Commits

Author SHA1 Message Date
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
Anna Schumaker 7cd77d3aed nowplaying: Create an Autopause Entry
This entry is inspired by the Gtk.SpinButton, but lets us set
placeholder text to display the current autopause value to the user.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 69c59438c2 buttons: Create a SplitButton
This is inspired by the Adw.SplitButton, except it allows for
configuring the secondary button so we can show the current autopause
count.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker b3c2dd25fb buttons: Add a generic Button class
This Button is like a Gtk.Button, except it provides ways to set the
icon-size. I also default to large buttons, since that'll be a good
portion of the users.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 8ec5239acc audio: Add play(), pause(), play_pause(), and stop() functions
These functions are set up to take an unused argument list so they can
be connected to signals directly. I also add a 'playing' property to
track the current state of the playbin and a 'status' property to
translate 'playing' into something mpris understands.

Implements: #7 (Implement MPRIS2 Support)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 4bced82b1f nowplaying: Add a TagInfo window to the Now Playing card
And bind the Player tag properties 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 dbc2ec03f2 nowplaying: Add a widget for displaying the current track's tags
And expose properties for setting their values.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker c434f6672e nowplaying: Add the PreferArtistMenu to the ArtistLabel
This menu is used to select between showing the artist or album artist
tag.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 997b1de012 nowplaying: Create a PreferArtistMenu
For selecting between showing the artist or album-artist tag.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 81fdfe66cb nowplaying: Create an ArtistLabel
This label has properties for both Artist and Album Artist, and chooses
which to display based on the prefer-artist property and which tags have
been set.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker c899c15c42 nowplaying: Create a custom Label for displaying tags
This Label supports setting a prefix that is applied to the displayed
string and setting a font size in pixels.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 318b2564ce audio: Add properties for track tags
I set these properties when the bus sends us tag messages, and wire them
up do the mpris2.Player object to notify dbus of their values.

These properties are cleared on both EOS and when a new file is started.
This is to account for the user changing the file mid-playback.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 35d0d815ca audio: Load a track requested by the user
Either through the command line, mpris2, or the open button in the
header.

Implements: #7 (Add MPRIS2 Support)
Implements: #47 (Signal that the track has changed when it actually changes)
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 51096104ce nowplaying: Add a basic Now Playing widget
It doesn't have any children yet, the application will save its size
when the user resizes it.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:58 -04:00
Anna Schumaker 88e4fa4b0c emmental: Add a Player instance to the application
And wire it up to the Header and Mpris.Player so we can apply volume &
replaygain changes as they happen.

Implements: #42 ("Remove global audio.Player instance")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:41:42 -04:00