Automatisch muziek directories voorzien van playlists

[:en]Mijn muziekcollectie is enigszins gegroeid en daardoor niet altijd goed bijgehouden. Een van de problemen is het ontbreken van de playlists. Gelukkig is hier onder unix een mooie tool voor, genaamd fapg, wat staat voor Fast Audio Playlist Generator. Deze tool maakt een playlist voor alle files binnen een map.

Voor meer informatie en de recente versie van FAPG is hier te vinden.

Maar wat als je voor alle mappen binnen een pad inidividuele playlists wilt maken? Tijd om een bash script te maken!

BASH script samenstellen

Allereerst moeten we alle directories van de map vinden, dat kan met het find commando:

find . -type d

Dit commando hebben we al eerder gebruikt bij onder andere de incrementele backup.

Het resultaat daarvan moeten we doorlopen, dat kan o.a. met een while loop:

find . -type d | while read dir
do
        echo $dir
done

Dit print alle directories binnen de map waarin het script is uitgevoerd.

Omdat we de playlists binnen de individuele mappen willen maken, is het handig om naar de daadwerkelijk map te navigeren. Hiervoor moeten we het volledige pad hebben, gelukkig is daar de tool readlink voor beschikbaar. Daarnaast willen we het resultaat daarvan opslaan als variabele.

Om bij het begin te beginnen, variabelen binnen bash zijn kinderlijk eenvoudig:

a="test"
echo $a
-> test

Om het resultaat van readlink in een variabele op te slaan moeten we het volgende doen:

realpath=$(readlink -f "$dir")
-> data/volledig/en/absoluut/pad

readlink – display file status. Voor meer informatie, zie manpage readlink

Dit pad eindigt alleen nog niet met een slash, we zullen aan deze string dus een andere string moeten toevoegen, dat gaat in bash erg makkelijk:

a="test"
b="abc"
echo $a$b
-> testabc

Als we dat dus op bovenstaande code toepassen:

realpath=$(readlink -f "$dir")"/"
-> data/volledig/en/absoluut/pad/

Vervolgens kunnen we met het cd commando naar deze map navigeren.

Voordat we een playlist kunnen gaan maken, is het wel verstandig om te bekijken of een map wel muziek (in dit geval mp3) bestanden heeft. Dat kunnen we wederom met het find commando doen:

mp3num=$(find . -maxdepth 1 -name '*.mp3' | wc -l)

Hier gaat find uit van het volgende: in de huidige directory, geen subdirectories (maxdepth 1 = huidige directory, 0 = enkel command line) en de naam moet eindigen op .mp3. Die geeft alle gevonden mp3 bestanden onder elkaar. Vervolgens gaat het resultaat daarvan door de tool wc die het daadwerkelijk tellen regelt. De -l parameter telt het aantal lijnen.

wc – print the number of newlines, words, and bytes in files. Voor meer informatie zie manpage wc

Dan moeten we het aantal gevonden mp3’s gaan bekijken, het aantal moet minimaal 1 zijn (oftewel groter dan 0). Dat kan met het if commando:

a=10
if [ a -gt 5 ]; then
        echo "groter dan 5"
else
        echo "niet groter dan 5"
fi

De operatoren van BASH zijn anders dan in vele programmeertalen, echter zijn ze niet per definitie moeilijker aangezien ze beschrijvend zijn en string|integer vergelijkingen gescheiden zijn. De meeste operatoren en voorbeelden ervan zijn te vinden in de volgende lijst van vergelijkende operatoren.

In dit geval krijgen we dan het volgende if statement:

if [ "$mp3num" -gt 0 ]; then
..
fi

Nu zitten we in de map, weten we dat er mp3 bestanden inzitten, dus kunnen we de playlist gaan maken. Echter voordat we zover zijn willen we de bestandsnaam van de playlist gaan bepalen. In mijn geval neem ik de naam van de map daarvoor. Dat doe ik met het basename commando, voeg er “.m3u” aan toe en het resultaat wordt opgeslagen in een variabele.

outputfn=$(basename "$realpath")".m3u"

basename – return filename portion of pathname. Voor meer informatie, zie manpage basename

Nu we alles klaar hebben staan kunnen we de playlist gaan maken met fapg:

fapg . --output="$outputfn" &> /dev/null

Hiermee genereren we voor de huidige directory een playlist naar de filename die in de variabele output zit. Het resultaat van de functie, inclusief errors, stuur ik direct naar /dev/null. Dat wil zeggen, niet naar het scherm maar gelijk weggooien. Als errors wel wenselijk zijn kan:

&> /dev/null

vervangen worden door:

> /dev/null

Hierna navigeren we weer terug naar de directory waaruit het script aangroepen is, dit kan gevonden worden door bovenaan in het script het volgende te zetten:

basepath=$(pwd)

pwd – return working directory name. Voor meer informatie, zie manpage pwd

Het volledige script

Voor het eindresultaat hierbij het volledige script:

#!/bin/bash
# determine from which directory this script is called
basepath=$(pwd)

# find all the directories in the current path
find . -type d | while read dir
do
        # navigate to the full path
        realpath=$(readlink -f "$dir")"/"
        cd "$realpath"

        # check to see if any mp3's in the directory
        mp3num=$(find . -maxdepth 1 -name '*.mp3' | wc -l)

        # if there are any mp3's in the directory
        if [ "$mp3num" -gt 0 ]; then
                # determine the playlist filename by it's parent directory
                outputfn=$(basename "$realpath")".m3u"

                # generate the playlist with fapg (output is suppressed)
                fapg . --output="$outputfn" &> /dev/null
        fi

        # return to the start path (because we're in a while loop)
        cd "$basepath"
done

Sla deze op (bijv. als plgen) en maak ‘m uitvoerbaar met:

chmod +x plgen

Het beste is het script opslaan in een path die overal te bereiken is. In mijn geval heb ik ‘m opgeslagen in ~/bin/. Hoe je die toevoegt aan je path zal ik morgen uitleggen.[:nl]Mijn muziekcollectie is enigszins gegroeid en daardoor niet altijd goed bijgehouden. Een van de problemen is het ontbreken van de playlists. Gelukkig is hier onder unix een mooie tool voor, genaamd fapg, wat staat voor Fast Audio Playlist Generator. Deze tool maakt een playlist voor alle files binnen een map.

Voor meer informatie en de recente versie van FAPG is hier te vinden.

Maar wat als je voor alle mappen binnen een pad inidividuele playlists wilt maken? Tijd om een bash script te maken!

BASH script samenstellen

Allereerst moeten we alle directories van de map vinden, dat kan met het find commando:

find . -type d

Dit commando hebben we al eerder gebruikt bij onder andere de incrementele backup.

Het resultaat daarvan moeten we doorlopen, dat kan o.a. met een while loop:

find . -type d | while read dir
do
        echo $dir
done

Dit print alle directories binnen de map waarin het script is uitgevoerd.

Omdat we de playlists binnen de individuele mappen willen maken, is het handig om naar de daadwerkelijk map te navigeren. Hiervoor moeten we het volledige pad hebben, gelukkig is daar de tool readlink voor beschikbaar. Daarnaast willen we het resultaat daarvan opslaan als variabele.

Om bij het begin te beginnen, variabelen binnen bash zijn kinderlijk eenvoudig:

a="test"
echo $a
-> test

Om het resultaat van readlink in een variabele op te slaan moeten we het volgende doen:

realpath=$(readlink -f "$dir")
-> data/volledig/en/absoluut/pad

readlink – display file status. Voor meer informatie, zie manpage readlink

Dit pad eindigt alleen nog niet met een slash, we zullen aan deze string dus een andere string moeten toevoegen, dat gaat in bash erg makkelijk:

a="test"
b="abc"
echo $a$b
-> testabc

Als we dat dus op bovenstaande code toepassen:

realpath=$(readlink -f "$dir")"/"
-> data/volledig/en/absoluut/pad/

Vervolgens kunnen we met het cd commando naar deze map navigeren.

Voordat we een playlist kunnen gaan maken, is het wel verstandig om te bekijken of een map wel muziek (in dit geval mp3) bestanden heeft. Dat kunnen we wederom met het find commando doen:

mp3num=$(find . -maxdepth 1 -name '*.mp3' | wc -l)

Hier gaat find uit van het volgende: in de huidige directory, geen subdirectories (maxdepth 1 = huidige directory, 0 = enkel command line) en de naam moet eindigen op .mp3. Die geeft alle gevonden mp3 bestanden onder elkaar. Vervolgens gaat het resultaat daarvan door de tool wc die het daadwerkelijk tellen regelt. De -l parameter telt het aantal lijnen.

wc – print the number of newlines, words, and bytes in files. Voor meer informatie zie manpage wc

Dan moeten we het aantal gevonden mp3’s gaan bekijken, het aantal moet minimaal 1 zijn (oftewel groter dan 0). Dat kan met het if commando:

a=10
if [ a -gt 5 ]; then
        echo "groter dan 5"
else
        echo "niet groter dan 5"
fi

De operatoren van BASH zijn anders dan in vele programmeertalen, echter zijn ze niet per definitie moeilijker aangezien ze beschrijvend zijn en string|integer vergelijkingen gescheiden zijn. De meeste operatoren en voorbeelden ervan zijn te vinden in de volgende lijst van vergelijkende operatoren.

In dit geval krijgen we dan het volgende if statement:

if [ "$mp3num" -gt 0 ]; then
..
fi

Nu zitten we in de map, weten we dat er mp3 bestanden inzitten, dus kunnen we de playlist gaan maken. Echter voordat we zover zijn willen we de bestandsnaam van de playlist gaan bepalen. In mijn geval neem ik de naam van de map daarvoor. Dat doe ik met het basename commando, voeg er “.m3u” aan toe en het resultaat wordt opgeslagen in een variabele.

outputfn=$(basename "$realpath")".m3u"

basename – return filename portion of pathname. Voor meer informatie, zie manpage basename

Nu we alles klaar hebben staan kunnen we de playlist gaan maken met fapg:

fapg . --output="$outputfn" &> /dev/null

Hiermee genereren we voor de huidige directory een playlist naar de filename die in de variabele output zit. Het resultaat van de functie, inclusief errors, stuur ik direct naar /dev/null. Dat wil zeggen, niet naar het scherm maar gelijk weggooien. Als errors wel wenselijk zijn kan:

&> /dev/null

vervangen worden door:

> /dev/null

Hierna navigeren we weer terug naar de directory waaruit het script aangroepen is, dit kan gevonden worden door bovenaan in het script het volgende te zetten:

basepath=$(pwd)

pwd – return working directory name. Voor meer informatie, zie manpage pwd

Het volledige script

Voor het eindresultaat hierbij het volledige script:

#!/bin/bash
# determine from which directory this script is called
basepath=$(pwd)

# find all the directories in the current path
find . -type d | while read dir
do
        # navigate to the full path
        realpath=$(readlink -f "$dir")"/"
        cd "$realpath"

        # check to see if any mp3's in the directory
        mp3num=$(find . -maxdepth 1 -name '*.mp3' | wc -l)

        # if there are any mp3's in the directory
        if [ "$mp3num" -gt 0 ]; then
                # determine the playlist filename by it's parent directory
                outputfn=$(basename "$realpath")".m3u"

                # generate the playlist with fapg (output is suppressed)
                fapg . --output="$outputfn" &> /dev/null
        fi

        # return to the start path (because we're in a while loop)
        cd "$basepath"
done

Sla deze op (bijv. als plgen) en maak ‘m uitvoerbaar met:

chmod +x plgen

Het beste is het script opslaan in een path die overal te bereiken is. In mijn geval heb ik ‘m opgeslagen in ~/bin/. Hoe je die toevoegt aan je path zal ik morgen uitleggen.[:]

Leave a Reply

Your email address will not be published. Required fields are marked *