Podscope podcast CMS https://podscope.cc
  • PHP 75.3%
  • Blade 24.1%
  • CSS 0.3%
  • Shell 0.2%
Find a file
gdadkisson 7abdb9bee7 v0.96 -- imports cannot record an episode whose upload failed, plus a missing-file report
Episode #50 on production is published and serves a dead enclosure: it points
at an audio object that exists on no disk. Root cause: ImportEpisodeJob
discarded the return value of writeStream(), and both disks are configured
throw => false, so a failed upload returned false instead of raising and the
job carried on to save the episode anyway. file_size and duration were
recorded from the temp file, so the download had succeeded -- only the upload
was lost, silently.

- The import now checks the write AND confirms the object is really there
  before creating the row, deletes any half-written object, and throws so the
  import is marked failed and can be re-run. A missing episode is recoverable;
  a live episode with a broken file is not.
- Artwork had the same silent-false problem. It stays optional, but the path is
  only recorded if the write landed, and a failure is logged rather than
  swallowed.
- New missing-file report: the mirror of the orphan report. Existence is judged
  on the disk the row records, because that is the disk the site reads from --
  an object sitting on the other disk is still a broken episode. The bucket
  listing is only a fast path; anything it does not vouch for gets a real
  existence check, so a listing hiccup cannot invent a scare. Shown on
  Administration > Storage and in storage:scan, which now exits non-zero so the
  nightly run surfaces instead of passing quietly.
- pathOwners() is now derived from a richer referencedAssets(), so orphans and
  missing files are two questions answered from one list.

- Tests: 11 new (MissingMediaTest), suite 269 with only the 2 documented
  pre-existing failures. Verified live on prod: the report finds episode #50 and
  nothing else, and the page renders it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132BK5cfjUj8VaYT8roJq5K
2026-08-13 11:48:16 -04:00
.claude/commands security: remove hardcoded Forgejo token from release.md 2026-06-19 17:22:47 -04:00
app v0.96 -- imports cannot record an episode whose upload failed, plus a missing-file report 2026-08-13 11:48:16 -04:00
bootstrap v0.90 -- per-show newsletters (double opt-in, Mailgun, public archive) 2026-07-31 11:58:52 -04:00
config v0.96 -- imports cannot record an episode whose upload failed, plus a missing-file report 2026-08-13 11:48:16 -04:00
database v0.93 -- estimated storage cost (Administration > Storage) 2026-08-13 10:05:12 -04:00
public v0.86 -- Self-host Plyr icon sprite (fixes missing player icons under enforced CSP) 2026-07-16 11:47:58 -04:00
resources v0.96 -- imports cannot record an episode whose upload failed, plus a missing-file report 2026-08-13 11:48:16 -04:00
routes v0.93 -- estimated storage cost (Administration > Storage) 2026-08-13 10:05:12 -04:00
storage Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
tests v0.96 -- imports cannot record an episode whose upload failed, plus a missing-file report 2026-08-13 11:48:16 -04:00
.editorconfig Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
.env.example Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
.gitattributes Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
.gitignore Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
.npmrc Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
artisan Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
composer.json Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
composer.lock v0.78 - Security review 2026-07-03 follow-up (high-value trio) 2026-07-03 10:01:04 -04:00
install.sh Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
LICENSE Add AGPL-3.0 license 2026-06-09 19:12:09 +00:00
package-lock.json Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
package.json Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
phpunit.xml Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
README.md Initial public release -- v0.1 2026-05-21 18:53:00 +00:00
vite.config.js Initial public release -- v0.1 2026-05-21 18:53:00 +00:00

Podscope

A self-hosted podcast CMS and membership platform. Publish shows, manage episodes, accept paid memberships via Stripe, and distribute private RSS feeds to subscribers.

Features

  • Multi-show support
  • Episode management with Podcasting 2.0 metadata (persons, transcripts, chapters, soundbites)
  • Paid membership plans per show or site-wide (via Stripe / Laravel Cashier)
  • Private RSS feeds for subscribers
  • Free preview window for paid episodes
  • Episode play tracking and stats
  • Individual episode purchases
  • Public RSS feeds with OP3 tracking support
  • Filament admin panel
  • Backblaze B2 storage support
  • Redis queue support
  • Alpine.js frontend

Requirements

  • PHP 8.4+
  • PostgreSQL 14+
  • Redis
  • Node.js 20+ (for asset compilation)
  • Composer
  • A Stripe account (for memberships and purchases)
  • A Backblaze B2 bucket (optional, for media storage)

Quick Install

bash <(curl -fsSL https://git.podscope.cc/podscope/podscope/raw/branch/main/install.sh)

Manual Install

git clone https://git.podscope.cc/podscope/podscope.git
cd podscope
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate
# Edit .env with your database, Stripe, and storage credentials
php artisan migrate --force
php artisan storage:link
npm install && npm run build
php artisan config:cache
php artisan route:cache
php artisan view:cache

Then configure your web server to point at the public/ directory and set up a queue worker:

php artisan queue:work --sleep=3 --tries=3

Configuration

All site settings (name, artwork, RSS options) are managed in the Filament admin panel under Settings.

Key .env values:

Variable Description
APP_URL Your site URL (e.g. https://example.com)
DB_CONNECTION Set to pgsql
DB_HOST, DB_DATABASE, DB_USERNAME, DB_PASSWORD PostgreSQL credentials
STRIPE_KEY, STRIPE_SECRET, STRIPE_WEBHOOK_SECRET Stripe API keys
FILESYSTEM_DISK public for local storage or b2 for Backblaze
AWS_* / B2_* Backblaze B2 credentials if using cloud storage
REDIS_HOST Redis connection
MAIL_* Mail settings for transactional email

License

MIT