I added a "user-token" property to the ListenBrainz object, and watch for
changes to query the liblistenbrainz client. I also set up an idle
callback so we can set the "valid-token" property so the user can know
if there is a problem.
Implements: #69 ("Add ListenBrainz support")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I created the foundation of what I'll need for working with
ListenBrainz. This includes a ListenBrainz GObject, a threading.Thread
implementation that will do the actual work for communicating with
ListenBrainz, and what will become a priority queue to make sure we do
certain operations (such as setting the user's auth token) first.
These are mostly placeholder classes right now, and future patches will
expand on what they can do.
Implements: #69 ("Add ListenBrainz support")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The user can fill this out to connect to their listenbrainz account and
submit listens. I add a listenbrainz logo icon based on their icon from
the website. I also create a symbolic version that I end up using in the
popover menu.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This contains all the steps needed to open the settings editor window. I
move it into the "Menu" popover list since it's not a common action, so
it can be hidden from the main UI.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
And put it in the new Menu button popover list. I don't expect this to
be a common action, so the extra button press is acceptable.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This will be expanded to contain the open file button, the settings
dialog, and eventually a listenbrainz configuration option.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I'm going to add a second popover button to the header, so I need to
clarify which button these objects are being attached to.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I bump the user_version to 3 at the same time. This table will be used
to hold listenbrainz listens that have not yet been submitted to the
listenbrainz server. I also give the Track table functions to get and
delete listens from this table as needed by the listenbrainz thread.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This lets us do a lot of the basic Thread operations through common
code, allowing us to focus on tagging in this file instead of basic
Thread controls.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I found that I'm rewriting some of the same features every time I need
to spin up a Thread for something. This is a reusable Thread that can be
inherited for specific work.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This class is desigend to make it easier to pass data to and from a
running Thread. This was inspired by the types.SimpleNamespace object so
we can set generic values and use them as class members.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Rather than doing the work ourselves to calculate if the database has
been loaded, use the new property to notify us.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This can be checked or connected to so other parts of the application
can easily know if all database tables have been loaded or not.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I haven't run tests in the evening in a long time, so I never noticed
these failures due to sqlite returning utc timestamps when we expect
localtime.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The Breeze "media-playlist-consecutive" icon looks terrible, and we want
to use "media-playlist-normal" to get the same look as with the Adwaita
icon theme. Unfortunately, Adwaita doesn't have the
"media-playlist-normal" icon. So I created a function to ask the icon
theme if it has a specific icon, and modify the shuffle button to change
the inactive icon when toggled to keep up with icon theme changes.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
These are various icon changes that I noticed after using emmental with
KDE & the Breeze icon theme for a while.
- Replace the go-jump icon with arrow4-down-symbolic
- Replace the view-list-ordered icon with list-compact
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I was relying on the icon theme to fallback to symbolic icons when I
initially wrote it. Turns out, some icon themes do provide a color icon
for this, so I specifically ask for the symoblic icon for consistency.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This test started failing after updating to pytest 8.0. I fix it by
clearing the cache so the test can begin with a clear slate.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Allowing us to show and hide the sidebar by clicking the button in the
header. I also save the current state of the sidebar, although the
Adw.OverlaySplitView might override this when the window is shown.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I'm planning to build on this widget over the next several releases.
It'll eventually be fully adaptable to window size changes made by the
user.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Replacing our tracklist-specific one that caches textures, but not to
local disk.
Implements #53 ("Convert the TrackList to use the Texture Cache")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I make sure to clear an existing texture before setting a new one in
case the user downloads a new file with the same path. Otherwise we'll
end up using a stale texture in the list.
Implements: #54 ("Convert the SideBar to use the Texture Cache")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I use the filepath of the requested item to derive a cache file name in
the user's xdg cachedirectory. I also add a way to update items in the
cache if we detect that the mtime has changed, and support loading items
from the cache if the source file has been deleted.
Implements #52 ("Save the Texture Cache textures to the .cache directory")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The Texture Cache will be used to map filenames to Gdk.Textures loaded
into memory. The application can then re-use textures instead of making
expensive filesystem calls and loading the same images multiple times.
Implements: #51 ("Texture Cache")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The New Tracks playlist shows tracks that have been added within the
past week. We should automatically reload it a few seconds after
midnight to keep it up to date as tracks drop off the list.
Implements: #58 ("Reload New Tracks playlist at midnight")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
An alarm is a callback that triggers at a specific time, rather than at
a specific interval. I build this using GLib.timeout_add_seconds() and
wrapping it with logic to calculate the amount of time until the alarm
should be triggered next.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I found that deleted and restorted tracks were incorrectly showing up in
the "New Tracks" playlist. I can fix this by saving the track added date
when the tracks is deleted. The only thing I can't do easily is get the
added date for tracks that have already been deleted, so I set this to
the date of the database upgrade.
Fixes: #64 ("Save the tracks.added date when deleting")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Prepare for database modifications. The first step is to bump the
database version, and it's cleaner to do that in a separate patch.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is a wrapper function that takes a pathlib.Path object, reads it,
and calls the sqlite3 executescript() function. I update the main
db.Connection object to call this function to set up our database tables
while I'm at it.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Attempting to set this field gives me "expected enumeration type void,
but got PyGLibOptionArg instead". I'm not sure what's wanted here, so
I'm commenting out this line for now and can revisit later.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Rather than trying to implement this myself through manually moving the
scrolled window. It's much easier to simply let Gtk do the work for us.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I've found that during startup, we sometimes try to select the current
playlist before the Gtk sidebar widgets are completely loaded. This
results showing the right section, but not actually selecting a
playlist. We can fix this by selecting the playlist after a short
timeout to give everything a chance to load.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Rather than activating an action through a GLib.Variant, we can use the
newly added scroll_to() function to do most of the work for us.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The following shortcuts are implemented:
- Escape to unselect any selected tracks
- Delete to remove selected tracks from the current playlist
- <Control>/ to focus the "filter tracks" entry
- <Control>l to cycle the loop state of the current playlist
- <Control>s to toggle the shuffle state of the current playlist
- <Control>Up to move the selected track up one position
- <Control>Down to move the selected track down one position
I also change the volume up and down shortcuts to use the <Shift>
modifier. This matches how other Header shortcuts are triggered, and
frees up the non-shifted versions to use here.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>