17 votes

Self-hosting a podcast server

I am wanting to setup a personal podcast server but I am not really sure how to go about that.

I have my own server at home with docker and I am not sure if there are any well-known FOSS (preferable dockerized) podcast server applications that I can spin up and load some podcast episodes into so that I can create my own custom podcast feed that only I would subscribe to?

and I want to be able to support video podcasts.

10 comments

  1. [4]
    vord
    Link
    Somebody clued me in to audiobookshelf, which handles audiobooks, podcasts, and ebooks. It's been pretty great for those. Doesn't do video sadly.

    Somebody clued me in to audiobookshelf, which handles audiobooks, podcasts, and ebooks. It's been pretty great for those.

    Doesn't do video sadly.

    8 votes
    1. [3]
      turmacar
      Link Parent
      Audiobookshelf itself is fantastic, but I've learned from family that if you need iphone support that app is in beta and with the way betas work in Apple's ecosystem you can get locked out. (only...

      Audiobookshelf itself is fantastic, but I've learned from family that if you need iphone support that app is in beta and with the way betas work in Apple's ecosystem you can get locked out. (only first ~10k(?) get in after an update) I made a plex library for them and they're using some frontend app to connect to that, but plex isn't open source for OP.

      4 votes
      1. [2]
        gary
        Link Parent
        Try plappa as a (third-party) frontend for Audiobookshelf on iOS. Very reasonably priced and a nicer, more native UI than the Audiobookshelf app. It's not good for the podcasts feature on...

        Try plappa as a (third-party) frontend for Audiobookshelf on iOS. Very reasonably priced and a nicer, more native UI than the Audiobookshelf app. It's not good for the podcasts feature on Audiobookshelf though, but good at audiobooks.

        3 votes
  2. [2]
    cinnamontrout
    Link
    https://www.dircaster.org/ Super simple. You setup an apache2 server with php, and drop the files in a directory. Edit the one configuration file to give the podcast a name and other info, and...

    https://www.dircaster.org/

    Super simple. You setup an apache2 server with php, and drop the files in a directory. Edit the one configuration file to give the podcast a name and other info, and then whatever files you add to the directory are automatically added when someone loads the directory, which produces an RSS feed which is what podcasts clients use.

    I don't know if a docker-ized version exists, but it's probably the simplest to operate. No extra servers to run (except for apache2 server), and beyond editing the initial configuration file, nothing needs to be done when you add or remove files for your podcast.

    I use it to self-host my own files which I download separately so that I have a super-fast cache of podcast files which won't be deleted if I decide to re-install my podcast apps on my phone, which often happens before I've listened to some of my backlog.

    6 votes
    1. vord
      Link Parent
      There are apache containers, so theoretically you'd just have to mount the app directory as a volume.

      There are apache containers, so theoretically you'd just have to mount the app directory as a volume.

      2 votes
  3. tomf
    Link
    this is most likely too simple, but I've been using this with a cronjob to pull episodes without ads using yt-dlp Anyway.. here's the php <?php /* @license http://www.gnu.org/licenses/agpl.txt...

    this is most likely too simple, but I've been using this with a cronjob to pull episodes without ads using yt-dlp

    Anyway.. here's the php
    
    <?php
    /*
        @license    http://www.gnu.org/licenses/agpl.txt
        @copyright  2014 Sourcefabric o.p.s.
        @link       http://www.sourcefabric.org
        @author     Micz Flor <micz.flor@sourcefabric.org>
    
        This php script will create a podcast XML on the fly
        listing all mp3/m4a files in the same directory.
    */
    
    $channeltitle   = "Favorite Podcasts (No Ads)";
    $channelauthor  = "Cool Pods w/o bad products";
    /* $sortby sets the order in which tracks are listed.
       Options:
       "newest" = newest on top
       "oldest" = oldest on top
       "filedesc" = alphabetically descending
       "fileasc" = alphabetically ascending
       default: "filedesc" (== how streamplan.sh works)
    */
    $sortby = "filedesc";
    
    $dir = "https://" . $_SERVER['SERVER_NAME'];
    $parts = explode('/', $_SERVER['REQUEST_URI']);
    for ($i = 0; $i < count($parts) - 1; $i++) {
        $dir .= $parts[$i] . "/";
    }
    
    header('Content-type: text/xml', true);
    echo "<?xml version='1.0' encoding='UTF-8'?>\n";
    echo "<rss xmlns:itunes='http://www.itunes.com/DTDs/Podcast-1.0.dtd' version='2.0'>\n";
    echo "<channel>\n";
    echo "  <title>" . htmlspecialchars($channeltitle, ENT_XML1, 'UTF-8') . "</title>\n";
    echo "  <link>$dir</link>\n";
    echo "  <description>Favorite Podcasts</description>\n";
    echo "  <itunes:image href='https://path.com/to/cover.png'/>\n";
    echo "  <itunes:author>" . htmlspecialchars($channelauthor, ENT_XML1, 'UTF-8') . "</itunes:author>\n";
    echo "  <language>en-us</language>\n";
    
    // Read all mp3 and m4a files in the directory
    $mp3files = glob("*.{mp3,m4a}", GLOB_BRACE);
    
    // Create an array with timestamp.filename as key
    $fileArray = [];
    foreach ($mp3files as $filename) {
        $fileArray[filemtime($filename) . $filename] = $filename;
    }
    
    // Change the order of the list according to $sortby set above
    switch ($sortby) {
        case "newest":
            krsort($fileArray);
            break;
        case "oldest":
            ksort($fileArray);
            break;
        case "fileasc":
            natcasesort($fileArray);
            break;
        default:
            // filedesc
            natcasesort($fileArray);
            $fileArray = array_reverse($fileArray);
            break;
    }
    
    // Function to get metadata using ffprobe
    function getMetadata($file) {
        $command = "ffprobe -v quiet -print_format json -show_format -show_streams \"$file\"";
        $output = shell_exec($command);
        $metadata = json_decode($output, true);
    
        $title = $metadata['format']['tags']['title'] ?? pathinfo($file, PATHINFO_FILENAME);
        $artist = $metadata['format']['tags']['artist'] ?? 'Unknown Artist';
        $duration = $metadata['format']['duration'] ?? '0';
    
        // Remove everything after the first "|"
        if (strpos($title, '|') !== false) {
            $title = trim(explode('|', $title)[0]);
        }
    
        return [
            'title' => htmlspecialchars($title, ENT_XML1, 'UTF-8'),
            'artist' => htmlspecialchars($artist, ENT_XML1, 'UTF-8'),
            'duration' => gmdate("H:i:s", $duration),
        ];
    }
    
    // Go through files and create <item> for podcast
    foreach ($fileArray as $filename) {
        $metadata = getMetadata($filename);
    
        // Properly encode filename for URL
        $fileUrl = $dir . str_replace(' ', '%20', urlencode($filename));
        $filesize = filesize($filename);
    
        // Format title as "title (artist)"
        $formattedTitle = "{$metadata['title']} ({$metadata['artist']})";
    
        echo "  <item>\n";
        echo "    <title>" . htmlspecialchars($formattedTitle, ENT_XML1, 'UTF-8') . "</title>\n";
        echo "    <itunes:subtitle>" . htmlspecialchars($formattedTitle, ENT_XML1, 'UTF-8') . "</itunes:subtitle>\n";
        echo "    <enclosure url=\"$fileUrl\" length=\"$filesize\" type=\"audio/mpeg\"/>\n";
        echo "    <itunes:duration>{$metadata['duration']}</itunes:duration>\n";
        echo "    <guid>$fileUrl</guid>\n";
        echo "    <pubDate>" . date("r", filemtime($filename)) . "</pubDate>\n";
        echo "  </item>\n";
    }
    
    echo "</channel>\n";
    echo "</rss>\n";
    ?>
    
    
    4 votes
  4. Bauke
    Link
    Last week I was playing around with AzuraCast which is more for doing web radio stuff but I think it does also have some podcast features. It was easy enough to get set up with Docker and the...

    Last week I was playing around with AzuraCast which is more for doing web radio stuff but I think it does also have some podcast features. It was easy enough to get set up with Docker and the documentation is pretty good too.

    3 votes
  5. balooga
    Link
    I’m sure you’re looking for a polished, battle-tested solution with lots of features, but if you’re feeling adventurous I built a PHP tool for my own use many years ago. It’s mainly intended to...

    I’m sure you’re looking for a polished, battle-tested solution with lots of features, but if you’re feeling adventurous I built a PHP tool for my own use many years ago. It’s mainly intended to merge external feeds into a single RSS file but over time I added a few niceties:

    • filter out unwanted episodes with regex
    • replay a show’s archive beginning from a specified date, according to a custom schedule (cron syntax)
    • manually schedule other files by URL for inclusion

    It’s pretty basic. No UI (everything’s configured in a JSON file). But I’ve been using it for almost a decade now and it’s rock-solid for my needs. If you’re curious, here’s the link to the dusty old GitHub repo (use the develop branch, I never got around to merging it to mainline, lol). It’s not dockerized, I can’t help you with it, and there’s zero community around it… what could go wrong? But I’m satisfied with it, it’s been dutifully managing my podcast subscriptions for ages.

    I think it’ll probably work with video shows too? I haven’t tested that but I’m not aware of any reason why it wouldn’t.

    2 votes
  6. Xerto
    Link
    You can have a look at Castopod. It's a dockerized FOSS solution for hosting podcasts. Never tried it myself though.

    You can have a look at Castopod. It's a dockerized FOSS solution for hosting podcasts. Never tried it myself though.

    1 vote