Control Pocket Casts Web Player from the command line on macOS.
This project is intentionally starting with browser automation (Safari/Chrome via AppleScript) so play/pause/next/prev works without needing Pocket Casts’ private HTTP APIs. Queue/account APIs can be added later by observing the Web Player network calls.
Supported browsers for automation depend on whether the macOS app is scriptable; you can set --browser to chrome, safari, arc, dia, brave, edge, or pass a custom app name with --browser-app.
cd pocketcastsctl
mkdir -p bin
go build -o ./bin/pocketcastsctl ./cmd/pocketcastsctl
./bin/pocketcastsctl helpAfter a tagged release:
- Homebrew tap (macOS):
brew tap agisilaos/tap && brew install pocketcastsctl - Prebuilt tarballs: download from GitHub Releases (
pocketcastsctl_<ver>_darwin_<arch>.tar.gz) - Go install:
go install github.com/agisilaos/pocketcastsctl/cmd/pocketcastsctl@latest
For local iteration:
make build # builds ./pocketcastsctl
make test # runs unit tests
make test-scripts # runs script failure-path tests
make test-scripts-cover # runs scripts package coverage
make check-help-docs
make release-check VERSION=vX.Y.Z
make release-dry-run VERSION=vX.Y.Z
make release VERSION=vX.Y.ZShow build metadata:
./bin/pocketcastsctl --version
./bin/pocketcastsctl help
./bin/pocketcastsctl now
./bin/pocketcastsctl now --watch
./bin/pocketcastsctl help setup
./bin/pocketcastsctl help queue apiRecommended first-run flow:
./bin/pocketcastsctl setup
./bin/pocketcastsctl queue api ls
./bin/pocketcastsctl queue api play 1doctor validates setup (browser automation, config, auth presence, and API auth validity when configured) and suggests next actions.
setup is the guided onboarding command (start is kept as a deprecated alias).
Setup modes:
./bin/pocketcastsctl setup # full guided flow (interactive on TTY)
./bin/pocketcastsctl setup run --json # full agentic report
./bin/pocketcastsctl setup check --plain # quick readiness checks only
./bin/pocketcastsctl setup auth --no-input # auth-only non-interactive
./bin/pocketcastsctl setup verify --json # verify-only machine outputDoctor modes:
./bin/pocketcastsctl doctor --quick
./bin/pocketcastsctl doctor --full
./bin/pocketcastsctl doctor --fix # suggestions only; no changes made
./bin/pocketcastsctl doctor --fix --apply # apply supported in-tool fixes
./bin/pocketcastsctl doctor --json- Primary command output is written to
stdout. - Diagnostics, warnings, prompts, and errors are written to
stderr. - For scripting:
- Prefer
--jsonwhere available for structured output. - Prefer
--plainfor stable tab/line-oriented output.
- Prefer
- Read/status commands now support machine-friendly output modes consistently:
- See the contract table below.
- Destructive safety checks (for example
queue api rmwithout--forcein non-interactive mode) fail with a non-zero exit code and error text onstderr.
Output contract table:
| Command | Human | --plain |
--json |
|---|---|---|---|
now |
dashboard | key/value lines | full snapshot object |
setup |
guided onboarding | key/value step report | structured step report |
doctor |
checklist | tab-separated checks | structured checks + counts |
auth tabs |
URL list | URL list | JSON array of URLs |
auth status |
checklist | key/value lines | status object |
auth verify |
checklist | key/value lines | verification object |
web status |
single state line | single state line | { \"state\": ... } |
local status |
human status line | key/value lines | { \"status\": ... } |
Open https://play.pocketcasts.com and sign in. Then:
./bin/pocketcastsctl web status
./bin/pocketcastsctl web status --json
./bin/pocketcastsctl web toggle
./bin/pocketcastsctl web nextUse now as the main dashboard command:
./bin/pocketcastsctl now
./bin/pocketcastsctl now --watch --interval 3s
./bin/pocketcastsctl now --verify-auth
./bin/pocketcastsctl now --jsonnow merges web status, local status, queue health, auth state, and next-action suggestions in one view.
Sample output:
POCKETCASTS NOW
========================================================================
Updated: 2026-02-13 22:39:30
Web : PAUSED
Local : STOPPED
Queue : READY (4 items, 1 in progress) | next: Ep. 6 – On Being God
Auth : CONFIGURED
------------------------------------------------------------------------
Recommended next actions:
1. pocketcastsctl web toggle
2. pocketcastsctl local pick --in-progress --recent
3. pocketcastsctl queue api pick --recent
Deprecated short aliases (still work for now, but print warnings):
./bin/pocketcastsctl status
./bin/pocketcastsctl toggle
./bin/pocketcastsctl nextThis plays the episode audio directly on your machine (uses mpv if installed; otherwise downloads and uses macOS afplay).
By default, local play starts from Pocket Casts progress (playedUpTo) when available.
./bin/pocketcastsctl local pick
./bin/pocketcastsctl local play 3
./bin/pocketcastsctl local play --from-start 3
./bin/pocketcastsctl local pause
./bin/pocketcastsctl local resume
./bin/pocketcastsctl local stop
./bin/pocketcastsctl local status --jsonResume/start-offset behavior:
mpvsupports starting at saved progress (playedUpTo).afplaydoes not support seek-on-start; playback starts at the beginning.- If you want reliable resume-from-progress, install
mpv:
brew install mpvFlags:
--browser chrome|safari(default:chrome)--url-contains <substring>(default:pocketcasts.com)
macOS may prompt you to allow osascript to control your browser (Automation permission).
queue ls reads visible episode links from the current Pocket Casts tab and prints them.
./bin/pocketcastsctl queue ls
./bin/pocketcastsctl queue ls --jsonThis path calls Pocket Casts’ private API (currently up_next/list, up_next/play_next, up_next/remove) using an auth token extracted from your logged-in Web Player tab.
./bin/pocketcastsctl auth login
./bin/pocketcastsctl auth refresh
./bin/pocketcastsctl auth status
./bin/pocketcastsctl auth verify
./bin/pocketcastsctl queue api ls
./bin/pocketcastsctl queue api play 1
./bin/pocketcastsctl queue api pick --in-progress --recent
./bin/pocketcastsctl queue api bump 5
./bin/pocketcastsctl queue api move 5 2
./bin/pocketcastsctl queue api dedupe --dry-runauth refresh is a guided flow: open login page, sync token, then verify.
auth status shows whether a token exists and, when possible, token expiry signals.
auth verify performs an explicit API verification check for the stored token.
doctor explain <code> explains specific doctor failure/warning codes and the fastest fix.
Examples:
./bin/pocketcastsctl doctor explain doctor.auth.invalid
./bin/pocketcastsctl doctor explain doctor.auth.invalid --jsonFor automation/non-interactive use:
./bin/pocketcastsctl auth refresh --sync-only --no-inputTo retry token selection and verification with multiple candidate passes:
./bin/pocketcastsctl auth refresh --candidate-passes 2Deprecated short aliases (still work for now, but print warnings):
./bin/pocketcastsctl ls
./bin/pocketcastsctl pick
./bin/pocketcastsctl play 3
./bin/pocketcastsctl rm <episode-uuid>pick uses fzf if it’s installed (nice arrow-key selector). If not, it falls back to a simple numbered prompt.
Picker filters:
--recent: sort episodes by publish time (newest first)--unplayed: only episodes without saved progress--in-progress: only episodes with saved progress
These are available on both queue api pick and local pick.
If auth sync can’t find a token, reload https://play.pocketcasts.com while logged in and try again.
If it finds the wrong thing, use:
./bin/pocketcastsctl auth sync --dry-run
./bin/pocketcastsctl auth sync --key-contains tokenIf queue api commands return 401 Unauthorized, refresh credentials:
./bin/pocketcastsctl auth refreshNote: some setups appear to work without an explicit stored auth header; queue api ls will attempt the request either way.
Remove from Up Next:
./bin/pocketcastsctl queue api rm --dry-run <episode-uuid>
./bin/pocketcastsctl queue api rm --force <episode-uuid>By default, queue api rm prompts for confirmation on TTY. In non-interactive mode, you must pass --force (or use --dry-run).
Play a specific item from Up Next:
./bin/pocketcastsctl queue api ls
./bin/pocketcastsctl queue api play 3Add “Play Next” (requires episode fields observed in HAR; easiest is --episode-json):
./bin/pocketcastsctl queue api add --episode-json '{"uuid":"...","podcast":"...","published":"...","title":"...","url":"..."}'Show config path and current config (redacts api_headers values by default):
./bin/pocketcastsctl config path
./bin/pocketcastsctl config show
./bin/pocketcastsctl config show --json
./bin/pocketcastsctl config show --json --reveal-secretsEnvironment overrides:
POCKETCASTS_CONFIG(override config file path)POCKETCASTS_BROWSERPOCKETCASTS_BROWSER_APPPOCKETCASTS_URL_CONTAINSPOCKETCASTS_API_BASE_URL
The release workflow mirrors homepodctl:
- Version metadata is embedded via ldflags (
main.version,main.commit,main.date);pocketcastsctl --versionshows it. make release-check VERSION=vX.Y.Zrunsscripts/release-check.shand validates tests/vet/docs/format + stamped version output.make release-dry-run VERSION=vX.Y.Zbuilds release archives without changelog/tag/push/release/tap writes.make release VERSION=vX.Y.Zrunsscripts/release.shto:- Generate release notes from commit titles and descriptions since the previous tag
- Insert the generated notes into
CHANGELOG.mdunder the new version - Tag and push
main+ the tag - Build macOS arm64/amd64 tarballs under
dist/with checksums - Create a GitHub Release
- Update the Homebrew tap (
agisilaos/homebrew-tap)
- Release scripts:
scripts/release-check.shandscripts/release.sh
Run the release on macOS with a clean git tree.
- CLI help snapshots:
docs/cli-help/help-root.txt,docs/cli-help/help-start.txt - Product roadmap:
ROADMAP.md - Release history:
CHANGELOG.md
See ROADMAP.md for the current milestone plan (v0.1.5) and acceptance criteria.