Hack:stop after current
From Music Player Daemon Community Wiki
A hack to wait until the current song is finished, then stop MPD.
Per request in the Hacks list.
I'm sure there are many nicer ways to achieve this, but it works for me.
In shuffle mode, the playlist is re-shuffled after playing is stopped. This script pauses, instead of stopping, to avoid this. If you want to really stop playing, change the last line from 'pause' to 'stop'.
by Harun Vos
Requires mpc
stop_after_current.sh
#!/bin/bash
# MPD script to stop playback after the current track.
# Was a simple hack, but I got a bit carried away.
# Designed to run in the background eg. in response to a keypress, so no error messages.
# If it detects that anything has changed outside of its control, it silently aborts.
# Written by Harun Vos, released into the public domain.
# Requires mpc - set the path here.
MPC=mpc
# Extract the interesting line from the mpc output.
# There must be a nicer way of dealing with multi-line shell variables.
function getDetailsLine()
{
local d=$($MPC --format "no file name")
d=$(tail -1 <<EOL
$(head -2 <<EOL2
$d
EOL2)
EOL)
echo $d
}
# Get the playing/paused status.
function getStateFromLine()
{
echo $*|cut -d' ' -f1
}
# Get the number of the currently playing song.
function getSongFromLine()
{
echo $*|cut -d' ' -f2|cut -d'/' -f1|cut -d'#' -f2
}
# Convert the mm:ss format to the number of seconds.
# This 10#stuff forces the numbers into base 10, even if they start with a 0.
function minToSec()
{
echo $((10#$(echo $1|cut -d':' -f1) * 60 + 10#$(echo $1|cut -d':' -f2)))
}
# Find out how long we should wait for the end of the current song.
function getTimeRemainingFromLine()
{
local current=$(echo $*|cut -d' ' -f3|cut -d'/' -f1)
local total=$(echo $*|cut -d' ' -f3|cut -d'/' -f2)
current=$(minToSec $current)
total=$(minToSec $total)
echo $(($total - $current))
}
# Dont do anything if we arent playing.
details=$(getDetailsLine)
currState=$(getStateFromLine $details)
if [ "$currState" != "[playing]" ]; then exit; fi
currSong=$(getSongFromLine $details)
timeRemaining=$(getTimeRemainingFromLine $details)
# Wait till almost the end of the current song.
# Try to stop before the end of the current song, rather than after the start of the next one.
if [ $timeRemaining -gt 1 ]; then
sleep $(($timeRemaining - 1))
fi
# Make sure it is still playing.
details=$(getDetailsLine)
currState=$(getStateFromLine $details)
if [ "$currState" != "[playing]" ]; then exit; fi
# And make sure it is still the same song.
finalSong=$(getSongFromLine $details)
if [ "$finalSong" != "$currSong" ]; then exit; fi
# And make sure the song is actually ending now - it hasnt been restarted.
timeRemaining=$(getTimeRemainingFromLine $details)
if [ $timeRemaining -gt 2 ]; then exit; fi
# Make sure the playlist advances, even though we stop on the old song.
$MPC --no-status pause 2>&1 >> /dev/null
details=$(getDetailsLine)
finalSong=$(getSongFromLine $details)
if [ "$finalSong" == "$currSong" ]; then
$MPC --no-status next 2>&1 >> /dev/null
fi
$MPC --no-status pause 2>&1 >> /dev/null
By JustJ:
I made a few arragements which are mostly suited for a remote controlled pc e.g. linux music box. Also in this version you can, by starting the program multiple times, make the program stop after 2 or more songs. Basicly I've splitted the program in two, one is the original programm that is always running (now called mpdsaca (mpd stop after current always)), and the other one is a start script (mpdsac) which if the main program is not yet running starts is, but if it's running it does the following:
it checks for a file, in this case /tog/mpdsac, if it doesn't exist, it creates it and writes 1 in it. If it already exists it adds one to the number.
I've altered the main program in a way that instead of pausing (which is the last paragraph) it checks weather /tog/mpdsac exists, then if its content is 1 removes it and if it's a number higher than 1 it decreases it by 1 and in both cases restarts itself. If the file (/tog/mpdsac) is no longer present it does what it was supposed to do and pauses mpd.
What I have to add is that the syntax is possiby horrible, and the style wharever you may call it, this is just a built-to-work solution.
Main file (mpdsaca)
#!/bin/bash
# MPD script to stop playback after the current track.
# Was a simple hack, but I got a bit carried away.
# Designed to run in the background eg. in response to a keypress, so no
#error messages.
# If it detects that anything has changed outside of its control, it
#silently aborts.
# Written by Harun Vos, released into the public domain.
# Requires mpc - set the path here.
MPC=mpc
# Extract the interesting line from the mpc output.
# There must be a nicer way of dealing with multi-line shell variables.
function getDetailsLine()
{
local d=$($MPC --format "no file name")
d=$(tail -1 <<EOL
$(head -2 <<EOL2
$d
EOL2)
EOL)
echo $d
}
# Get the playing/paused status.
function getStateFromLine()
{
echo $*|cut -d' ' -f1
}
# Get the number of the currently playing song.
function getSongFromLine()
{
echo $*|cut -d' ' -f2|cut -d'/' -f1|cut -d'#' -f2
}
# Convert the mm:ss format to the number of seconds.
# This 10#stuff forces the numbers into base 10, even if they start with a
#0.
function minToSec()
{
echo $((10#$(echo $1|cut -d':' -f1) * 60 + 10#$(echo $1|cut -d':' -f2)))
}
# Find out how long we should wait for the end of the current song.
function getTimeRemainingFromLine()
{
local current=$(echo $*|cut -d' ' -f3|cut -d'/' -f1)
local total=$(echo $*|cut -d' ' -f3|cut -d'/' -f2)
current=$(minToSec $current)
total=$(minToSec $total)
echo $(($total - $current))
}
# Dont do anything if we arent playing.
details=$(getDetailsLine)
currState=$(getStateFromLine $details)
if [ "$currState" != "[playing]" ]; then exit; fi
currSong=$(getSongFromLine $details)
timeRemaining=$(getTimeRemainingFromLine $details)
# Wait till almost the end of the current song.
# Try to stop before the end of the current song, rather than after the
# start of the next one.
if [ $timeRemaining -gt 1 ]; then
sleep $(($timeRemaining - 1))
fi
# Make sure it is still playing.
details=$(getDetailsLine)
currState=$(getStateFromLine $details)
if [ "$currState" != "[playing]" ]; then exit; fi
# And make sure it is still the same song.
finalSong=$(getSongFromLine $details)
if [ "$finalSong" != "$currSong" ]; then exit; fi
# And make sure the song is actually ending now - it hasnt been restarted.
timeRemaining=$(getTimeRemainingFromLine $details)
if [ $timeRemaining -gt 2 ]; then exit; fi
# Make sure the playlist advances, even though we stop on the old song.
# This is the only area that is modified in the alternate script
TOG="/tog/mpdsac"
if [ -e $TOG ]
then
if [ $(head -1 $TOG ) = "1"
then
rm $TOG
mpdsaca &
else
echo $(expr $(head -1 $TOG) - 1) > $TOG
mpdsaca &
fi
else
$MPC --no-status pause 2>&1 >> /dev/null
details=$(getDetailsLine)
finalSong=$(getSongFromLine $details)
if [ "$finalSong" == "$currSong" ]; then
$MPC --no-status next 2>&1 >> /dev/null
fi
$MPC --no-status pause 2>&1 >> /dev/null
fi
Start File (mpdsac)
#!/bin/sh TOG="/tog/mpdsac" if [ $(pexist mpdsaca) ] then if [ -e $TOG ] then echo $(expr $(head -1 $TOG) + 1) > $TOG else echo 1 > $TOG fi else rm -f /tog/mpdsac mpdsaca & fi
there are also two more or less usefull scripts:
This one completely aborts the stopping after a song
#!/bin/sh killall mpdsaca>/dev/null 2>/dev/null rm -f /tog/mpdsac
And this one announces via any speak synthesizer (here represented by speak) how many songs till stop
#!/bin/sh if [ $(pexist mpdsaca) ] then if [ -e /tog/mpdsac ] then speak "stopping after $(expr $(head -1 /tog/mpdsac) + 1 ) songs" else speak "stopping after this song" fi else speak "not stopping at all" fi
