Skip to main content

A local-first podcast feed generator and editor.

Project description

offshore

HomepageRepositoryPyPI packageTickets

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 offshore-podcast

Or from source:

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.1.tar.gz (83.5 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.1-py3-none-any.whl (64.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: offshore_podcast-0.1.1.tar.gz
  • Upload date:
  • Size: 83.5 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.1.tar.gz
Algorithm Hash digest
SHA256 6997af02e5110c7e684b619c1e15c2cf03c68a1532a31c6ab5b644dc0455525f
MD5 b897cd79b72b29bc32b046300c9496c4
BLAKE2b-256 546b42924c43a7834f5d0ac63185f6b89c092f26a0b90d47557d6f03f786f7cf

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for offshore_podcast-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cf0aad82da6ebccb63698c480a19063e17f2c9646f2cb99b75837fc1fd9bad5c
MD5 b787645a552eb3489c6c64b930eeba12
BLAKE2b-256 393c3f5952359c7948e6e8127f7b980f8832b9103396d55d0306ba0d7e4ebd92

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