Random Script: mkplaylist.sh Part 2
Genre playlists thanks to recutils · January 20th, 2023
13:46 <~mio> neat music playlist script by the way :) 13:46 <~mio> on your blog post 13:46 <~mio> I hadn't thought of generating playlists on the fly like that
Thanks for the praise mio! And in return for it, I've extended mkplaylist to generate genre based playlists based off of a rec file! Like much of my music organization this is a semi manual, semi automated process, which seems to work well. I'm way too lazy to actually curate my playlists out side of very large overarching themes (ie: arists, or genre), hence all of the generation.
When I add a new artist to the mix, or an artist release a new album, I typically regenerate these playlists to include that content. So this is a really simple, almost natural extension of what I was already doing. And I really appreciate the idea!
Lets jump right into things, here's v2 of the wonderful funderful mkplaylist script, I've sort of lazily broken it into functions so I could entirely avoid implementing some sort of flag parsing system a la case. Instead I've tried to preserve the no argument invocation for most functionality.
#!/bin/bash
#Generate a playlist of all songs from the CWD
genre=$1
cwd=$(pwd)
mkplaylist(){
#If we're in the root music directory, make a mix tape!
if [ "$cwd" == "/home/durrendal/Documents/Music" ]; then
find $cwd/* -name '*.opus' > mixtape.m3u
else
artist=$(basename $cwd)
#Delete conflicting playlists before generating
if [ -f "$artist.m3u" ]; then
rm $artist.m3u
fi
touch $artists.m3u
#For each album the artists has
for album in $(ls -d */); do
#Gather up all the opus tracks, and create a playlist
for track in $album*.opus; do
printf "$cwd/$track\n" >> $artist.m3u
done
done
fi
}
mkgenreplaylist() {
if [ -z $genre ]; then
echo "Cannot generate a genre playlist without a genre!"
exit 1
fi
if [ "$cwd" != "/home/durrendal/Documents/Music" ]; then
echo "Cannot generate genre playlist outside of the root music directory!"
exit 1
fi
if [ -z "1_Playlists/Genres.rec" ]; then
printf "Where the hell did the database go?!
Fix it by creating a file name Genres.rec and adding records like:
Artist: Someone
Genre: Something
Genre: AnotherSomething
"
exit 1
fi
#Strip new lines from recsel output
artists=$(recsel -P Artist -e 'Genre = "'$genre'"' 1_Playlists/Genres.rec | sed 's\/n\\g')
#Delete conflicting playlists before generating
if [ -f 1_Playlists/$genre.m3u ]; then
rm 1_Playlists/$genre.m3u
fi
#For each artist of a given genre
for artist in $artists; do
#For each album the artists has
for album in $(ls -d $artist/*/); do
#Gather up all the opus tracks, and create a playlist
for track in $album/*.opus; do
printf "$cwd/$track\n" >> 1_Playlists/$genre.m3u
done
done
done
}
if [ -z $1 ]; then
mkplaylist
elif [ "$1" == "All" ]; then
all_genres=$(recsel -U -C -P Genre 1_Playlists/Genres.rec | sort | uniq)
for genre in $all_genres; do
mkgenreplaylist $genre
done
else
mkgenreplaylist
fi
If it's not obvious from the script, what I mean by that is that these are the sorts of invocations you'd expect with mkplaylist.
- mkplaylist
- If I'm in ~/Documents/Music, this will make a mixtape.m3u of all songs.
- cd $artist && mkplaylist
- Jumping into an artists directory will make an $artist.m3u of all albums I have for that artist.
- mkplaylist $genre
- Now into the new stuff! If I pass a $genre, like Metal, it'll make a Metal.m3u of all songs by any artists I tag as that genre
- mkplaylist All
- And because I'm lazy, this will generate playlists for all unique genres in my recfile!
I didn't want to remember too much with this thing honestly, so the simpler the interface the more I'll remember to use it. Honestly, these sorts of things don't need to be as complicated as the interface for tkts where you're switching context and such what. It just needs to make text files!
Anyways, I mentioned recutils, that's where all of the magic happens here. I initially tried to see if I could embed genre info inside of each opus file, and you can if you're willing to re-encode all of your files, which I really just don't want to do. So I scratched that. Especially after realizing that even after I added genre tags, I would need to iterate over the files, recollect that information & cache it in a database somewhere after I query some API to figure out what song by what artist is what genre. Too much effort for this script. Just thinking about it makes me want to rewrite the entire utility in nim, it would be a great tool, but that's now what this is for.
Right, yes recutils, that's what we're here for. Recfiles are wonderful little plain text database files, they are dead simple to write and query. They have awesome command line tools, and a super powered emacs mode. Plus I maintain the package for Alpine, so I may as well use the toys I have. The contents of my Genres.rec file looks just like this.
%rec: genre
Artist: 21_Savage
Genre: Rap
Artist: Blaqk_Audio
Genre: Punk
Artist: Caamp
Genre: Indie
Genre: Chill
It's dead simple, right? Even if you've never used recfiles before you know exactly what's going on here. The rec file sets up a single table (genre). That table has two fields, Artist and Genre. The Artist field is the same as the directory name inside of ~/Documents/Music. And the Genre field is the genre I want to sort that artists music into, there can be multiple Genre fields.
Seriously, I love how simple this setup is. I can edit the recfile by hand when I add a new artist. I don't really care if my Genres are 100% accurate because I just want big semi sorted lists for when I want to exclusively listen to Metal or something at work. And at the end of the day, all of this took maybe 45 minutes to figure out including writing my new recfile database!
And here's the result with the current batch of music I've got. Seriously, I love the results and can't wait to throw on my Metal playlist next time I need to seriously focus on a technical issue!
/home/durrendal/Documents/Music/1_Playlists:
total used in directory 220 available 79.4 GiB
drwxr-sr-x 2 durrendal durrendal 4096 Jan 20 15:47 .
drwxr-sr-x 45 durrendal durrendal 4096 Jan 20 15:22 ..
-rw-r--r-- 1 durrendal durrendal 5979 Jan 20 15:47 British.m3u
-rw-r--r-- 1 durrendal durrendal 23832 Jan 20 15:47 Chill.m3u
-rw-r--r-- 1 durrendal durrendal 14805 Jan 20 15:47 EDM.m3u
-rw-r--r-- 1 durrendal durrendal 887 Jan 20 15:47 French.m3u
-rw-r--r-- 1 durrendal durrendal 1380 Jan 20 15:45 Genres.rec
-rw-r--r-- 1 durrendal durrendal 24524 Jan 20 15:47 German.m3u
-rw-r--r-- 1 durrendal durrendal 10004 Jan 20 15:47 Indie.m3u
-rw-r--r-- 1 durrendal durrendal 34765 Jan 20 15:47 Metal.m3u
-rw-r--r-- 1 durrendal durrendal 3370 Jan 20 15:47 Ponk.m3u
-rw-r--r-- 1 durrendal durrendal 13223 Jan 20 15:47 Pop.m3u
-rw-r--r-- 1 durrendal durrendal 13710 Jan 20 15:47 Punk.m3u
-rw-r--r-- 1 durrendal durrendal 30657 Jan 20 15:47 Rap.m3u
-rw-r--r-- 1 durrendal durrendal 14150 Jan 20 15:47 Rock.m3u