fix: disambiguate tmdb ids by media type across lookups (#2577)
This commit is contained in:
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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<Media[]> {
|
||||
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 [];
|
||||
|
||||
@@ -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<Watchlist | null> {
|
||||
const watchlistRepository = getRepository(this);
|
||||
const watchlist = await watchlistRepository.findOneBy({
|
||||
tmdbId,
|
||||
mediaType,
|
||||
requestedBy: { id: user.id },
|
||||
});
|
||||
if (!watchlist) {
|
||||
|
||||
@@ -173,7 +173,7 @@ class BlocklistedTagProcessor implements RunnableScanner<StatusBase> {
|
||||
|
||||
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<StatusBase> {
|
||||
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();
|
||||
|
||||
|
||||
@@ -367,7 +367,6 @@ class PlexScanner
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaIds.tvdbId) {
|
||||
await this.processShow(
|
||||
mediaIds.tmdbId,
|
||||
mediaIds.tvdbId ?? tvShow.external_ids.tvdb_id,
|
||||
@@ -379,7 +378,6 @@ class PlexScanner
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async getMediaIds(plexitem: PlexLibraryItem): Promise<MediaIds> {
|
||||
let mediaIds: Partial<MediaIds> = {};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddMediaTypeToUniqueConstraints1772048000333 implements MigrationInterface {
|
||||
name = 'AddMediaTypeToUniqueConstraints1772048000333';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// 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<void> {
|
||||
// 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"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import type { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddMediaTypeToUniqueConstraints1772047972752 implements MigrationInterface {
|
||||
name = 'AddMediaTypeToUniqueConstraints1772047972752';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
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<void> {
|
||||
// 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") `
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -178,7 +178,10 @@ const Blocklist = () => {
|
||||
) : (
|
||||
data.results.map((item: BlocklistItem) => {
|
||||
return (
|
||||
<div className="py-2" key={`request-list-${item.tmdbId}`}>
|
||||
<div
|
||||
className="py-2"
|
||||
key={`request-list-${item.mediaType}-${item.tmdbId}`}
|
||||
>
|
||||
<BlocklistedItem item={item} revalidateList={revalidate} />
|
||||
</div>
|
||||
);
|
||||
@@ -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(
|
||||
<span>
|
||||
|
||||
@@ -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<Blocklist>(`/api/v1/blocklist/${tmdbId}`);
|
||||
const { data } = useSWR<Blocklist>(
|
||||
`/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(
|
||||
<span>
|
||||
|
||||
@@ -322,6 +322,7 @@ const ManageSlideOver = ({
|
||||
<div className="overflow-hidden rounded-md border border-gray-700 shadow">
|
||||
<BlocklistBlock
|
||||
tmdbId={data.mediaInfo.tmdbId}
|
||||
mediaType={data.mediaInfo.mediaType}
|
||||
onUpdate={() => revalidate()}
|
||||
onDelete={() => onClose()}
|
||||
/>
|
||||
|
||||
@@ -358,7 +358,9 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
const onClickDeleteWatchlistBtn = async (): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
await axios.delete(`/api/v1/watchlist/${movie?.id}`);
|
||||
await axios.delete(
|
||||
`/api/v1/watchlist/${movie?.id}?mediaType=${MediaType.MOVIE}`
|
||||
);
|
||||
|
||||
addToast(
|
||||
<span>
|
||||
|
||||
@@ -139,7 +139,9 @@ const TitleCard = ({
|
||||
const onClickDeleteWatchlistBtn = async (): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const response = await axios.delete<Watchlist>('/api/v1/watchlist/' + id);
|
||||
const response = await axios.delete<Watchlist>(
|
||||
`/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(
|
||||
|
||||
@@ -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(
|
||||
<span>
|
||||
|
||||
Reference in New Issue
Block a user