Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Creating M3U Playlists Automatically (Read 1342 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Creating M3U Playlists Automatically

I have a method that I have been using to generate playlists automatically for a specific genre of music.  I do this to allow fb2k to play a specific genre of music only including specific tracks that are not duplicated.  To do this, I use XML technology, and the tools to do it are 100% free and downloadable on the Internet.  I will share the procedure I follow to do this below with step by step instructions.

One thing important is that your entire music library must exist as an XML document.  I do not currently have any components that will export the fb2k library as XML; however, I do use a batch tag editor that exports all files with the tag content of my choosing to an XML document.  This is the tag editor I currently use and have written the XSL transformation for.  I know of no other tag editors available today that does this other than a single one-- Tag&Rename.  Therefore, these steps are written expecting Tag&Rename's XML schema.  If you have your audio file library in another XML format, both the XQuery and XSL Transformation must be modified to support that schema.

Step #1:
Make sure you have the Java runtime installed of at least version 1.8.  You may determine this by typing the following at a command prompt:

Code: [Select]
D:\Media\Digital Audio Library>java -version
java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

If you do not have a minimum of version 1.8 as depicted above, you must download then install the Java runtime from here:
https://java.com/en/download/

Step #2:
Download the open source Saxon library from here:
https://sourceforge.net/projects/saxon/

Step #3:
Create a new folder on your local drive using the name of your choice.  For my example, I created D:\Media\Digital Audio Library.  After creating this folder, extract three files from the SaxonHE10-1J.zip file you downloaded in step #2 above:

  • saxon-he-10.1.jar
  • saxon-xqj-10.1.jar
  • jline-2.9.jar

Place all three of the files above into the new folder you created above.  In my case, I placed them all into D:\Media\Digital Audio Library.

Step #4:
Place the following two files into the new folder you created in step #3 above:

  • Electronica.xquery
  • GenerateElectronicaPlaylist.xsl

Step #5:
Generate the XML document that contains your library content.  I used Tag&Rename version 3.9.15 to create the XML.  Also, the xquery file was written support this schema.  When you do export, choose as many tag fields as you wish to enhance your XQuery selection criteria.  The minimum fields required for what I created are:

  • File Name
  • Artist
  • Title
  • Album
  • Year
  • Genre
  • Track#
  • Rating
  • Album Artist
  • Time

Adding more fields gives you flexibility when creating and/or modifying your XQuery.  Select as many as you wish.  If you use Tag&Rename to create the XML document, select the option for all folders and all files, select all files, and finally export.  It will take a few minutes to create the XML document depending on the size of your library.  Once it is created though, you may save this generated XML document to execute additional queries later.  Note that I saved my XML document with the file name Digital_Audio.xml.  If you use a different name, the XQuery must be modified to support this different name.

Note that my example Electronica.xquery file has XQuery criteria to exclude specific artists and album names.  All text is case sensitive and must match exactly how it is entered in your tags.  If your tags are not consistent and have different case for the same artists and albums, you may duplicate the entries with matching casing.  Using a batch tag edit to match all files with the same letter case would be the best option.

Step #6:
Generate the m3u8 playlist using the following commands:
Code: [Select]
java -cp saxon-he-10.1.jar;saxon-xqj-10.1.jar net.sf.saxon.Query Electronica.xquery -o:ElectronicaPlaylist.xml

java -cp saxon-he-10.1.jar net.sf.saxon.Transform -s:ElectronicaPlaylist.xml -xsl:GenerateElectronicaPlaylist.xsl -o:Electronica.m3u8
After this is complete, you will find that you have a new playlist file created called Electronica.m3u8 in your newly created folder.

Note that if you make changes to the file names, you must alter the parameters above with the new names you selected.  My example was for an Electronica playlist.

Hopefully this is useful to someone.  If you have any questions or need help, you may ask me for assistance.

Re: Creating M3U Playlists Automatically

Reply #1
Below is the full contents of the Electronica.xquery file.  Simply save this into the new folder you created as a text file with the Electronica.xquery file name:

Code: [Select]
xquery version "3.0";
declare option saxon:output "encoding=UTF-8";
declare option saxon:output "method=xml";
declare option saxon:output "indent=yes";
declare option saxon:output "omit-xml-declaration=no";

<output>{
for $x in doc("Digital_Audio.xml")/body/media_file
where Xs:short($x/tag/rating) > 3 and
      $x/file/duration_in_seconds > 90 and
      matches($x/tag/genre, 'Industrial|Electronic') and
      $x/tag/artist != 'Kraftwerk' and
      $x/tag/artist != 'The Art of Noise' and
      $x/tag/album != 'Quake Soundtrack' and
      $x/tag/album != '20XX Volume I: A Tribute To The Music of Mega Man X (Full-Length Edition)' and
      $x/tag/album != '20XX Volume II: A Tribute To The Music of Mega Man X'
order by $x/@src
return <result>{$x/@src} {$x/tag/artist} {$x/tag/album} {$x/tag/title}</result>
}</output>

Below is the contents of the GenerateElectronicaPlaylist.xsl XSL Transformation.  Similar to above, save this as a new text file with the GenerateElectronicaPlaylist.xsl file name:

Code: [Select]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  version="1.0">
  <xsl:output method="text" encoding="UTF-8" byte-order-mark="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="/">#EXTM3U<xsl:text>&#xa;</xsl:text>
    <xsl:for-each select="/output/result/@src">#EXTINF:<xsl:value-of select="position()"/>,<xsl:value-of select="../artist"/> - <xsl:value-of select="../title"/><xsl:text>&#xa;</xsl:text><xsl:value-of select="."/><xsl:text>&#xa;</xsl:text></xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Re: Creating M3U Playlists Automatically

Reply #2
Very misleading thread title. There doesn't seem to be anything "automatic" about that!

Also, the average fb2k user would just use the built in search...

%genre% IS blah AND %rating% GREATER 3 AND %length_seconds% GREATER 90 AND NOT %artist% IS something AND NOT %artist% IS something else AND...

Re: Creating M3U Playlists Automatically

Reply #3
Very misleading thread title. There doesn't seem to be anything "automatic" about that!

Also, the average fb2k user would just use the built in search...

%genre% IS blah AND %rating% GREATER 3 AND %length_seconds% GREATER 90 AND NOT %artist% IS something AND NOT %artist% IS something else AND...

Thanks for your feedback.

It would have been super helpful to respond to my initial question here:

https://hydrogenaud.io/index.php?topic=119407.msg984511#msg984511

Re: Creating M3U Playlists Automatically

Reply #4
I am going to post the XQuery I currently use to select all individual Electronica from my library.  This shows an example of how you may query based on tags.  I added XQuery comments that describe the purpose of some filters.  This is currently reliant on accurate tags:

Code: [Select]
xquery version "3.0";
declare option saxon:output "encoding=UTF-8";
declare option saxon:output "method=xml";
declare option saxon:output "indent=yes";
declare option saxon:output "omit-xml-declaration=no";

<output>{
for $x in doc("Digital_Audio.xml")/body/media_file
where xs:short($x/tag/rating) > 3 and
      $x/file/duration_in_seconds > 90 and
      matches($x/tag/genre, 'Industrial|Electronic') and
      matches($x/file/name/text(), '.*\.flac|.*\.ogg|.*\.mp3|.*\.ape') and
(:    below are artists to generally exclude                                                         :)
      $x/tag/artist != 'Kraftwerk' and
(:    below are albums to generally exclude                                                          :)
      $x/tag/album != 'Quake Soundtrack' and
      $x/tag/album != 'Music For Yoga' and
      $x/tag/album != '20XX Volume I: A Tribute To The Music of Mega Man X (Full-Length Edition)' and
      $x/tag/album != '20XX Volume II: A Tribute To The Music of Mega Man X' and
      $x/tag/album != 'Stalker: Scenes From A Slasher Film' and
      $x/tag/album != 'Future Past: A Tribute Collection' and
      $x/tag/album != 'Stalker: The Final Cut' and
      $x/tag/album != 'Syndicate: The Heist' and
      $x/tag/album != '5 Minute Meditations' and
      $x/tag/album != 'Remedy' and
      $x/tag/album != 'Rooty' and
      $x/tag/album != 'Kish Kash' and
      $x/tag/album != 'No Time To Lose - Ingen tid å miste (original soundtrack)' and
      $x/tag/album != 'Asimov (music inspired by the writings of)' and
      $x/tag/album != "World's End" and
      $x/tag/album != 'Featherlight (featuring Heather Feather ASMR)' and
(:    below are 16-bit releases to exclude due to having 24-bit releases to play instead             :)
      $x/tag/album != 'Oxygene 7-13' and
      $x/tag/album != 'Oxygene' and
      $x/tag/album != 'Dreamtime Physics' and
      $x/tag/album != 'Kykeon' and
      $x/tag/album != 'Hypatia' and
      $x/tag/album != 'The Slip' and
      $x/tag/album != 'Hesitation Marks' and
(:    temporarily excluding this album below, because it currently gets played too much              :)
      $x/tag/album != 'The Complete Discography Vol. 1' and
(:    below are singles from compilations to exclude due to also existing on released albums         :)
      (($x/tag/album != 'A Synthwave Compilation' and
       ($x/tag/title != 'Nitronaut' or
        $x/tag/title != 'Soulreaper' or
        $x/tag/title != 'Wormhole' or
        $x/tag/title != 'Flagellation' or
        $x/tag/title != 'Murdercycle' or
        $x/tag/title != 'Deadly Curves'))
      ) and
(:    below are singles to exclude due to also existing on released albums                           :)
      (($x/tag/album != '' and
       ($x/tag/title != 'Berserker (Single)' or
        $x/tag/title != 'Save The World [Single]' or
        $x/tag/title != 'Say maS' or
        $x/tag/title != 'Supermodel' or
        $x/tag/title != 'Void Calm' or
        $x/tag/title != 'Dogs of War' or
        $x/tag/title != 'S T y X' or
        $x/tag/title != 'Reborn'))
      ) and
(:    below are singles from compilations to exclude due to also existing on released albums         :)
      not($x/tag/album = 'Various Objekts 4: A Bass Agenda Recordings Sampler' and $x/tag/title = 'Surrender or Be Destroyed!!!') and
(:    below are singles from compilations to exclude due to also existing on released albums         :)
      not($x/tag/album = 'Various Objekts 5' and $x/tag/title = 'Engineering')
order by $x/@src
return <result>{$x/@src} {$x/tag/artist} {$x/tag/album} {$x/tag/title}</result>
}</output>

 

Re: Creating M3U Playlists Automatically

Reply #5
I am attaching my latest Electronica.xquery that I currently use to generate m3u8 playlists containing unique tracks from my library.  This one has some query issues resolved.  Also this shows examples of different ways to filter in XQuery:

Code: [Select]
xquery version "3.0";
declare option saxon:output "encoding=UTF-8";
declare option saxon:output "method=xml";
declare option saxon:output "indent=yes";
declare option saxon:output "omit-xml-declaration=no";

<output>{
for $x in doc("..\Digital_Audio.xml")/body/media_file
where (xs:short($x/tag/rating) > 3 or xs:short($x/tag/rating) = 0) and
      $x/file/duration_in_seconds > 90 and
      $x/file/duration_in_seconds < 1200 and
      matches($x/tag/genre, 'Industrial|Electronic') and
      matches($x/file/name/text(), '.*\.flac|.*\.ogg|.*\.mp3|.*\.ape') and
(:    below are artists to generally exclude                                                         :)
      $x/tag/artist != 'Kraftwerk' and
(:    below are albums to generally exclude                                                          :)
      $x/tag/album != 'Quake Soundtrack' and
      $x/tag/album != 'Music For Yoga' and
      $x/tag/album != '20XX Volume I: A Tribute To The Music of Mega Man X (Full-Length Edition)' and
      $x/tag/album != '20XX Volume II: A Tribute To The Music of Mega Man X' and
      $x/tag/album != 'Stalker: Scenes From A Slasher Film' and
      $x/tag/album != 'Future Past: A Tribute Collection' and
      $x/tag/album != 'Stalker: The Final Cut' and
      $x/tag/album != 'Syndicate: The Heist' and
      $x/tag/album != '5 Minute Meditations' and
      $x/tag/album != 'Remedy' and
      $x/tag/album != 'Rooty' and
      $x/tag/album != 'Kish Kash' and
      $x/tag/album != 'No Time To Lose - Ingen tid å miste (original soundtrack)' and
      $x/tag/album != 'Asimov (music inspired by the writings of)' and
      $x/tag/album != "World's End" and
      $x/tag/album != 'Featherlight (featuring Heather Feather ASMR)' and
      not($x/tag/album = 'Trappist-1' and $x/tag/album_artist = 'Lee Rosevere') and
      not($x/tag/album = 'Borderlands' and $x/tag/album_artist = 'Evan Bartholomew') and
      not($x/tag/album = 'Live' and $x/tag/album_artist = 'Street Cleaner') and
      not($x/tag/album = 'The Grid' and $x/tag/album_artist = 'Occams Laser') and
      not($x/tag/album = 'Return To The Grid' and $x/tag/album_artist = 'Occams Laser') and
(:    below are 16-bit releases to exclude due to having 24-bit releases to play instead             :)
      $x/tag/album != 'Oxygene 7-13' and
      $x/tag/album != 'Oxygene' and
      $x/tag/album != 'Dreamtime Physics' and
      $x/tag/album != 'Kykeon' and
      $x/tag/album != 'Hypatia' and
      $x/tag/album != 'The Slip' and
      $x/tag/album != 'Hesitation Marks' and
      $x/tag/album != 'Hesitation Marks (48kHz)' and
(:    below are albums to exclude due to having re-releases to play instead                          :)
      not($x/tag/album = 'Elementary Particles + Prima Materia' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'Sines And Singularities' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'The 4 Horsemen of The Electrocalypse: The Red Horse' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'The 4 Horsemen of The Electrocalypse: The White Horse' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'The 4 Horsemen of The Electrocalypse: The Black Horse' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'The 4 Horsemen of The Electrocalypse: The Pale Horse' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'The 4 Horsemen of The Electrocalypse: The Pale Horse' and $x/tag/album_artist = 'Bluetech') and
      not($x/tag/album = 'Evil Awaits' and $x/tag/album_artist = 'Stilz') and
      not($x/tag/album = 'Cannibal Girls' and $x/tag/album_artist = 'Destryur') and
      not($x/tag/album = 'Incipience 4: Live at LuvAFair October 6th, 1982' and $x/tag/album_artist = 'Images In Vogue') and
(:    below are tracks to generally exclude                                                          :)
      not($x/tag/album = 'Sleepwalking (Album) [Deluxe Edition]' and $x/tag/album_artist = 'Nina' and xs:short($x/tag/track) >= 12) and
      not($x/tag/album = 'Singularities' and $x/tag/album_artist = 'Bluetech') and
(:    temporarily excluding these below, because they currently get played too much                  :)
      not($x/tag/album = 'The Complete Discography Vol. 1' and $x/tag/album_artist = 'The Enigma TNG') and
      not($x/tag/album = 'Anthology' and $x/tag/album_artist = 'Ponies At Dawn') and
      not($x/tag/album = 'Rebirth' and $x/tag/album_artist = 'Ponies At Dawn') and
      not($x/tag/album_artist = 'Stilz' and xs:short($x/tag/rating) < 5 and xs:integer($x/tag/year) < 2018) and
      not($x/tag/artist = 'Occams Laser' and xs:short($x/tag/rating) < 5  and xs:integer($x/tag/year) < 2018) and
(:    below are singles from compilations to exclude due to also existing on released albums         :)
      not($x/tag/album = 'A Synthwave Compilation' and
       ($x/tag/title = 'Nitronaut' or
        $x/tag/title = 'Soulreaper' or
        $x/tag/title = 'Wormhole' or
        $x/tag/title = 'Flagellation' or
        $x/tag/title = 'Murdercycle' or
        $x/tag/title = 'DiscoDeath' or
        $x/tag/title = 'Deadly Curves')
      ) and
      not($x/tag/album = 'French Synthwave Compilation Vol.1' and
       ($x/tag/title = 'Infosphere' or
        $x/tag/title = 'Nominal Performance' or
        $x/tag/title = 'Wormhole' or
        $x/tag/title = 'Flagellation' or
        $x/tag/title = 'Murdercycle' or
        $x/tag/title = 'Deadly Curves')
      ) and
      not($x/tag/album = 'French Synthwave Compilation Vol.2' and
       ($x/tag/title = 'Color Depth' or
        $x/tag/title = 'Deathcruiser')
      ) and
(:    below are singles to exclude due to also existing on released albums                           :)
      not($x/tag/album = '' and $x/tag/title = 'Berserker (Single)' and $x/tag/album_artist = 'Contre-Attaque') and
      not($x/tag/album = '' and $x/tag/title = 'Save The World [Single]' and $x/tag/album_artist = 'Contre-Attaque') and
      not($x/tag/album = '' and $x/tag/title = 'Say maS' and $x/tag/album_artist = 'Processor') and
      not($x/tag/album = '' and $x/tag/title = 'Supermodel' and $x/tag/album_artist = 'Processor') and
      not($x/tag/album = '' and $x/tag/title = 'Void Calm' and $x/tag/album_artist = 'Processor') and
      not($x/tag/album = '' and $x/tag/title = 'Dogs of War' and $x/tag/album_artist = 'Processor') and
      not($x/tag/album = '' and $x/tag/title = 'S T y X' and $x/tag/album_artist = 'Processor') and
      not($x/tag/album = '' and $x/tag/title = 'Reborn' and $x/tag/album_artist = 'Processor') and
      not($x/tag/album = '' and $x/tag/title = 'Overseer' and $x/tag/album_artist = 'Daniel Deluxe') and
      not($x/tag/album = '' and $x/tag/title = 'Freedom Bill' and $x/tag/album_artist = 'Infected Mushroom, Freedom Fighters and Mr. Bill') and
      not($x/tag/album = '' and $x/tag/title = 'Altered Code' and $x/tag/album_artist = 'D-Noise') and
      not($x/tag/album = '' and $x/tag/title = 'Atomic' and $x/tag/album_artist = 'she') and
      not($x/tag/album = 'Collected Works' and $x/tag/album_artist = 'Roex') and
      not($x/tag/album = 'Various Objekts 4: A Bass Agenda Recordings Sampler' and $x/tag/title = 'Surrender or Be Destroyed!!!' and $x/tag/album_artist = 'Bass Agenda Recordings') and
      not($x/tag/album = 'Various Objekts 4: A Bass Agenda Recordings Sampler' and $x/tag/title = 'Anybody In Town' and $x/tag/album_artist = 'Bass Agenda Recordings') and
      not($x/tag/album = 'Various Objekts 5' and $x/tag/title = 'Engineering' and $x/tag/album_artist = 'Bass Agenda Recordings') and
      not($x/tag/album = 'Tokyo Rose &amp; Friends' and $x/tag/title = 'Moshi Moshi' and $x/tag/album_artist = 'Tokyo Rose') and
      not($x/tag/album = 'Tokyo Rose &amp; Friends' and $x/tag/title = 'BLVCK' and $x/tag/album_artist = 'Tokyo Rose') and
      not($x/tag/album = 'Tokyo Rose &amp; Friends' and $x/tag/title = 'Genesis' and $x/tag/album_artist = 'Tokyo Rose') and
      not($x/tag/album = 'Tokyo Rose &amp; Friends' and $x/tag/title = 'Nobodies' and $x/tag/album_artist = 'Tokyo Rose') and
      not($x/tag/album = 'Best of 2016 - 2018' and $x/tag/title = 'I See Murder (feat Lebrock)' and $x/tag/album_artist = 'PowerNerd') and
      not($x/tag/album = '' and $x/tag/title = 'Dehumanize' and $x/tag/album_artist = 'Deathwire') and
      not($x/tag/album = '' and $x/tag/title = 'Fxck My Brain [Single]' and $x/tag/album_artist = 'Deathwire') and
      not($x/tag/album = 'Drift' and $x/tag/album_artist = 'DeadLife') and
      not($x/tag/album = 'Vanitas' and $x/tag/title = 'Apotheosica' and $x/tag/album_artist = 'King Stephen') and
      not($x/tag/album = 'Hellraiser' and $x/tag/title = 'Impulse' and $x/tag/album_artist = 'D-Noise') and
      not($x/tag/album = 'Non Paradisi' and $x/tag/title = '4th' and $x/tag/album_artist = 'GosT') and
      not($x/tag/album = 'Just Drive (Part 1)' and $x/tag/title = 'A Sea of Stars (feat. Dora Pereli)' and $x/tag/album_artist = 'WolfClub') and
      not($x/tag/album = 'Runaways' and $x/tag/title = 'All We Live For' and $x/tag/album_artist = 'WolfClub') and
      not($x/tag/album = 'Runaways' and $x/tag/title = 'Rebels' and $x/tag/album_artist = 'WolfClub') and
      not($x/tag/album = "The 80's Dream Compilation Tape - Vol. 2" and $x/tag/title = 'Dataline' and $x/tag/album_artist = 'NewRetroWave') and
      not($x/tag/album = 'Monstercat - Best of 2020' and $x/tag/title = 'Ani Mevushal' and $x/tag/album_artist = 'Monstercat') and
      not($x/tag/album = 'Monstercat - Best of 2020' and $x/tag/title = 'Gold' and $x/tag/album_artist = 'Monstercat') and
      not($x/tag/album = 'Accelerate LP' and $x/tag/title = 'XLD' and $x/tag/album_artist = 'Tyr Kohout') and
      not($x/tag/album = 'Accelerate LP' and $x/tag/title = 'Avarice' and $x/tag/album_artist = 'Tyr Kohout') and
      not($x/tag/album = 'Prismtek (Single)' and $x/tag/title = 'Prismtek' and $x/tag/album_artist = 'DreamReaper') and
      not($x/tag/album = 'Criquets' and $x/tag/title = 'Shadows' and $x/tag/album_artist = 'Index Code') and
      not($x/tag/album = 'My Life' and $x/tag/title = 'My Life (Original Mix)' and $x/tag/album_artist = 'FM Attack') and
      not($x/tag/album = 'Artoffact Records 2020 Sampler' and $x/tag/title = 'Night Flower (with Edward KaSpel)' and $x/tag/album_artist = 'Artoffact Records') and
      not($x/tag/album = 'Artoffact Records 2020 Sampler' and $x/tag/title = 'Battle Cry' and $x/tag/album_artist = 'Artoffact Records') and
      not($x/tag/album = 'Odyssey' and $x/tag/title = 'Queen of Hearts' and $x/tag/album_artist = 'Michael Oakley') and
      not($x/tag/album = 'Jaded Shadow' and $x/tag/album_artist = 'Signal Void' and $x/tag/track = ('02', '03', '05', '09', '10', '11', '12'))
order by $x/@src
return <result>{$x/@src} {$x/tag/artist} {$x/tag/album} {$x/tag/title}</result>
}</output>