Skip to content

Add a read-only podcast lookup endpoint - #987

Merged
dannyvfilms merged 3 commits into
dannyvfilms:latestfrom
elemated:podcast-itunes-lookup-api
Aug 25, 2026
Merged

Add a read-only podcast lookup endpoint#987
dannyvfilms merged 3 commits into
dannyvfilms:latestfrom
elemated:podcast-itunes-lookup-api

Conversation

@elemated

@elemated elemated commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • Adds GET /api/v1/podcasts/lookup/{itunes_id}/, a read-only preview of an iTunes podcast search result. It returns the show metadata and the feed's episode list without creating a PodcastShow or a PodcastEpisode, so a client can show someone what a podcast is before they decide to track it.
  • Returns show_id when the podcast is already in the library, using the same feed-then-uuid match the importer uses, so a client can tell "add this" apart from "already added".
  • Episodes come back in the same limit/offset envelope the rest of the API uses, since a feed can run past a thousand of them.
  • Pulls the description fallback and the duplicate-show lookup out of the importer into two shared helpers, so the preview and the import can never disagree about what a show looks like. resolve_show_metadata now returns a new dict rather than quietly writing author and language back into its caller's data.
  • Caches each feed read for 15 minutes. Both the metadata read and the episode read happen inside the request cycle, and this endpoint gets hit while paging through search results.
  • Feed URLs now pass through safe_url before being logged, because private podcast feeds carry access tokens in their query string.
  • Separate second commit, unrelated to the feature: sorts the import block in src/api/serializers.py. ruff check src had been failing on that one I001 since the tmdb-backdrops-api merge, and this restores the lint baseline to zero.

AI Assistance

  • claude-opus-5

How It Was Tested

Local uv is pinned to a version this machine does not have, so these ran through the venv interpreter directly. They are the scripts/test.sh runs with the same tag exclusions.

  • SECRET=test-only PYTHONPATH=mcp_server .venv/bin/python src/manage.py test api app.tests.test_api_contracts --parallel --buffer --exclude-tag slow --exclude-tag network -> 595 tests, no failures. Two errors, both ModuleNotFoundError: pyld, which is a stale local venv rather than a code problem, since pyld==2.0.4 is declared in pyproject.toml.
  • New coverage in api.tests.test_fork_podcast.PodcastLookupTests, 9 tests: whole-feed read, pagination envelope, already-imported show_id, RSS description fallback, cache hit, missing feed URL, unreadable feed, unknown id 404, and provider outage 500.
  • .venv/bin/ruff check src -> All checks passed.
  • OpenAPI artifact regenerated to a scratch path and diffed against src/api/contracts/openapi.yaml -> byte identical. The static artifact is the verified MCP subset, and this route is not in that subset, so it produces no diff. The finding allowlist in schema_contract.py and its count assertion were updated instead.

Public API & Documentation Handoff

  • Domain terminology is up to date (python -m app.domain_vocabulary --check)
  • OpenAPI schema contracts verified (if API endpoints changed)
  • Not applicable (no API or vocabulary changes)

Human Review & Quality Assurance

  • Code review completed
  • Visual or manual QA verified (e.g. /gstack-qa or browser testing)

Database & Migration Safety (Only if modifying models)

  • No existing or shared migrations were altered or renumbered
  • Migration hygiene passed (uv run --no-sync python src/manage.py check_migration_hygiene --strict)
  • Not applicable (no database changes)

Related Issues

#986

elemated and others added 2 commits August 24, 2026 20:06
- Restores `ruff check src` to zero, which had been failing on a single
  I001 since the tmdb-backdrops-api merge (7a47eec).
- Import order only, no behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- GET /api/v1/podcasts/lookup/{itunes_id}/ previews an iTunes search result
  before anything is imported, returning show metadata plus the feed's
  episodes in the standard limit/offset envelope, and `show_id` for the
  show an import would reuse.
- Shares the import path's metadata resolution and dedupe through two new
  helpers in podcast_import, and caches the feed read for 15 minutes so
  paging through search results does not re-download it.
- Registers the route in the schema-finding allowlist and the auth-coverage
  endpoint cases.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread src/api/fork_views_podcast.py Fixed
- The unreachable-provider branch of ProviderAPIError builds its message
  from the underlying exception, so returning str(error) exposed it to the
  caller. Reports the provider label alone, matching the provider-search
  handler in views.py. Flagged by CodeQL py/stack-trace-exposure.
- Adds a regression test asserting the failure body carries nothing else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dannyvfilms
dannyvfilms merged commit 3e63604 into dannyvfilms:latest Aug 25, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants