Reply To: Playlists and Podcasts

#13266
Anonymous
Inactive

Hi

I’m using a more heavily modified version of the BashPodder script on my DS 107e. I’m including it below should anyone be interested.

Features

– Keep variable amounts of podcasts per feed
– Builds m3u playlists and tells FireFly to rescan
– Modify ID3 tags on podcasts that set them badly
– Seems reasonably robust on my 107

I’m very new to FireFly (4 days!) but I’m already rather interested in integrating this more tightly but I’m still figuring out how FireFly works to allow me to do that.

Cheers

Martin


#!/bin/ash
# By Linc 10/1/2004
# Find the latest script at http://linc.homeunix.org:8080/scripts/bashpodder
# Last revision 07/01/2005 - Many Contributers!
# If you use this and have made improvements or have comments
# drop me an email at linc dot fessenden at gmail dot com
# I'd appreciate it!
#
# Modified by Martin Croome - Many mods but still largly inspired by the above
# Requires curl, xsltproc, wget and taged (which coredumps too often)
# Talks to firefly if it's present
# Tested on a Synology 107e with FireFly

# Directories and files
LOCKFILE="/tmp/lock.newpodder"
DATADIR="/volume1/music/podcasts"
LOGFILE="/opt/etc/podcast.history"
PODCASTCONF="/opt/etc/podcast.conf"
TEMPDIR="/tmp"
PLAYLISTS="Playlists"
M3ULASTDAY="Podcasts (Today).m3u"
M3UALL="Podcasts (All).m3u"

# PODCASTCONF file format is made up of lines like this:
#
# podcast_url|dir|count|fixid3
# where
# podcast_url is the URL for the podcast
# dir is the directory under DATADIR where the podcast will be stored
# count is the number of "episodes" to keep. -1 keeps all of them.
# fixid3 rewrites various ID3 headers with information from the RSS
# This uses taged which barfs on some files. I use this to
# rewrite the tags on some podcasts that don't set them well.
#
# e.g. http://podcast.rtl.fr/onrefaitlemonde.xml|OnRefaitLeMonde|2|1
# http://downloads.bbc.co.uk/podcasts/radio4/fooc/rss.xml|FOOC|2|0

TEMPLOG="${TEMPDIR}/newpodder.$$.log"
TEMPXSLT="${TEMPDIR}/newpodder.$$.xsl"

# External programs. NOTE: Redefined further below for debug
TAGED="/opt/bin/taged -n"
XSLTPROC="/opt/bin/xsltproc"
WGET="/opt/bin/wget -q"
CURL="/opt/bin/curl -s"

# Parameter defaults
CATCHUP=0
VERBOSE=0
FIREFLYPASS="xxxxxx"
MAXTITLELENGTH=50
FORCE=""
LOGREDIRECT=""
QUIET=0

pdebug()
{
if [ $VERBOSE -eq "1" ]
then
if [ "X${LOGREDIRECT}" == "X" ]
then
echo "DEBUG: $*"
else
echo "DEBUG: $*" >> $LOGREDIRECT
fi
fi
}

plog()
{
if [ $QUIET -eq "0" ]
then
if [ "X${LOGREDIRECT}" == "X" ]
then
echo "`date`: $*"
else
echo "`date`: $*" >> $LOGREDIRECT
fi
fi
}

RDATE=""
dateformat()
{
RDATE=`echo $1 | awk '/(^Mon,)|(^Tue,)|(^Wed,)|(^Thu,)|(^Fri,)|(^Sat,)|(^Sun,)/ { printf("%4d %3s %2d", $4, $3, $2); }' -`
if [ ${#RDATE} -eq 0 ]
then
RDATE=$1
fi
return 0
}

cleanup()
{
pdebug "Cleaning up"
rm -f "${TEMPDIR}/newpodder.*"
}

instance_lock()
{
TEMPFILE="${TEMPDIR}/bashpodder.temp.$$"
echo $$ > $TEMPFILE ||
{
echo "Could not create lock file"
return 1
}
trap "rm -f $TEMPFILE; exit" SIGINT SIGTERM
ln $TEMPFILE $LOCKFILE 2> /dev/null &&
{
rm -f $TEMPFILE
trap "cleanup; rm -f $LOCKFILE; exit" SIGINT SIGTERM
return 0
}
kill -0 `cat $LOCKFILE` 2> /dev/null &&
{
rm -f $TEMPFILE
return 1
}
pdebug "Removing stale lock file"
rm -f $LOCKFILE
ln $TEMPFILE $LOCKFILE 2> /dev/null &&
{
rm -f $TEMPFILE
trap "cleanup; rm -f $LOCKFILE; exit" SIGINT SIGTERM
return 0
}
rm -f $TEMPFILE
trap - SIGINT SIGTERM
return 1
}

listfeeds()
{
echo "FixID3 Count Poddir"
echo "


"
while read line
do
# Ignore comment lines
if [ `expr match "$line" ' *#'` -gt 0 ]
then
continue
fi

# Split up parameters
OIFS="$IFS"
IFS='|'
set -- $(echo "$line")
IFS="$OIFS"
echo "$4 $3 $2"
done < $PODCASTCONF
}

generatem3u()
{
plog "Creating playlists"
playlistdir="${DATADIR}/${PLAYLISTS}"
if ! [ -d "${playlistdir}" ]
then
mkdir $playlistdir
fi

# Use this to generate relative paths. I don't want to cd around.
escapeddir=$(echo $DATADIR | sed 's///\//g')
# Create a playlist of the files added in the last 24 hours
# Busybox find doesn't support | in wildcard
find "$DATADIR" -mtime -1 -print -name '*' |
egrep ".*.(mp3|m4a|m4p|ogg|flac)$" |
sed "s/${escapeddir}/../" > "${playlistdir}/${M3ULASTDAY}"
# Create a playlist of all the files
find "$DATADIR" -print -name '*' |
egrep ".*.(mp3|m4a|m4p|ogg|flac)$" |
sed "s/${escapeddir}/../" > "${playlistdir}/${M3UALL}"
}

# Make script crontab friendly:
cd $(dirname $0)

#Check if another instance is already running
instance_lock || {
echo "An instance of $0 is already running"; exit 1
}

# Get options
while getopts lv:cp:m:f:L:qg o
do case "$o" in
v)
case "$OPTARG" in
1)
VERBOSE=1
;;
2)
VERBOSE=1
# Debug versions of external programs
XSLTPROC="/opt/bin/xsltproc -v"
WGET="/opt/bin/wget -v"
TAGED="/opt/bin/taged -v"
CURL="/opt/bin/curl -v"
;;
esac
;;
c)
CATCHUP=1
;;
p)
FIREFLYPASS="$OPTARG"
;;
m)
MAXTITLELENGTH="$OPTARG"
;;
l)
listfeeds
cleanup
exit 0
;;
f)
FORCE="$OPTARG"
;;
L)
LOGREDIRECT="${OPTARG}"
if ! [ -e "$LOGREDIRECT" ]
then
touch $LOGREDIRECT
fi
;;
q)
QUIET=1
;;
g)
generatem3u
exit 0
;;
[?])
cat >&2 <<ENDOFUSAGE

Usage: $0 [-cvpmlfLq]

-c Catchup. Don't download just log.
-f [poddir] Force download of a specific podcast. Regex on
podcast directory.
-g Just generate m3u files.
-l List configured podcasts.
-L [logfile] Log to file
-m [Length] Limit title length when setting ID3 title.
-p [password] Password for Firefly server.
-q Quiet. Switch off all messages
-v [1|2] Verbose. 2 levels. 2 is highest.
ENDOFUSAGE
exit 1
;;
esac
done

# Create XSLT for processing RSS feeds
cat > $TEMPXSLT <<ENDOFXSLT























ENDOFXSLT

# DATADIR is the root for podacast directories
# Check for and create DATADIR if necessary:
if test ! -d $DATADIR
then
pdebug "Creating data directory"
mkdir $DATADIR
fi

# If this is the first time, touch LOGFILE to shut up the grep below.
if ! [ -e $LOGFILE ]
then
touch $LOGFILE
fi

# Read the PODCASTCONF file.
plog "Start run"
while read line
do

# Ignore comment lines
if [ `expr match "$line" ' *#'` -gt 0 ]
then
pdebug "Comment ${line}"
continue
fi


# Split up parameters
OIFS="$IFS"
IFS='|'
set -- $(echo "$line")
IFS="$OIFS"
podcast=$1
poddir=$2
count=$3
fixid3=$4
pdebug "Podcast=${podcast} Poddir=${poddir} Cnt=${count} FixID3=${fixid3}"

# If forcing match regex
if [ "X${FORCE}" != "X" ] && [ `expr match "${poddir}" "${FORCE}"` -eq 0 ]
then
pdebug "Skip ${poddir}"
continue
fi

# count 0 means that no files will be downloaded
# count -1 means that all files will be downloaded
# count n>0 means that the n latest files will be kept or downloaded
if [ "$CATCHUP" -eq "1" ]
then
count=0
fi

# Create feed directory
feeddir="${DATADIR}/${poddir}"
pdebug "Feeddir=${feeddir}"
if test ! -d $feeddir
then
mkdir $feeddir
pdebug "Creating ${feeddir}"
fi

# Download and process RSS

# Order of stream produced by XSLT
# title
# ttl
# Then for each item
# pubDate
# description
# url

pdebug "Now parse items"
# Readstate states
# 0 - Read feedname
# 1 - Read TTL
# 5 - Read pubDate
# 6 - Read description
# 9 - Read url and process
readstate=0
$WGET $podcast -O - | $XSLTPROC $TEMPXSLT - 2> /dev/null | while read feed
do
pdebug "State ${readstate} Feed ${feed}"
# 0-4 states for header
case "$readstate" in
0)
feedname=$feed
readstate=1
continue
;;
1)
ttl=$feed
readstate=5
pdebug "Feedname=${feedname} TTL=${ttl}"
continue
;;
5)
pubDate=$feed
readstate=6
continue
;;
6)
description=$feed
readstate=9
continue
;;
9)
url=$feed
pdebug "Pubdate=${pubDate} Url=${url}"
realurl=`curl -s -I -L -w %{url_effective} --url "$url" | tail -n 1`
filename=`echo "$realurl" | awk -F / '{print $NF}' | sed -e "s/%20/ /g" -e "s/%27/'/g" -e "s/%23/#/g" | awk -F ? '{print $1}'`
filepath="${poddir}/${filename}"

# remove older files
if [ "$count" -eq "0" ]
then
if [ -e $feeddir/$filename ]
then
pdebug "Removing file ${filepath}"
rm $feeddir/$filename
fi
fi

# If file has not previously been processed
# NOTE: If the number of files downloaded is increased on a feed
# this will prevent older files being downloaded
if [ "X$FORCE" != "X" ] || ! grep "${filepath}" $LOGFILE > /dev/null
then
pdebug "${filepath} not found in log or forced"
if [ "$count" -eq "0" ]
then
pdebug "Log file ${filepath}"
echo "${filepath}" >> $TEMPLOG
else
pdebug "Download file ${filepath}"
# Try to resume if we can
if { $WGET -t 1 -N -c $realurl -O $feeddir/$filename || $WGET -t 1 -N $realurl -O $feeddir/$filename; } && [ -e ${feeddir}/${filename} ]
then
plog "Downloaded ${filepath}"
echo "${filepath}" >> $TEMPLOG

# Now check if we need to fix the ID3 tag
if [ "$fixid3" -eq "1" ]
then
dateformat "${pubDate}"
newtitle="${feedname} ${RDATE}"

if [ "${#newtitle}" -gt "$MAXTITLELENGTH" ]
then
titlelength=`expr $MAXTITLELENGTH - ${#RDATE} - 1`
newtitle="`expr substr "$feedname" 1 $titlelength` ${RDATE}"
fi

pdebug "Change tags title=${newtitle}"

# Note: Update to ID3v2 to avoid ID3v1 core dump with tags longer than 30 chars
# -u & -2 does not seem to work
$TAGED -u -2 -A "${feedname}" -t "${newtitle}" -g "Podcast" -c "${description}" $feeddir/$filename || plog "ERROR: ${feedname} ID3 Edit failed"
fi
else
plog "ERROR: Failed to download ${filepath}"
fi

fi
fi

# Count the first n files that exist
if [ "$count" -gt "0" ] && [ -e ${feeddir}/${filename} ]
then
count=`expr $count - 1`
pdebug "Count = ${count}"
fi

readstate=5
continue
;;
esac
done
done < $PODCASTCONF

# Move dynamically created log file to permanent log file:
pdebug "Processing log file"
cat $LOGFILE >> $TEMPLOG
sort $TEMPLOG | uniq > $LOGFILE
rm $TEMPLOG

# Generate playlists
generatem3u

# Force firefly to rescan
pdebug "Asking FireFly to rescan"
$WGET --delete-after "http://localhost:${FIREFLYPASS}@localhost:3689/config-update.html?action=rescan" || plog "ERROR: Unable to notify FireFly"

#Release lock
cleanup
plog "Run finished"
rm -f $LOCKFILE

[/code]