parent
6e96f4ce52
commit
b11fc4b9c0
@ -0,0 +1,120 @@
|
||||
import { getRepository, Not } from 'typeorm';
|
||||
import PlexTvAPI from '../api/plextv';
|
||||
import { User } from '../entity/User';
|
||||
import Media from '../entity/Media';
|
||||
import logger from '../logger';
|
||||
import { MediaType } from '../constants/media';
|
||||
import { MediaStatus } from '../constants/media';
|
||||
import {
|
||||
DuplicateMediaRequestError,
|
||||
MediaRequest,
|
||||
NoSeasonsAvailableError,
|
||||
QuotaRestrictedError,
|
||||
RequestPermissionError,
|
||||
} from '../entity/MediaRequest';
|
||||
|
||||
class WatchlistSync {
|
||||
public async syncWatchlist() {
|
||||
const userRepository = getRepository(User);
|
||||
|
||||
// Get users who actually have plex tokens
|
||||
const users = await userRepository.find({
|
||||
select: ['id', 'plexToken', 'permissions'],
|
||||
where: {
|
||||
plexToken: Not(''),
|
||||
},
|
||||
});
|
||||
|
||||
Promise.all(users.map((user) => this.syncUserWatchlist(user)));
|
||||
}
|
||||
|
||||
private async syncUserWatchlist(user: User) {
|
||||
if (!user.plexToken) {
|
||||
logger.debug('Skipping user watchlist sync for user without plex token', {
|
||||
label: 'Plex Watchlist Sync',
|
||||
userId: user.id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const plexTvApi = new PlexTvAPI(user.plexToken);
|
||||
|
||||
const response = await plexTvApi.getWatchlist({ size: 200 });
|
||||
|
||||
const mediaItems = await Media.getRelatedMedia(
|
||||
response.items.map((i) => i.tmdbId)
|
||||
);
|
||||
|
||||
const unavailableItems = response.items.filter(
|
||||
// If we can find watchlist items in our database that are also available, we should exclude them
|
||||
(i) =>
|
||||
!mediaItems.find(
|
||||
(m) =>
|
||||
m.tmdbId === i.tmdbId &&
|
||||
((m.status !== MediaStatus.UNKNOWN && m.mediaType === 'movie') ||
|
||||
(m.mediaType === 'tv' && m.status === MediaStatus.AVAILABLE))
|
||||
)
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
unavailableItems.map(async (mediaItem) => {
|
||||
try {
|
||||
logger.info("Creating media request from user's Plex Watchlist", {
|
||||
label: 'Watchlist Sync',
|
||||
userId: user.id,
|
||||
mediaTitle: mediaItem.title,
|
||||
});
|
||||
|
||||
if (mediaItem.type === 'show' && !mediaItem.tvdbId) {
|
||||
throw new Error('Missing TVDB ID from Plex Metadata');
|
||||
}
|
||||
|
||||
await MediaRequest.request(
|
||||
{
|
||||
mediaId: mediaItem.tmdbId,
|
||||
mediaType:
|
||||
mediaItem.type === 'show' ? MediaType.TV : MediaType.MOVIE,
|
||||
seasons: mediaItem.type === 'show' ? 'all' : undefined,
|
||||
tvdbId: mediaItem.tvdbId,
|
||||
is4k: false,
|
||||
},
|
||||
user
|
||||
);
|
||||
} catch (e) {
|
||||
if (!(e instanceof Error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.constructor) {
|
||||
// During watchlist sync, these errors aren't necessarily
|
||||
// a problem with Overseerr. Since we are auto syncing these constantly, it's
|
||||
// possible they are unexpectedly at their quota limit, for example. So we'll
|
||||
// instead log these as debug messages.
|
||||
case RequestPermissionError:
|
||||
case DuplicateMediaRequestError:
|
||||
case QuotaRestrictedError:
|
||||
case NoSeasonsAvailableError:
|
||||
logger.debug('Failed to create media request from watchlist', {
|
||||
label: 'Watchlist Sync',
|
||||
userId: user.id,
|
||||
mediaTitle: mediaItem.title,
|
||||
errorMessage: e.message,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
logger.error('Failed to create media request from watchlist', {
|
||||
label: 'Watchlist Sync',
|
||||
userId: user.id,
|
||||
mediaTitle: mediaItem.title,
|
||||
errorMessage: e.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const watchlistSync = new WatchlistSync();
|
||||
|
||||
export default watchlistSync;
|
Loading…
Reference in new issue