Playable Show Notes

Since the beginning, the Teach Better Podcast has included pretty good show notes along with our episodes. This has mostly been the work of my cohost Edward O’Neill, who also handles all our audio production. In these notes, we provide references and links to resources we mention in our conversations. We also break every episode into 5-10 minute chunks and give you a rough sense of the content of each. As of today that these chunks are individually playable from the website!

Now, when you’re looking at the show notes for our conversation with mathematician Steven Strogatz and his discussion of eigenvectors at 52:27 piques your interest, you can immediately play it. And when you want to know how Don Kagan was inspired by Otto von Bismarck at 44:58 (one of my all time favorite moments), you just press the play button. And you can now go straight to the part of our conversation with Laurie Santos (9:32) where she talks about teaching “Sex, Evolution, and Human Nature” in Yale’s Battell Chapel. I’ve had great fun digging around in our old episodes, and I encourage you to try out this new way to interact with our archive. And keep in mind that we’re not done–This is just the first step toward truly unlocking our podcast archive.

Most readers of this site won’t be interested in the implementation details, but I’m going to provide some in hopes that other podcasts will be inspired to provide the same feature. The first step was to put all our show notes into a standardized format that we can automatically translate into HTML. We’ve chosen to use YAML as it is easy to create in a text editor and process with code (in our case Ruby). Here’s the beginning of the YAML for our conversation with Steven Strogatz:

episode: 45
podcast: tbp-episode-45.mp3
chunks:
    - start: 0:00:00
      end: 0:00:42
      tags:
      description: |
           Intro

    - start: 0:0:42
      end: 0:01:20
      tags:
      description: |
           Welcome Steven!

           * [The Calculus of Friendship: What a Teacher and a Student Learned about Life while Corresponding about Math](https://www.amazon.com/Calculus-Friendship-Teacher-Student-Corresponding/dp/0691150389)
           * [Sync: How Order Emerges from Chaos in the Universe, Nature, and Daily Life](https://www.amazon.com/Sync-Order-Emerges-Universe-Nature/dp/0786887214)
           * [The Joy of x: A Guided Tour of Math, from One to Infinity](https://www.amazon.com/Joy-Guided-Tour-Math-Infinity/dp/0544105850)

    - start: 0:01:20
      end: 0:02:40
      tags:
      description: |
          No such thing as [an original idea](https://www.amazon.com/Joy-Overview-Window-System-2nd/dp/0201565129); Kids these days don’t get all [our jokes](https://en.wikipedia.org/wiki/The_Brady_Bunch)

Each chunk has its beginning, end, and description clearly marked. We also have a place to put tags on chunks, though we aren’t using them yet. Our site is built in Octopress, which is in turn a layer on top of the static blogging engine Jeckyll. Articles are written in Markdown and contain a few plugins for things like images or links to social media. We’ve created a new plugin called “shownotes” that does the YAML to HTML translation. The Ruby code behind the plugin inserts the HTML audio widget for the episode, some Javascript to manage playing and pausing, and the HTML for the show notes themselves including the shiny new play/pause buttons:

# Title: shownotes Tag for Jekyll
# Author: Doug McKee
# Date: 2017-01-20
# Description: Translate podcast metadata files into nicely formatted show notes
#
# Syntax { % shownotes path/to/file % }
#
# Paths are interpreted as being under source/podcast-metadata/

require 'pathname'

module Jekyll

  class ShowNotesTag < Liquid::Tag
    include TemplateWrapper
    def initialize(tag_name, markup, tokens)
      @file = markup
      super
    end

    def render(context)
      metadata_dir = 'podcast-metadata'
      metadata_path = (Pathname.new(context.registers[:site].source) + metadata_dir).expand_path
      file = (metadata_path + @file).to_s.strip

      if File.symlink?(metadata_path)
        return "Metadata directory '#{metadata_path}' cannot be a symlink"
      end

      unless File.file?(file)
        return "File #{file} could not be found"
      end

      Dir.chdir(metadata_path) do
        metadata = YAML.load_file(file)
        podcasturl = "http://www.podtrac.com/pts/redirect.mp3/s3.amazonaws.com/tbp-episodes/"+metadata["podcast"]

        shownotes = <<-FOO


You can subscribe to the [Teach Better Podcast](http://teachbetter.co/podcast.html)
through your favorite podcast app or simply
[subscribe through iTunes](https://itunes.apple.com/us/podcast/the-teach-better-podcast/id965589233?mt=2&uo=4)
if you don't have one yet.

<audio id="mp3" controls preload>
  <source src="#{podcasturl}" type="audio/mp3">
</audio>

<script>
var audio = document.getElementById('mp3');
var segmentEnd;

audio.addEventListener('timeupdate', function (){
    if (segmentEnd && audio.currentTime >= segmentEnd) {
        audio.pause();
    }   
    console.log("current: " + audio.currentTime + ", segmentEnd: " + segmentEnd);
}, false);

function playpauseSegment(startTime, endTime){
    if (audio.paused) {
        if (audio.currentTime <= startTime || audio.currentTime >= endTime) {
            audio.currentTime = startTime;
        }
        segmentEnd = endTime;
        audio.play();
    } else {
        audio.pause();      
    }
}
</script>
FOO
        shownotes += "\n## Show Notes\n\n"

        chunks = metadata["chunks"]

        chunks.each do |chunk|
          startchunk = chunk["start"]
          starthour = startchunk / 3600
          startminute = (startchunk % 3600) / 60
          startsecond = startchunk % 60
          endchunk = chunk["end"]
          description = chunk["description"]
          if (starthour>0)
            shownotes += "**#{starthour}:#{startminute.to_s.rjust(2,"0")}:#{startsecond.to_s.rjust(2,"0")}**"
          else
            shownotes += "**#{startminute.to_s}:#{startsecond.to_s.rjust(2,"0")}**"
          end
          shownotes += " <a href=\"javascript:playpauseSegment(#{startchunk},#{endchunk});\">&#9199;</a>"
          shownotes += " #{description}\n\n"
        end

        shownotes
      end
    end
  end

end

Liquid::Template.register_tag('shownotes', Jekyll::ShowNotesTag)

Now that we are letting listeners jump around between specific spots in our episodes, we have to encode our audio files as Constant Bit Rate (CBR) mp3’s. That’s because the audio libraries built into iOS and macOS don’t seek accurately in the alternative, Variable Bit Rate (VBR) mp3’s. As Marco Arment points out in his plea for Apple to fix this, it’s a real shame since VBR’s are smaller and sound better.

Very few podcasts use Octopress and thus can copy our code wholesale. That said, I think (hope) it would be pretty easy to translate this to Wordpress or Squarespace or whatever else other folks are actually using. As a frequent podcast listener, I hope at least some of my favorite podcasts do.