TITLE: I made a command line podcast grabber in Python
DATE: 2018-03-26
AUTHOR: John L. Godlee
====================================================================


Pypodd

I've been working on and off since around Summer last year, making
a simplistic command line podcast catching app. I had a go with
lots of other podcatchers: upodder, greg, podcatcher and others
that I can't remember, but I found them to be just a bit overblown,
and many had difficulty when RSS feeds were constructed with
certain types of formatting. So while I was on trying to learn
Python I set myself the task of writing a little app.

 [upodder]: https://github.com/m3nu/upodder
 [greg]: https://github.com/manolomartinez/greg
 [podcatcher]: https://github.com/doga/podcatcher

In the end, it only took 120 lines of code, which I think is quite
good, though it doesn't do anything fancy like let you store
databases of played episodes, it just lists podcasts through a
heirarchical menu system and lets you download them one at a time.


   #!/usr/local/opt/python/libexec/bin/python

   # Modules
   import feedparser
   import urllib
   import os
   import csv
   from progress.bar import Bar

   # Define system location variables
   destDir = os.environ['HOME'] + os.sep + "Downloads" + os.sep  #
Note trailing `os.sep`
   subsLoc = "subscriptions.txt"
   # e.g. subsLoc = os.environ['HOME'] + os.sep +
"subscriptions.txt"

   # Define progress bar class for urllib.urlretrieve()
   class dlProg:
       def get(self, url, to):
           self.p = None

           def update(blocks, bs, size):
               if not self.p:
                   self.p = Bar(to, max=size)
               else:
                   if size < 0:
                       self.p.update()
                   else:
                       self.p.goto(blocks * bs)

           urllib.urlretrieve(url, to, update)
           self.p.finish()

   # Import subscriptions list from csv containing [url, name]
   with open(subsLoc) as f:
       subs = csv.reader(f)
       subs = list(subs)

   # Split subs into URLs and titles
   urlList = [x[0] for x in subs]
   subList = [x[1] for x in subs]

   # Select a podcast to download, from list of subscriptions
   count_subs = 1
   input_exit = 1

   while input_exit != 2:  # Terminate program if input_exit == 2
       for i in subList:
           print(str(count_subs) + ") " + i)  # Print list of
podcasts - could convert to parsed feeds later
           count_subs += 1
       input_subs = input("\nChoose a podcast [1-n]: ")  # Take
input
       input_subs = int(input_subs)  # Convert input to integer
       index_subs = input_subs - 1  # For 0-base indexing

       # Parse chosen feed
       feedChooseList = []  # Create empty list
       feedChooseList.append(urlList[index_subs])  # Fill list
with URL of chosen podcast

       feedList = []  # Create empty list
       for i in feedChooseList:
           feedList.append(feedparser.parse(i))  # Fill list
parsed data from URL

       # Extract episode names from parsed feed
       nameList = []  # Create empty list
       for i in feedList[0]['entries']:  # Fill list with names of
episodes
           nameList.append(i['title'])

       # Extract episode links from parsed feed
       linkList = []  # Create empty list
       for i in feedList[0]['entries']:  # Fill list with episode
URLs
           linkList.append(i['enclosures'][0]['href'])

       # Select episode to download - most recent or other - menu 1
       count_epi_1 = 1
       optList = ['Most recent episode', 'Another episode']  #
Create a list of options

       print("\nMost recent episode: " + nameList[0] + "\n")  #
Print name of most recent episode
       for i in optList:  # Choose between most recent or other
episode
               print(str(count_epi_1) + ") " + i)
               count_epi_1 += 1
       input_epi_1 = input("\nChoose an episode to download: ")  #
Take user input
       input_epi_1 = int(input_epi_1)

       # Select an episode to download - menu 2
       count_epi_2 = 1
       count_epi_3 = 1
       len_nameList = len(nameList)

       if input_epi_1 == 1:  # Outer if statement, if user inputs
1 or 2, do stuff, otherwise, return to beginning
           print ("\nDownloading most recent episode")
           dlExt = ".mp3"
           dlName = nameList[0]
           dlFile = destDir + dlName + dlExt
           dlURL = linkList[0]
           dlProg().get(dlURL, dlFile)
           optList = ['Download more', 'Exit']  # Create a list of
options
           for i in optList:  # Give option to download more
podcasts (return to main menu or exit
               print(str(count_epi_2) + ") " + i)
               count_epi_2 += 1
           input_exit = input("\nFinished Downloading. Get more?")
           input_exit = int(input_exit)
       elif input_epi_1 == 2:
           for i in nameList:
               print(str(count_epi_2) + ") " + i)
               count_epi_2 +=1
           input_epi_2 = input("\nChoose an episode to download: ")
           input_epi_2 = int(input_epi_2) - 1
           if input_epi_2 <= len_nameList:  # Inner if statement,
If user inputs a valid episode number, do stuff, otherwise, try
again
               dlExt = ".mp3"
               dlName = nameList[input_epi_2]
               dlFile = destDir + dlName + dlExt
               dlURL = linkList[input_epi_2]
               dlProg().get(dlURL, dlFile)
               optList = ['Download more', 'Exit']  # Create a
list of options
               for i in optList:
                   print(str(count_epi_3) + ") " + i)
                   count_epi_3 += 1
               input_exit = input("\nFinished Downloading. Get
more?")
               input_exit = int(input_exit)
           else:
               raw_input("Invalid choice, press Enter to try
again...")
       else:
           raw_input("Invalid choice, press Enter to try again...")

So basically, it's just a set of if statements linked to the value
of user input variables, all wrapped in a big while loop so it
continues to return to the first level menu unless you tell it to
exit.

The structure goes like this, where each number refers to a menu
requiring user input:

   1. Choose a podcast to download
   │
   └─2. Choose an episode to download, most recent or another
     │
     ├─Most Recent
     │ │
     │ └─Downloading most recent episode
     └─Another
       │
       └─3. Choose a specific episode to download
         │
         └─Download the specific episode

 ![Pypodd
screenshot](https://johngodlee.xyz/img_full/pypodd/pypodd_scrot.png)