Skip to content

Instantly share code, notes, and snippets.

@dreamcat4
Last active August 23, 2020 14:13
Show Gist options
  • Save dreamcat4/8e6c06a6bcfbd3f0f03c13b5603000db to your computer and use it in GitHub Desktop.
Save dreamcat4/8e6c06a6bcfbd3f0f03c13b5603000db to your computer and use it in GitHub Desktop.
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: Apache 2.0
#
set -x
# 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
#
# This is a background task for my other script 'aud-jumptofile'
# It is required to put both scripts on your $PATH.
#
# Author: Dreamcat4 (2018) dreamcat4@gmail.com
# License Type: Apache 2.0
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 "$@"
)
@dreamcat4
Copy link
Author

The problem is that is in the embedded script. So the 2nd script is dumb and does not know how to read setting in the main script. However you can edit by hand the line. Same thing here, it's like a 2nd instance declaration:

https://gist.github.com/dreamcat4/8e6c06a6bcfbd3f0f03c13b5603000db#file-aud-jumptofile-sh-L176

Sorry about that. I just don't seem to have time to fix this properly. The right way. Since the program is split into 2 seperate scripts. Perhaps a better solution might be to merge the scripts together. So it can be invoked in 2 different ways. Then we can just declare them all as global variables, these user config settings, at the top of the script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment