Skip to main content

podcast-chapter-tools

Lint and Test Ruff Checked with mypy twitter

Extract and transform podcast chapter information between PodcastIndex (PCI) chapters, Podlove Simple Chapters (PSC), description embeds (timestamps in show notes), and ID3v2 CHAP frames.

Installation

pip install podcast-chapter-tools

# with ID3 chapter support (mutagen)
pip install 'podcast-chapter-tools[id3]'

Usage

Command line

# Show-notes timestamps -> PodcastIndex chapters JSON
podcast-chapters from-description shownotes.txt --to pci

# PSC chapters from a feed (file or URL) -> description text
podcast-chapters from-psc feed.xml --guid "episode-guid" --to description

# PodcastIndex chapters JSON (file or URL) -> PSC XML
podcast-chapters from-pci https://example.com/chapters.json --to psc

# ID3 CHAP frames from an MP3 (requires the [id3] extra)
podcast-chapters from-id3 episode.mp3

All subcommands accept --to {pci,psc,description}, --normalize (sort, dedupe, strip HTML from titles), and --output FILE.

Library

Every extractor returns list[Chapter] | None, where Chapter is a named tuple of (start, title, url, image) with the start time in seconds:

from podcast_chapter_tools import (
    extract_description_chapters,
    extract_psc_chapters_from_file,
    get_and_extract_pci_chapters,
    chapters_to_pci_json,
    normalize_chapters,
)
from pathlib import Path

# From show notes (plain text or HTML)
chapters = extract_description_chapters("0:00 Intro\n2:30 Interview\n")
# [Chapter(start=0, title='Intro', ...), Chapter(start=150, title='Interview', ...)]

# From a feed's Podlove Simple Chapters
chapters = extract_psc_chapters_from_file(Path("feed.xml"), guid="episode-guid")

# From a PodcastIndex chapters URL
chapters = get_and_extract_pci_chapters("https://example.com/chapters.json")

# Clean up and convert
chapters = normalize_chapters(chapters, strip_titles=True)
print(chapters_to_pci_json(chapters, indent=2))

Other useful entry points:

  • extract_all_psc_chapters_from_file(feed) - chapters for every episode, keyed by GUID
  • extract_psc_chapters_from_url(feed_url, guid) - fetch the feed for you
  • find_pci_chapters_url(feed, guid) - discover the <podcast:chapters> URL declared in a feed
  • extract_id3_chapters(Path("episode.mp3")) - ID3v2 CHAP frames (requires the [id3] extra)
  • chapters_to_psc_xml(chapters) / chapters_to_description(chapters) - other writers
  • ts_to_secs("1:02:03") / secs_to_ts(3723) - timestamp helpers

Development

git clone git@github.com:hbmartin/podcast-chapter-tools.git
cd podcast-chapter-tools
python3 -m venv venv
source venv/bin/activate
pip install -e '.[lint,test]'

pytest
ruff check .
ruff format --check .
mypy podcast_chapter_tools

The ai/ directory contains experimental scripts for generating chapters from transcripts with LLMs; its dependencies live in ai/requirements.txt and are not part of the published package.

See also

Authors

Metadata

Release files for podcast-chapter-tools 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for podcast-chapter-tools 0.2.0
File Size Uploaded
podcast_chapter_tools-0.2.0.tar.gz 27.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for podcast-chapter-tools 0.2.0
File Interpreter ABI Platform
podcast_chapter_tools-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 48.8 kB

Release files / podcast_chapter_tools-0.2.0.tar.gz

Download URL podcast_chapter_tools-0.2.0.tar.gz
Size 27.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b74074fc27e7c32b5d34c4486b0742ae3dd000c682e5c25a46414642e4cda157
BLAKE2b-256 checksum
How to use checksums
c03c6749fd6affff8067369e2b7063b771ceaa37f771ad48ee923a1cc3416f40
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.

Transparency log

Release files / podcast_chapter_tools-0.2.0-py3-none-any.whl

Download URL podcast_chapter_tools-0.2.0-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4c817624e16917e4a46de5e08949a7c5490992d7516d5f1149a075f66eef2cd6
BLAKE2b-256 checksum
How to use checksums
a7d4f0b1077f2f9d2798821473c368c4343dc51ccd95291d3de40def6a71b6d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page