1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
|
#!/bin/bash
# Podracer v1.4
# By Lorenzo Taylor
#
# Podracer is based on Bashpodder by Linc Fessenden of The Linux Link Tech Show.
# Bashpodder can be found at:
# http://linc.homeunix.org:8080/scripts/bashpodder
# See the CREDITS file for more information about all the great work that helped
# make Podracer the winner of the pod race.
# This is the name, version and configuration files for this program
progname=podracer
version=1.4
releasedate='19 February 2006'
progdir="$HOME/.$progname"
systemconf=/etc/$progname.conf
userconf=$progdir/$progname.conf
# We respond to version queries here, since we don't want to do anything but
# display the program name and version number if -v, -version, --version or
# version is specified on the command line.
case $1 in -v|-version|--version|version)
echo $progname version $version
echo Released $releasedate
echo by Lorenzo Taylor \<lorenzo@taylor.homelinux.net\>
exit 0
esac
# All default configuration values are set here to avoid forcing the user to
# create a configuration file. However, an example showing the defaults is
# provided for those of us who think outside the box.
# poddir is the directory where you want podcasts to be saved
# Directory names may be created dynamically based on time, date and many other
# possibilities. Directory names based on feeds are listed in the subscriptions
# file.
poddir=$HOME/podcasts/$(date +%Y-%m-%d)
# incoming is the directory where incoming podcasts are saved before completion
incoming=$progdir/part
# Subscription file - this holds a list of RSS feeds for podcasts
subscriptions=$progdir/subscriptions
# The location of the sample subscription file that will be copied to a user's
# directory when running Podracer for the first time
sample=/usr/share/$progname/sample.subscriptions
# A temporary place to store subscriptions. It allows for comments in the
# original subscription file.
tempsub=$progdir/tempsub
# Set to the amount of time in seconds you would like to seed torrents, or if
# you don't have the upload bandwidth to seed, set to 0. I personally recommend
# seeding for at least an hour, (3600 seconds), which is the default.
seedtime=3600
# Set maximum upload speed in kB/s when seeding - defaults to 0 (no limit)
uploadrate=0
# Directory where torrents are stored
torrentdir=$progdir/torrents
# The number of times Podracer will retry a download if an error occurs
retries=2
# Set the minimum download speed in bytes per second for non-torrent downloads.
# Podracer will abort a download to prevent hangs if the connection drops below
# this speed for the time given below in the downloadtimeout option.
minspeed=1
# set the time in seconds after which to abort a download if the connection
# speed drops below the above minspeed value.
downloadtimeout=60
# Logfiles
podcastlog=$progdir/podcast.log
temppodcastlog=$progdir/temp.log
# Many podcasters check their logs to determine how many people use a podcast
# aggregator vs. directly downloading the shows. This setting will allow
# Podracer to be recognized as a true podcast agregator.
longname="$progname v$version; $(uname -o); $(uname -m)"
# set to a filename to create an m3u playlist which will be saved in $poddir
# If no filename is set, an m3u playlist will not be created.
m3u=$(date +%Y-%m-%d)-podcasts.m3u
# Override defaults with config file settings
test -e "$systemconf" && source "$systemconf"
test -e "$userconf" && source "$userconf"
# Check for and create progdir if necessary:
test -d "$progdir" || mkdir -p "$progdir"
# A subscription file is required in order to receive podcasts
# A sample will be created on user request
if test ! -e "$subscriptions"
then
echo You haven\'t subscribed to any podcasts.
until [[ $copysample == [YyNn] ]]
do
echo -n "Would you like a sample subscription file? [Y/N] "
read -n 1 copysample
echo
done
case $copysample in
N|n)
echo Please create a file called $subscriptions and add the feeds for the
echo podcasts you want to receive. You may also add comments preceeded by
echo hash marks, "(#)" to make a note of the name of each podcast or any other
echo information you may wish to add to your subscription file.
exit 0
;;
Y|y)
if test -e $sample
then
cp $sample $subscriptions
echo The sample subscription file has been saved to
echo $subscriptions.
echo -n Please wait while\
case $1 in
-c | -catchup | --catchup | catchup)
echo the log is updated
;;
*)
echo your podcasts are downloaded.
esac
else
echo The sample subscription file is missing. This indicates either improper
echo installation or misconfiguration. Please contact your system administrator to
echo fix this problem.
exit 2
fi
esac
fi
# Check for and create poddir if necessary:
test -d "$poddir" || mkdir -p "$poddir"
# Check for and create incoming directory
test -d "$incoming" || mkdir -p "$incoming"
# Each user may only run one copy of Podracer at a time. It will fail if it is
# already running for the current user.
test -e "$tempsub" && exit 1
touch "$tempsub"
# Delete any temp file when interrupted or terminated:
trap 'echo Signal caught: exiting.;rm -f "$temppodcastlog";rm -f $tempsub;exit 3' hup int quit term
# If catching up, remove podcast log. This keeps the log's size manageable by
# removing unnecessary old entries.
case $1 in -c|-catchup|--catchup|catchup)
rm $podcastlog
esac
# Check for and create podcast log to avoid spurious errors
test -e "$podcastlog" || touch "$podcastlog"
# Read the subscription file and curl or catch up any url not already in the podcast log:
grep -E "^(http|ftp|/)" "$subscriptions" > "$tempsub"
while read podcast feeddir
do
test -z "$feeddir" -o -d "$poddir/$feeddir" || mkdir -p "$poddir/$feeddir"
file=$(curl -A "$longname" -s -L $podcast | tr '\r' '\n' | tr \' \" | sed 's/>/>\n/g' | sed -n 's/.*url="\([^"]*\)".*/\1/p' | sed 's/&/&/g')
for url in $file
do
if ! grep "$url" $podcastlog > /dev/null
then
#Catch up on new subscriptions or after a vacationso every podcast isn't downloaded
case $1 in -c | -catchup | --catchup | catchup)
echo "$url" >> "$podcastlog"
;;
*)
#lets get the real url in the case of dynamically generated feeds like lugradio
realurl=`curl -s -I -L -w %{url_effective} --url "$url" | tail -n 1`
#we'll need filename to allow us to move the podcast into poddir[/feeddir]
filename=`echo "$realurl" | awk -F / '{print $NF}' | sed -e "s/%20/ /g" -e "s/%27/'/g" -e "s/%23/#/g" | awk '-F?' '{print $1}'`
#file is a torrent so use the internal torrent downloader
if [ "$(echo $filename | grep .torrent)" ]
then
gottorrent='Yes'
#We only need to create torrentdir if we
#have never downloaded a torrent file or
#if torrentdir has been removed.
test -d $torrentdir || mkdir -p $torrentdir
curl -A "$longname" -s -y $downloadtimeout -Y $minspeed --retry $retries -L "$realurl" -o "$incoming/$filename"
#try with and without .py extension
if [ `which btshowmetainfo` ];
then
audiofilename=`btshowmetainfo "$incoming/$filename" | grep name | sed 's/file name.....: //'`
else
audiofilename=`btshowmetainfo.py "$incoming/$filename" | grep name | sed 's/file name.....: //'`
fi
#the embedded python code will download the file referenced in the torrent and then
#return control to Podracer
python - --saveas "$incoming/$audiofilename" "$incoming/$filename" << "END" > /dev/null
########## Begin python code ##########
# Written by Bram Cohen
# Modified by Lorenzo Taylor for use in Podracer
# Uses code by Huw Lynes <huw@hlynhes.com>
import sys
from BitTorrent.download import download
from threading import Event
from os.path import abspath
from sys import argv, stdout
from cStringIO import StringIO
from string import find
from time import time
def hours(n):
if n == -1:
return '<unknown>'
if n == 0:
return 'complete!'
n = long(n)
h, r = divmod(n, 60 * 60)
m, sec = divmod(r, 60)
if h > 1000000:
return '<unknown>'
if h > 0:
return '%d hour %02d min %02d sec' % (h, m, sec)
else:
return '%d min %02d sec' % (m, sec)
class HeadlessDisplayer:
def __init__(self):
self.done = False
self.file = ''
self.percentDone = ''
self.timeEst = ''
self.downloadTo = ''
self.downRate = ''
self.upRate = ''
self.downTotal = ''
self.upTotal = ''
self.errors = []
self.last_update_time = 0
def finished(self):
self.done = True
self.percentDone = '100'
self.timeEst = 'Download Succeeded!'
self.downRate = ''
self.display({})
self.errors.append("sys.exit(0)")
sys.exit(0)
def failed(self):
self.done = True
self.percentDone = '0'
self.timeEst = 'Download Failed!'
self.downRate = ''
self.display({})
def error(self, errormsg):
noneerror = 0
self.errors.append(errormsg)
for errorstr in self.errors:
self.display({})
if find(errorstr, "sys.exit(0)") != -1:
noneerror = 1
if noneerror == 1 :
sys.exit(0)
else:
self.display({})
sys.exit(1)
def display(self, dict):
if self.last_update_time + 0.1 > time() and dict.get('fractionDone') not in (0.0, 1.0) and not dict.has_key('activity'):
return
self.last_update_time = time()
if dict.has_key('spew'):
print_spew(dict['spew'])
if dict.has_key('fractionDone'):
self.percentDone = str(float(int(dict['fractionDone'] * 1000)) / 10)
if dict.has_key('timeEst'):
self.timeEst = hours(dict['timeEst'])
if dict.has_key('activity') and not self.done:
self.timeEst = dict['activity']
if dict.has_key('downRate'):
self.downRate = '%.2f K/s' % (float(dict['downRate']) / (1 << 10))
if dict.has_key('upRate'):
self.upRate = '%.2f K/s' % (float(dict['upRate']) / (1 << 10))
if dict.has_key('upTotal'):
self.upTotal = '%.1f M' % (dict['upTotal'])
if dict.has_key('downTotal'):
self.downTotal = '%.1f M' % (dict['downTotal'])
print '\n\n'
for err in self.errors:
print 'ERROR: ' + err + '\n'
print 'saving: ', self.file
print 'percent done: ', self.percentDone
print 'time left: ', self.timeEst
print 'download to: ', self.downloadTo
if self.downRate != '':
print 'download rate: ', self.downRate
if self.upRate != '':
print 'upload rate: ', self.upRate
if self.downTotal != '':
print 'download total:', self.downTotal
if self.upTotal != '':
print 'upload total: ', self.upTotal
stdout.flush()
def chooseFile(self, default, size, saveas, dir):
self.file = '%s (%.1f M)' % (default, float(size) / (1 << 20))
if saveas != '':
default = saveas
self.downloadTo = abspath(default)
return default
def newpath(self, path):
self.downloadTo = path
def print_spew(spew):
s = StringIO()
s.write('\n\n\n')
for c in spew:
s.write('%20s ' % c['ip'])
if c['initiation'] == 'local':
s.write('l')
else:
s.write('r')
rate, interested, choked = c['upload']
s.write(' %10s ' % str(int(rate)))
if c['is_optimistic_unchoke']:
s.write('*')
else:
s.write(' ')
if interested:
s.write('i')
else:
s.write(' ')
if choked:
s.write('c')
else:
s.write(' ')
rate, interested, choked, snubbed = c['download']
s.write(' %10s ' % str(int(rate)))
if interested:
s.write('i')
else:
s.write(' ')
if choked:
s.write('c')
else:
s.write(' ')
if snubbed:
s.write('s')
else:
s.write(' ')
s.write('\n')
print s.getvalue()
def run(params):
cols = 80
h = HeadlessDisplayer()
download(params, h.chooseFile, h.display, h.finished, h.error, Event(), cols, h.newpath)
if not h.done:
h.failed()
if __name__ == '__main__':
run(argv[1:])
########## End python code ##########
END
#if we've succeeded add file to podcast log and move to poddir[/feeddir]
if [ $? -eq 0 ]
then
mv "$incoming/$audiofilename" "$poddir/$feeddir"
mv "$incoming/$filename" "$torrentdir"
echo "$url" >> $podcastlog
fi
#not a torrent so directly download the enclosure
else
curl -A "$longname" -s -y $downloadtimeout -Y $minspeed --retry $retries -L -C - -o "$incoming/$filename" "$realurl"
#if we succeeded in the download add the file to the log
if [ $? -eq 0 ]
then
mv "$incoming/$filename" "$poddir/$feeddir"
echo "$url" >> $podcastlog
fi
fi
esac
fi
done
test $feeddir && rmdir --ignore-fail-on-non-empty "$poddir/$feeddir"
unset feeddir
done < "$tempsub"
rm "$tempsub"
# Move dynamically created log file to permanent log file:
cat "$podcastlog" > "$temppodcastlog"
sort "$temppodcastlog" | uniq > "$podcastlog"
rm "$temppodcastlog"
# remove poddir if there are no files
if [[ -z $(ls $poddir) ]]; then
rmdir $poddir
else
# Create an m3u playlist if configured by the user
#slightly more complicated to deal with
#geeknewscentral who put their stuff in directories
if [ $m3u ]
then
(
cd "$poddir"
find . -name \*.mp3 > "$m3u"
find . -name \*.ogg >> "$m3u"
find . -name \*.m4a >> "$m3u"
find . -name \*.m4b >> "$m3u"
)
fi
fi
# Relaunch the torrents to seed. This will timeout automatically, so there is no
# need to find the process and shut it down.
if [ $seedtime -gt 0 ]
then
#we only need to do the following if we actually encountered
#some torrents
if [ $gottorrent ]
then
#Some distributions of BitTorrent remove the .py
#extention. We will try both cases.
if [ `which btlaunchmany` ]; then
#Screen has the advantage here of letting podracer
#finish normally while letting torrents seed in
#the background.
screen -d -m timeout $seedtime btlaunchmany --max_upload_rate $uploadrate --saveas "$poddir/$feeddir" "$torrentdir"
else
screen -d -m timeout $seedtime btlaunchmany.py --saveas "$poddir/$feeddir" "$torrentdir"
fi
fi
fi
|