From 0be18968b4d9bd58b2f17fbfe7154b855c566509 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+fallenbagel@users.noreply.github.com> Date: Sat, 14 Mar 2026 23:47:21 +0500 Subject: [PATCH] fix: disambiguate tmdb ids by media type across lookups (#2577) --- server/entity/Blocklist.ts | 3 +- server/entity/Media.ts | 17 +- server/entity/Watchlist.ts | 4 +- server/job/blocklistedTagsProcessor.ts | 8 +- server/lib/scanners/plex/index.ts | 22 +- server/lib/watchlistsync.ts | 25 +- ...8000333-AddMediaTypeToUniqueConstraints.ts | 51 ++++ ...7972752-AddMediaTypeToUniqueConstraints.ts | 225 ++++++++++++++++++ server/routes/blocklist.ts | 35 ++- server/routes/collection.ts | 6 +- server/routes/discover.ts | 60 ++++- server/routes/movie.ts | 11 +- server/routes/person.ts | 14 +- server/routes/search.ts | 5 +- server/routes/tv.ts | 11 +- server/routes/watchlist.ts | 17 +- src/components/Blocklist/index.tsx | 9 +- src/components/BlocklistBlock/index.tsx | 9 +- src/components/ManageSlideOver/index.tsx | 1 + src/components/MovieDetails/index.tsx | 4 +- src/components/TitleCard/index.tsx | 8 +- src/components/TvDetails/index.tsx | 4 +- 22 files changed, 479 insertions(+), 70 deletions(-) create mode 100644 server/migration/postgres/1772048000333-AddMediaTypeToUniqueConstraints.ts create mode 100644 server/migration/sqlite/1772047972752-AddMediaTypeToUniqueConstraints.ts diff --git a/server/entity/Blocklist.ts b/server/entity/Blocklist.ts index 2cf32979..df994365 100644 --- a/server/entity/Blocklist.ts +++ b/server/entity/Blocklist.ts @@ -18,7 +18,7 @@ import { import type { ZodNumber, ZodOptional, ZodString } from 'zod'; @Entity() -@Unique(['tmdbId']) +@Unique(['tmdbId', 'mediaType']) export class Blocklist implements BlocklistItem { @PrimaryGeneratedColumn() public id: number; @@ -77,6 +77,7 @@ export class Blocklist implements BlocklistItem { let media = await mediaRepository.findOne({ where: { tmdbId: blocklistRequest.tmdbId, + mediaType: blocklistRequest.mediaType, }, }); diff --git a/server/entity/Media.ts b/server/entity/Media.ts index de1b68ca..3746a6c0 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -30,22 +30,17 @@ import Season from './Season'; class Media { public static async getRelatedMedia( user: User | undefined, - tmdbIds: number | number[] + items: { tmdbId: number; mediaType: string }[] ): Promise { const mediaRepository = getRepository(Media); try { - let finalIds: number[]; - if (!Array.isArray(tmdbIds)) { - finalIds = [tmdbIds]; - } else { - finalIds = tmdbIds; - } - - if (finalIds.length === 0) { + if (items.length === 0) { return []; } + const finalIds = [...new Set(items.map((i) => i.tmdbId))]; + const media = await mediaRepository .createQueryBuilder('media') .leftJoinAndSelect( @@ -57,7 +52,9 @@ class Media { .where(' media.tmdbId in (:...finalIds)', { finalIds }) .getMany(); - return media; + return media.filter((m) => + items.some((i) => i.tmdbId === m.tmdbId && i.mediaType === m.mediaType) + ); } catch (e) { logger.error(e.message); return []; diff --git a/server/entity/Watchlist.ts b/server/entity/Watchlist.ts index 54e65140..a84f0c42 100644 --- a/server/entity/Watchlist.ts +++ b/server/entity/Watchlist.ts @@ -25,7 +25,7 @@ export class NotFoundError extends Error { } @Entity() -@Unique('UNIQUE_USER_DB', ['tmdbId', 'requestedBy']) +@Unique('UNIQUE_USER_DB', ['tmdbId', 'mediaType', 'requestedBy']) export class Watchlist implements WatchlistItem { @PrimaryGeneratedColumn() id: number; @@ -142,11 +142,13 @@ export class Watchlist implements WatchlistItem { public static async deleteWatchlist( tmdbId: Watchlist['tmdbId'], + mediaType: MediaType, user: User ): Promise { const watchlistRepository = getRepository(this); const watchlist = await watchlistRepository.findOneBy({ tmdbId, + mediaType, requestedBy: { id: user.id }, }); if (!watchlist) { diff --git a/server/job/blocklistedTagsProcessor.ts b/server/job/blocklistedTagsProcessor.ts index d6bf7509..37cf3124 100644 --- a/server/job/blocklistedTagsProcessor.ts +++ b/server/job/blocklistedTagsProcessor.ts @@ -173,7 +173,7 @@ class BlocklistedTagProcessor implements RunnableScanner { for (const entry of response.results) { const blocklistEntry = await blocklistRepository.findOne({ - where: { tmdbId: entry.id }, + where: { tmdbId: entry.id, mediaType }, }); if (blocklistEntry) { @@ -209,7 +209,11 @@ class BlocklistedTagProcessor implements RunnableScanner { const mediaRepository = em.getRepository(Media); const mediaToRemove = await mediaRepository .createQueryBuilder('media') - .innerJoinAndSelect(Blocklist, 'blist', 'blist.tmdbId = media.tmdbId') + .innerJoinAndSelect( + Blocklist, + 'blist', + 'blist.tmdbId = media.tmdbId AND blist.mediaType = media.mediaType' + ) .where(`blist.blocklistedTags IS NOT NULL`) .getMany(); diff --git a/server/lib/scanners/plex/index.ts b/server/lib/scanners/plex/index.ts index b464008e..58111ca8 100644 --- a/server/lib/scanners/plex/index.ts +++ b/server/lib/scanners/plex/index.ts @@ -367,18 +367,16 @@ class PlexScanner } } - if (mediaIds.tvdbId) { - await this.processShow( - mediaIds.tmdbId, - mediaIds.tvdbId ?? tvShow.external_ids.tvdb_id, - processableSeasons, - { - mediaAddedAt: new Date(metadata.addedAt * 1000), - ratingKey: ratingKey, - title: metadata.title, - } - ); - } + await this.processShow( + mediaIds.tmdbId, + mediaIds.tvdbId ?? tvShow.external_ids.tvdb_id, + processableSeasons, + { + mediaAddedAt: new Date(metadata.addedAt * 1000), + ratingKey: ratingKey, + title: metadata.title, + } + ); } private async getMediaIds(plexitem: PlexLibraryItem): Promise { diff --git a/server/lib/watchlistsync.ts b/server/lib/watchlistsync.ts index 8afea0df..bc721e32 100644 --- a/server/lib/watchlistsync.ts +++ b/server/lib/watchlistsync.ts @@ -67,7 +67,10 @@ class WatchlistSync { const mediaItems = await Media.getRelatedMedia( user, - response.items.map((i) => i.tmdbId) + response.items.map((i) => ({ + tmdbId: i.tmdbId, + mediaType: i.type === 'show' ? MediaType.TV : MediaType.MOVIE, + })) ); const watchlistTmdbIds = response.items.map((i) => i.tmdbId); @@ -87,19 +90,23 @@ class WatchlistSync { .map((r) => `${r.media.mediaType}:${r.media.tmdbId}`) ); - const unavailableItems = response.items.filter( - (i) => - !autoRequestedTmdbIds.has( - `${i.type === 'show' ? MediaType.TV : MediaType.MOVIE}:${i.tmdbId}` - ) && + const unavailableItems = response.items.filter((i) => { + const itemMediaType = i.type === 'show' ? MediaType.TV : MediaType.MOVIE; + + return ( + !autoRequestedTmdbIds.has(`${itemMediaType}:${i.tmdbId}`) && !mediaItems.find( (m) => m.tmdbId === i.tmdbId && + m.mediaType === itemMediaType && (m.status === MediaStatus.BLOCKLISTED || - (m.status !== MediaStatus.UNKNOWN && m.mediaType === 'movie') || - (m.mediaType === 'tv' && m.status === MediaStatus.AVAILABLE)) + (itemMediaType === MediaType.MOVIE && + m.status !== MediaStatus.UNKNOWN) || + (itemMediaType === MediaType.TV && + m.status === MediaStatus.AVAILABLE)) ) - ); + ); + }); for (const mediaItem of unavailableItems) { try { diff --git a/server/migration/postgres/1772048000333-AddMediaTypeToUniqueConstraints.ts b/server/migration/postgres/1772048000333-AddMediaTypeToUniqueConstraints.ts new file mode 100644 index 00000000..62fd79b2 --- /dev/null +++ b/server/migration/postgres/1772048000333-AddMediaTypeToUniqueConstraints.ts @@ -0,0 +1,51 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMediaTypeToUniqueConstraints1772048000333 implements MigrationInterface { + name = 'AddMediaTypeToUniqueConstraints1772048000333'; + + public async up(queryRunner: QueryRunner): Promise { + // Manually added: TypeORM migration:generate does not detect changes to named unique constraints. + await queryRunner.query( + `ALTER TABLE "watchlist" DROP CONSTRAINT "UNIQUE_USER_DB"` + ); + await queryRunner.query( + `ALTER TABLE "watchlist" ADD CONSTRAINT "UNIQUE_USER_DB" UNIQUE ("tmdbId", "mediaType", "requestedById")` + ); + + // Auto-generated by TypeORM + await queryRunner.query( + `CREATE SEQUENCE IF NOT EXISTS "blocklist_id_seq" OWNED BY "blocklist"."id"` + ); + await queryRunner.query( + `ALTER TABLE "blocklist" ALTER COLUMN "id" SET DEFAULT nextval('"blocklist_id_seq"')` + ); + await queryRunner.query( + `ALTER TABLE "blocklist" DROP CONSTRAINT "UQ_6bbafa28411e6046421991ea21c"` + ); + await queryRunner.query( + `ALTER TABLE "blocklist" ADD CONSTRAINT "UQ_81504e02db89b4c1e3152729fa6" UNIQUE ("tmdbId", "mediaType")` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // Manually added: TypeORM migration:generate does not detect changes to named unique constraints. + await queryRunner.query( + `ALTER TABLE "watchlist" DROP CONSTRAINT "UNIQUE_USER_DB"` + ); + await queryRunner.query( + `ALTER TABLE "watchlist" ADD CONSTRAINT "UNIQUE_USER_DB" UNIQUE ("tmdbId", "requestedById")` + ); + + // Auto-generated by TypeORM + await queryRunner.query( + `ALTER TABLE "blocklist" DROP CONSTRAINT "UQ_81504e02db89b4c1e3152729fa6"` + ); + await queryRunner.query( + `ALTER TABLE "blocklist" ADD CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId")` + ); + await queryRunner.query( + `ALTER TABLE "blocklist" ALTER COLUMN "id" DROP DEFAULT` + ); + await queryRunner.query(`DROP SEQUENCE "blocklist_id_seq"`); + } +} diff --git a/server/migration/sqlite/1772047972752-AddMediaTypeToUniqueConstraints.ts b/server/migration/sqlite/1772047972752-AddMediaTypeToUniqueConstraints.ts new file mode 100644 index 00000000..2c55f76f --- /dev/null +++ b/server/migration/sqlite/1772047972752-AddMediaTypeToUniqueConstraints.ts @@ -0,0 +1,225 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMediaTypeToUniqueConstraints1772047972752 implements MigrationInterface { + name = 'AddMediaTypeToUniqueConstraints1772047972752'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_03f7958328e311761b0de675fb"`); + await queryRunner.query( + `CREATE TABLE "temporary_user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b" UNIQUE ("auth"), CONSTRAINT "UQ_6427d07d9a171a3a1ab87480005" UNIQUE ("endpoint", "userId"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "user_push_subscription"` + ); + await queryRunner.query(`DROP TABLE "user_push_subscription"`); + await queryRunner.query( + `ALTER TABLE "temporary_user_push_subscription" RENAME TO "user_push_subscription"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_03f7958328e311761b0de675fb" ON "user_push_subscription" ("userId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_356721a49f145aa439c16e6b99"`); + await queryRunner.query(`DROP INDEX "IDX_09b94c932e84635c5461f3c0a9"`); + await queryRunner.query( + `CREATE TABLE "temporary_blocklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blocklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_5c8af2d0e83b3be6d250eccc19d" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_356721a49f145aa439c16e6b999" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_blocklist"("id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId" FROM "blocklist"` + ); + await queryRunner.query(`DROP TABLE "blocklist"`); + await queryRunner.query( + `ALTER TABLE "temporary_blocklist" RENAME TO "blocklist"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_356721a49f145aa439c16e6b99" ON "blocklist" ("userId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_09b94c932e84635c5461f3c0a9" ON "blocklist" ("tmdbId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_03f7958328e311761b0de675fb"`); + await queryRunner.query( + `CREATE TABLE "temporary_user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b" UNIQUE ("auth"), CONSTRAINT "UQ_6427d07d9a171a3a1ab87480005" UNIQUE ("endpoint", "userId"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "user_push_subscription"` + ); + await queryRunner.query(`DROP TABLE "user_push_subscription"`); + await queryRunner.query( + `ALTER TABLE "temporary_user_push_subscription" RENAME TO "user_push_subscription"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_03f7958328e311761b0de675fb" ON "user_push_subscription" ("userId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_356721a49f145aa439c16e6b99"`); + await queryRunner.query(`DROP INDEX "IDX_09b94c932e84635c5461f3c0a9"`); + await queryRunner.query( + `CREATE TABLE "temporary_blocklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blocklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_5c8af2d0e83b3be6d250eccc19d" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_356721a49f145aa439c16e6b999" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_blocklist"("id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId" FROM "blocklist"` + ); + await queryRunner.query(`DROP TABLE "blocklist"`); + await queryRunner.query( + `ALTER TABLE "temporary_blocklist" RENAME TO "blocklist"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_356721a49f145aa439c16e6b99" ON "blocklist" ("userId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_09b94c932e84635c5461f3c0a9" ON "blocklist" ("tmdbId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_356721a49f145aa439c16e6b99"`); + await queryRunner.query(`DROP INDEX "IDX_09b94c932e84635c5461f3c0a9"`); + + await queryRunner.query( + `CREATE TABLE "temporary_blocklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blocklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "userId" integer, "mediaId" integer, CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "UQ_81504e02db89b4c1e3152729fa6" UNIQUE ("tmdbId", "mediaType"), CONSTRAINT "FK_5c8af2d0e83b3be6d250eccc19d" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_356721a49f145aa439c16e6b999" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_blocklist"("id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId" FROM "blocklist"` + ); + await queryRunner.query(`DROP TABLE "blocklist"`); + await queryRunner.query( + `ALTER TABLE "temporary_blocklist" RENAME TO "blocklist"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_356721a49f145aa439c16e6b99" ON "blocklist" ("userId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_09b94c932e84635c5461f3c0a9" ON "blocklist" ("tmdbId") ` + ); + + // Manually added as TypeORM migration:generate does not detect changes to named unique constraints. + await queryRunner.query(`DROP INDEX "IDX_939f205946256cc0d2a1ac51a8"`); + await queryRunner.query(`DROP INDEX "IDX_ae34e6b153a90672eb9dc4857d"`); + await queryRunner.query(`DROP INDEX "IDX_6641da8d831b93dfcb429f8b8b"`); + await queryRunner.query( + `CREATE TABLE "temporary_watchlist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "ratingKey" varchar NOT NULL, "mediaType" varchar NOT NULL, "title" varchar NOT NULL, "tmdbId" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "updatedAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "requestedById" integer, "mediaId" integer, CONSTRAINT "UNIQUE_USER_DB" UNIQUE ("tmdbId", "mediaType", "requestedById"), CONSTRAINT "FK_6641da8d831b93dfcb429f8b8bc" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_ae34e6b153a90672eb9dc4857d7" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_watchlist"("id", "ratingKey", "mediaType", "title", "tmdbId", "createdAt", "updatedAt", "requestedById", "mediaId") SELECT "id", "ratingKey", "mediaType", "title", "tmdbId", "createdAt", "updatedAt", "requestedById", "mediaId" FROM "watchlist"` + ); + await queryRunner.query(`DROP TABLE "watchlist"`); + await queryRunner.query( + `ALTER TABLE "temporary_watchlist" RENAME TO "watchlist"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_939f205946256cc0d2a1ac51a8" ON "watchlist" ("tmdbId")` + ); + await queryRunner.query( + `CREATE INDEX "IDX_ae34e6b153a90672eb9dc4857d" ON "watchlist" ("requestedById")` + ); + await queryRunner.query( + `CREATE INDEX "IDX_6641da8d831b93dfcb429f8b8b" ON "watchlist" ("mediaId")` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // Manually added as TypeORM migration:generate does not detect changes to named unique constraints. + await queryRunner.query(`DROP INDEX "IDX_939f205946256cc0d2a1ac51a8"`); + await queryRunner.query(`DROP INDEX "IDX_ae34e6b153a90672eb9dc4857d"`); + await queryRunner.query(`DROP INDEX "IDX_6641da8d831b93dfcb429f8b8b"`); + await queryRunner.query( + `CREATE TABLE "temporary_watchlist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "ratingKey" varchar NOT NULL, "mediaType" varchar NOT NULL, "title" varchar NOT NULL, "tmdbId" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "updatedAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "requestedById" integer, "mediaId" integer, CONSTRAINT "UNIQUE_USER_DB" UNIQUE ("tmdbId", "requestedById"), CONSTRAINT "FK_6641da8d831b93dfcb429f8b8bc" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_ae34e6b153a90672eb9dc4857d7" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_watchlist"("id", "ratingKey", "mediaType", "title", "tmdbId", "createdAt", "updatedAt", "requestedById", "mediaId") SELECT "id", "ratingKey", "mediaType", "title", "tmdbId", "createdAt", "updatedAt", "requestedById", "mediaId" FROM "watchlist"` + ); + await queryRunner.query(`DROP TABLE "watchlist"`); + await queryRunner.query( + `ALTER TABLE "temporary_watchlist" RENAME TO "watchlist"` + ); + await queryRunner.query( + `CREATE INDEX "IDX_939f205946256cc0d2a1ac51a8" ON "watchlist" ("tmdbId")` + ); + await queryRunner.query( + `CREATE INDEX "IDX_ae34e6b153a90672eb9dc4857d" ON "watchlist" ("requestedById")` + ); + await queryRunner.query( + `CREATE INDEX "IDX_6641da8d831b93dfcb429f8b8b" ON "watchlist" ("mediaId")` + ); + + // Blocklist: revert to original + await queryRunner.query(`DROP INDEX "IDX_09b94c932e84635c5461f3c0a9"`); + await queryRunner.query(`DROP INDEX "IDX_356721a49f145aa439c16e6b99"`); + await queryRunner.query( + `ALTER TABLE "blocklist" RENAME TO "temporary_blocklist"` + ); + await queryRunner.query( + `CREATE TABLE "blocklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blocklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_5c8af2d0e83b3be6d250eccc19d" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_356721a49f145aa439c16e6b999" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "blocklist"("id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId" FROM "temporary_blocklist"` + ); + await queryRunner.query(`DROP TABLE "temporary_blocklist"`); + await queryRunner.query( + `CREATE INDEX "IDX_09b94c932e84635c5461f3c0a9" ON "blocklist" ("tmdbId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_356721a49f145aa439c16e6b99" ON "blocklist" ("userId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_09b94c932e84635c5461f3c0a9"`); + await queryRunner.query(`DROP INDEX "IDX_356721a49f145aa439c16e6b99"`); + await queryRunner.query( + `ALTER TABLE "blocklist" RENAME TO "temporary_blocklist"` + ); + await queryRunner.query( + `CREATE TABLE "blocklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blocklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_5c8af2d0e83b3be6d250eccc19d" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_356721a49f145aa439c16e6b999" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "blocklist"("id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId" FROM "temporary_blocklist"` + ); + await queryRunner.query(`DROP TABLE "temporary_blocklist"`); + await queryRunner.query( + `CREATE INDEX "IDX_09b94c932e84635c5461f3c0a9" ON "blocklist" ("tmdbId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_356721a49f145aa439c16e6b99" ON "blocklist" ("userId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_03f7958328e311761b0de675fb"`); + await queryRunner.query( + `ALTER TABLE "user_push_subscription" RENAME TO "temporary_user_push_subscription"` + ); + await queryRunner.query( + `CREATE TABLE "user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b" UNIQUE ("auth"), CONSTRAINT "UQ_6427d07d9a171a3a1ab87480005" UNIQUE ("endpoint", "userId"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "temporary_user_push_subscription"` + ); + await queryRunner.query(`DROP TABLE "temporary_user_push_subscription"`); + await queryRunner.query( + `CREATE INDEX "IDX_03f7958328e311761b0de675fb" ON "user_push_subscription" ("userId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_09b94c932e84635c5461f3c0a9"`); + await queryRunner.query(`DROP INDEX "IDX_356721a49f145aa439c16e6b99"`); + await queryRunner.query( + `ALTER TABLE "blocklist" RENAME TO "temporary_blocklist"` + ); + await queryRunner.query( + `CREATE TABLE "blocklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blocklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_5c8af2d0e83b3be6d250eccc19d" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_356721a49f145aa439c16e6b999" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "blocklist"("id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blocklistedTags", "createdAt", "userId", "mediaId" FROM "temporary_blocklist"` + ); + await queryRunner.query(`DROP TABLE "temporary_blocklist"`); + await queryRunner.query( + `CREATE INDEX "IDX_09b94c932e84635c5461f3c0a9" ON "blocklist" ("tmdbId") ` + ); + await queryRunner.query( + `CREATE INDEX "IDX_356721a49f145aa439c16e6b99" ON "blocklist" ("userId") ` + ); + await queryRunner.query(`DROP INDEX "IDX_03f7958328e311761b0de675fb"`); + await queryRunner.query( + `ALTER TABLE "user_push_subscription" RENAME TO "temporary_user_push_subscription"` + ); + await queryRunner.query( + `CREATE TABLE "user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b" UNIQUE ("auth"), CONSTRAINT "UQ_6427d07d9a171a3a1ab87480005" UNIQUE ("endpoint", "userId"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "temporary_user_push_subscription"` + ); + await queryRunner.query(`DROP TABLE "temporary_user_push_subscription"`); + await queryRunner.query( + `CREATE INDEX "IDX_03f7958328e311761b0de675fb" ON "user_push_subscription" ("userId") ` + ); + } +} diff --git a/server/routes/blocklist.ts b/server/routes/blocklist.ts index 8a322f3e..a3ec6abd 100644 --- a/server/routes/blocklist.ts +++ b/server/routes/blocklist.ts @@ -89,18 +89,29 @@ blocklistRoutes.get( type: 'or', }), async (req, res, next) => { + const mediaType = req.query.mediaType; + if (mediaType !== MediaType.MOVIE && mediaType !== MediaType.TV) { + return next({ + status: 400, + message: 'Invalid or missing mediaType query parameter.', + }); + } + try { const blocklisteRepository = getRepository(Blocklist); const blocklistItem = await blocklisteRepository.findOneOrFail({ - where: { tmdbId: Number(req.params.id) }, + where: { + tmdbId: Number(req.params.id), + mediaType, + }, }); return res.status(200).send(blocklistItem); } catch (e) { if (e instanceof EntityNotFoundError) { return next({ - status: 401, + status: 404, message: e.message, }); } @@ -153,11 +164,22 @@ blocklistRoutes.delete( type: 'or', }), async (req, res, next) => { + const mediaType = req.query.mediaType; + if (mediaType !== MediaType.MOVIE && mediaType !== MediaType.TV) { + return next({ + status: 400, + message: 'Invalid or missing mediaType query parameter.', + }); + } + try { const blocklisteRepository = getRepository(Blocklist); const blocklistItem = await blocklisteRepository.findOneOrFail({ - where: { tmdbId: Number(req.params.id) }, + where: { + tmdbId: Number(req.params.id), + mediaType, + }, }); await blocklisteRepository.remove(blocklistItem); @@ -165,7 +187,10 @@ blocklistRoutes.delete( const mediaRepository = getRepository(Media); const mediaItem = await mediaRepository.findOneOrFail({ - where: { tmdbId: Number(req.params.id) }, + where: { + tmdbId: Number(req.params.id), + mediaType: req.query.mediaType as MediaType, + }, }); await mediaRepository.remove(mediaItem); @@ -174,7 +199,7 @@ blocklistRoutes.delete( } catch (e) { if (e instanceof EntityNotFoundError) { return next({ - status: 401, + status: 404, message: e.message, }); } diff --git a/server/routes/collection.ts b/server/routes/collection.ts index 8b1cd9ef..f2a60322 100644 --- a/server/routes/collection.ts +++ b/server/routes/collection.ts @@ -1,4 +1,5 @@ import TheMovieDb from '@server/api/themoviedb'; +import { MediaType } from '@server/constants/media'; import Media from '@server/entity/Media'; import logger from '@server/logger'; import { mapCollection } from '@server/models/Collection'; @@ -17,7 +18,10 @@ collectionRoutes.get<{ id: string }>('/:id', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - collection.parts.map((part) => part.id) + collection.parts.map((part) => ({ + tmdbId: part.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json(mapCollection(collection, media)); diff --git a/server/routes/discover.ts b/server/routes/discover.ts index d467976e..4f12ce7d 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -124,7 +124,10 @@ discoverRoutes.get('/movies', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); let keywordData: TmdbKeyword[] = []; @@ -193,7 +196,10 @@ discoverRoutes.get<{ language: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ @@ -251,7 +257,10 @@ discoverRoutes.get<{ genreId: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ @@ -299,7 +308,10 @@ discoverRoutes.get<{ studioId: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ @@ -349,7 +361,10 @@ discoverRoutes.get('/movies/upcoming', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ @@ -417,7 +432,10 @@ discoverRoutes.get('/tv', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); let keywordData: TmdbKeyword[] = []; @@ -485,7 +503,10 @@ discoverRoutes.get<{ language: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); return res.status(200).json({ @@ -543,7 +564,10 @@ discoverRoutes.get<{ genreId: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); return res.status(200).json({ @@ -591,7 +615,10 @@ discoverRoutes.get<{ networkId: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); return res.status(200).json({ @@ -641,7 +668,10 @@ discoverRoutes.get('/tv/upcoming', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); return res.status(200).json({ @@ -711,7 +741,10 @@ discoverRoutes.get('/trending', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: isMovie(result) ? MediaType.MOVIE : MediaType.TV, + })) ); return res.status(200).json({ @@ -755,7 +788,10 @@ discoverRoutes.get<{ keywordId: string }>( const media = await Media.getRelatedMedia( req.user, - data.results.map((result) => result.id) + data.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ diff --git a/server/routes/movie.ts b/server/routes/movie.ts index 80b8a300..bd86e447 100644 --- a/server/routes/movie.ts +++ b/server/routes/movie.ts @@ -27,6 +27,7 @@ movieRoutes.get('/:id', async (req, res, next) => { const onUserWatchlist = await getRepository(Watchlist).exist({ where: { tmdbId: Number(req.params.id), + mediaType: MediaType.MOVIE, requestedBy: { id: req.user?.id, }, @@ -67,7 +68,10 @@ movieRoutes.get('/:id/recommendations', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - results.results.map((result) => result.id) + results.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ @@ -109,7 +113,10 @@ movieRoutes.get('/:id/similar', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - results.results.map((result) => result.id) + results.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.MOVIE, + })) ); return res.status(200).json({ diff --git a/server/routes/person.ts b/server/routes/person.ts index 7462328c..2d88a492 100644 --- a/server/routes/person.ts +++ b/server/routes/person.ts @@ -43,12 +43,22 @@ personRoutes.get('/:id/combined_credits', async (req, res, next) => { const castMedia = await Media.getRelatedMedia( req.user, - combinedCredits.cast.map((result) => result.id) + combinedCredits.cast + .filter((result) => result.media_type) + .map((result) => ({ + tmdbId: result.id, + mediaType: result.media_type!, + })) ); const crewMedia = await Media.getRelatedMedia( req.user, - combinedCredits.crew.map((result) => result.id) + combinedCredits.crew + .filter((result) => result.media_type) + .map((result) => ({ + tmdbId: result.id, + mediaType: result.media_type!, + })) ); return res.status(200).json({ diff --git a/server/routes/search.ts b/server/routes/search.ts index ee2fd9eb..cccb38f2 100644 --- a/server/routes/search.ts +++ b/server/routes/search.ts @@ -35,7 +35,10 @@ searchRoutes.get('/', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - results.results.map((result) => result.id) + results.results.map((result) => ({ + tmdbId: result.id, + mediaType: result.media_type, + })) ); return res.status(200).json({ diff --git a/server/routes/tv.ts b/server/routes/tv.ts index f5632398..743f407e 100644 --- a/server/routes/tv.ts +++ b/server/routes/tv.ts @@ -35,6 +35,7 @@ tvRoutes.get('/:id', async (req, res, next) => { const onUserWatchlist = await getRepository(Watchlist).exist({ where: { tmdbId: Number(req.params.id), + mediaType: MediaType.TV, requestedBy: { id: req.user?.id, }, @@ -110,7 +111,10 @@ tvRoutes.get('/:id/recommendations', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - results.results.map((result) => result.id) + results.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); return res.status(200).json({ @@ -151,7 +155,10 @@ tvRoutes.get('/:id/similar', async (req, res, next) => { const media = await Media.getRelatedMedia( req.user, - results.results.map((result) => result.id) + results.results.map((result) => ({ + tmdbId: result.id, + mediaType: MediaType.TV, + })) ); return res.status(200).json({ diff --git a/server/routes/watchlist.ts b/server/routes/watchlist.ts index bbb44da0..9a975d6c 100644 --- a/server/routes/watchlist.ts +++ b/server/routes/watchlist.ts @@ -7,6 +7,7 @@ import logger from '@server/logger'; import { Router } from 'express'; import { QueryFailedError } from 'typeorm'; +import { MediaType } from '@server/constants/media'; import { watchlistCreate } from '@server/interfaces/api/watchlistCreate'; const watchlistRoutes = Router(); @@ -57,12 +58,24 @@ watchlistRoutes.delete('/:tmdbId', async (req, res, next) => { }); } try { - await Watchlist.deleteWatchlist(Number(req.params.tmdbId), req.user); + const mediaType = req.query.mediaType; + if (mediaType !== MediaType.MOVIE && mediaType !== MediaType.TV) { + return next({ + status: 400, + message: 'Invalid mediaType query parameter.', + }); + } + + await Watchlist.deleteWatchlist( + Number(req.params.tmdbId), + mediaType, + req.user + ); return res.status(204).send(); } catch (e) { if (e instanceof NotFoundError) { return next({ - status: 401, + status: 404, message: e.message, }); } diff --git a/src/components/Blocklist/index.tsx b/src/components/Blocklist/index.tsx index c25eb506..addabecd 100644 --- a/src/components/Blocklist/index.tsx +++ b/src/components/Blocklist/index.tsx @@ -178,7 +178,10 @@ const Blocklist = () => { ) : ( data.results.map((item: BlocklistItem) => { return ( -
+
); @@ -297,7 +300,9 @@ const BlocklistedItem = ({ item, revalidateList }: BlocklistedItemProps) => { setIsUpdating(true); try { - await axios.delete(`/api/v1/blocklist/${tmdbId}`); + await axios.delete( + `/api/v1/blocklist/${tmdbId}?mediaType=${item.mediaType}` + ); addToast( diff --git a/src/components/BlocklistBlock/index.tsx b/src/components/BlocklistBlock/index.tsx index 2eb48e23..af910f2f 100644 --- a/src/components/BlocklistBlock/index.tsx +++ b/src/components/BlocklistBlock/index.tsx @@ -7,6 +7,7 @@ import { useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { CalendarIcon, TrashIcon, UserIcon } from '@heroicons/react/24/solid'; +import type { MediaType } from '@server/constants/media'; import type { Blocklist } from '@server/entity/Blocklist'; import axios from 'axios'; import Link from 'next/link'; @@ -22,12 +23,14 @@ const messages = defineMessages('component.BlocklistBlock', { interface BlocklistBlockProps { tmdbId: number; + mediaType: MediaType; onUpdate?: () => void; onDelete?: () => void; } const BlocklistBlock = ({ tmdbId, + mediaType, onUpdate, onDelete, }: BlocklistBlockProps) => { @@ -35,13 +38,15 @@ const BlocklistBlock = ({ const intl = useIntl(); const [isUpdating, setIsUpdating] = useState(false); const { addToast } = useToasts(); - const { data } = useSWR(`/api/v1/blocklist/${tmdbId}`); + const { data } = useSWR( + `/api/v1/blocklist/${tmdbId}?mediaType=${mediaType}` + ); const removeFromBlocklist = async (tmdbId: number, title?: string) => { setIsUpdating(true); try { - await axios.delete('/api/v1/blocklist/' + tmdbId); + await axios.delete(`/api/v1/blocklist/${tmdbId}?mediaType=${mediaType}`); addToast( diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index df94f368..86c7808f 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -322,6 +322,7 @@ const ManageSlideOver = ({
revalidate()} onDelete={() => onClose()} /> diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index 9baae60a..4fa57e8e 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -358,7 +358,9 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { const onClickDeleteWatchlistBtn = async (): Promise => { setIsUpdating(true); try { - await axios.delete(`/api/v1/watchlist/${movie?.id}`); + await axios.delete( + `/api/v1/watchlist/${movie?.id}?mediaType=${MediaType.MOVIE}` + ); addToast( diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index bff52ead..bb722b00 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -139,7 +139,9 @@ const TitleCard = ({ const onClickDeleteWatchlistBtn = async (): Promise => { setIsUpdating(true); try { - const response = await axios.delete('/api/v1/watchlist/' + id); + const response = await axios.delete( + `/api/v1/watchlist/${id}?mediaType=${mediaType}` + ); if (response.status === 204) { addToast( @@ -223,7 +225,9 @@ const TitleCard = ({ const topNode = cardRef.current; if (topNode) { - const res = await axios.delete('/api/v1/blocklist/' + id); + const res = await axios.delete( + `/api/v1/blocklist/${id}?mediaType=${mediaType}` + ); if (res.status === 204) { addToast( diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 9f510182..8be41932 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -386,7 +386,9 @@ const TvDetails = ({ tv }: TvDetailsProps) => { setIsUpdating(true); try { - await axios.delete('/api/v1/watchlist/' + tv?.id); + await axios.delete( + `/api/v1/watchlist/${tv?.id}?mediaType=${MediaType.TV}` + ); addToast(