Hello Guys,
I would like to share with you script that I was working on time-by-time for past month or two. I was originally using numerous scripts I found over the web to do what I need, but non of them were able to manage Transmission queue on My Book due to the bug related to this system that prevent showing torrent status (Stopped/Download/Seed) correctly from transmission-remote list output.
So I start developing my own script to use debug mode of Transmission Remote and parse JSON within bash to properly identify torrent state and to apply manage logic. Finally I end-up with single script to do everything I would think of someone would need from Transmission.
Let's call it Transmission All-in-One manage script. Version 1.0
Features:
- Install/UnInstall startup and shutdown scripts (overwrites default Optware scripts)
- Enable/Disable startup of Transmission on boot as check flag
- Start/Stop/ReStart of Transmission
- Stability fix by setting EVENT_NOEPOLL=1; EVENT_NOEVPORT=1; EVENT_NODEVPOLL=1
- Updating Blocklist from download.m0k.org
- Edit Transmission config file and if changed force Transmission to re-read config file
- Watch Transmission and Restart if not responding
- Status of torrents in Transmission Remote native output format
- Torrents Queue manage:
- start and stop torrents according to MAX_TO_DOWNLOAD and MAX_TO_SEED values
- stop seeding once Max Ratio is reached
- respects ratio-limit from Transmission config file
- move torrent data to Complete folder once enough seeded and remove from Transmission list if succeeded
- use minimum calls to Transmission Remote
- watch check included
- overcome torrent Status bug on WD MyBook system by parsing JSON instead of regular list output
Installation:
cd /opt/bin
wget -O transmission http://pavel.kuzub.com/scripts/transmission
chmod a+x transmission
transmission install
CRON Jobs:
#Transmission Queue Control every 5 minutes
*/5 * * * * /opt/bin/transmission queue
#Transmission Restart every 2 hours as stability workaround
0 */2 * * * /opt/bin/transmission restart
Usage:
# Start Transmission
transmission start
# Stop Transmission
transmission stop
# Call Boot procedure
transmission startup
# Enable starting Transmission on boot procedure
transmission startup on
# Disable starting Transmission on boot procedure
transmission startup off
# Show Transmission Remote List
transmission status
# Restart Transmission
transmission restart
# Open VI (or another editor) to edit settings.json
transmission edit
# Watch Transmission and kick if needed
transmission watch
# Queue manage (for CRON)
transmission queue
# Install startup and shutdown scripts
transmission install
# Uninstall startup and shutdown scripts
transmission uninstall
# Show commands
transmission usage
Script Code:
#!/bin/sh
#
# Transmission All-in-One manage script. Version 1.00
# Tested with Transmission Daemon versions 1.90 and 2.03 on WD My Book World Edition White Light
# Script by Pavel Kuzub. Some code is re-used from their respective owners.
# On embedded systems consider installing the following packages:
# ipkg install busybox-base
# ipkg install busybox-links
#
# Full list of dependency is not available
#
# Installation:
# wget -O transmission http://pavel.kuzub.com/scripts/transmission
# chmod a+x transmission
# transmission install
#
# Cron Jobs:
# #Transmission Queue Control every 5 minutes
# */5 * * * * /opt/bin/transmission queue
#
# #Transmission Restart every 2 hours as stability workaround
# 0 */2 * * * /opt/bin/transmission restart
#
USAGETEXT="Usage: $0 (start|stop|startup|startup on|startup off|status|restart|edit|watch|queue|install|uninstall|usage)"
#
start_time=$(date +%s)
# Change below folders to match your own system folders
# Log file for DEBUG
LOGFILE=/DataVolume/Download/transmission-daemon.log
# Move downloaded and fully seeded files to Completed folder
COMPLETED_DATA_FOLDER=/DataVolume/Download/!Completed
# Location of init.d folder on your system
INITDIR=/etc/init.d
# If using authentication specify Login and Pass
REMOTE_LOGIN=admin
REMOTE_PASS=password
# Maximum simultaneous torrents in Download state
MAX_TO_DOWNLOAD=3
# Maximum simultaneous torrents in Seed state
MAX_TO_SEED=25
# Enable moving torrent data after reaching Max Ratio. Value 1/0
MOVE_AFTER_COMPLETE=1
# Enable dump to log file. Value 1/0
DEBUG=1
#
# More system related options
EDITOR=vi
prefix="/opt"
INITSTARTSCRIPT=${INITDIR}/S90transmission
INITSTOPSCRIPT=${INITDIR}/K10transmission
PATH=${prefix}/bin:${prefix}/sbin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMONNAME=transmission-daemon
REMOTENAME=transmission-remote
DAEMON=${prefix}/bin/${DAEMONNAME}
REMOTE=${prefix}/bin/${REMOTENAME}
CONFIGDIR=${HOME}/.config/transmission-daemon
CONFIGFILE=${CONFIGDIR}/settings.json
NOSTARTUPFILE=${CONFIGDIR}/.transmissionNoStartup
test -x $DAEMON || exit 0
if [ -z "$1" ] ; then
case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
S??*) rc="startup" ;;
K??*) rc="stop" ;;
*) rc="usage" ;;
esac
else
rc="$1"
fi
myecho() {
curtime=`date +"[%H:%M:%S:000]"`
echo "$curtime $1"
if [ ${DEBUG} -eq 1 ]; then
echo "$curtime $1" >>$LOGFILE
fi
}
start() {
if [ -n "`pidof $DAEMONNAME`" ]; then
myecho "$DAEMONNAME is already running."
else
cd ${CONFIGDIR}/blocklists
if [ $? -eq 0 ]; then
find . -mtime +4 -type f -name level1 -exec rm {} \;
if [ ! -f ${CONFIGDIR}/blocklists/level1 ]; then
# update blocklist
myecho "Updating blocklist, ~4MB"
wget -q -O level1.gz http://download.m0k.org/transmission/files/level1.gz
if [ -f level1.gz ]; then
gunzip level1.gz
if [ $? -eq 0 ]; then
chmod go+r level1
else
rm -f level1*
fi
fi
fi
cd - 2>&1 >/dev/null
fi
myecho "Starting Torrent client: $DAEMONNAME"
EVENT_NOEPOLL=1; export EVENT_NOEPOLL
EVENT_NOEVPORT=1; export EVENT_NOEVPORT
EVENT_NODEVPOLL=1; export EVENT_NODEVPOLL
nice -n 19 $DAEMON -g ${CONFIGDIR} -e $LOGFILE
fi
}
watch() {
if [ -n "`pidof $DAEMONNAME`" ]; then
echo "Watch: $DAEMONNAME is running"
$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -si > /dev/null || killall -HUP $DAEMONNAME
else
start
fi
}
stop() {
if [ -n "`pidof $DAEMONNAME`" ]; then
myecho "Stopping Torrent client: $DAEMONNAME"
n=1
while true; do
killall $DAEMONNAME 2> /dev/null
sleep 1
[ ! -n "`pidof $DAEMONNAME`" ] && break
myecho "Waiting for another 5 seconds. Attempt $n"
sleep 4
[ $n -gt 20 ] && break
let n+=1
done
n=1
while true; do
killall -9 $DAEMONNAME 2> /dev/null
sleep 1
[ ! -n "`pidof $DAEMONNAME`" ] && break
sleep 2
[ $n -gt 10 ] && break
let n+=1
done
if [ -n "`pidof $DAEMONNAME`" ]; then
myecho "Termination of $DAEMONNAME was not successful, it keeps running"
else
myecho "$DAEMONNAME terminated successfuly"
fi
else
myecho "$DAEMONNAME already stopped"
fi
}
startup() {
if [[ -e $NOSTARTUPFILE ]]; then
echo "Transmission Daemon disabled on startup"
else
start
fi
}
startup_on() {
echo "Enabling Transmission Daemon on startup"
if [[ -e $NOSTARTUPFILE ]]; then
rm "$NOSTARTUPFILE"
fi
}
startup_off() {
echo "Disabling Transmission Daemon on startup"
touch "$NOSTARTUPFILE"
}
edit() {
MD5BEFORE=`cat "$CONFIGFILE" | md5sum`
myecho "Starting $EDITOR to edit ${CONFIGFILE}"
$EDITOR "${CONFIGFILE}"
MD5AFTER=`cat "$CONFIGFILE" | md5sum`
if [ "$MD5BEFORE" = "$MD5AFTER" ]; then
echo "No changes to config file"
else
myecho "Sending SIGHUP to Transmission to reload config file"
killall -HUP $DAEMONNAME 2> /dev/null
fi
}
status() {
$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -l
}
install() {
echo "Creating link: $INITSTARTSCRIPT"
ln -s -f "$0" "$INITSTARTSCRIPT"
chmod a+x "$INITSTARTSCRIPT"
echo "Creating link: $INITSTOPSCRIPT"
ln -s -f "$0" "$INITSTOPSCRIPT"
chmod a+x "$INITSTOPSCRIPT"
}
uninstall() {
echo "Removing: $INITSTARTSCRIPT"
rm "$INITSTARTSCRIPT"
echo "Removing: $INITSTOPSCRIPT"
rm "$INITSTOPSCRIPT"
}
queue() {
MAX_RATIO=$(cat "${CONFIGFILE}" | grep \"ratio-limit\": | tr -dc '[.0-9]')
JSON=`$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -b -l 2>&1 | grep "{\"arguments\":{\"torrents\":"`
#Build data matrix in internal format. It will be used for all further references
torrentStatus=`echo $JSON | awk '{n=split($0,a,"["); text=""; for (i=2;i<=n;i++) text=text a[i]; print text}
' | awk '{n=split($0,a,"]"); text=""; for (i=1;i<n;i++) text=text a[i]; print text}
' | sed -e 's/[{]//g' | awk -v max_ratio="$MAX_RATIO" '{n=split($0,a,"\},"); for (i=1;i<=n;i++) {
id="";left="";total="";status="";ratio="";percent=""
m=split(a[i],b,",")
for (t=1;t<=m;t++) {
o=split(b[t],c,":")
if (c[1]=="\"id\"")id=c[2]
if (c[1]=="\"leftUntilDone\"")left=c[2]
if (c[1]=="\"sizeWhenDone\"")total=c[2]
if (c[1]=="\"status\""){
if (c[2]=="1") status="CHECK_WAIT"
else if (c[2]=="2") status="CHECK"
else if (c[2]=="4") status="DOWNLOAD"
else if (c[2]=="8") status="SEED"
else if (c[2]=="16") status="STOPPED"
else status=c[2]
}
if (c[1]=="\"uploadRatio\"")ratio=c[2]
}
if (left==0) percent=100
else if (total>0){
percent=((total-left)/total)*100
split(percent,percent_split,".")
percent=percent_split[1]
} else percent=000
for (l=length(percent);l<3;l++) percent="0"percent
if(ratio>=max_ratio) ratio_status="OVER"
else ratio_status="UNDER"
print id" "percent"% "status" "ratio" "ratio_status
}}' | sed -e 's/[}]//g'`
#Make status calculations
count_download=`echo "$torrentStatus" | grep -e 'DOWNLOAD' -e 'CHECK' | wc -l`
count_seed=`echo "$torrentStatus" | grep SEED | wc -l`
count_stopped=`echo "$torrentStatus" | grep STOPPED | wc -l`
count_stopped_100=`echo "$torrentStatus" | grep STOPPED | grep 100% | wc -l`
if [ $MAX_TO_DOWNLOAD -gt $count_download ]
then count_download_to_start=$(($MAX_TO_DOWNLOAD - $count_download))
else count_download_to_start=0
fi
if [ $MAX_TO_SEED -gt $count_seed ]
then count_seed_to_start=$(($MAX_TO_SEED - $count_seed))
else count_seed_to_start=0
fi
#Echo data for user
echo "$torrentStatus"
echo "Max Ratio: $MAX_RATIO"
echo "Downloading: $count_download (max $MAX_TO_DOWNLOAD)(to start $count_download_to_start)"
echo "Seeding: $count_seed (max $MAX_TO_SEED)(to start $count_seed_to_start)"
echo "Stopped: $count_stopped and Stopped on 100: $count_stopped_100"
#Logic part
downloads_to_stop=`echo "$torrentStatus" | grep DOWNLOAD | sort -r -k 2,2 | tail -n +$((MAX_TO_DOWNLOAD+1)) | \
awk '{print $1;}' | tr '\n' ',' | sed 's/\(.*\)./\1/'`
seeds_to_stop=`echo "$torrentStatus" | grep SEED | sort -r -k 4,4 | tail -n +$((MAX_TO_SEED+1)) | \
awk '{print $1;}' | tr '\n' ',' | sed 's/\(.*\)./\1/'`
#check length and assign delimeter
if [ ${#downloads_to_stop} -gt 0 -a ${#seeds_to_stop} -gt 0 ]
then delim_to_stop=","
else delim_to_stop=""
fi
ids_to_stop="$downloads_to_stop$delim_to_stop$seeds_to_stop"
echo "Torrent IDs to Stop: $ids_to_stop"
downloads_to_start=`echo "$torrentStatus" | grep STOPPED | grep -v 100% | sort -k 2,2 | tail -n $((count_download_to_start)) | \
awk '{print $1;}' | tr '\n' ',' | sed 's/\(.*\)./\1/'`
seeds_to_start=`echo "$torrentStatus" | grep STOPPED | grep 100% | grep UNDER | sort -k 4,4 | tail -n $((count_seed_to_start)) | \
awk '{print $1;}' | tr '\n' ',' | sed 's/\(.*\)./\1/'`
#check length and assign delimeter
if [ ${#downloads_to_start} -gt 0 -a ${#seeds_to_start} -gt 0 ]
then delim_to_start=","
else delim_to_start=""
fi
ids_to_start="$downloads_to_start$delim_to_start$seeds_to_start"
echo "Torrent IDs to Start: $ids_to_start"
ids_to_remove_one_per_line=`echo "$torrentStatus" | grep -e 'STOPPED' -e 'SEED' | grep 100% | grep OVER | \
awk '{print $1;}'`
ids_to_remove=`echo "$torrentStatus" | grep -e 'STOPPED' -e 'SEED' | grep 100% | grep OVER | \
awk '{print $1;}' | tr '\n' ',' | sed 's/\(.*\)./\1/'`
echo "Torrent IDs to Remove: $ids_to_remove"
#Execute Actions
if [ ${#ids_to_stop} -gt 0 ]; then
myecho "Stopping torrents: $ids_to_stop"
$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -t $ids_to_stop --stop
fi
if [ ${#ids_to_start} -gt 0 ]; then
myecho "Starting torrents: $ids_to_start"
$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -t $ids_to_start --start
fi
if [ ${#ids_to_remove} -gt 0 -a ${MOVE_AFTER_COMPLETE} -eq 1 ]; then
myecho "Moving torrents: $ids_to_remove"
$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -t $ids_to_remove --move "$COMPLETED_DATA_FOLDER"
echo "Removing torrents that are in Completed folder..."
for i in $ids_to_remove_one_per_line; do
this_torrent_data_location=`$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -t $i -i | awk '/Location/'|sed -e 's/ Location: //'`
echo "Location of torrent $i is: $this_torrent_data_location"
if [ "$this_torrent_data_location" = "$COMPLETED_DATA_FOLDER" ]; then
myecho "Removing Torrent $i from the list..."
$REMOTE -n $REMOTE_LOGIN:$REMOTE_PASS -t $i --remove
fi
done
fi
}
case "$rc" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
startup)
if [ "$2" = "on" ]; then startup_on
elif [ "$2" = "off" ]; then startup_off
else startup
fi
;;
edit)
edit
;;
status)
status
;;
watch)
watch
;;
queue)
watch
queue
;;
install)
install
;;
uninstall)
uninstall
;;
*)
echo ${USAGETEXT}
;;
esac
finish_time=$(date +%s)
echo "Execution Time: $((finish_time - start_time)) secs."
exit 0