M3U Playlist Plugin

Thinking about one of my previous questions in brief discussion with @adrian regarding generating playlist files I have come up with the following…

Proposal for new Beets plugin

name:	m3ulist

desc:	A plugin to create m3u playlists...
		- for albums during import operations
		- for albums already in database (auto-gen one / some / all)
		- for all items returned by a custom search query

details: The plugin should...
    - allow for relative or absolute paths in a playlist file
	- output to album folder, a pre-configured global 'Playlists' folder 
      or custom destination
	- add a stage to the import pipeline for albums, to generate the playlist
	- provide a stand-alone subcommand on the CLI for single/multiple album 
      operations
	- hook in to the `list` command (if possible) to generate a playlist from 
      a custom search query
	
considerations:
	- optionally include the UTF-8 Byte Order Mark (BOM) 
      [(still?) required by certain players]
	- limit the number of items allowed per playlist (or per generate operation)
      [possibly provide ability to append new/more items to an existing 
       playlist file?]
	- method for updating playlist file(s) when items are updated/moved etc.
	  [preferably on-demand for an individual playlist or bulk operation from 
       the CLI - i.e. search for playlist items (per playlist) and update 
       using pre-configured options or interactively]

I have been looking at the source code for some of the existing plugins and other associated classes/modules etc. for ideas and wondered what other people think … Is it something anyone else wants and would use? How feasible are my suggestions? What other ideas/suggestions do people have?

Thanks

Disclaimer: I am new to python and, although I used to be a web developer, truth be told I haven’t done any programming for a long time, but I thought this might be something I could sink my teeth in to and learn whilst I’m at it?!

… yes I expect it to be a steep learning curve with very slow progress :laughing:

Seems cool! I don’t think that an initial version should be too difficult to achieve. Wishing you luck—feel free to ask for any help that would be useful!

Thanks @adrian

I was wondering how to call a beets function from within my plugin?
~ i.e. I want to call beets list ... for the album I want to generate a playlist for
… since this is preferable and I’m guessing much quicker than walking through the filesystem

(I’m guessing it’ll be in a similar way, but) I’d also like to know how (if it’s possible) to call another plugin from within my plugin please.

Lastly (for now), what is the best way for me to proceed with regards to git and version control?
…I could set up my own repo just for this plugin, at least until it has some basic functionality and coherence, or should I just jump straight in by forking the beets repo and creating a new branch to develop the plugin in?

To clarify, you don’t want to call the beet list command—that’s a UI wrappers for humans to use. You want to use the same internal machinery that beet list uses to query the library. It might be instructive to look at the code for the list command in the beets.ui.commands module as a template.

It’s pretty uncommon for plugins to depend on one another. That would require both plugins to be enabled for one of them to work. Is there any way around it?

If I were you, I’d start my own, independent plugin repository. That way you won’t have to wait for anyone on the beets core team to help merge in your plugin before it’s “ready.” And then later, down the line, we can consider merging it in as an “official” plugin if there’s interest.

Ah right yes, sorry. I’d only really browsed the various plugin files’ code so far, and the db core … which I’ll admit started to loose me, probably 'cos I’d missed the interface step…

Thanks, I’ll look at the code you recommend and set myself up with a repo

:+1:

I’ve created a git repository and established some skeleton files which outline the plugin
Please see: https://github.com/apitofme/beets-m3u

For now the main plugin file is written as structured comments, questions and considerations to help me plan it out. I will summarise a couple of these questions here now:

  • Should I create the playlist as a temporary file initially or go straight for a regular file?
  • Should the playlist contents be written straight in to the file line-by-line or compiled in a string-buffer first?
  • Should the CLI command functions be defined within the class or as external functions (as per the documentation’s example)?

One thing that I’ve noticed so far is that, given the somewhat fractured nature of the Python language, it isn’t always clear from the numerous guides and tutorials available exactly what the current preferred methods and best practices are :confused:

Cool! FWIW, I think writing directly to a file is fine—without buffering or going to a temporary file. And it’s likely to be simpler.

I don’t think there are strong reasons to go either way about the location of the command function. Sometimes it’s nice to have it nearby to the command definition, but other than that, it’s not a big deal.

Hi FlibrJibr

I am a total n00b with beets. I really love the idea of you creating the “m3u playlists” plug-in. Well done. The idea of making .pls lists is also very appealing.

If I may…

  1. It would be a terrific thing if somewhere the ‘total play-out time’ for the playlist were known to the user. For example… there are 10 songs in the playlist and total playtime is 43 minutes and 34 seconds?

  2. As an option: Being able to “point it” at a pre-populated folder so the plug-in can do its ‘magic’ would be convenient… (stand alone use)? I think " - for albums already in database (auto-gen one / some / all)" might cover this.

  3. As an option: Suppose… I do a search for all the songs I have that are about, for instance, ‘rain’… and then make a play list using your plug-in. Could the plug-in copy (or move) all of those songs to a separate folder (or USB stick) while ‘creating’. That is: not just write the playlist… but copy the files within the list to a nominated folder. You’re already considering " - for all items returned by a custom search query"… but you don’t mention ‘doing anything’ with those items?

  4. Because the operating system lists songs in a folder alphabetically, they always play in that order (~ if you don’t have shuffle on). It would be very helpful if (optionally) a zero padded number could be prepended to the name, so the songs play in an order selected by the user (rather than by the o/s). Perhaps sorting the list first by Artist, Title, Track, Length, Year (of release), BPM, ‘date added to beets’, or randomly would help with this numbering? So… a kind of ‘pre-shuffle’? The prepended number might also be the track number within the album, so I can store them one way, (without numbers in the name) and play them another way (in track order)? Optionally, this ‘numbering’ could happen within the m3u… or to the actual items (FLAC’s or mp3’s) if they are being copied. It probably shouldn’t happen to ‘stored’ items.

  5. It would also be very helpful, if when creating a playlist, the user could run another ‘process’ on the songs. So… (for example) have all the songs been processed with a ReplayGain value?, and if not… call another plug-in or command line app, and run items through that process as the playlist is being created. (Using FFMPEG or LAME to re code from FLAC to mp3 for different media players, also comes to mind.) This would make your plug-in very useful for people preparing lists to use with digital media players. Although, this is probably not your intended use… I think " - provide a stand-alone sub-command on the CLI for single/multiple album operations" may cover this… but I’m unsure?

I hope this is not overstepping the bounds of courtesy? Again, I think the idea is great!

Good luck & regards
Shawn

Absolutely not … TBH I wasn’t really expecting any interest in this, was just something I thought of that might be useful and could be a learning project :smile: – I appreciate the support though!

I’ll address your points as I understand them and in context of what I had in mind, but if I may first summarise…

I think generally your suggestions are based around creating (and more significantly managing) potentially extensive Mix / Compilation playlists … which I can understand your desire for but it’s way beyond what I had in mind for this plugin, and likely massively more complex (if even possible with beets), but I will give it all consideration.

To clarify, my reasoning for this plugin was primarily to enable playlist creation quickly and easily for albums in the beets library, allowing album-centric playback for people who want to use beets to manage their library and just use a simple music player for listening (and who aren’t willing to explore the BPD with clients option)

This was based on my (somewhat shakey) understanding that the official playlist plugin allows the use of a playlist file as the input for a query on the beets library (to provide some kind of interface with BPD/MPD type players), and more importantly to track playlist contents against changes in the beets library … but it doesn’t actually create playlists for albums when they’re imported with beets (@adrian please correct me if I’m wrong) – perhaps as this is primarily the functionality I want I’d be better requesting/implementing it as an enhancement of the existing playlist plugin?!

Note: although I am primarily interested in album-centric playlists I fully appreciate that, given the power of the beets’ query functionality, it would be possible to generate some quite interesting playlists using beets. Indeed this is something I was keen to explore and hence why I covered ‘custom’ options in my proposal

  1. I can see functionality and user-facing info regarding playlist length, total time etc. being useful and would definitely consider implementing this in some way

  2. My thinking here was that for any given album already in the beets library
    – check if a playlist exists (i.e. look in the folder for a .m3u file)
    – if not then create one using the beets library, as this is quicker than walking the filesystem and has the added bonus of allowing playlists for files that are not all stored in the same filesystem path (custom playlists)
    However providing the functionality to ‘point at any given folder and make a playlist from what you find’ should be simple enough, with the caveat that it won’t traverse the filesystem or recurse in to sub-folders! (keeping it simple)

  3. beets can already move files (optionally copying them instead) which essentially allows you to export selections of tracks to an external (non-library) folder without affecting the library, so either the ‘standalone’ functionality you suggested in #2 or somehow patching the playlist generation function on to the back of the move function would achieve the same result!

  4. Ahh yes I remember this problem well, in fact I actually wrote a program in VB when I was at college specifically for exporting playlists with correct leading zeros … no idea what happened to my files for it … anyway, I digress. – This plugin (will) really just create the playlists (as I mentioned, beets can already move files around for you), I can see the appeal of ‘pre-shuffling’ tracks but honestly managing the ordering of playlist for custom mixes and compilations is likely best served elsewhere. However this does raise the question, if outputting a playlist from a custom query (rather than album) then how is/should it be ordered … obviously in the order returned by the query, but yes this could be manipulated so… :thinking:

  5. There is of course already the replay-gain plugin and the convert plugin so the functionality is possible in theory, in practice however it is not adviseable for one beets plugin to rely on another…
    Honestly this sounds more like something that could be composed in to some kind of script file combining multiple operations using different pre-existing utilities (e.g. piping the output from one command as the input to another on the command-line)
    …that said, really I could do just that! …pipe a beets ls command with an appropriate -f [FORMAT] string straight in to a file
    …am I right?
    …anyone… :man_facepalming:

So just quickly I wanted to point out how simple this can be (as I continue to ask myself why I didn’t think of this sooner)

First redirect the output to a file:
beet ls -p the police >> test.m3u

…that’s basically it   :rofl:

Check what is in the file:

cat test.m3u
X:\Music\MP3\Loose Tracks\The Police - Greatest Hits - 05 - Walking on the Moon.mp3
X:\Music\MP3\Loose Tracks\The Police - Greatest Hits - 10 - Invisible Sun.mp3
X:\Music\MP3\Loose Tracks\The Police - Greatest Hits - 11 - Spirits in the Material World.mp3

Add more files:
beet ls -p blue lines >> test.m3u

cat test.m3u
    X:\Music\MP3\Loose Tracks\The Police - Greatest Hits - 05 - Walking on the Moon.mp3
    X:\Music\MP3\Loose Tracks\The Police - Greatest Hits - 10 - Invisible Sun.mp3
    X:\Music\MP3\Loose Tracks\The Police - Greatest Hits - 11 - Spirits in the Material World.mp3
    X:\Music\MP3\Loose Tracks\Massive Attack - Blue Lines - 01 - Safe From Harm.mp3
    X:\Music\MP3\Loose Tracks\Massive Attack - Blue Lines - 04 - Be Thankful for What You▒ve Got.mp3
    X:\Music\MP3\Loose Tracks\Massive Attack - Blue Lines - 06 - Unfinished Sympathy.mp3
    X:\Music\MP3\Loose Tracks\Massive Attack - Blue Lines - 09 - Hymn of the Big Wheel.mp3

Overwrite it (note single arrow!):
beet ls -p homework > test.m3u

cat test.m3u
X:\Music\MP3\Loose Tracks\Daft Punk - Homework - 01 - Daftendirekt.mp3
X:\Music\MP3\Loose Tracks\Daft Punk - Homework - 04 - Da Funk.mp3
X:\Music\MP3\Loose Tracks\Daft Punk - Homework - 07 - Around the World.mp3

An M3U playlist file is basically just a list of file paths, with each path on it’s own line! Of course if you wanted to add the #EXTINF (extra information) line then we just need to know how to get a new-line character in to beets’ -f [FORMAT] syntax so that the output for one entry is printed on two lines (the first being the info, the second the path)

– Edit –
n.b. this probably requires a competent command-line shell (i.e. Unix-like), a standard on any Linux distro but Windows cmd (or even PowerShell) likely doesn’t cut it so users will probably want to look at a suitable terminal emulator (e.g. cmdr) which is actually I’m using :smiley:

Hi FlibrJibr
Thank-you for responding so quickly, and thanks for looking into the suggestions…
I understand your plug-in was probably never meant to be used as I’ve suggested… and I had no wish to overly complicate your ‘learning project’.
The idea of making ‘useful’ playlists easily with just one plug-in (without piping or redirecting the output to other ‘processes’) is a very appealing one (to me and perhaps other n00bs) but as you’ve indicated, may ‘massively complicate’ your endeavour, never my intention. Having read your Github ‘spec’ ~ I believe most of the functionality I’ve suggested is already proposed… and your project will be a very useful addition to beats. I look forward to seeing/using it.
Thanks again.

Cheers Music_Minion :+1: :sunglasses: