Open-source, local-first macOS reader for structured Podcast extraction. It is a reader, not a note-taking app.
The app ships with 21 public YouTube Podcast sources and their source-specific editorial prompts. It is designed for personal use and does not redistribute Podcast Transcripts or analysis databases.
- App:
~/Applications/Podcast Reader.app - Database:
~/Library/Application Support/PodcastNotes/podcast_notes.sqlite3 - Schedule: Codex automation
Podcast Reader 每日增量更新, every day at 07:30 local time - Runtime: one scheduled run starts, updates the local library, and exits; there is no LaunchAgent, login item, helper, API server, or resident process
The toolbar provides optional one-shot manual update and add-link actions. Supported links are YouTube channels, playlists, and individual episodes. On launch, an existing library is read directly from SQLite, so a completed scheduled run is already visible without clicking refresh. Appearance can follow macOS or be pinned to light/dark mode. While open, the reader checks SQLite data_version on a retained connection every ten seconds and reloads only after an external commit so scheduled content and progress appear without restarting.
- YouTube RSS and channel pages discover episodes.
- YouTube caption extraction is disabled in a fresh public checkout. After reviewing LEGAL.md, an authorized user may explicitly enable the user-installed
/opt/homebrew/bin/summarizepath; its Homebrewyt-dlpdependency provides the transcript-only fallback. - Audio is never downloaded and no ASR backend is called. Caption extraction first uses YouTube's low-cost web transcript, then falls back to
yt-dlpfor YouTube's own subtitle track; missing or incomplete captions becomeno_transcriptwith a visible reason. - A newly published episode with no caption track is shown as
字幕生成中for 72 hours. The reader immediately presents the official description, guest card, and Show Notes chapters as a clearly labeled preview; daily retries replace that preview with the full Transcript, translation, and structured analysis once captions appear. /opt/homebrew/bin/codex exec -m gpt-5.6-lunaperforms source-specific extraction and bilingual translation. Content calls use Luna's default reasoning configuration; a separately configured Codex automation may usemaxreasoning effort.- Original Transcript, Chinese translation, participant research, topic map, core insights, evidence limits, and source links are saved in local SQLite.
- The reading order is one-sentence takeaway, one-paragraph core summary, participants, topic map, core insights, evidence, and bilingual Transcript. Original publication and local organization timestamps are shown to the second in the Mac's current time zone.
- The sidebar keeps a compact receipt for the 07:30 Codex schedule, including current status, exact completion time, and run counts. Each episode and reader mode persists its own exact scroll offset across selection changes and App launches.
- Every manual or scheduled update completes discovery, extraction, and any missing translation before the one-shot worker exits.
- Daily discovery looks back 30 days but accepts unseen items at or after each source's local publish-time watermark, deduplicates by YouTube video ID, retries failed/missing-caption items, and records source-level failures in the run receipt.
- New subscriptions automatically receive a Luna-generated source profile; users never manage prompts.
The code and installed runtime do not read or write Hermes files, databases, prompts, jobs, logs, Notion state, or Telegram state.
- MIT covers the source and original App Icon. See LICENSE and ASSETS.md.
- Podcast content remains owned by its publishers. Review LEGAL.md before enabling caption extraction.
- “Local-first” does not mean fully offline: Transcript content is sent through the user's authenticated Codex CLI. See PRIVACY.md.
- Report vulnerabilities privately according to SECURITY.md.
- Runtime dependencies and licenses are listed in THIRD_PARTY_NOTICES.md.
scripts/build_app.sh
scripts/install_personal_app.sh/bin/zsh scripts/daily_update.shscripts/verify.shThis runs the Swift build with warnings treated as errors, five isolated Swift reader/database regression checks, Python syntax checks, isolated pipeline tests, shell syntax validation, source-catalog validation, plist validation, and installed-app signature verification when present.
Sources/PodcastNotesApp/ContentView.swift: native three-column reader UI.Sources/PodcastNotesApp/Resources/pipeline.py: independent one-shot discovery, Transcript, extraction, and translation pipeline.Sources/PodcastNotesApp/Resources/seed_sources.json: independent 21-source catalog and source-specific profiles.Assets/AppIcon-master-v2.pngandAssets/AppIcon.icns: transparent, borderless production macOS icon.PRODUCT_SPEC.md: current product, data, automation, and acceptance contract.SOURCE_PROFILES.md: research rationale behind the 21 source-specific extraction profiles; the executable profile text lives inseed_sources.json.
- All one-shot CLI writers share a database-specific OS lock, including manual processing, source management, and scheduled updates. A busy worker exits 75; filesystem or processing failures exit nonzero rather than reporting a successful skip. The lock file remains on disk; ownership is released by the OS when the worker exits. Do not delete this file to unlock a running worker.
- The same update receipt covers source-profile retries, discovery, analysis, and the existing translation backlog. Disabled or archived subscriptions are excluded from automatic processing. Interrupted extraction/analysis resumes on the next update; abandoned run receipts are marked interrupted after lock acquisition.
- Incremental batches at or above an existing source watermark process older unseen items first, so the per-source limit does not silently skip the remaining newer feed items. Initial subscriptions still start with the latest items; the upstream feed/window limit still applies.
- Translation batches are validated in full before commit; duplicate, missing, foreign, or empty segment results remain retryable. Successful earlier batches are retained. Manual processing no longer invokes translation twice.
- Library reads share a SQLite snapshot. Unchanged polling avoids full list scans and redundant SwiftUI publication; translated content refreshes even if episode status does not change. Custom source categories appear automatically and empty Today lists no longer select an unrelated old episode at launch.
PODCAST_NOTES_DBcan point the app and daily script at an isolated library for local acceptance checks. The worker CLI also accepts--db.