1

So I have written a bash script (named music.sh) for a Raspberry Pi to perform the following functions:

  • When executed, look into one single directory (Music folder) and select a random folder to look into. (Note: none of these folders here have subdirectories)
  • Once a folder within "Music" has been selected, then play all mp3 files IN ORDER until the last mp3 file has been reached
  • At this point, the script would go back to the folders in the "Music" directory and select another random folder
  • Then it would again play all mp3 files in that folder in order
  • Loop indefinitely until input from user

I have this code which does all of the above EXCEPT for the following items:

  • I would like to NOT play any other "album" that has been played before
  • Once all albums played once, then shutdown the system

Here is my code so far that is working (WITH duplicates allowed):

#!/bin/bash
    folderarray=($(ls -d /home/alphekka/Music/*/))
    for i in "${folderarray[@]}";
    do
    folderitems=(${folderarray[RANDOM % ${#folderarray[@]}]})    
        for j in "${folderitems[@]}";
        do
        echo `ls $j`
        cvlc --play-and-exit "${j[@]}"
        done
   done
exit 0

Please note that there isn't a single folder or file that has a space in the name. If there is a space, then I face some issues with this code working.

Anyways, I'm getting close, but I'm not quite there with the entire functionality I'm looking for. Any help would be greatly appreciated! Thank you kindly! :)

1
  • 2
    Instead of tracking names and checking later if a randomly selected one has been repeated, can you randomize the array before starting the loop, and then just loop through that randomized list. Commented Nov 2, 2016 at 20:50

3 Answers 3

4

Use an associative array as a set. Note that this will work for all valid folder and file names.

#!/bin/bash

declare -A folderarray
# Each folder name is a key mapped to an empty string
for d in /home/alphekka/Music/*/; do
  folderarray["$d"]=
done

while [[ "${!folderarray[*]}" ]]; do
  # Get a list of the remaining folder names
  foldernames=( "${!folderarray[@]}" )
  # Pick a folder at random
  folder=${foldernames[RANDOM%${#foldernames[@]}]}
  # Remove the folder from the set
  # Must use single quotes; see below
  unset folderarray['$folder']
  for j in "$folder"/*; do
    cvlc --play-and-exit "$j"
  done
done

Dealing with keys that contain spaces (and possibly other special characters) is tricky. The quotes shown in the call to unset above are not syntactic quotes in the usual sense. They do not prevent $folder from being expanded, but they do appear to be used by unset itself to quote the resulting string.

Sign up to request clarification or add additional context in comments.

4 Comments

Would there be a way to allow folder/files that have spaces without causing too much of a headache?
version 4.3.46(1)-release (x86_64-pc-linux-gnu). My shell is on Ubuntu 16.04 LTS
I got it to work now! I copied your code again and this time it ran and didn't duplicate anything. Now why it didn't work like that before I don't know. Thank you for your help @chepner!
Glad we got it straightened out!
0

Here's another solution: randomize the list of directories first, save the result in an array and then play (my script just prints) the files from each element of the array

MUSIC=/home/alphekka/Music
OLDIFS=$IFS
IFS=$'\n'
folderarray=($(ls -d $MUSIC/*/|while read line; do echo $RANDOM $line; done| sort -n | cut -f2- -d' '))
for folder in ${folderarray[*]};
do
 printf "Folder: %s\n" $folder
 fileArray=($(find $folder -type f))
 for j in ${fileArray[@]};
 do
  printf "play %s\n" $j
 done
done

For the random shuffling I used this answer.

3 Comments

@alphekka In what order did you want to play the songs?
In order by name. The music I'm working with is named "01 abc.mp3" and so on. When I tried this it had a random order that i was playing songs. The folders were random as expected though.
To sort by filename you can change the line fileArray=($(find $folder -type f)) to fileArray=($(find $folder -type f|sort)) passing the list of files through the sort command.
0

One liner solution with mpv, rl (randomlines), xargs, find:

find /home/alphekka/Music/ -maxdepth 1 -type d -print0 | rl -d \0 | xargs -0 -l1 mpv

2 Comments

By the time tr reads the input, it's too late to replace newlines with \0. To make this work for such files, find needs to output a null-delimited list of files, and rl would need to be able to read that. (The output of rl could then be piped directly to xargs.)
I couldn't get this to work at all... looks like it has some amazing potential though! Thanks for your thoughts!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.