Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
audacious - cmdline helper script - Jump directly to mp3 file or radio station, if its already in 'Library' or 'Radio' playlists
#!/bin/sh
#
# aud-jumptofile <file>
#
# audacious - cmdline helper script - Jump directly to mp3 file or radio station, if its already in 'Library' or 'Radio' playlists
#
# This script finds which track to play within the currently playing playlist
# instead of what audacious normally does, which is either creating a brand new playlist
# or appending the same track multiple times to the end of the currently playing playlist
#
# This script also includes instructions how to get an equivalent functionality as the win10
# start menu, for direct and immediate playing of mp3s. By configuring this script to be launched
# by the tool 'albert' on linux. Or any other program which respects MIME types (such as 'xdg-open')
#
# Author: Dreamcat4 (2018) dreamcat4@gmail.com
# https://gist.github.com/dreamcat4/8e6c06a6bcfbd3f0f03c13b5603000db
#
# License Type: BSD / MIT
#
# Requirements:
# ======================================================================
#
# Requires audacious music player with:
# * a playlist named 'Library', for your mp3, m4a, and aac music files
# * a playlist named 'Radio', for your m3u internet stations
# Installation instructions:
# ======================================================================
#
# * Make this script executable, and put this script somewhere on your $PATH
# * Set up mime types associations, as shown below. And reboot your system
# * Install the daemon with the command: $ sudo aud-jumptofile -i
# * Cache your library with the command: $ aud-jumptofile -u
# * Close and re-open audacious. Double check Settings --> Playlist --> Custom String:
# It MUST say: ${file-path}/${file-name} | ${artist}
#
# (optional, but highly recommended)
# * Configure the linux program 'albert', to play mp3s or switch radio stations
# * Make sure albert is configured with the 'Files' extension enabled
# * Add there the Path to your Music Library folder
# * Check the box to enable indexing of 'Audio' MIME types
# * Click 'Advanced' button for mime types, and select 'audio/*'
# * ALSO BE SURE TO CHECK 'Index Hidden Files', for radio station .m3us
# because we save the radio .m3u files to ~/Music/.radio_m3us
# Creating mime type associations, on gnome / ubuntu
# ======================================================================
# Example .desktop file
# ~/.local/share/applications/aud-jumptofile.desktop:
#
# [Desktop Entry]
# Type=Application
# Exec=aud-jumptofile -o %f
# Hidden=false
# NoDisplay=false
# X-GNOME-Autostart-enabled=true
# Name[en_GB]=Aud Jump To File
# Name=Aud Jump To File
# Comment[en_GB]=In the currently playing playlist
# Comment=In the currently playing playlist
#
# Example mime types
# ~/.config/mimeapps.list:
#
# [Default Applications]
# audio/m3u=aud-jumptofile.desktop
# audio/x-mpegurl=aud-jumptofile.desktop
# audio/m4a=aud-jumptofile.desktop
# audio/mpeg=aud-jumptofile.desktop
# audio/x-mpegurl=aud-jumptofile.desktop
# audio/x-scpls=aud-jumptofile.desktop
# audio/x-vorbis+ogg=aud-jumptofile.desktop
# audio/x-wav=aud-jumptofile.desktop
#
# Check to see your MIME types are recognised properly, the litmus test:
# open <file>
#
# Script Settings
# ======================================================================
#
# BE AWARE THAT THESE ARE ALSO HARD-CODED INTO THE INLINED DAEMON SCRIPT FURTHER DOWN
# SO HAVE TO MATCH THE SAME VARIABLES OVER THERE
#
# name of the program which is periodically retreiving and saving a textual copy of the currently playing playlist
_daemon="aud-fplcacher"
#
_cached_playlist_library="$HOME/.cache/audacious/library.cached_playlist"
_cached_playlist_radio="$HOME/.cache/audacious/radio.cached_playlist"
_radio_m3us="$HOME/Music/.radio_m3us"
#
# how often to update the file where the currently playing playlist is being cached
_daemon_update_interval=1000
#
# the number of file path components to match against, from right to left. so that non-matching top level folders are ignored
_num_path_components=3
#
# kill audtool playlist-display if audtool has been stuck gathering a playlist for more than this number seconds
lastActivationTimeoutSeconds=30
#
#
#
_program="$(basename $0)"
err()
{
_rc="$1"
shift
while [ "$1" ]; do
echo "${_program}: error: $1"
shift
done
exit $_rc
}
warn()
{
echo "${_program}: warning: $@"
}
info()
{
echo "${_program}: info: $@"
}
_cat_daemon()
{
cat <<- "EOF"
#!/bin/sh
# audacious-jumptofile playlist file cacher
# re-reads the audacious playlist into a text file every few seconds
if [ "$1" ]; then
_timeout="$1"
else
_timeout="1000"
fi
_cached_playlist_library="$HOME/.cache/audacious/library.cached_playlist"
_cached_playlist_radio="$HOME/.cache/audacious/radio.cached_playlist"
_radio_m3us="$HOME/Music/.radio_m3us"
mkdir -p ~/.cache/audacious
# configure the setting --> playlists --> title bar display
sed -i -e 's#generic_title_format=.*#generic_title_format=${file-path}/${file-name} | ${artist}#g' "$HOME/.config/audacious/config"
_radio_create_m3us()
{
mkdir -p "$_radio_m3us"
cd "$_radio_m3us"
cat "$_cached_playlist_radio" | while read _line; do
_radio_name="$(echo "$_line" | sed -e "s/^.* | //g" -e "s/ | .*$//g")"
_radio_url="$(echo "$_line" | grep -o "http[^|]*" | sed -e "s/ *$//g")"
_m3u_matches="$(grep -l "^${_radio_url}$" * 2> /dev/null)"
if [ "$_m3u_matches" != "${_radio_name}.m3u" ]; then
# remove old version(s)
echo "$_m3u_matches" | while read _m3u; do
rm -f "$_m3u"
done
if [ ! "$_radio_name" ]; then
_radio_name="$(echo "$_radio_url" | sed -e "s|http.*//||g" -e "s|/||g")"
fi
# write a new .m3u file
echo "$_radio_url" > "${_radio_name}.m3u"
fi
done
}
_cache_playlist()
{
_current_playlist_name="$(audtool current-playlist-name)"
case "$_current_playlist_name" in
Library)
audtool playlist-display | grep -o -E ".*~/Music[^|]*" > "$_cached_playlist_library"
;;
Radio)
audtool playlist-display | grep -o -E ".*http[^|]* \| [^|]*" | sed -e "s/\ *$//g" > "$_cached_playlist_radio"
_radio_create_m3us;
;;
*)
;;
esac
}
while true; do
if audtool playback-stopped || audtool playback-paused; then
_num_pl="$(audtool number-of-playlists)"
_pl_orig="$(audtool current-playlist)"
i=1
while [ "$i" -le "$_num_pl" ]; do
audtool set-current-playlist $i
_cache_playlist;
i="$(expr $i + 1)"
done
audtool set-current-playlist $_pl_orig
else
_cache_playlist;
fi
sleep $_timeout
done
EOF
}
_install_daemon()
{
if [ ! "$(id -u)" = "0" ]; then
err 1 "operation requires elevated permissions"
fi
_old_daemon="$(command -v "$_daemon")"
if [ ! "$_old_daemon" ]; then
_try_install=true
elif [ "$_old_daemon" ] && [ "$_force_install_daemon" ]; then
_try_install=true
else
err 1 "daemon already installed at \"$_old_daemon\"" \
"run again with -f flag reinstall and force overwrite / removal"
fi
if [ "$_try_install" ]; then
info "installing the daemon \"$_daemon\"..."
_bin="$(echo $PATH | grep -o '/usr/local/bin')"
if [ ! "$_bin" ]; then
_bin="$(echo $PATH | grep -o '/usr/bin')"
fi
_binpath="${_bin}/${_daemon}"
touch "${_bin}/${_daemon}"
if [ ! -e "$_binpath" ]; then
err 1 "cannot write to destination folder \"$_bin\""
fi
if [ "$_old_daemon" ]; then
rm "$_old_daemon"
fi
if [ -e "$_old_daemon" ]; then
err 1 "cannot remove previous daemon at \"$_old_daemon\""
fi
_cat_daemon > "$_binpath"
chmod +x "$_binpath"
_new_daemon="$(command -v "$_daemon")"
if [ "$_new_daemon" ]; then
info "daemon installed at \"$_new_daemon\""
exit 0
else
err 1 "new daemon installed, but not executable at \"$_new_daemon\""
fi
fi
}
_check_deps()
{
_deps="audacious audtool ps killall expr grep wc shuf sed cut"
for _prog in $_deps; do
if [ ! "$(command -v "$_prog")" ]; then
err 1 "the required program dependancy \"$_prog\" is missing from your \$PATH
The current \$PATH is:
$PATH
"
fi
done
}
_kill_daemon() {
killall -q "$_daemon"
killall -q "audtool"
for _cache_file in "$_cached_playlist_library" "$_cached_playlist_radio"; do
if [ -e "$_cache_file" ]; then
rm "$_cache_file"
fi
done
}
_cat_help()
{
cat <<- EOF
usage:
$_program [options] <file>
Where <file> is a single audio file, playable in audacious
options:
--only-in-playlist, -o
Only play the file specified if it was found in Library or Radio playlist
--update-playlist-cache, -u
Update the cached lookup entries right now, for Library & Radio playlists
--install-daemon, -i, [-f]
Install the background runner program into your \$PATH
--help, -h:
Display this message and exit
EOF
}
_parse_args()
{
while [ "$1" ]; do
arg="$1"
case $arg in
--install-daemon|-i) _install_daemon=true ;;
-f) _force_install_daemon=true ;;
--update-playlist-cache|-u) _update_playlist_cache=true ;;
--only-in-playlist|-o) _only_in_playlist=true ;;
--help|-h) _help=true ;;
*) _file="$1" ;;
esac
shift
done
if [ "$_help" ]; then
_cat_help
exit 0
fi
if [ "$_install_daemon" ]; then
_install_daemon
elif [ ! "$(command -v "$_daemon")" ]; then
err 1 "daemon not found on you \$PATH... you cannot continue" \
"run with -i flag to install the daemon program, \"$_daemon\""
fi
if [ "$_update_playlist_cache" ]; then
_killall=true
fi
if [ "$_file" ]; then
_file="$(realpath "$_file")"
_file_noext="$(echo "${_file%.*}")"
elif [ ! "$_killall" ]; then
warn "no filename given"
_cat_help
exit 1
fi
}
_process_mp3()
{
# check for match in library playlist
# only use n last path components for the search string
unset _regex
_regex_1_level="/[^/]*"
_i="$_num_path_components"
while [ "$_i" -gt "0" ]; do
_regex="${_regex}${_regex_1_level}"
_i="$(expr $_i - 1)"
done
_regex="${_regex}$"
_search_str="$(echo "$_file_noext" | grep -o -E "$_regex")"
if [ "$_search_str" ]; then
_matches="$(grep "$_search_str" "$_cached_playlist_library")"
_num_matches="$(echo "$_matches" | wc -l)"
if [ "$_num_matches" -eq "1" ]; then
_match="$_matches"
elif [ "$_num_matches" -gt "1" ]; then
echo "warning: the specified file matches multiple songs in your current playlist"
_i_rand="$(shuf -i "1-${_num_matches}" -n 1)"
_match="$(echo "$_matches" | sed "${_i_rand}q;d")"
fi
if [ "$_match" ]; then
unset _pl_orig
# switch to Library playlist
_current_playlist_name="$(audtool current-playlist-name)"
if [ "$_current_playlist_name" != "Library" ]; then
_pl_orig="$(audtool current-playlist)"
_num_pl="$(audtool number-of-playlists)"
i=1
while [ "$i" -le "$_num_pl" ]; do
audtool set-current-playlist $i
_current_playlist_name="$(audtool current-playlist-name)"
if [ "$_current_playlist_name" = "Library" ]; then
break
elif [ "$i" -eq "$_num_pl" ]; then
audtool set-current-playlist $_pl_orig
fi
i="$(expr i + 1)"
done
fi
if [ "$_current_playlist_name" = "Library" ]; then
_index="$(echo "$_match" | sed -e 's/^[^0-9]*//g' | cut -d' ' -f1)"
echo "match found! index = $_index"
# echo "$_match"
audtool playqueue-add "$_index"
audacious --fwd
# audacious --play
audtool play-current-playlist
# if [ "$_pl_orig" ]; then
# audtool set-current-playlist $_pl_orig
# fi
else
audacious "$_file"
fi
elif [ "$_only_in_playlist" ]; then
err 1 "no matching song found in Library playlist for file \"$_file\""
else
audacious "$_file"
fi
else
err 1 "no search string left after stripping ($_num_path_components) path components"
fi
}
_process_m3u()
{
_m3u_file="$_file"
_m3u_name="$_file_noext"
_m3u_url="$(cat "$_m3u_file")"
# cat "$_cached_playlist_radio" | while read _line; do
# _radio_name="$(echo "$_line" | sed -e "s/^.* | //g" -e "s/ | .*$//g")"
# _radio_url="$(echo "$_line" | grep -o "http[^|]*" | sed -e "s/ *$//g")"
_matches="$(grep " | ${_m3u_url} | " "$_cached_playlist_radio" 2> /dev/null)"
_num_matches="$(echo "$_matches" | wc -l)"
if [ "$_num_matches" -eq "1" ]; then
_match="$_matches"
elif [ "$_num_matches" -gt "1" ]; then
_first_matched_by_name="$(echo "$_matches" | grep -m 1 " | ${_m3u_name}$" 2> /dev/null)"
if [ "$_first_matched_by_name" ]; then
_match="$_first_matched_by_name"
else
echo "warning: the specified file matches multiple songs in your current playlist"
_i_rand="$(shuf -i "1-${_num_matches}" -n 1)"
_match="$(echo "$_matches" | sed "${_i_rand}q;d")"
fi
fi
if [ "$_match" ]; then
# switch to Radio playlist
_current_playlist_name="$(audtool current-playlist-name)"
unset _pl_orig
if [ "$_current_playlist_name" != "Radio" ]; then
_pl_orig="$(audtool current-playlist)"
_num_pl="$(audtool number-of-playlists)"
i=1
while [ "$i" -le "$_num_pl" ]; do
audtool set-current-playlist $i
_current_playlist_name="$(audtool current-playlist-name)"
if [ "$_current_playlist_name" = "Radio" ]; then
break
elif [ "$i" -eq "$_num_pl" ]; then
audtool set-current-playlist $_pl_orig
fi
i="$(expr $i + 1)"
done
fi
if [ "$_current_playlist_name" = "Radio" ]; then
_index="$(echo "$_match" | sed -e 's/^[^0-9]*//g' | cut -d' ' -f1)"
echo "match found! index = $_index"
# echo "$_match"
audtool playqueue-add "$_index"
audacious --fwd
# audacious --play
audtool play-current-playlist
# if [ "$_pl_orig" ]; then
# audtool set-current-playlist $_pl_orig
# fi
else
audacious "$_file"
fi
elif [ "$_only_in_playlist" ]; then
err 1 "no matching song found in Radio playlist for file \"$_file\""
else
audacious "$_file"
fi
}
_main()
{
psAudTool="`pgrep -f 'audtool '`"
_hung_daemon=0
for pid in $psAudTool; do
ptime="$(ps -o etimes= -p "$pid")"
if [ "$ptime" -ge "$lastActivationTimeoutSeconds" ]; then
_killall=true
break
fi
_hung_daemon="$(expr $i + 1)"
done
if [ "$_hung_daemon" -gt "1" ]; then
_killall=true
fi
# if a process to activate skippy already exists, with a runtime that is too long
# (> 1sec). Then assume skippy-xd is stuck, so we must kill its all of its locked processess
if [ "$_killall" ]; then
_kill_daemon;
fi
# if the action requires the skippy-xd daemon, but it is not already running, we should start it
if [ ! "$(pgrep -f "$_daemon")" ]; then
"$_daemon" "$_daemon_update_interval" &
fi
if [ "$_file" ]; then
if [ ! -e "$_file" ]; then
err 1 "the specified file \"$_file\", does not exit"
# elif [ ! -f "$_file" ]; then
# err "the specified file \"$_file\", is not a regular file"
fi
_file_ext="$(echo "${_file##*.}")"
if [ "$_file_ext" = "m3u" ]; then
_process_m3u;
else
_process_mp3;
fi
fi
}
# begin
(
_check_deps
_parse_args "$@";
_main "$@"
)
@Lupccs

This comment has been minimized.

Copy link

Lupccs commented Feb 20, 2019

Hi! This script seems to be just was I was looking for, so thanks for your work!
I'm still trying to make it work: given I'm using an Italian version of Audacious I changed the word "Library" to "Libreria" to match the actual playlist - even tried to change the name of the playlist to Library without any progress.
Basically the first problem seems to be that aud-jumptofile -u doesn't create the cache file in ~/.cache/audacious, but the deamon does.
When I try to launch an mpe with aud-jumptofile -o song.mp3
aud-jumptofile: error: no matching song found in Library playlist for file "/home/user/Musica/song.mp3"
the problem seems to be that in the library.cached_playlist file all the lines are in a ~/Musica/song while the command looks for "/home/user/Musica/song" string: I manually edited the library.cached_playlist accordingly and it did find a match!
Any idea about why's that or how to proceed to identify my mistake?
Consider I created a Radio playlist just to make sure everything could work but I don't really need that part and I'd be glad to cut it off. Also, is the deamon really necessary if what I'm trying to achieve is just that when I launch an mp3 file it plays on the title on the library playlist instead of just putting it to the end of the queue?

Thanks for your help anyway!

@Lupccs

This comment has been minimized.

Copy link

Lupccs commented Feb 25, 2019

After some day aud-jumptofile -u still doesn't work but the daemon does the job after a while. oddly, at first it did work properly but after some times now it does find a match in the library but it doesn't play the requested song, it only plays the next song in library (in alphabetical order)!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.