Why do podcast apps suck?

Ok, so this is largely hyperbole. But I did get so irritated with finding a podcast app that did what I want that I ended up writing one.

You can see how that project is going on my github. This post is more an overview of why I made it and what it does.

What should a podcast app do?

Fundamentally, at least in my opinion, a podcast is a book; a series of chapters that should be consumed in order. You should be able to stop, jam in a bookmark, and then pick up exactly where you left off whenever you want.

Apparently making this an easy thing to do is not the primary goal of most podcast apps however. I used to use spotify for my podcasts as it worked pretty well. You could sort the episodes by date and have it ignore played episodes. It pretty much worked for my use case until a recent change to the spotify app that basically threw all the features that made it usable for podcasts out the window.

Well shit, what now?

A journey of disappointment

I figured, someone must have made a podcast app that makes it easy to just listen to stuff in order right? So I embarked on a journey trying every podcast app I could find. My use case was simple: Select a feed and press play. That should be it. Just press play and let it play the episodes, automatically go to the next episode and keep playing until the feed is done.

Nothing did this. There was almost invariably faffing about with adding things to the Queue through some unintuitive interface. I am a simple bean with simple needs, and one of those needs is convenience. So being a naive thembean I figured I’d just make my own…

And here we are

Yapa (Yet Another Podcast App) is my baby. You add a feed and then you tell it to play it. Done. Hit Ctrl+C when you want to pause and it’ll remember where you left off and resume at exactly that point the next time you play that feed/playlist.

The question of how to store feed data was a fun one. Initially I thought of using a docstore, some kind of embedded NoSQL db or maybe just sqlite but in the end I settled on just a plain JSON file. Why? Because JSON in Go is easy. You just tag your structs and then you can serialize/deserialize your data in a quick typesafe way.

Obviously there is one major drawback to this; Yapa reads the entire store into memory when it starts and writes that out to file on data changes. if Yapa is playing a feed and you make a change to the store in a separate terminal that change will be overwritten when the feed starts a new episode because it has to mark the previous episode as played and then write the store. It doesn’t know or care that the store on file has changed in the meantime and I’m not sure that there’s an elegent solution to that. I’m ok with that though. I can just Ctrl+C the player, make changes and then restart it and it’ll pick up exactly where I left off so the inconvenience is minimal.

My primary concern was that adding, searching, and playing feeds be as pain free as possible and I think Yapa does that well enough. You can filter episodes by ID or regular expression, mark them played/unplayed etc… Here’s how it looks:

┌─ nick@fnord
│ ~ 
└─❯ yapa list

ID Name                             Eps Played Last Updated
0  The Cast Die Podcast             30  0      2021-07-11 00:04
1  RQ Early Access Patron Feed      703 23     2021-07-11 00:04
2  D&D is For Nerds                 355 61     2021-07-10 14:00
3  VAST Horizon                     27  0      2021-07-09 03:59
4  Dark Dice                        30  26     2021-07-09 03:59
5  Critical Role                    240 107    2021-07-08 12:01
6  The Adventure Zone               197 5      2021-07-08 11:00
7  Dark Air with Terry Carnation    16  7      2021-07-08 08:00
8  Out of Place                     16  16     2021-07-06 20:33
9  This Paranormal Life             222 221    2021-07-06 20:29
10 Sweden Rolls                     165 6      2021-07-05 20:00
11 Hearty Dice Friends              206 4      2021-07-02 11:47
12 The Hotel                        38  0      2021-06-01 06:00
13 Power Word Roll                  70  2      2021-01-28 11:00
14 Lake Clarity                     26  0      2020-11-17 21:28
15 The Theatre of Tomorrow          50  0      2020-10-26 06:00
16 Swan Dive by the Savage Godlings 22  0      2020-08-04 14:35

┌─ nick@fnord
│ ~ 
└─❯ yapa list -f4 -e0-5

ID Name                            Played Pub Date
0  Dark Dice: Episode 0 - The Game Yes    2018-09-25 03:59
1  Dark Dice: Meet the Innskeep    Yes    2018-09-29 00:42
2  Chapter 1: The Silent One       Yes    2018-11-01 19:44
3  Chapter 2: Mindless             Yes    2018-12-04 01:33
4  Dark Dice: Meet the Bard        Yes    2018-12-31 21:54
5  Chapter 3: Captive              Yes    2019-02-06 04:48

┌─ nick@fnord
│ ~ 
└─❯ yapa list -f4 -r'^The Long'

ID Name                                 Played Pub Date
26 The Long Road: Chapter 1A: Recovery  No     2021-02-26 02:54
27 The Long Road: Chapter 1B: Unavenged No     2021-05-12 11:23
28 The Long Road: Chapter 2B: Quartet   No     2021-06-25 03:59

┌─ nick@fnord
│ ~ 
└─❯ yapa list -f4 -r'^The Long' -s'The Long Road'

ID Name                                 Played Pub Date
26 The Long Road: Chapter 1A: Recovery  No     2021-02-26 02:54
27 The Long Road: Chapter 1B: Unavenged No     2021-05-12 11:23
28 The Long Road: Chapter 2B: Quartet   No     2021-06-25 03:59
Playlist saved as 'The Long Road'

┌─ nick@fnord
│ ~ 
└─❯ yapa play -f4 -l'The Long Road'
Feed:    Dark Dice
Playing: The Long Road: Chapter 1A: Recovery
Elapsed: 0m 6s

I’d hoped to write it in pure Go to handle the mp3 playing by itself, but I had some weird issues with libraries I tried to use for that so I settled on just running mpv and tracking time/catching signals in a go routine instead.

Since then I’ve added RE2 based filtering for lists to make it easier pull out episodes I want and save them to playlists. I think the playlists management is largely done at this point. I’m open to pull requests if anyone wants to add features but the mission statement of Yapa needs to stay faithful to its intent: Keep it simple.

PS. Oh hai Alex

Here’s a binary build for ARM7 that (should?) run on your pi