Spring naar hoofdtekst

Recursieve jukebox met find en mplayer

Geplaatst op door ,
Laatste aanpassing op .

Inleiding

Laatst vulde ik een usb-stick met mp3-bestanden voor tijdens de volgende lange autorit. Eenmaal gereed wilde ik er graag in willekeurige volgorde doorheen luisteren om te horen of normalize-audio zijn werk goed had gedaan. Het moet toch mogelijk zijn om find, sort en mplayer aan elkaar te knopen?

Onderzoek (1)

Als eerste zocht ik in de handleiding van mplayer (man 1 mplayer) naar een optie om een map recursief uit te lezen; tevergeefs. Ergens anders had ik gelezen over het programma xargs, waarmee je grote hoeveelheden parameters aan programma's kunt doorgeven. Ik probeerde de volgende one-liner:

$ find . -type f -print0 | sort -z -R | xargs -0 mplayer

Het werkte! find zoekt in de huidige map (.) naar bestanden (-type f) en voert deze aan sort, die ze in een willekeurige volgorde (-R) plaatst. Uiteindelijk geeft xargs de lijst door aan mplayer. Om problemen met bestandsnamen met spaties of newline karakters te vermijden, gebruik ik de opties -print0, -z en -0. Hierdoor worden de namen gescheiden door een null-byte in plaats van een newline.

Maar wat was dat? De toetsenbordbediening van mplayer werkte niet meer! Ik kon niets anders dan met Ctrl + C de uitvoer afbreken.

Onderzoek (2)

Mplayer heeft ook een -playlist optie, die als argument een lijst van af te spelen bestanden verwacht. In een ver grijs verleden had ik ooit een Bash subshell hiervoor gebruikt in de volgende vorm:

$ mplayer -playlist <( find ${PWD} -type f | sort -R ); 

Dit werkt, en ook de toetsenbordbediening van mplayer functioneert naar behoren. Maar, wat als je buiten de huidige map wil zoeken?

Script

Uiteindelijk wilde ik een functie die één optionele parameter accepteert: de map waarin moet worden gezocht. Is deze parameter leeg, dan wordt de huidige map (${PWD}) gebruikt.

Daarna wordt een tijdelijk bestand aangemaakt (mktemp), waarin finds uitvoer in terecht komt. sort -R zorgt, net als in bovenstaand script, voor de willekeurige volgorde.

Omdat mplayer niet per sé in dezelde map start als find, moeten de bestandsnamen en paden in de afspeellijst absoluut zijn. Daarom wordt voor elke treffer een -exec readlik -f \; uitgevoerd.

function mus()
{
    FOLDER="$1";
    if [ "${FOLDER}"x = x ]; then
        FOLDER="${PWD}";
    fi

    TMP="$( mktemp )";
    find "${FOLDER}" -type f -exec readlink -f "{}" \; | sort -R > "${TMP}";
    mplayer -playlist "${TMP}";
    rm "${TMP}";
}

Ik heb nog geprobeerd om dit script zonder tijdelijk bestand, met behulp van een subshell te maken. Dat werd me te lastig :-) Het blijkt behoorlijk moeilijk om variabelen vanuit de hoofd- naar de subshell door te geven.

Conclusie

Het bovenstaand script heb ik aan mijn ~/.bashrc-bestand toegevoegd. Daardoor kan ik in elk terminal­venster of tekstconsole de functie mus oproepen; al dan niet met de af te spelen map als argument.

$ mus ~/Music

Inhoudsopgave

Klik op één van de onderstaande categorieën om de lijst met artikelen te filteren.