Release History
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning
1.2.5
Fixed
RemoteLibrary.log_tracks()now displays correct counts
1.2.5
Fixed
RemoteLibrary.log_sync()now correctly gets max width from results whenRemotePlaylistis given in results
1.2.4
Fixed
RemotePlaylistis now hashable again
1.2.3
Changed
Allow
RemotePlaylistto be passed toRemoteLibrary.log_sync()
1.2.2
Changed
Trackimplementations 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
ItemSorterItemSorterignore case when sorting string values
Fixed
Paths are now sanitised when assigning
filenametoLocalTrackComparerno longer needs an expected value set for methods which do not use itComparerhandles null values in expected values as needed
1.2.0
Added
Can now get tags from any
MusifyItemand set tags on anyLocalTrackusing the relevantFieldenumsEquality comparison methods to all implementations of
FilterBasicLocalCollectionfor creating and managing arbitrary local collectionsMusifyEnumnow displayed correctly when outputting tojsononPrettyPrinterobjectsLocalTrack.move()andLocalTrack.rename()methods to handle moving the file on the disk.Set the
pathandfilenameproperties on aLocalTrackto 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 apathtype tag field as an argument.
Changed
Track number zero fill amount is now calculated from the track total value when writing track tags on
LocalTrackSimplified
dictoutput fromFilterComparersField names displayed as lower case in
dictoutput on relevantPrettyPrinterimplementationsExpand logging for results from saving tracks on local collections
Fixed
Drop
nullresponses from Spotify API which cause bugs in execution logicBug in
LocalLibrary.load_tracks()that would cause it to storeNonewhen the track could not be loaded
1.1.10
Fixed
Bug in
Comparermethods which caused them to fail on invalid expected values
1.1.9
Changed
Comparernow correctly ignores the reference track given when thereference_requiredflag is False.
Fixed
Bug in
XAutoPFwhich 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
RemoteItemCheckerthat 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
TagReaderby returningNone.
1.1.6
Fixed
Remove ‘&’ character handling in
XMLPlaylistParser. Was being handled twice as it is already handled by thexmltodictpackage.
1.1.5
Fixed
Bug in escaping of ‘&’ characters when export
XAutoPFplaylists with theXMLPlaylistParser. Was previously escaping multiple times when already escaped e.g. ‘&’ > ‘&’. Now correctly skips already occurrences of ‘&’.
1.1.4
Added
LocalPlaylistnow allows setting of thepathpropertyLocalLibrarynow allows setting of thenameproperty. Addednameas 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
M3Uplaylist 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
mkdireverywhere.
1.1.1
Changed
Update aiorequestful version to 1.0
1.1.0
Changed
ItemDownloadHelperonly 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’RemoteCollectionLoadernow inherits fromMusifyIteminterface. The class already implemented all necessary methods for this interface and was always designed to be an implementation of theMusifyIteminterface.Rename
printmethod onMusifyLoggertoMusifyLogger.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
typesmodules and removed allenummodules.
Documentation
Fix references to non-existent packages + add missing packages in API reference index
1.0.2
Added
Expanded error message on
DynamicProcessorprocessor lookup exception.Improved logging of bad responses on
RequestHandlerwait_maxtime to cap wait time between requests forRequestHandlerAdd log on
CachedSessionfor when either a cache hit or a HTTP request happens.
Removed
limiter_deduplicationattribute from print output onXAutoPF
Fixed
Bug in
XMLLibraryParserwhich would not read ‘Playlists’ keys.Moved ‘check api’ logic later when deleting playlists in
RemoteItemCheckerto ensure API is not checked on close when checker has not actually run.RequestHandlernow handles wait and backoff logic asynchronously.Tracks on playlists from the JSON output of
LocalLibrarynow 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-cachepackage. Currently only supports SQLite backend. More backends can be implemented in future if desired.Cache settings for specific GET request endpoints on
SpotifyAPIreplacing need for per methoduse_cacheparameter.- The following classes should now be run as AsyncContextManagers to function correctly:
SQLiteCacheRequestHandlerCachedSession
Introduce print wrapper for logger and remove most bare
printstatements across package.SpotifyAPI.extend_items()now enriches collection item responses with the parent collection response.ARTISTS field added to LocalTrackField
Add compatibility with
yarlpackage 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()sourceproperty onLibraryRemoteAPI.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()methodRemoteDataWranglerand its implementations now handle URL objects from theyarlpackageRemoteAPI.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
RequestHandlernow handles requests asynchronously. These changes to async calls have been implemented all the way onRemoteAPIand 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 takesAPIAuthoriserand generator forClientSessionobjects for instantiation instead of kwargs forAPIAuthoriser.Dependency injection pattern for
RemoteAPI. Now takesAPIAuthoriserand generator forResponseCacheobjects for instantiation instead of kwargs forAPIAuthoriser.APIAuthoriserkwargs given toSpotifyAPInow merge with default kwargs.Moved
remote_wranglerattribute fromMusifyCollectiontoLocalCollection. This attribute was only needed byLocalCollectionbranch of child classes.Moved
loggerattribute fromLibrarytoRemoteLibrary.Switch some dependencies to be optional for groups of operation: progress bars, musicbee, sqlite
Replace urllib usages with
yarlpackage.Replace all path logic to use pathlib.Path instead. All
SpotifyAPInow logs to the new centralRequestHandler.log()method to help unify log formatting.user_idanduser_namenow raise an error when called before settinguser_dataattribute. 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 privateLocalTracknow 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.RemoteItemCheckernow 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.RemoteItemCheckeralso 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
RemoteItemCheckerandRemoteItemSearcherto musify.processors package.Moved
RemoteDataWranglerup 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.
LocalTracknow 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 aLocalArtistobject 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
requestspackage in favour ofaiohttpfor async requests.Dependency on
requests-cachepackage in favour of custom cache implementation.use_cacheparameter from allRemoteAPIrelated methods. Cache settings now handled byResponseCacheThreadPoolExecutor 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_SOURCESglobal variable in thelibraries.remotemodule which lists the names of all the fully supported remote sources. Also, added theSOURCE_NAMEglobal variable for the Spotify module.
Changed
FilterComparersnow accepts a singleCompareron thecomparersargument.MusicBeeclass attributes were renamed to classify that full paths are also valid, not just filenames.ItemDownloadHelperurlsinit arg now has default arg of empty tuple.
Documentation
Fixed error in ‘sync data’ how-to.
Fixed
Comparernow 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
RemoteAPImethods now acceptRemoteResponseobjects as input, refreshing them automaticallyProperty ‘kind’ to all objects which have an associated
RemoteObjectTypeIntroduced
MusifyItemSettableclass to allow distinction between items that can have their properties set and those that can’tExtend
FilterMatcherwith group_by tag functionalityNow fully supports parsing of processors relating to
XAutoPFobjects with full I/O of settings to/from their related XML files on diskNow supports creating new
XAutoPFfiles 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
MusifyCollectionand 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()fromLocalTrackto super classTagReaderSpotifyAPI.extend_items()now skips on responses that are already fully extendedSpotifyArtist.load()now uses the base load method fromSpotifyCollectionLoadermeaning 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 allSpotifyObjecttypesRenamed ‘kind’ property on
LocalTrackto ‘type’ to avoid clashing property namesItemMatcher,RemoteItemChecker, andRemoteItemSearchernow accept all MusifyItem types that may have their URI property set manuallyRemoteItemCheckerandRemoteItemSearcherno longer inherit fromItemMatcher. Composite pattern used instead.ItemSorternow shuffles randomly on unsupported types + prioritises fields settings over shuffle settingsComparer._in_range()now uses inclusive range i.e.a <= x <= bwherexis the value to compare andaandbare the limits. Previously used exclusive range i.e.a < x < bRemoved
from_xmlandto_xmlmethods from allMusicBeeProcessorsubclasses. Moved this logic toXMLPlaylistParseras distinct ‘get’ methods for each processor typeMoved loading of XML file logic from
XAutoPFtoXMLPlaylistParser.XMLPlaylistParseris now solely responsible for all XML parsing and handling forXAutoPFfiles
Fixed
Comparerdynamic processor methods which process string values now cast expected types before processing
Removed
Redundant ShuffleBy enum and related arguments from
ItemSorterItemProcessorandMusicBeeProcessorabstraction layers. No longer needed after some refactoringget_filtered_playlistsmethod 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
ItemSorternow acceptsshuffle_weightbetween -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()fromLocalTrackto super classFile
Documentation
References to python objects now link correctly
Fixed
Comments from
LocalTrackmetadata loading no longer gets wiped after setting URI on initTweaked assignment of description of IDv3 comment tags for
MP3align_string()function now handles combining unicode characters properly for fixed-width fontsLocalTrack.get_filepaths()on LocalTrack no longer returns paths from$RECYCLE.BINfolders. 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 outputFilterMatcherandFilterComparersnow 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 accommodateXMLLibraryParsernow handles empty arrays correctly. Previously would crashFixed
Comparerdynamic process method alternate names forin_the_lastandnot_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
LocalLibrarynow uses folder names as relative to the library folders of theLocalLibrary. This now supports nested folder structures betterWriting date tags to
LocalTracknow supports partial dates of only YYYY-MMWriting date tags to
LocalTrackskips 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
RequestHandlernow handles correctlyAdded safe characters and replacements for path conversion in MusicBee
XMLLibraryParser. Now converts path to expected XML format correctlyFilterMatchernow handles ‘&’ character correctlySpotifyAPInow 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
ItemDownloadHelpergeneral processor
Changed
Factor out logging handlers to their own script to avoid circular import issues
Abstract away input methods of
RemoteItemCheckertoInputProcessorbase classFactor out patch_input method to function in
InputProcessorderived tests
Fixed
Captured stdout assertions in
RemoteItemCheckertests 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
LocalTrackwould break when trying to save tags for unmapped tag names, now handles correctly
0.7.2
Fixed
MusifyLoggerwould 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
CurrentTimeRotatingFileHandlernow creates dirs for new log directories
0.7.0
Initial release! 🎉