The Waving Flag: Podcasts, Podget, Audacious, and Linux

Friday, 7 June 2024

Podcasts, Podget, Audacious, and Linux

For over ten years I've been using gPodder to manage my subscriptions to music and hobby podcasts.  Recently, I realised that gPodder was using almost 0.25 Gb of memory all the time just to monitor podcast feeds every hour.  This seemed an excessive overhead.

To make matters worse I'd developed all sorts of different podcast habits; copying music podcasts to an archive folder etc etc.  The habits weren't onerous but I began to wonder if there was a different way to do things with less intervention on my part.

From now on this gets "techie".  So, if this isn't your thing, look away now!

If you just want the scripts discussed below download this archive.

[1] gPodder via the command line

Gpodder comes with a command line (CLI) version called gpo which is great.   I wrote a simple user script to update my feeds but I was still had to open gPodder to select and play podcasts.

Furthermore, I found that, when the script ran as a cron job, (cron is the linux scheduling tool) it failed to read my configuration files, which controlled where the files were stored, and I didn't like the default locations.

[2] Using podget with cron

Bimbling around the web I discovered podget.  It was easy to install as it's available in the standard Ubuntu repositories.

Configuring it was really easy too; there are just two files to edit.  The location of the downloaded files are set in podgetrc and the feeds in serverlist.  There's a basic tutorial online.

With serverlist I was able to create themed subfolders (Hobby, Music etc) and file each podcast accordingly.  I was also able to rename each file if necessary.  One podcast kept the same filename for each episode!

I soon had podget downloading new podcasts daily using the basic cron job given in the tutorial.

[3] Automating archiving (pod_archive.sh)

I set podget up to delete all podcast files after 31 days, but I wanted to keep some music files.  To do this I wrote a script to copy certain files to an archive subfolder.  I added a function to check for an archive subfolder for the current year and create one if one didn't exist.

#!/bin/sh

# Get current year
# -------------------
year=$(date +'%Y')

# Function: create new archive folders
# -------------------
new_folder()
{
if [ ! -d "$year" ]; then
  mkdir $year
fi
}

# Copy Music Podcasts
# -------------------
cd '/home/vexillia/Downloads/Podcasts/Music/Downtown Soulville/'
new_folder
cp *.mp3 $year

exit

This script has the added advantage that I don't have to create new archive folders every January.

[4] Combined podget & archiving script (podget.sh)

The next stage was to write a simple script to run podget followed by the archive script and to run it as a daily cron job:

#!/bin/sh

# Begin logging
# -------------------
echo "\n# -------------------\nDate: $(date '+%a %d %B, %Y%n%H:%M:%S') - Script started" >> /home/vexillia/System/scripts/log/podcast.log

# Fetch podcasts
# -------------------
podget -s &&
echo "$(date +%H:%M:%S) - PodGet run OK" >> /home/vexillia/System/scripts/log/podcast.log ||
echo "$(date +%H:%M:%S) - PodGet failed" >> /home/vexillia/System/scripts/log/podcast.log

# Archive Music Podcasts
# -------------------
/home/vexillia/System/scripts/pod_archive.sh
echo "$(date +%H:%M:%S) - Archive script run OK" >> /home/vexillia/System/scripts/log/podcast.log

exit

As you can see, I've added some lines to log each step.  You can take them out if you want but I find them helpful.

In the end I didn't write bash scripts but used the simpler Bourne shell as this causes far fewer problems when run as a cron job.

[5] Add to playlist script (pod_cronlist.sh)

The next step was to add the newly downloaded files to my media player via another script.  Podget creates a m3u playlist beginning with "New" each time it's run and finds a new podcast.  So all I had to do was import the latest file into the "Podcasts" playlist in my media player.  As I use Audacious I thought this would be easy.  It wasn't.

First I had to get the name of the latest m.3u file.  Initially, I used:

# Get filename of the latest file beginning with "New"
# -------------------
latest=$(ls -t New* | head -1)

Later I added a test to end the script if there weren't any playlists.  This use case will arise after a periodic deletion of the daily playlist files so it's worth having.

# Get filename of the latest file beginning with "New" (if exists)
# -------------------
latest=$(ls -t New* | head -1)

if [ "$latest" = "" ]; then
	echo "$(date +%H:%M:%S) - No playlists found" >> /home/vexillia/System/scripts/log/podcast.log
	exit
else
	echo "$(date +%H:%M:%S) - Playlists found" >> /home/vexillia/System/scripts/log/podcast.log
fi

The second step was to import the m3u fie into a specific Audacious playlist.  Using the CLI tool for Audacious, audtool, I could set a playlist as current but only by its number not by its name.

Therefore, a loop was required to cycle through all the playlists by number (--set-current-playlist $i); whilst checking their names (--current-playlist-name) looking for my "Podcasts" playlist; before importing the m3u file (--playlist-addurl $latest ); and breaking out of the loop.  A touch inelegant but it works and is very quick:

# Variables for playlist search
# -------------------
i=1
playlist=""
target="Podcasts"

# Loop through playlists for target
# Add file when found & break loop
# -------------------
for i in $(seq 1 100)
do
	/bin/audtool --set-current-playlist $i
        playlist=$(/bin/audtool --current-playlist-name) 
        if [ "$playlist" = "$target" ]; then
           /bin/audtool --playlist-addurl $latest 
           echo "$(date +%H:%M:%S) - Added new playlist" >> /home/vexillia/System/scripts/log/podcast.log
           break
        fi
done

The last scripting step was to avoid repeatedly importing the the same m3u file.  This arises on days where there are no new podcasts. To do that I used a plain text file stored in /var/tmp/:

# Read previous filename from file (if exists)
# -------------------
valuefile="/var/tmp/var_playlist.dat"
if [ ! -f "$valuefile" ]; then
    previous_file=""
else
    previous_file=$(cat "$valuefile")
fi

# Test for new playlist & update data file if new
# -------------------
if [ "$latest" = "$previous_file" ]; then
	echo "$(date +%H:%M:%S) - Old playlist found" >> /home/vexillia/System/scripts/log/podcast.log
	echo "$(date +%H:%M:%S) - Nothing to add" >> /home/vexillia/System/scripts/log/podcast.log
	exit
else
	echo "$(date +%H:%M:%S) - New playist found" >> /home/vexillia/System/scripts/log/podcast.log
	echo "${latest}" > "$valuefile"
fi

This code block was inserted before the playlist search loop.   With everything in place, the script now:

  • retrieved the filename of the latest m3u file;
  • retrieved the previous filename (if it exists);
  • compared the two and wrote the latest filename to a temporary file (if new);
  • cycled through my Audacious playlists; and imported any new podcasts to my "Podcasts" playlist.

The next stage was to check that the "add to playlist" script would run as a daily cron job.  Needless to say it didn't.   It ran fine as a user script but not in the restricted environment used by cron.

Long story, short.  There's a problem with audtool under cron: it needs the DBUS_SESSION_BUS_ADDRESS of the environment running the audacious process to be defined.  Eventually, I found a solution (from 2015!) and added this code to the script:

# Fix for DBUS issue - see https://is.gd/BAfOgc
# -------------------
export "$(strings /proc/"$(pidof audacious)"/environ | grep DBUS_SESSION_BUS_ADDRESS)" 
  

Don't ask me to explain what it does but it works!  The full import script is:

#!/bin/sh

# Fix for DBUS issue - see https://is.gd/BAfOgc
# -------------------
export "$(strings /proc/"$(pidof audacious)"/environ | grep DBUS_SESSION_BUS_ADDRESS)" 

# Switch to playlist dir
# -------------------
cd '/home/vexillia/Downloads/Podcasts/'

# Get filename of the latest file beginning with "New" (if exists)
# -------------------
latest=$(ls -t New* | head -1)
if [ "$latest" = "" ]; then
	echo "$(date +%H:%M:%S) - No playlists found" >> /home/vexillia/System/scripts/log/podcast.log
	exit
else
	echo "$(date +%H:%M:%S) - Playlists found" >> /home/vexillia/System/scripts/log/podcast.log
fi

# Read previous filename from file (if exists)
# -------------------
valuefile="/var/tmp/var_playlist.dat"
if [ ! -f "$valuefile" ]; then
    previous_file=""
else
    previous_file=$(cat "$valuefile")
fi

# Test for new playlist & update data file if new
# -------------------
if [ "$latest" = "$previous_file" ]; then
	echo "$(date +%H:%M:%S) - Old playlist found" >> /home/vexillia/System/scripts/log/podcast.log
	echo "$(date +%H:%M:%S) - Nothing to add" >> /home/vexillia/System/scripts/log/podcast.log
	exit
else
	echo "$(date +%H:%M:%S) - New playist found" >> /home/vexillia/System/scripts/log/podcast.log
	echo "${latest}" > "$valuefile"
fi

# Variables for playlist search
# -------------------
i=1
playlist=""
target="Podcasts"

# Loop through playlists for target
# Add file when found & break loop
# -------------------
for i in $(seq 1 100)
do
	/bin/audtool --set-current-playlist $i
        playlist=$(/bin/audtool --current-playlist-name) 
        if [ "$playlist" = "$target" ]; then
           /bin/audtool --playlist-addurl $latest 
           echo "$(date +%H:%M:%S) - Added new playlist" >> /home/vexillia/System/scripts/log/podcast.log
           break
        fi
done

exit
  

This seemed so simple at the outset but this script took a lot longer to debug than I thought.  Now it's done it's a real time saver.

[6] Final master script (podget.sh)

Finally, I added the import script to podget.sh:

#!/bin/sh

# Begin logging
# -------------------
echo "\n# -------------------\nDate: $(date '+%a %d %B, %Y%n%H:%M:%S') - Script started" >> /home/vexillia/System/scripts/log/podcast.log

# Fetch podcasts
# -------------------
podget -s &&
echo "$(date +%H:%M:%S) - PodGet run OK" >> /home/vexillia/System/scripts/log/podcast.log ||
echo "$(date +%H:%M:%S) - PodGet failed" >> /home/vexillia/System/scripts/log/podcast.log

# Archive Music Podcasts
# -------------------
/home/vexillia/System/scripts/pod_archive.sh
echo "$(date +%H:%M:%S) - Archive script run OK" >> /home/vexillia/System/scripts/log/podcast.log

# Add to Playlist
# -------------------
/home/vexillia/System/scripts/pod_cronlist.sh
echo "$(date +%H:%M:%S) - New playlist script run OK" >> /home/vexillia/System/scripts/log/podcast.log

# End logging
# -------------------
echo "$(date +%H:%M:%S) - Script ended" >> /home/vexillia/System/scripts/log/podcast.log

exit
  

The only other thing to note is that all three scripts are designed to run from the same (sub)folder.  In my case /home/vexillia/System/scripts and log to /home/vexillia/System/scripts/log.  Feel free to edit these locations.

I hope you've found this useful.  If you do, or you find an error, let me know in the comments.

2 comments :

Vexillia said...

One "quirk" (feature?) of podget that isn't obvious: podget uses the playlist files it creates to know when to delete old podcast files. If you've enabled this in podget.rc that is.

This is explained here.

I found that, as I'd deleted some older m3u files, my old podcast weren't being cleared automatically. I've manually deleted all the old files so that podget can do it for me in future.

Vexillia said...

Further "quirk", but not with podget. Some, and in my experience most, podcast hosts produce a feed in descending date order. Others use an ascending order. The latter feeds can cause problems if you limit the number of items to be downloaded.

By default podget assumes that the feed will list from the newest to oldest items. If not, you need to change the default for that feed by adding the OPT_FEED_ORDER_ASCENDING to the line in your serverlist file.

Salute The Flag

If you'd like to support this blog why not leave a comment, or buy me a beer.