{"id":50827193,"url":"https://github.com/joshuahiggins/moldy-pumpkins","last_synced_at":"2026-06-13T20:05:51.636Z","repository":{"id":362999586,"uuid":"119709218","full_name":"joshuahiggins/moldy-pumpkins","owner":"joshuahiggins","description":"Curated movie \u0026 TV lists published daily as JSON for Radarr/Sonarr import lists. Serverless TypeScript on AWS Lambda.","archived":false,"fork":false,"pushed_at":"2026-06-06T22:34:42.000Z","size":83,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T00:13:09.336Z","etag":null,"topics":["aws-lambda","esbuild","import-list","movies","plex","radarr","rotten-tomatoes","self-hosted","serverless","sonarr","tv","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joshuahiggins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-01-31T15:58:44.000Z","updated_at":"2026-06-06T22:34:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/joshuahiggins/moldy-pumpkins","commit_stats":null,"previous_names":["joshuahiggins/moldy-pumpkins"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/joshuahiggins/moldy-pumpkins","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuahiggins%2Fmoldy-pumpkins","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuahiggins%2Fmoldy-pumpkins/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuahiggins%2Fmoldy-pumpkins/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuahiggins%2Fmoldy-pumpkins/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joshuahiggins","download_url":"https://codeload.github.com/joshuahiggins/moldy-pumpkins/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshuahiggins%2Fmoldy-pumpkins/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34298269,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-lambda","esbuild","import-list","movies","plex","radarr","rotten-tomatoes","self-hosted","serverless","sonarr","tv","typescript"],"created_at":"2026-06-13T20:05:50.987Z","updated_at":"2026-06-13T20:05:51.630Z","avatar_url":"https://github.com/joshuahiggins.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# moldy-pumpkins\n\nCurated movie and TV browse lists, published daily as JSON for import into\n**Radarr** (movies) and **Sonarr** (TV) as custom lists. A scheduled AWS Lambda\nbuilds each list, resolves every title to an `imdb_id` (movies, via TMDB) or a\n`tvdbId` (TV, via TheTVDB), and writes the results to public S3 files you can\npoint your \\*arr instance at directly.\n\nYou do **not** need to run anything to use these lists — the URLs below are live.\n\n## Use the lists in Radarr / Sonarr\n\nAll lists are served from `https://moldy-pumpkins.s3.us-east-1.amazonaws.com/`\nand refreshed once per day.\n\n| List                                | App    | URL                                                                                         |\n| ----------------------------------- | ------ | ------------------------------------------------------------------------------------------- |\n| [Movies: Certified Fresh in Theaters](https://www.rottentomatoes.com/browse/movies_in_theaters/critics:certified_fresh~sort:popular) | Radarr | \u003chttps://moldy-pumpkins.s3.us-east-1.amazonaws.com/movies-certified-fresh-in-theaters.json\u003e |\n| [Movies: Certified Fresh at Home](https://www.rottentomatoes.com/browse/movies_at_home/critics:certified_fresh~sort:popular)     | Radarr | \u003chttps://moldy-pumpkins.s3.us-east-1.amazonaws.com/movies-certified-fresh-at-home.json\u003e     |\n| [Movies: Most Popular Coming Soon](https://www.rottentomatoes.com/browse/movies_coming_soon/sort:popular)    | Radarr | \u003chttps://moldy-pumpkins.s3.us-east-1.amazonaws.com/movies-most-popular-coming-soon.json\u003e    |\n| [TV: Certified Fresh Newest](https://www.rottentomatoes.com/browse/tv_series_browse/critics:fresh~sort:newest)          | Sonarr | \u003chttps://moldy-pumpkins.s3.us-east-1.amazonaws.com/tv-certified-fresh-newest.json\u003e          |\n| [TV: Certified Fresh Most Popular](https://www.rottentomatoes.com/browse/tv_series_browse/critics:fresh~sort:popular)    | Sonarr | \u003chttps://moldy-pumpkins.s3.us-east-1.amazonaws.com/tv-certified-fresh-most-popular.json\u003e    |\n\n### Add a movie list to Radarr\n\n1. Go to **Settings → Lists** and click **+** to add a list.\n2. Under **Advanced**, choose **Custom Lists**.\n3. Give it a **Name**, then paste one of the movie URLs above into **List URL**.\n4. Set **Monitor**, **Quality Profile**, **Root Folder**, and any tags you want\n   applied to titles from this list.\n5. Click **Test**, then **Save**.\n\n### Add a TV list to Sonarr\n\n1. Go to **Settings → Import Lists** and click **+** to add a list.\n2. Under **Advanced List**, choose **Custom List**.\n3. Give it a **Name**, then paste one of the TV URLs above into **List URL**.\n4. Set **Quality Profile**, **Root Folder**, and **Monitor** options.\n5. Click **Test**, then **Save**.\n\n### Notes\n\n- Lists regenerate once per day; there is no benefit to polling more often.\n- Titles drop off as they roll off the source browse lists. To keep what you've\n  already grabbed, enable a **List Exclusions**-friendly monitor setting or take\n  a library snapshot rather than letting Radarr/Sonarr unmonitor removed titles.\n- Movie entries are `{ \"title\", \"imdb_id\", \"poster_url\" }`; TV entries are\n  `{ \"title\", \"tvdbId\", \"poster_url\" }`.\n\n---\n\n## Run it yourself\n\nEverything below is for self-hosters who want to publish their own copy of the\nlists to their own S3 bucket. Most users do not need this.\n\n### How it works\n\nA single Lambda runs on a daily schedule and, for each configured list:\n\n1. Fetches the source browse page and parses out the titles.\n2. Resolves each title to an external ID — movies to `imdb_id` via TMDB, TV to\n   `tvdbId` via TheTVDB — and attaches a poster URL.\n3. Uploads the resolved array as JSON to a public S3 object.\n\nEach list is isolated: one list failing (or returning empty) never overwrites a\nhealthy live list, and a run only fails outright if _every_ list fails.\n\n### Tech stack\n\n- **Node 22** + **TypeScript** (ESM), bundled with **esbuild**\n- **AWS Lambda** + **SAM** / CloudFormation (arm64)\n- **cheerio** for HTML parsing, global `fetch` with retry/backoff\n- **AWS SDK v3** (`@aws-sdk/client-s3`) for uploads\n- `node:test` for tests (no network or credentials required)\n\n### Environment variables\n\nConfiguration is read lazily from environment variables (see `src/config.ts`).\n\n| Variable          | Required | Default                           | Purpose                                                                                                     |\n| ----------------- | -------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| `TMDB_KEY`        | yes      | —                                 | TMDB v3 API key, used to resolve movie titles to `imdb_id`.                                                 |\n| `TVDB_KEY`        | yes      | —                                 | TheTVDB v4 API key, used to resolve TV titles to `tvdbId`.                                                  |\n| `TVDB_PIN`        | no       | _(empty)_                         | TheTVDB subscriber PIN. Only required for user-subscription keys; leave unset for project/v4 keys.          |\n| `TMDB_POSTER_URL` | no       | `https://image.tmdb.org/t/p/w500` | Base URL prepended to TMDB poster paths.                                                                    |\n| `S3_BUCKET`       | no       | `moldy-pumpkins`                  | Destination bucket for the JSON lists.                                                                      |\n| `DRY_RUN`         | no       | `false`                           | When `true` (or `1`), resolve and log the lists instead of uploading. See [Dry run](#dry-run-no-s3-writes). |\n\n\u003e The bucket variable is `S3_BUCKET`, not `AWS_BUCKET`: AWS Lambda reserves all\n\u003e `AWS_`-prefixed environment-variable names, so the function would refuse to\n\u003e deploy with that name.\n\nIf a required variable (`TMDB_KEY` or `TVDB_KEY`) is unset when the handler runs,\nit logs a `missing required configuration` error naming the absent variables, so\na misconfigured deployment is obvious in the logs rather than surfacing only as\nunresolved titles.\n\n### Develop\n\nRequirements:\n\n- Node 22 (`nvm use`)\n- AWS CLI configured with credentials that can `s3:PutObject` to your bucket\n  (for a real `npm start` only)\n\nInstall and run the checks:\n\n```sh\nnpm install\nnpm test         # runs against injected fakes + frozen fixtures — no creds/network\nnpm run lint\nnpm run typecheck\nnpm run format   # or format:check in CI\n```\n\n#### Setting variables locally\n\nLocal runs load a git-ignored `.env` file via `dotenv` (wired up in\n`src/local.ts`, the local entry point — the deployed Lambda never imports it):\n\n```sh\n# .env\nTMDB_KEY=your_tmdb_v3_key\nTVDB_KEY=your_thetvdb_v4_key\n# TVDB_PIN=your_pin            # only if your TheTVDB key requires one\n# TMDB_POSTER_URL=https://image.tmdb.org/t/p/w500\n# S3_BUCKET=moldy-pumpkins     # override to write to a test bucket\n```\n\nOr export them in your shell before running:\n\n```sh\nexport TMDB_KEY=your_tmdb_v3_key\nexport TVDB_KEY=your_thetvdb_v4_key\n```\n\n#### Running locally\n\n`npm start` invokes the handler once and prints the per-list summary as JSON.\nThis performs a **real** fetch, real TMDB/TheTVDB calls, and a real `PutObject`,\nso you need both the API keys above and AWS credentials in scope. Point\n`S3_BUCKET` at a scratch bucket if you do not want to overwrite live lists.\n\n```sh\nnpm start\n```\n\n#### Dry run (no S3 writes)\n\nSet `DRY_RUN=true` to resolve every list and log the results **without**\nuploading. This exercises the full fetch-and-resolve pipeline with only the\nTMDB/TheTVDB keys — no AWS credentials or bucket access required.\n\n```sh\nnpm run start:dry          # equivalent to: DRY_RUN=true npm start\n```\n\nEach list prints a `dry run - skipping upload` log line with the resolved\nrecords, and the summary marks every list `{ \"written\": false, \"dryRun\": true }`.\n\n### Deploy\n\nThe deployed Lambda does **not** read a `.env` file — its environment variables\nare set by CloudFormation from the SAM template parameters in `template.yaml`.\nYou supply secret values as parameter overrides at deploy time.\n\n`BucketName` is **required** and has no default: S3 bucket names share one global\nnamespace, so each deployment must supply its own globally-unique name (which is\nalso the bucket SAM creates).\n\nFirst-time, interactive deploy (prompts for region, stack name, bucket name, and\nthe secret parameters, then saves your answers to `samconfig.toml`):\n\n```sh\nnpm run deploy:guided\n```\n\nWhen prompted, supply your bucket name and credentials:\n\n```\nBucketName: your-unique-bucket-name\nTmdbKey: \u003cTMDB_KEY\u003e\nTvdbKey: \u003cTVDB_KEY\u003e\nTvdbPin: \u003cTVDB_PIN\u003e   # leave blank if unused\n```\n\nSubsequent deploys reuse the saved config:\n\n```sh\nnpm run deploy\n```\n\nBoth scripts run `sam build` first, so the deployed artifact always reflects the\ncurrent source. To pass parameters non-interactively (e.g. in CI):\n\n```sh\nnpm run deploy -- --parameter-overrides \\\n  BucketName=your-unique-bucket-name TmdbKey=\u003cTMDB_KEY\u003e TvdbKey=\u003cTVDB_KEY\u003e\n```\n\n| Template parameter | Maps to env var   | Notes                                                          |\n| ------------------ | ----------------- | -------------------------------------------------------------- |\n| `BucketName`       | `S3_BUCKET`       | **Required**, no default. Names the public bucket SAM creates. |\n| `TmdbKey`          | `TMDB_KEY`        | `NoEcho` — not shown in CloudFormation output.                 |\n| `TvdbKey`          | `TVDB_KEY`        | `NoEcho`.                                                      |\n| `TvdbPin`          | `TVDB_PIN`        | `NoEcho`; defaults to empty. Omit the override if unused.      |\n| `TmdbPosterUrl`    | `TMDB_POSTER_URL` | Defaults to the TMDB w500 base URL.                            |\n\nTo rotate a key, redeploy with a new `--parameter-overrides` value — there is no\nneed to touch `.env`, which is local-only.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshuahiggins%2Fmoldy-pumpkins","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshuahiggins%2Fmoldy-pumpkins","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshuahiggins%2Fmoldy-pumpkins/lists"}