Skip to main content

A local-first podcast feed generator and editor.

Project description

offshore

Local-first podcast feed generator and editor. Manage metadata locally and generate feed from assets hosted anywhere.

Conventional podcast hosts like Fireside or Transistor work fine, but they can be expensive for small shows, and handing all your data to a single service is a real dependency. Self-hosted alternatives exist, but most are still designed as full-stack replacements that bundles frontend website, audio storage, and feed generation into one thing.

A podcast feed is just a plain-text XML file. Hosting a handful of audio files and a text file is much easier, and cheaper than running a media server with a web frontend and database. And if you don’t want the website that comes with a podcast host then that part just becomes redundant.

offshore aims to be a more basic solution. It simply helps you manages your show metadata locally and generates feed you can host wherever makes sense — a static site, an object storage, a CDN. Audio files and any other asset live wherever you want to host them. offshore ensures your metadata and assets comply with podcast standards, but otherwise stays out of the way. It can be used as a local feed generator, or reliably on a server to serve the feed directly.

Installation

Python 3.11+.

pip install -e .

Data layout

Shows are stored in a dedicated directory outside the project, defaulting to $XDG_DATA_HOME or $HOME. On first run you will be prompted to confirm or change this.

~/offshore/              # default shows root
  <slug>/
    <slug>.json          # show + episodes
    <slug>.lock.json     # feed stability lock 
    podcast.xml          # generated
    rss.xsl              # generated

Each show lives in its own subdirectory. The JSON contains all show metadata and episodes. The lock file records each episode's GUID and pub_date to catch accidental edits that would reorder the feed for existing subscribers.

To inspect or change the configured root:

offshore config                          # show current setting
offshore config --shows-dir ~/mypodcasts # update it

Usage

Web UI

offshore serve

Opens at http://127.0.0.1:8765. On first run with no shows, the landing page offers three options: New, Import, or Open an existing file.

All CLI commands are also available from the UI.

CLI

New show:

offshore init <slug>

Import from an RSS feed URL or an offshore JSON file:

offshore import <url-or-path> --slug <slug> --feed-self-url <public-feed-url>

Validate:

offshore check            # run checks (structural + remote assets)
offshore check --no-remote  # skip network checks

Probe audio metadata:

offshore probe --all      # all episodes
offshore probe <guid>     # single episode

Probing downloads only the first 256 KB of each file to read the header. Results are cached in the episode JSON.

Generate feed:

offshore generate
offshore generate --force           # skip check failures
offshore generate --output-dir <path>  # write to a different directory

Output goes to <shows-root>/<slug>/podcast.xml by default. Falls back to the current directory if the shows root is not writable.

Republish an episode (changes pub_date, updates lock):

offshore republish <guid> --date <ISO8601>

Open a specific show file:

offshore serve --show /path/to/<slug>.json

Serving the feed

shows/<slug>/ is a static directory. Point nginx (or any web server) at it:

server {
    listen 80;
    server_name your.domain.com;
    root /path/to/shows/my-show;

    location /podcast.xml {
        add_header Content-Type "application/rss+xml; charset=utf-8";
    }
}

Make sure feed_self_url in the show JSON matches the public URL you serve the feed at.

To redirect feed output to a shared web root:

offshore generate --output-dir /var/www/html
offshore serve --output-dir /var/www/html

offshore serve can also be exposed directly behind a reverse proxy (--host 0.0.0.0). Each show's feed is available at /download/<slug>/podcast.xml.

Audio

MP3 (audio/mpeg) and M4A/AAC (audio/mp4, audio/aac) are accepted. Format is verified when an episode is saved. If the URL is unreachable at save time you can skip verification and probe later.

Checks

offshore check runs before every generate. Fail-level findings block generation; --force overrides. Checks cover:

  • All required fields on the show and each episode
  • Audio probe cache present (duration + byte count are required for a valid enclosure)
  • Audio and image URLs reachable with the correct MIME type
  • Episode pub_date is not in the future
  • All person roster references resolve
  • Episode GUID and pub_date match the lock file

Development

tests/ includes a sample feed and a group of scripts to validate the output for development.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

offshore_podcast-0.1.0.tar.gz (55.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

offshore_podcast-0.1.0-py3-none-any.whl (61.9 kB view details)

Uploaded Python 3

File details

Details for the file offshore_podcast-0.1.0.tar.gz.

File metadata

  • Download URL: offshore_podcast-0.1.0.tar.gz
  • Upload date:
  • Size: 55.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for offshore_podcast-0.1.0.tar.gz
Algorithm Hash digest
SHA256 04cd894f4efc5a241f5a2ff17e2d34b23ef6e4809f3ee307ef371170c00ce8ad
MD5 008871f9904615e9474188f9ad1b8512
BLAKE2b-256 2e44d2578fbadbb77b1877b635d196ec5da78284ca9f5f4f14db8c0b4676ac03

See more details on using hashes here.

File details

Details for the file offshore_podcast-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for offshore_podcast-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 60f0e6cecd36bd439c74717937e517abd37bb3e1756c0e10180728959b969515
MD5 95987fe2f32bb5533738085e73975e08
BLAKE2b-256 1379b5ad8cc2d1cab78e2dbb2c003a6f6a3a042b3ccf533c5268550e96042034

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page