Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion server/entity/Blocklist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import type { ZodNumber, ZodOptional, ZodString } from 'zod';

@Entity()
@Unique(['tmdbId'])
@Unique(['tmdbId', 'mediaType'])
export class Blocklist implements BlocklistItem {
Comment thread
fallenbagel marked this conversation as resolved.
@PrimaryGeneratedColumn()
public id: number;
Expand Down Expand Up @@ -77,6 +77,7 @@ export class Blocklist implements BlocklistItem {
let media = await mediaRepository.findOne({
where: {
tmdbId: blocklistRequest.tmdbId,
mediaType: blocklistRequest.mediaType,
},
});

Expand Down
17 changes: 7 additions & 10 deletions server/entity/Media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 [];
Expand Down
4 changes: 3 additions & 1 deletion server/entity/Watchlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 6 additions & 2 deletions server/job/blocklistedTagsProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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();

Expand Down
22 changes: 10 additions & 12 deletions server/lib/scanners/plex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<MediaIds> {
Expand Down
25 changes: 16 additions & 9 deletions server/lib/watchlistsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}))
Comment thread
fallenbagel marked this conversation as resolved.
);

const watchlistTmdbIds = response.items.map((i) => i.tmdbId);
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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"`);
}
}
Loading
Loading