Release History

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning

1.2.2

Changed

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 to LocalTrack

  • Comparer no longer needs an expected value set for methods which do not use it

  • Comparer handles null values in expected values as needed

1.2.0

Added

Changed

  • Track number zero fill amount is now calculated from the track total value when writing track tags on LocalTrack

  • Simplified dict output from FilterComparers

  • Field names displayed as lower case in dict output on relevant PrettyPrinter implementations

  • Expand logging for results from saving tracks on local collections

Fixed

  • Drop null responses from Spotify API which cause bugs in execution logic

  • Bug in LocalLibrary.load_tracks() that would cause it to store None 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 the reference_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 returning None.

1.1.6

Fixed

  • Remove ‘&’ character handling in XMLPlaylistParser. Was being handled twice as it is already handled by the xmltodict package.

1.1.5

Fixed

  • Bug in escaping of ‘&’ characters when export XAutoPF playlists with the XMLPlaylistParser. 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 the path property

  • LocalLibrary now allows setting of the name property. Added name as an init parameter too.

Changed

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 calling M3U.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 from MusifyItem interface. The class already implemented all necessary methods for this interface and was always designed to be an implementation of the MusifyItem interface.

  • Rename print method on MusifyLogger to MusifyLogger.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 all enum 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 for RequestHandler

  • Add log on CachedSession for when either a cache hit or a HTTP request happens.

Removed

  • limiter_deduplication attribute from print output on XAutoPF

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 method use_cache parameter.

  • The following classes should now be run as AsyncContextManagers to function correctly:
  • 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 on Library

  • RemoteAPI.get_or_create_playlist() method for only creating a playlist when it doesn’t already exist by name. Gets the existing playlist otherwise

  • Added MusifyCollection.outer_difference() method to cover the logic previously handled by the mislabelled MusifyCollection.outer_difference() method

  • RemoteDataWrangler and its implementations now handle URL objects from the yarl package

  • RemoteAPI.follow_playlist() method

  • Wait 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 on RemoteAPI 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 takes APIAuthoriser and generator for ClientSession objects for instantiation instead of kwargs for APIAuthoriser.

  • Dependency injection pattern for RemoteAPI. Now takes APIAuthoriser and generator for ResponseCache objects for instantiation instead of kwargs for APIAuthoriser.

  • APIAuthoriser kwargs given to SpotifyAPI now merge with default kwargs.

  • Moved remote_wrangler attribute from MusifyCollection to LocalCollection. This attribute was only needed by LocalCollection branch of child classes.

  • Moved logger attribute from Library to RemoteLibrary.

  • 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 central RequestHandler.log() method to help unify log formatting.

  • user_id and user_name now raise an error when called before setting user_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() to LocalGenres.related_genres()

  • Reduced scope of TagWriter._delete_tag() method to private

  • LocalTrack 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 new RemoteAPI.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 new RemoteAPI.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 and RemoteItemSearcher 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

Removed

  • Dependency on requests package in favour of aiohttp for async requests.

  • Dependency on requests-cache package in favour of custom cache implementation.

  • use_cache parameter from all RemoteAPI related methods. Cache settings now handled by ResponseCache

  • 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 the libraries.remote module which lists the names of all the fully supported remote sources. Also, added the SOURCE_NAME global variable for the Spotify module.

Changed

  • FilterComparers now accepts a single Comparer on the comparers 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 of results

0.9.0

Added

  • RemoteAPI methods now accept RemoteResponse objects as input, refreshing them automatically

  • Property ‘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’t

  • Extend FilterMatcher with group_by tag functionality

  • Now fully supports parsing of processors relating to XAutoPF objects with full I/O of settings to/from their related XML files on disk

  • Now 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

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 and MusicBeeProcessor abstraction layers. No longer needed after some refactoring

  • get_filtered_playlists method from Library. 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 accepts shuffle_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 yet

  • Move get_filepaths() from LocalTrack to super class File

Documentation

  • References to python objects now link correctly

Fixed

  • Comments from LocalTrack metadata loading no longer gets wiped after setting URI on init

  • Tweaked assignment of description of IDv3 comment tags for MP3

  • align_string() function now handles combining unicode characters properly for fixed-width fonts

  • LocalTrack.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 them

  • PrettyPrinter.json() and PrettyPrinter._to_str() converts attribute keys to string to ensure safe json/str/repr output

  • FilterMatcher and FilterComparers now correctly import conditions from XML playlist files. Previously, these filters could not import nested match conditions from files. Changes to logic also made to Comparer.from_xml() to accommodate

  • XMLLibraryParser now handles empty arrays correctly. Previously would crash

  • Fixed Comparer dynamic process method alternate names for in_the_last and not_in_the_last

Removed

  • Abstract uri.setter method on Item

0.8.0

Added

Changed

  • Generating folders for a LocalLibrary now uses folder names as relative to the library folders of the LocalLibrary. This now supports nested folder structures better

  • Writing date tags to LocalTrack now supports partial dates of only YYYY-MM

  • Writing 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 correctly

  • Added safe characters and replacements for path conversion in MusicBee XMLLibraryParser. Now converts path to expected XML format correctly

  • FilterMatcher now handles ‘&’ character correctly

  • SpotifyAPI now only requests batches of up to 20 items when getting albums. Now matches Spotify Web API specifications better

  • Loading 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

Changed

  • Factor out logging handlers to their own script to avoid circular import issues

  • Abstract away input methods of RemoteItemChecker to InputProcessor base class

  • Factor out patch_input method to function in InputProcessor derived tests

Fixed

  • Captured stdout assertions in RemoteItemChecker tests re-enabled, now fixed

  • Surround 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

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! 🎉