Release History
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning
1.2.2
Changed
Track
implementations no longer match on name only. This avoids over matching that was noticed when runningLocalCollection.merge_tracks()
.
1.2.1
Changed
May now define custom ignore words when sorting string values using the
ItemSorter
ItemSorter
ignore case when sorting string values
Fixed
Paths are now sanitised when assigning
filename
toLocalTrack
Comparer
no longer needs an expected value set for methods which do not use itComparer
handles null values in expected values as needed
1.2.0
Added
Can now get tags from any
MusifyItem
and set tags on anyLocalTrack
using the relevantField
enumsEquality comparison methods to all implementations of
Filter
BasicLocalCollection
for creating and managing arbitrary local collectionsMusifyEnum
now displayed correctly when outputting tojson
onPrettyPrinter
objectsLocalTrack.move()
andLocalTrack.rename()
methods to handle moving the file on the disk.Set the
path
andfilename
properties on aLocalTrack
to defer the movement of a file on the disk. Setting a new path in this way does not immediately move a file. Instead, the file will be moved whenLocalTrack.save()
is called with apath
type tag field as an argument.
Changed
Track number zero fill amount is now calculated from the track total value when writing track tags on
LocalTrack
Simplified
dict
output fromFilterComparers
Field names displayed as lower case in
dict
output on relevantPrettyPrinter
implementationsExpand logging for results from saving tracks on local collections
Fixed
Drop
null
responses from Spotify API which cause bugs in execution logicBug in
LocalLibrary.load_tracks()
that would cause it to storeNone
when the track could not be loaded
1.1.10
Fixed
Bug in
Comparer
methods which caused them to fail on invalid expected values
1.1.9
Changed
Comparer
now correctly ignores the reference track given when thereference_required
flag is False.
Fixed
Bug in
XAutoPF
which caused it to always add the tracks that matched the associated tags of the last played track when the expected values for the condition are null or empty.
1.1.8
Fixed
Bug in
RemoteItemChecker
that tries to remove items from the added list when they are not present whilst trying to match items to remote playlist.
1.1.7
Fixed
Handle bad values for bpm and compilation in
TagReader
by returningNone
.
1.1.6
Fixed
Remove ‘&’ character handling in
XMLPlaylistParser
. Was being handled twice as it is already handled by thexmltodict
package.
1.1.5
Fixed
Bug in escaping of ‘&’ characters when export
XAutoPF
playlists with theXMLPlaylistParser
. Was previously escaping multiple times when already escaped e.g. ‘&’ > ‘&’. Now correctly skips already occurrences of ‘&’.
1.1.4
Added
LocalPlaylist
now allows setting of thepath
propertyLocalLibrary
now allows setting of thename
property. Addedname
as an init parameter too.
Changed
LocalLibrary.merge_playlists()
now updates the path of new playlists added to the library to be relative to the library’splaylist_folder
1.1.3
Fixed
When given an empty
M3U
playlist file, produces expected result i.e. an empty playlist. Previously always added all given tracks to playlist when callingM3U.load()
1.1.2
Changed
File.get_filepaths()
now ignores hidden files.Replace os.makedirs with Pathlib implementation of
mkdir
everywhere.
1.1.1
Changed
Update aiorequestful version to 1.0
1.1.0
Changed
ItemDownloadHelper
only ever takes the first field when the singular name of a field is given and many values are available for that field. e.g. only ever takes the first artist when multiple artists are present and the requested field is ‘artist’ and not ‘artists’RemoteCollectionLoader
now inherits fromMusifyItem
interface. The class already implemented all necessary methods for this interface and was always designed to be an implementation of theMusifyItem
interface.Rename
print
method onMusifyLogger
toMusifyLogger.print_line()
Removed
Implementation of REST API handling including all cache + authorisation implementations. Separated this off to a new package.
Moved all enum definitions to
types
modules and removed allenum
modules.
Documentation
Fix references to non-existent packages + add missing packages in API reference index
1.0.2
Added
Expanded error message on
DynamicProcessor
processor lookup exception.Improved logging of bad responses on
RequestHandler
wait_max
time to cap wait time between requests forRequestHandler
Add log on
CachedSession
for when either a cache hit or a HTTP request happens.
Removed
limiter_deduplication
attribute from print output onXAutoPF
Fixed
Bug in
XMLLibraryParser
which would not read ‘Playlists’ keys.Moved ‘check api’ logic later when deleting playlists in
RemoteItemChecker
to ensure API is not checked on close when checker has not actually run.RequestHandler
now handles wait and backoff logic asynchronously.Tracks on playlists from the JSON output of
LocalLibrary
now display correctly. Previously showed ‘null’ for every track.
1.0.1
Documentation
Mark release as stable/production.
1.0.0
Added
Custom API caching backend to replace dependency on
requests-cache
package. Currently only supports SQLite backend. More backends can be implemented in future if desired.Cache settings for specific GET request endpoints on
SpotifyAPI
replacing need for per methoduse_cache
parameter.- The following classes should now be run as AsyncContextManagers to function correctly:
SQLiteCache
RequestHandler
CachedSession
Introduce print wrapper for logger and remove most bare
print
statements across package.SpotifyAPI.extend_items()
now enriches collection item responses with the parent collection response.ARTISTS field added to LocalTrackField
Add compatibility with
yarl
package for any logic which uses URL logic.Add compatibility for pathlib.Path for any logic which uses path logic.
Extended logging on
report_playlist_differences()
source
property onLibrary
RemoteAPI.get_or_create_playlist()
method for only creating a playlist when it doesn’t already exist by name. Gets the existing playlist otherwiseAdded
MusifyCollection.outer_difference()
method to cover the logic previously handled by the mislabelledMusifyCollection.outer_difference()
methodRemoteDataWrangler
and its implementations now handle URL objects from theyarl
packageRemoteAPI.follow_playlist()
methodWait time logic for
RequestHandler
. This waits by a certain time after each request, incrementing this wait time every time a 429 code is returned. This allows better handling of rate limits, with the aim of preventing a lock out from a service.
Changed
RequestHandler
now handles requests asynchronously. These changes to async calls have been implemented all the way onRemoteAPI
and all other objects that depend on it.All I/O operations on local libraries and their dependent objects now run asynchronously.
Dependency injection pattern for
RequestHandler
. Now takesAPIAuthoriser
and generator forClientSession
objects for instantiation instead of kwargs forAPIAuthoriser
.Dependency injection pattern for
RemoteAPI
. Now takesAPIAuthoriser
and generator forResponseCache
objects for instantiation instead of kwargs forAPIAuthoriser
.APIAuthoriser
kwargs given toSpotifyAPI
now merge with default kwargs.Moved
remote_wrangler
attribute fromMusifyCollection
toLocalCollection
. This attribute was only needed byLocalCollection
branch of child classes.Moved
logger
attribute fromLibrary
toRemoteLibrary
.Switch some dependencies to be optional for groups of operation: progress bars, musicbee, sqlite
Replace urllib usages with
yarl
package.Replace all path logic to use pathlib.Path instead. All
SpotifyAPI
now logs to the new centralRequestHandler.log()
method to help unify log formatting.user_id
anduser_name
now raise an error when called before settinguser_data
attribute. This is due to avoiding asynchronous calls in a property. It is therefore best to now enter the async context of the api to set these automatically.Renamed
LocalGenres.genres()
toLocalGenres.related_genres()
Reduced scope of
TagWriter._delete_tag()
method to privateLocalTrack
now removes any loaded embedded image from the mutagen file object. This is to reduce memory usage when loading many of these objects.Extend logging on
LocalCollection.log_save_tracks_result()
to show when no tags have been or would be updated.RemoteItemChecker
now uses the newRemoteAPI.get_or_create_playlist()
method when creating playlists to avoid creating many duplicate playlists which could have lead to playlist creation explosion in repeated uses. The processor also accounts for any items that may have existed in the playlist before it was run and discounts them from any matches.RemoteItemChecker
also uses the newRemoteAPI.follow_playlist()
method when creating playlists to ensure that a user is following the playlists it creates to avoid ‘ghost playlist’ issue.SpotifyAPI.create_playlist()
now returns the full response rather than just the URL of the playlist.Moved
RemoteItemChecker
andRemoteItemSearcher
to musify.processors package.Moved
RemoteDataWrangler
up a level to musify.libraries.remote.core.Renamed musify.libraries.remote.spotify.processors module to musify.libraries.remote.spotify.wrangle.
Moved musify.logger module to musify base package.
Restructured contents of musify.core package to modules in musify base package.
Fixed
Added missing variables to __slots__ definitions
Correctly applied __slots__ pattern to child classes. Now works as expected.
LocalTrack
now copies tags as expected when callingcopy.copy()
Bug where loading an M3U playlist with new track objects would force all created track objects to have lower case paths
RemoteLibrary.restore_playlists()
now correctly handles the backup output fromRemoteLibrary.backup_playlists()
Issue detecting stdout_handlers affecting
MusifyLogger.print()
andMusifyLogger.get_iterator()
. Now works as expected.LocalLibrary.artists()
now generates aLocalArtist
object per individual artist rather than on combined artistsIssue where
SpotifyAPI.extend_items()
did not show progress when extending some types of responsesFixed logic in
MusifyCollection.intersection()
andMusifyCollection.difference()
Removed
Dependency on
requests
package in favour ofaiohttp
for async requests.Dependency on
requests-cache
package in favour of custom cache implementation.use_cache
parameter from allRemoteAPI
related methods. Cache settings now handled byResponseCache
ThreadPoolExecutor use on
RemoteItemSearcher
. Now uses asynchronous logic instead.last_modified field as attribute to ignore when getting attributes to print on LocalCollection to improve performance
Removed logger filters and handlers. Moved to CLI repo.
Deleted musify.libraries.remote.core.processors package.
Documentation
Updated how-to section to reflect implementation of async logic to underlying code
Created a how-to page for installation
0.9.2
Added
REMOTE_SOURCES
global variable in thelibraries.remote
module which lists the names of all the fully supported remote sources. Also, added theSOURCE_NAME
global variable for the Spotify module.
Changed
FilterComparers
now accepts a singleComparer
on thecomparers
argument.MusicBee
class attributes were renamed to classify that full paths are also valid, not just filenames.ItemDownloadHelper
urls
init arg now has default arg of empty tuple.
Documentation
Fixed error in ‘sync data’ how-to.
Fixed
Comparer
now considers strings as converted on first pass when converting expected values.Printing of new line at the end of
RemoteLibrary.extend()
0.9.1
Fixed
Bug in
ItemMatcher.match()
where operations always returned the last item in the given list ofresults
0.9.0
Added
RemoteAPI
methods now acceptRemoteResponse
objects as input, refreshing them automaticallyProperty ‘kind’ to all objects which have an associated
RemoteObjectType
Introduced
MusifyItemSettable
class to allow distinction between items that can have their properties set and those that can’tExtend
FilterMatcher
with group_by tag functionalityNow fully supports parsing of processors relating to
XAutoPF
objects with full I/O of settings to/from their related XML files on diskNow supports creating new
XAutoPF
files from scratch without the file needing to already exist For XML values not directly controlled by Musify, users can use the ‘default_xml’ class attribute to control the initial default values applied in this scenario‘length’ property on
MusifyCollection
and implementation on all subclasses
Changed
Major refactoring and restructuring to all modules to improve modularity and add composition
- The following classes and methods have been modified to implement concurrency to improve performance:
LocalLibrary.save_tracks()
LocalLibrary.json()
+ optimisation for extracting JSON data from tracks
Made
load_tracks()
andload_playlists()
utility functions more DRYMove
TagReader.load()
fromLocalTrack
to super classTagReader
SpotifyAPI.extend_items()
now skips on responses that are already fully extendedSpotifyArtist.load()
now uses the base load method fromSpotifyCollectionLoader
meaning it now takes full advantage of the item filtering this method offers. As part of this, the base method was made more generic to accommodate allSpotifyObject
typesRenamed ‘kind’ property on
LocalTrack
to ‘type’ to avoid clashing property namesItemMatcher
,RemoteItemChecker
, andRemoteItemSearcher
now accept all MusifyItem types that may have their URI property set manuallyRemoteItemChecker
andRemoteItemSearcher
no longer inherit fromItemMatcher
. Composite pattern used instead.ItemSorter
now shuffles randomly on unsupported types + prioritises fields settings over shuffle settingsComparer._in_range()
now uses inclusive range i.e.a <= x <= b
wherex
is the value to compare anda
andb
are the limits. Previously used exclusive range i.e.a < x < b
Removed
from_xml
andto_xml
methods from allMusicBeeProcessor
subclasses. Moved this logic toXMLPlaylistParser
as distinct ‘get’ methods for each processor typeMoved loading of XML file logic from
XAutoPF
toXMLPlaylistParser
.XMLPlaylistParser
is now solely responsible for all XML parsing and handling forXAutoPF
files
Fixed
Comparer
dynamic processor methods which process string values now cast expected types before processing
Removed
Redundant ShuffleBy enum and related arguments from
ItemSorter
ItemProcessor
andMusicBeeProcessor
abstraction layers. No longer needed after some refactoringget_filtered_playlists
method fromLibrary
. This contained author specific logic and was not appropriate for general use
Documentation
Added info on lint checking for the contributing page
0.8.1
Changed
ItemSorter
now acceptsshuffle_weight
between -1 and 1 instead of 0 and 1. This parameter’s logic has not yet been implemented so no changes to functionality have been made yetMove
get_filepaths()
fromLocalTrack
to super classFile
Documentation
References to python objects now link correctly
Fixed
Comments from
LocalTrack
metadata loading no longer gets wiped after setting URI on initTweaked assignment of description of IDv3 comment tags for
MP3
align_string()
function now handles combining unicode characters properly for fixed-width fontsLocalTrack.get_filepaths()
on LocalTrack no longer returns paths from$RECYCLE.BIN
folders. These are deleted files and were causing the package to crash when trying to load themPrettyPrinter.json()
andPrettyPrinter._to_str()
converts attribute keys to string to ensure safe json/str/repr outputFilterMatcher
andFilterComparers
now correctly import conditions from XML playlist files. Previously, these filters could not import nested match conditions from files. Changes to logic also made toComparer.from_xml()
to accommodateXMLLibraryParser
now handles empty arrays correctly. Previously would crashFixed
Comparer
dynamic process method alternate names forin_the_last
andnot_in_the_last
Removed
Abstract uri.setter method on
Item
0.8.0
Added
Add debug log for error failure reason when loading tracks
MusifyCollection.intersection()
andMusifyCollection.difference()
methodsPlaylist.merge()
andLibrary.merge_playlists()
methods
Changed
Generating folders for a
LocalLibrary
now uses folder names as relative to the library folders of theLocalLibrary
. This now supports nested folder structures betterWriting date tags to
LocalTrack
now supports partial dates of only YYYY-MMWriting date tags to
LocalTrack
skips writing year, month, day tags if date tag already written
Removed
set_compilation_tags method removed from
LocalFolder
. This contained author specific logic and was not appropriate for general use
Fixed
ConnectionError catch in
RequestHandler
now handles correctlyAdded safe characters and replacements for path conversion in MusicBee
XMLLibraryParser
. Now converts path to expected XML format correctlyFilterMatcher
now handles ‘&’ character correctlySpotifyAPI
now only requests batches of up to 20 items when getting albums. Now matches Spotify Web API specifications betterLoading of logging yaml config uses UTF-8 encoding now
Removed dependency on pytest-lazy-fixture. Package is broken for pytest >8.0. Replaced functionality with forked version of code
0.7.6
Fixed
Rename __max_str in local/collection.py to _max_str - functions could not see variable
Add default value of 0 to sort_key in
ItemSorter.sort_by_field()
Fixed
RemoteItemChecker
_pause()
logic to only get playlist name when input is not False-y
0.7.5
Added
Add the
ItemDownloadHelper
general processor
Changed
Factor out logging handlers to their own script to avoid circular import issues
Abstract away input methods of
RemoteItemChecker
toInputProcessor
base classFactor out patch_input method to function in
InputProcessor
derived tests
Fixed
Captured stdout assertions in
RemoteItemChecker
tests re-enabled, now fixedSurround
RemoteAPI
‘user’ properties in try-except block so they can still be pretty printed even if API is not authorised
Documentation
Fix redirect/broken links
Change notes text to proper rst syntax
0.7.4
Fixed
Fix bug in
LocalLibrary.restore_tracks()
method on library due to ‘images’ tag name not being present in track properties
Documentation
Expand docstrings across entire package
Expand documentation with how to section, release history, and contributions pages
0.7.3
Changed
Remove x10 factor on bar threshold on _get_items_multi function in
SpotifyAPI
Fixed
LocalTrack
would break when trying to save tags for unmapped tag names, now handles correctly
0.7.2
Fixed
MusifyLogger
would not get file_paths for parent loggers when propagate == True, now it does
0.7.1
Changed
Remove automatic assignment of absolute path to package root for relative paths on
CurrentTimeRotatingFileHandler
Fixed
CurrentTimeRotatingFileHandler
now creates dirs for new log directories
0.7.0
Initial release! 🎉