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)