Package: nextsong
nextsongis a library and command line executable to support creating media playlists with a complex nested structure.Note: This project is theoretically platform agnostic, but isn't tested outside of Linux. If you encounter a problem using it on another platform please feel free to open an issue.FeaturesRecursive tree-based structure, where each item in the playlist is itself a playlist with various options for sampling songsXML format to save and load playlistsCommand-line executable to get the next song in the playlistezstreamintegrationUsageBasic exampleFirst create a playlist and save it to an XML file:fromnextsongimportPlaylistPlaylist("my_favorite_song.mp3","artist1/album1/*.mp3",loop=True,).save_xml()Each item the playlist can be a filepath, aglob pattern, or another playlist.This creates a file namednextsong.xmldescribing the playlist:<nextsong><meta/><playlistloop="true"><path>my_favorite_song.mp3</path><path>artist1/album1/*.mp3</path></playlist></nextsong>After creating the XML file, invokenextsongfrom the command line to get the next track in the playlist$nextsong
/home/myusername/media/music/my_favorite_song.mp3
$nextsong
/home/myusername/media/music/artist1/album1/01_hello_world.mp3
$nextsong
/home/myusername/media/music/artist1/album1/02_foobar.mp3Thenextsongcommand will print the absolute path of the next track to standard output or print an empty line once the end of the playlist has been reached. In this example, the playlist is set to loop, so it will never end. The state of iteration through the playlist is maintained in a pickle file namedstate.pickle.ConfigurationThe filepaths in the above example and other configuration options such as the root media directory can be changed from environment variables, command line arguments, and in scripts using thenextsong.config.Configclass. For examplefromnextsong.configimportConfigfromnextsongimportPlaylistwithConfig(playlist_path="my_playlist.xml")ascfg:# prints "my_playlist.xml"print(cfg.playlist_path)# prints "./media/" (the default)print(cfg.media_root)# saves to "my_playlist.xml"Playlist("my_favorite_song.mp3").save_xml()In the above example we create aConfigobject to override theplaylist_pathconfig. This override is in effect until the end of thewithblock. Config values can be accessed as attributes of theConfigobject. If theConfigobject doesn't override a value, the value is sourced from lower priority configs such as defaults and environment variables. This is seen when accessingmedia_root. Note that while the config value forplaylist_pathisn't explicitly passed down tosave_xml, it still determines the XML file's path. It is never necessary to pass aConfigobject into a function to have effect - just invoke the function inside theConfig'swithblock.See thenextsong.configmodule's docstring for a comprehensive overview of supported config options, their behaviors, and corresponding environment variables. Runimportnextsong;help(nextsong.config)Handling playlist updatesBy default the state of iteration through a playlist, saved instate.pickle, is based on a snapshot of the playlist at the moment the iteration began. A new state must be created (such as by deleting thestate.picklefile) for playlist changes to take effect. This behavior can be changed by setting theon_changeconfig. Currently the options are:on_changechoiceBehaviorignoreContinue based on the old playlist. This is the default behavior.restartStart over at the beginning of the new playlist.seekStart over with the new playlist, and seek to what would have been the next track in the old playlist. If this isn't possible for some reason, emit a warning and fall back to therestartbehavior.Note: playlist change detection is based on the playlist file's last modified time. This is a simple 'good enough' solution but has some pitfalls. Seemtime comparison considered harmfulfor a good overview.Ezstream integrationFirst create the playlist XML file using this package as described above.To update theezstreamXML file see theezstreamman page for the most fleshed out and up to date details. You need to create aprogramintake that runsnextsong. Overall yourintakeselement should look something like this:<intakes><intake><type>program</type><filename>nextsong</filename></intake></instakes>When runningnextsongthroughezstreamyou can use environment variables to adjust the configuration. For example, to setnextsong'smedia_rootconfig, runezstreamwithNEXTSONG_MEDIA_ROOTset to the desired value$NEXTSONG_MEDIA_ROOT=~/musicezstream-c~/ezstream.xmlDetails on config values and their corresponding environment variables can be found in thenextsong.configdocstring, which can be viewed in the Python interpreter by callinghelp(nextsong.config).Local playback example with vlcWhile actually playing the media is outside this library's scope, it's fairly straightforward to write a script that does media playback by invokingnextsongin a loop and feeding the result into a media player. For example, here's a bash script using vlc to play the playlist:trapbreakINTwhiletruedoTRACK="$(nextsong)"if[-z"$TRACK"]thenprintf"End of playlist\n"breakfiprintf"Playing %s\n""$TRACK"cvlc--play-and-exit"$TRACK">&/dev/nulldoneLearning moreAny module, class, or function can be passed into the builtinhelpfunction for detailed information. Seetests/cases/examplesfor complete usage examples. For help on the command line tool, invoke$nextsong--helpPlease feel free to open an issue for any further questions.InstallationRequires Python 3.7 or higherFromPyPIInstall using pip$python3-mpipinstallnextsongFrom sourceFirst install build dependencies$python3-mpipinstallbuildBuilding the distribution$gitclonehttps://gitlab.com/samflam/nextsong.git
$cdnextsong
$makeTo install, you canpip installthe built wheel indistor simply run$makeinstallTestingThere are some additional dependencies for testingblack: format checkerpylint: linterflake8: linter and style checkerFrom the top level, do$maketest