[SOLVED] How to randomly "cd" for cli mplayer music files
Linux - SoftwareThis forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Dman here and I have a question. But 1st let me give a scenario.
I have been using Mplayer through Terminal to play my music files via cli in order to boost my cli knowledge. I generally "cd" to the path of whichever album I would like to play and type:
Example
Code:
mplayer -shuffle *.*m*
Which will play .mp3, .m4a, & wma
Problem is Me Really, I haven't got the insight in order to play the files as I would via a GUI application which tends to have tons of bells n whistles. For example randomly playing files that are generally in different locations.
Instead of copying specific files into a directory / playlist on the fly I would rather do 1 of 2 things. Either randomly "cd" to 1 of my many many music directories or recursively "cd" from: example - Artists main directory to a sub containing that same artists album.
I hope I stated that clearly.
Some might say just use the GUI but I think it can be accomplished via cli but with a little more effort. Plus I am just trying to gain more cli knowledge.
Is this the part where I start reading about for loops and while conditions or something or other?
I wouldn't use find for this, since even with the "-exec +" variant there is no guarantee that all found files will land in the mplayer command, if the number is large enough find will still split this into separate commands, so that you won'get real random playing.
I think this is a case for the shell functions, here pattern matching. I use Zsh, so I would use
Code:
mplayer -shuffle **/*.m*
to search for files that match the pattern *.m* in all sub-directories.
That should also work on Bash, but it may help to have a look at the Bash manuals.
Distribution: openSUSE, Raspbian, Slackware. Previous: MacOS, Red Hat, Coherent, Consensys SVR4.2, Tru64, Solaris
Posts: 2,803
Rep:
Quote:
Originally Posted by Dman58
Instead of copying specific files into a directory / playlist on the fly I would rather do 1 of 2 things. Either randomly "cd" to 1 of my many many music directories or recursively "cd" from: example - Artists main directory to a sub containing that same artists album.
Seems clear enough. Sounds like you have a similar directory structure to mine: artist -> album [-> disc number, if needed] -> track (named as: tracknumber-title). (It is the natural way to store music files for transfer to my Cowon D2.)
If you're using Banshee, I think you can configure it to recognize the underlying directory/file structure. Check the "File Organization" section of the "Edit->Preferences->Source Specific" menu.
What I've been doing since the days when I used Alsaplayer (still my favorite but it doesn't seem to work any more) is to flatten the directory structure by creating symbolic links to the actual music files. To do this I wrote a Perl script that walks the tree of music files and created a symbolic link for each track in a subdirectory ($HOME/music/playlist) and then have the music player use that subdirectory as its "library". I use the album name (or the disk number for multidisc sets), the track number and track name in the name of the symbolic link. (Note: I recently ran into a flaw in my scheme where two albums wound up having a song by the same title as the same track number on their respective albums and I added the album name to the link name. A real rare bug that I might have fix would be if there turned out to be identically named tracks on the Nth disc of two (or more but that would be insanely rare) multidisk sets. I'd probably just insert a random number in the link name if there's a name clash.)
Even if Banshee doesn't need to see that flattened tree, it seems to work just fine. I used it with Amarok as well with no problems.
Bad, bad, bad. Any file with whitespace in it would be impossible to handle. It really needs to be converted into an array instead.
It also shouldn't need to rely on external commands like wc and cut.
Code:
#!/bin/bash
# to safely set the array:
while IFS='' read -rd '' fname; do
pl_array+=( "$fname" )
done < <( find /root/user/music -name "*.mp3" -print0 )
# Or bash's globstar could probably be used instead.
# just ensure there are no recursive symlinks first.
# shopt -s globstar
# pl_array=( **/*.mp3 )
num=${#pl_array[@]}
{ while true; do
r=$(( RANDOM % num ))
echo "HTTP/1.0 200 OK\nContent-Type: audio/x-mp3stream\n\n"
dd if=${pl_array[r]} bs=1024
done
} | nc -l -s address -p 8020
exit 0
Notice also that this is a truly random player, not shuffled, and will repeat songs. Nor is there any way to break out of the loop. One idea around this would be to remove duplicates and have it exit when the file list is empty.
Code:
{ while [[ -n ${pl_array[@]} ]]; do
num=${#pl_array[@]}
r=$(( RANDOM % num ))
echo "HTTP/1.0 200 OK\nContent-Type: audio/x-mp3stream\n\n"
dd if=${pl_array[r]} bs=1024
unset pl_array[r]
pl_array=( ${pl_array[@]} ) #re-indexes the array for the next pass
done
} | nc -l -s address -p 8020
Last edited by David the H.; 04-21-2013 at 03:58 PM.
Reason: fixed tags
Thank you all for your help, I appreciate the feedback.
@TobiSGD
Your response is actually more of what I was looking for. Very simple and to the point.
Code:
mplayer -shuffle **/*.m*
I didn't realize that the double asterisk would include sub-directories. That along with -shuffle makes my selection random enough for my tastes.
To everyone else Thank You very much I will experiment with those other suggestions as well in the near future. Most importantly read up on shell scripting, regular expressions, and everything else I need to better my understanding.
** is the new (from v.4) bashglobstar advanced globbing pattern, which I believe was copied over from ksh. You may have to enable it first with "shopt -s globstar". When followed by "/" it expands into a list of all subdirectories, allowing for recursive matching of file globbing patterns.
There is one weakness with it, however. If the file tree contains any directory symlinks that point back to a parent node it will get caught in a perpetual loop and chew up your memory. So be judicial in how you use it.
There is one weakness with it, however. If the file tree contains any directory symlinks that point back to a parent node it will get caught in a perpetual loop and chew up your memory. So be judicial in how you use it.
I wouldn't use find for this, since even with the "-exec +" variant there is no guarantee that all found files will land in the mplayer command, if the number is large enough find will still split this into separate commands, so that you won'get real random playing.
I think this is a case for the shell functions, here pattern matching. I use Zsh, so I would use
Code:
mplayer -shuffle **/*.m*
to search for files that match the pattern *.m* in all sub-directories.
Are you sure this won't end up with the same problem? I'm reasonably sure that shell will refuse to run a command if the command line is too long (or if not shell, than kernel). Than again, Linux limits the number of arguments at 0x7FFFFFFF.
If the number of files is large enough you will of course run into issues, but that would for me be the point to switch to a script to emulate shuffle without those limitations. The version with find you suggested does not circumvent those limitations, so using find in that way when the shell itself can do the job brings no advantages. FWIW, I just tried my solution on a directory with about 5300 MP3s and it worked without a problem. I am not sure how to test if find would already have splitted such a large number of files.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.