Codync
免費、開源的 1:1 替代方案,取代 Grok Bot 與 Muse。從 iPhone、Mac 或 Linux 桌面將 Claude Code、Codex、Cursor 與 40+ 程式碼代理人作為持續機器人傳訊。群組聊天、主題串…
AI 翻譯
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Python 3.13 and FastAPI power the API for https://www.podrapp.com/. The application
runs on Cloudflare Python Workers; a Python container supplies upstream egress for
iTunes and podcast RSS feeds, and parses feeds to JSON for /feed, which needs more
CPU than a Worker request allows.
Install Python 3.13, uv, Node.js 24 (used internally by pywrangler), and Docker (for the upstream proxy). Then:
uv sync --frozen
uv run pywrangler dev
For a standalone ASGI server without Cloudflare bindings or Docker:
uv run uvicorn podr.app:app --app-dir src --reload
Local ASGI mode uses bounded in-memory caching and direct upstream HTTP. Optional cloud features are disabled without bindings; Apple may reject direct requests. Use Wrangler to exercise production bindings and container egress.
The included devcontainer installs Python, uv, Node.js, GitHub CLI, and Docker client support. It reuses the host Docker daemon and persistent uv/npm cache volumes, so container rebuilds avoid reinstalling the toolchain and redownloading dependencies.
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run pywrangler deploy --dry-run --outdir=dist --containers-rollout=none
docker build -t podr-proxy .
The same commands are available as mise run check, mise run dev, mise run local,
and mise run container.
Base URL: https://podr-service.cascadiacollections.workers.dev
| Endpoint | Behavior |
|---|---|
GET / | Generated OpenAPI schema when q is absent |
GET /?q=python&limit=15 | Podcast search, limit 1–200 |
GET /?q=toppodcasts&limit=15&genre=1312 | Top podcasts, optional genre |
GET /podcast/42?summary=true | Metadata and recent episodes; optional RSS description |
GET /related?id=42&limit=10 | Genre-related podcasts, limit 1–20 |
GET /feed?rss_url=<feed>&count=300 | RSS 2.0 or Atom feed as JSON {status, feed, items}, newest first, count 1–300; parsed in the container, cached 30 minutes (longer if the feed's max-age says so, up to 24 hours) and revalidated with the feed's ETag/Last-Modified; weak ETag with If-None-Match → 304; feed.redirectedUrl appears when the feed moved permanently (301/308) |
GET /trending?limit=10&country=US | Seven-day trending queries, limit 1–50 |
GET /suggest?q=py&limit=5 | Prefix suggestions, limit 1–10 |
GET /semantic-search?q=python&limit=10 | Workers AI and Vectorize discovery |
GET /health | Liveness and circuit-breaker state |
GET /health/deep | Upstream probe through the container; 503 if unhealthy |
GET /openapi.json | Generated OpenAPI schema |
GET /docs, GET /redoc | Interactive API documentation |
Requests retain legacy integer-prefix parsing, clamping, plain-text errors, and GET-only behavior. Search responses retain provider fields. Health probes bypass rate limiting; other endpoints use 100 requests per minute per client IP when the binding is configured. Security and CORS headers are included on all responses.
src/podr/app.py: FastAPI routes, HTTP policy, generated documentation.src/podr/models.py: Pydantic response contracts.src/podr/validation.py: compatibility rules for query parameters.src/podr/services.py: iTunes, Podcast Index, RSS, related and semantic discovery.src/podr/cache.py: cache freshness, background revalidation, circuit breaker.src/podr/analytics.py: D1 aggregation, Analytics Engine metrics, R2 events.src/podr/runtime.py: native Python and Cloudflare binding adapters.src/entry.py: Workers ASGI entrypoint, daily cron and ITunesProxy Durable Object.container_src/main.py: streaming FastAPI/httpx upstream proxy.The Durable Object uses Cloudflare's low-level container API to manage readiness, port 8080, and five-minute idle shutdown in Python. Its class name, instance name, Worker name and migration history are preserved. No JavaScript application or Go proxy is required.
| Response | Fresh TTL |
|---|---|
| Search | 24 hours |
| Top podcasts | 2 hours |
| Podcast details and related | 4 hours |
| Semantic response | 1 hour |
| Trending and suggestions | 5 minutes |
| Root schema | 1 year, immutable |
Upstream cache entries retain an additional 24-hour stale window. Explicit storage timestamps determine freshness; stale responses trigger background refresh. Five upstream failures open the per-isolate circuit for 30 seconds. Stale entries can be served while the circuit is open. Analytics and cache writes use background work.
The daily 02:00 UTC cron warms the top ten D1 queries, or a fixed fallback list, using batches of five with 500 ms between batches.
wrangler.jsonc preserves the existing KV, R2, Analytics Engine, rate limiter,
Workers AI, and container bindings. D1 and Vectorize remain commented out until
provisioned. Set KV keys named flag:<name> to the literal string true:
| Flag | Behavior | Default |
|---|---|---|
trendingQueries | Enables trending and suggestions | false |
semanticSearch | Enables semantic search | false |
podcastSummaries | Enables requested RSS descriptions | false |
podcastIndex | Uses Podcast Index if both credentials exist | false |
enhancedCaching and analyticsExport were unused flags in the previous code.
R2 export continues whenever ANALYTICS_LAKE is configured, preserving actual
production behavior. R2 events contain query text; SHA-256 query hashes support D1
aggregation but do not anonymize its stored normalized queries.
Podcast Index credentials are Worker secrets:
uv run pywrangler secret put PODCAST_INDEX_KEY
uv run pywrangler secret put PODCAST_INDEX_SECRET
For D1, apply all migrations before enabling DB. Migration 0003 preserves old
rows and makes (query_hash, date, country) unique so country counts and atomic
upserts work. Empty country denotes global/unknown origin. Existing installations
must apply 0003 before deploying this Python version with DB enabled.
CI runs lint, formatting, tests, Worker dry-run packaging and a container build.
Successful CI for a push to main triggers deployment of that exact commit. Configure
CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID in GitHub secrets.
GitHub Actions is the only supported deploy path. Cloudflare's built-in Git
integration ("Workers Builds") must stay disconnected from this repository. It cannot
build the project: pywrangler vendors the Python dependencies into the Worker
bundle, so the npx wrangler deploy that Workers Builds runs by default cannot
produce a working deployment, and reproducing the real build there would duplicate
deploy.yml while skipping the CI gate that makes it safe. If a
Workers Builds: podr-service check reappears on pull requests, the repository has
been reconnected; disconnect it again under Workers & Pages > podr-service >
Settings > Build rather than trying to make that check pass.
uv run pywrangler deploy
Before the first production rollout, verify the Python Worker and container together in Wrangler/staging and measure CPU usage against the preserved 10 ms CPU budget. CPython contract tests do not validate Cloudflare's JavaScript FFI or actual Apple connectivity. For rollback, deploy the previous Git revision with its original Bun/TypeScript build and Go container.