fix(webhooks): ignore episode-level Plex TMDB GUIDs when parsing scrobble events (#53)

Plex attaches TMDB GUIDs that identify the individual episode, not the parent series, so using them as `tmdbId` caused incorrect series lookups. Now only movie scrobbles populate `tmdbId` from the TMDB GUID; episode scrobbles rely on IMDB/TVDB IDs (and the title) to resolve the show via `resolveShowTmdbId`.

- Extract IMDB/TMDB/TVDB prefix constants to avoid magic strings
- Add `parsePlexPayload` test asserting `tmdbId` is `undefined` for episode events
- Add `processWebhook` integration test verifying `resolveShowTmdbId` is called without a TMDB ID for Plex episodes
- Hoist `makePlexForm` helper to module scope so it's available across all describe blocks
This commit is contained in:
2026-07-12 16:53:04 -04:00
committed by GitHub
parent bb3b7891d4
commit 0c747a9275
2 changed files with 77 additions and 9 deletions
+12 -3
View File
@@ -13,6 +13,10 @@ import { logEpisodeWatch, logMovieWatch } from "./tracking";
const log = createLogger("webhooks");
const IMDB_GUID_PREFIX = "imdb://";
const TMDB_GUID_PREFIX = "tmdb://";
const TVDB_GUID_PREFIX = "tvdb://";
// ─── Types ──────────────────────────────────────────────────────────
export interface WebhookEvent {
@@ -71,9 +75,14 @@ export function parsePlexPayload(formData: FormData): WebhookEvent | null {
if (Array.isArray(guids)) {
for (const g of guids) {
const id = g.id ?? "";
if (id.startsWith("tmdb://")) tmdbId = toOptionalInt(id.slice(7));
else if (id.startsWith("imdb://")) imdbId = id.slice(7);
else if (id.startsWith("tvdb://")) tvdbId = id.slice(7);
if (id.startsWith(TMDB_GUID_PREFIX)) {
// Plex episode TMDB GUIDs identify episodes, not series.
if (isMovie) tmdbId = toOptionalInt(id.slice(TMDB_GUID_PREFIX.length));
} else if (id.startsWith(IMDB_GUID_PREFIX)) {
imdbId = id.slice(IMDB_GUID_PREFIX.length);
} else if (id.startsWith(TVDB_GUID_PREFIX)) {
tvdbId = id.slice(TVDB_GUID_PREFIX.length);
}
}
}