Now that we have a "new-track" notification, we can replace the add
function with a lookup function that more closely mirrors how we look up
album tags.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We might want to use these outside of the playlist code, so let's move
this to a generic place so it can be easily used.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The slash in the filepath was being intepreted as a subdirectory, so
let's change it into an underscore so everything is where we expect.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This replaces the code in playlist/__init__.py and turns it into a
class. This should make things easier to handle during testing, since we
can easily tear down the old instance and create a new one.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I expect this will mostly get used by testing code as we create and
delete a bunch of different objects.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is mostly needed by the UI but there are a few cases where we
might need it internally, such as adding newly creating tracks to all
the required playlists.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This playlist represents all tracks in a given subdirectory, and is how
we control adding or removing tracks.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The intention is that different playlist types will create a subclass of
this class to do whatever work they need.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This lets us convert string fields into the corresponding track tag. I
intend to use this so playlists can have a custom sort function.
I eventually intend to add the matching __setitem__() function for
changing tags.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I'll eventually need to be able to run everything as a single
application, so let's prepare for that now. This gives us a chance to
get the imports right from the start, rather than needing to go through
and fix things up again.
I also add a test to make sure everything works as expected.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
These functions pickle out the entire tag map onto disk. I also add a
stress test for the entire tags module that scans tracks using the
threadqueue, since that's how I intend everything to be used.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
There are cases, mostly during testing, where we might want to check if
a file exists or to remove it if it does. Let's add those functions
now so we can use them.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I've decided that it would work best if Album and Track classes both
inherit from the same parent class. This will give them some
functionality overlap, and it'll also make it conceptually easier to
store them both in the same tag dictionary.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We'll use this for scanning tracks, searching musicbrainz, and fetching
album art. The web services are ratelimited, so it doesn't make sense to
use more than one thread in this case. Additionally, scanning tracks
works best as a single thread since we end up holding a lock the entire
function anyway to prevent duplicates.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Now that we have the DataFile class we can remove this function as its
main use is now hidden from users.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This gives us a convenient way to handle saved data. We use the 'with'
statment to handle any errors without crashing.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I added a make target for code coverage, and used that to identify code
that isn't getting run.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I think it makes sense to do this transparently through the Track code
so higher layers don't have to do as much work. We'll just return a
Future-object corresponding to the track that will be added.
It makes sense to only have one thread in this case, since __add() needs
exclusive access to the track_list for its entire execution. Anything
more would just create conflicts and actually increase execution time.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
It's useful to have a way to override where data gets placed in the
filesystem so we don't accidentally clobber production data with test
data while running tests.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I'm planning to use the asyncio code for running background threads, so
let's switch to it for now to get a feel for how it works.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We'll use this for scanning and updating the library later. For now,
just keep everything in a list and do a uniqueness test whenever
something new is added.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We account for some fallback values, and also attempt to detect
discnumbers from the album name.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We want to be able to pull out just the album name to make a more
accurate musicbrainz search, and so multiple discs link to the same
album object.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We want to share album objects whenever possible, so add a lookup
function for checking this. Python built-in objects (like dictionaries)
are supposedly threadsafe, so we don't need a lock to access them but I
added the threadpool test anyway just in case.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We'll eventually pull out all the fields we need from a Mutagen
FileInfo class, but that has a dictionary-like interface so we can
easily fake one up for testing.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>