I need a command that plays a random mp3 from a directory. So far I have tried
ls *.mp3 | shuf -n 1 | omxplayer
Every different player just acts like it has not received a filename and spits out the help. Thanks for the help!
Firstly, I dislike Bash. Piping paths to processes isn't so nice, and causes all sorts of weirdness when not done 'just so'. That being said, many of the things that you are trying to do in Bash that are not kind with working or operating can be done with (unfortunately) more code, but can work the way you want it to, in other languages.
So, being somewhat bored and interested in creating something for this, I went and wrote a (very rough) Python script that can do what you're looking for. It may look complex, but it works pretty well, and I've put comments wherever or explained this below.
NOTE: I have only tested this with VLC player and Rhythmbox on my system, and with xdg-open
which opens the GUI's default handler for the given file. In my case, VLC is the default that xdg-open
calls. If you are on a GUI and just want to use the default Media player for the MP3 file, use xdg-open
for "player".
Package Requirements On Your System:
python
(Python 2.6 or higher, but not Python 3)python-dev
(Python libraries of importance)Script Installation Process:
Not really much work here. But to make it simpler, follow these steps:
bin
folder in your home directory: mkdir /home/$USER/bin
cd /home/$USER/bin
randommp3
. Copy-and-paste the code from the "Code/Script" section below into this file with a text editor. Save said file.chmod +x /home/$USER/bin/randommp3
oxmplayer
would be what you would put in place of player
when you execute the file./home/$USER/Music
(where $USER
is the currently logged in user), then you also have to specify the full directory path with the --dir
argument (or one of its aliases as explained in the "usage" section below). If the folder path contains any spaces at all, you must wrap it in single quotes (for example, for the "My Music" directory in a given path, you would enter it as /path/to/My Music
to the --dir
argument).Example execution:
Open a random MP3 file from the user's Music folder in their Home directory, in the GUI VLC Player
randommp3 vlc-wrapper
Open a random MP3 file from an external drive called "MusicDrive" mounted at Music Drive
in the /media
folder, in the media player called oxmplayer
randommp3 --dir '/media/Music Drive' oxmplayer
Usage
randommp3 [-h] [--dir DIRPATH] player
Open a random MP3 in the player of choice, or the default player
positional arguments:
player The executable name of the media player to open the
MP3 with. If none specified, uses the system default
player.
optional arguments:
-h, --help show this help message and exit
--dir DIRPATH, --directory DIRPATH, --music-dir DIRPATH
The path to the directory where your music is stored.
If the path has spaces, wrap the entire path in
single-quotes ('/home/ubuntu/My Music/' for example).
If not specified, the current user's Music directory
in their /home/ folder is used.
Code: (or a link that you can save if you are really that lazy)
#!/usr/bin/python
import getpass
import subprocess as sp
import os
import glob
import random
import argparse
if __name__ == "__main__":
# Parse arguments to the script
argparser = argparse.ArgumentParser(description="Open a random MP3 in the player of choice, or the default player",
add_help=True)
argparser.add_argument('--dir', '--directory', '--music-dir', dest='dirpath', type=str,
default=str('/home/' + getpass.getuser() + '/Music'), required=False,
help="The path to the directory where your music is stored. If the path has spaces, wrap the "
"entire path in single-quotes ('/home/ubuntu/My Music/' for example). If not specified, "
"the current user's Music directory in their /home/ folder is used.")
argparser.add_argument('player', type=str, help="The executable name of the media player "
"to open the MP3 with. If none specified, "
"uses the system default player.")
# Using the above 'argparser' items, get the arguments for what we're going to be using.
args = argparser.parse_args()
# Gp to the directory your music is in.
os.chdir(args.dirpath)
mp3s = glob.glob('*.mp3')
# Modify the directory path to make sure we have the trailing slash
dirpath = args.dirpath
if dirpath[-1] not in '/\\':
dirpath += '/'
# Actually open the MP3 file, and /dev/null to suppress output messages from the process.
DEV_NULL = open(os.devnull, 'w')
execpath = [args.player, '%s%s' % (dirpath, str(random.choice(mp3s)))]
sp.Popen(execpath, stdout=DEV_NULL, stderr=DEV_NULL)
Piping paths to processes isn't so nice, and causes all sorts of weirdness when not done 'just so'
I thoroughly disagree. Pipes one of the best things about bash. I cant see any "weirdness". One just has to understand the difference between passing arguments and stdin. Bash is awesome, because you can do the same thing you did with 50 lines in python with a one-liner (see Terrance's answer).
This command should work in bash. You might want to run it from being in the parent folder of where your MP3 files are at.
find . -type f -name '*.mp3' | shuf -n 1 | xargs -d "\n" omxplayer
or replace omxplayer
with your favorite media player.
Or another command that works is using the xdg-open
to use your default player like @muru has commented:
xdg-open "$(find . -type f -name '*.mp3' | shuf -n 1)"
NOTE: If you remove the -n 1
from the shuf
, then it will play all the MP3 files in a shuffled order. But this requires using the actual player and not xdg-open
. And it only works in the first command here. Just tested it.
Debian linux has a perl program called randomplay
that plays *.mp3 and files in other audio formats from one or more specified directories. I've used it for years.
For example, in my top level music directory, the following plays a randomized list of songs from all given directories:
randomplay artist1 artist2 artist3
Since it is perl, I presume it will run on any linux system when the dependencies are installed. No idea if it works on other OSs.
EDIT Just re-read the question and it sounds like the person might just want to play one random song. In that case, the --tracks=n
argument is useful:
randomplay --tracks=1 directory
| xargs omxplayer
? Was it a typo or do you truely have no idea how command lines work in MS-DOS, Windows, Unix etc.?omxplayer
probably expects the filename as an argument rather than as standard input: tryshuf -n 1 -ze *.mp3 | xargs -0 omxplayer
shuf -ze *.mp3
andxargs -0
that I suggested? That should be safe for filenames containing spaces and other weirdness.xdg-open "$(ls *.mp3 | shuf -n 1)"
works fine enough unless the filenames have weird characters - in which case steeldriver's suggestion to use NUL-delimited output should be used