podcast-chapter-tools
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 GUIDextract_psc_chapters_from_url(feed_url, guid)- fetch the feed for youfind_pci_chapters_url(feed, guid)- discover the<podcast:chapters>URL declared in a feedextract_id3_chapters(Path("episode.mp3"))- ID3v2 CHAP frames (requires the[id3]extra)chapters_to_psc_xml(chapters)/chapters_to_description(chapters)- other writersts_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
- https://github.com/hbmartin/podcast-transcript-convert
- https://github.com/Podcastindex-org/podcast-namespace/blob/main/transcripts/transcripts.md
- https://github.com/hbmartin/overcast-to-sqlite
- https://support.spotify.com/us/podcasters/article/enabling-podcast-chapters/
Authors
- Harold Martin - harold.martin at gmail
- Icon courtesy of Vecteezy.com
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file podcast_chapter_tools-0.2.0.tar.gz.
File metadata
- Download URL: podcast_chapter_tools-0.2.0.tar.gz
- Upload date:
- Size: 27.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b74074fc27e7c32b5d34c4486b0742ae3dd000c682e5c25a46414642e4cda157
|
|
| MD5 |
b9bc474bc51c866ba00c41fbfef5c9f0
|
|
| BLAKE2b-256 |
c03c6749fd6affff8067369e2b7063b771ceaa37f771ad48ee923a1cc3416f40
|
Provenance
The following attestation bundles were made for podcast_chapter_tools-0.2.0.tar.gz:
Publisher:
publish.yml on hbmartin/podcast-chapter-tools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
podcast_chapter_tools-0.2.0.tar.gz -
Subject digest:
b74074fc27e7c32b5d34c4486b0742ae3dd000c682e5c25a46414642e4cda157 - Sigstore transparency entry: 2103855898
- Sigstore integration time:
-
Permalink:
hbmartin/podcast-chapter-tools@d1edde8e856c3509516b9ac185ebd3bed5b6fbaf -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/hbmartin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d1edde8e856c3509516b9ac185ebd3bed5b6fbaf -
Trigger Event:
release
-
Statement type:
File details
Details for the file podcast_chapter_tools-0.2.0-py3-none-any.whl.
File metadata
- Download URL: podcast_chapter_tools-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c817624e16917e4a46de5e08949a7c5490992d7516d5f1149a075f66eef2cd6
|
|
| MD5 |
13338fa43453d33db956bf189e3ee752
|
|
| BLAKE2b-256 |
a7d4f0b1077f2f9d2798821473c368c4343dc51ccd95291d3de40def6a71b6d1
|
Provenance
The following attestation bundles were made for podcast_chapter_tools-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on hbmartin/podcast-chapter-tools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
podcast_chapter_tools-0.2.0-py3-none-any.whl -
Subject digest:
4c817624e16917e4a46de5e08949a7c5490992d7516d5f1149a075f66eef2cd6 - Sigstore transparency entry: 2103856164
- Sigstore integration time:
-
Permalink:
hbmartin/podcast-chapter-tools@d1edde8e856c3509516b9ac185ebd3bed5b6fbaf -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/hbmartin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d1edde8e856c3509516b9ac185ebd3bed5b6fbaf -
Trigger Event:
release
-
Statement type: