fix(tautulli): fetch additional user history as necessary to return 20 unique media (#2446)

* fix(tautulli): fetch additional user history as necessary to return 20 unique media

* refactor: rename var for clarity

* refactor: make single DB query for recently watched media

* fix: resolve query builder weirdness

* refactor: use find instead of qb

* refactor: minor refactor

* fix: also find 4K rating keys
pull/2611/head
TheCatLady 3 years ago committed by GitHub
parent 72c825d2a5
commit 7d19de6a4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,5 @@
import axios, { AxiosInstance } from 'axios'; import axios, { AxiosInstance } from 'axios';
import { uniqWith } from 'lodash';
import { User } from '../entity/User'; import { User } from '../entity/User';
import { TautulliSettings } from '../lib/settings'; import { TautulliSettings } from '../lib/settings';
import logger from '../logger'; import logger from '../logger';
@ -231,23 +232,48 @@ class TautulliAPI {
public async getUserWatchHistory( public async getUserWatchHistory(
user: User user: User
): Promise<TautulliHistoryRecord[]> { ): Promise<TautulliHistoryRecord[]> {
let results: TautulliHistoryRecord[] = [];
try { try {
if (!user.plexId) { if (!user.plexId) {
throw new Error('User does not have an associated Plex ID'); throw new Error('User does not have an associated Plex ID');
} }
return ( const take = 100;
await this.axios.get<TautulliHistoryResponse>('/api/v2', { let start = 0;
params: {
cmd: 'get_history', while (results.length < 20) {
grouping: 1, const tautulliData = (
order_column: 'date', await this.axios.get<TautulliHistoryResponse>('/api/v2', {
order_dir: 'desc', params: {
user_id: user.plexId, cmd: 'get_history',
length: 100, grouping: 1,
}, order_column: 'date',
}) order_dir: 'desc',
).data.response.data.data; user_id: user.plexId,
media_type: 'movie,episode',
length: take,
start,
},
})
).data.response.data.data;
if (!tautulliData.length) {
return results;
}
results = uniqWith(results.concat(tautulliData), (recordA, recordB) =>
recordA.grandparent_rating_key && recordB.grandparent_rating_key
? recordA.grandparent_rating_key === recordB.grandparent_rating_key
: recordA.parent_rating_key && recordB.parent_rating_key
? recordA.parent_rating_key === recordB.parent_rating_key
: recordA.rating_key === recordB.rating_key
);
start += take;
}
return results.slice(0, 20);
} catch (e) { } catch (e) {
logger.error( logger.error(
'Something went wrong fetching user watch history from Tautulli', 'Something went wrong fetching user watch history from Tautulli',

@ -1,9 +1,10 @@
import { Router } from 'express'; import { Router } from 'express';
import gravatarUrl from 'gravatar-url'; import gravatarUrl from 'gravatar-url';
import { uniqWith } from 'lodash'; import { findIndex, sortBy } from 'lodash';
import { getRepository, Not } from 'typeorm'; import { getRepository, In, Not } from 'typeorm';
import PlexTvAPI from '../../api/plextv'; import PlexTvAPI from '../../api/plextv';
import TautulliAPI from '../../api/tautulli'; import TautulliAPI from '../../api/tautulli';
import { MediaType } from '../../constants/media';
import { UserType } from '../../constants/user'; import { UserType } from '../../constants/user';
import Media from '../../entity/Media'; import Media from '../../entity/Media';
import { MediaRequest } from '../../entity/MediaRequest'; import { MediaRequest } from '../../entity/MediaRequest';
@ -521,7 +522,6 @@ router.get<{ id: string }, UserWatchDataResponse>(
} }
try { try {
const mediaRepository = getRepository(Media);
const user = await getRepository(User).findOneOrFail({ const user = await getRepository(User).findOneOrFail({
where: { id: Number(req.params.id) }, where: { id: Number(req.params.id) },
select: ['id', 'plexId'], select: ['id', 'plexId'],
@ -532,33 +532,64 @@ router.get<{ id: string }, UserWatchDataResponse>(
const watchStats = await tautulli.getUserWatchStats(user); const watchStats = await tautulli.getUserWatchStats(user);
const watchHistory = await tautulli.getUserWatchHistory(user); const watchHistory = await tautulli.getUserWatchHistory(user);
const media = ( const recentlyWatched = sortBy(
await Promise.all( await getRepository(Media).find({
uniqWith(watchHistory, (recordA, recordB) => where: [
recordA.grandparent_rating_key && recordB.grandparent_rating_key {
? recordA.grandparent_rating_key === mediaType: MediaType.MOVIE,
recordB.grandparent_rating_key ratingKey: In(
: recordA.parent_rating_key && recordB.parent_rating_key watchHistory
? recordA.parent_rating_key === recordB.parent_rating_key .filter((record) => record.media_type === 'movie')
: recordA.rating_key === recordB.rating_key .map((record) => record.rating_key)
) ),
.slice(0, 20) },
.map( {
async (record) => mediaType: MediaType.MOVIE,
await mediaRepository.findOne({ ratingKey4k: In(
where: { watchHistory
ratingKey: .filter((record) => record.media_type === 'movie')
record.media_type === 'movie' .map((record) => record.rating_key)
? record.rating_key ),
: record.grandparent_rating_key, },
}, {
}) mediaType: MediaType.TV,
) ratingKey: In(
) watchHistory
).filter((media) => !!media) as Media[]; .filter((record) => record.media_type === 'episode')
.map((record) => record.grandparent_rating_key)
),
},
{
mediaType: MediaType.TV,
ratingKey4k: In(
watchHistory
.filter((record) => record.media_type === 'episode')
.map((record) => record.grandparent_rating_key)
),
},
],
}),
[
(media) =>
findIndex(
watchHistory,
(record) =>
(!!media.ratingKey &&
parseInt(media.ratingKey) ===
(record.media_type === 'movie'
? record.rating_key
: record.grandparent_rating_key)) ||
(!!media.ratingKey4k &&
parseInt(media.ratingKey4k) ===
(record.media_type === 'movie'
? record.rating_key
: record.grandparent_rating_key))
),
]
);
return res.status(200).json({ return res.status(200).json({
recentlyWatched: media, recentlyWatched,
playCount: watchStats.total_plays, playCount: watchStats.total_plays,
}); });
} catch (e) { } catch (e) {

Loading…
Cancel
Save