From 00e47258de68a37e1de977adad4e4add74099384 Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Thu, 5 Dec 2024 14:17:31 +0100 Subject: [PATCH] fix: crash when personal shelf data is not valid --- .../common/src/services/FavoriteService.ts | 24 +++++++++++---- .../src/services/WatchHistoryService.ts | 30 +++++++++++++++---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/packages/common/src/services/FavoriteService.ts b/packages/common/src/services/FavoriteService.ts index b6eb968f6..3d0c6484c 100644 --- a/packages/common/src/services/FavoriteService.ts +++ b/packages/common/src/services/FavoriteService.ts @@ -7,7 +7,7 @@ import type { Customer } from '../../types/account'; import { getNamedModule } from '../modules/container'; import { INTEGRATION_TYPE } from '../modules/types'; import { MAX_WATCHLIST_ITEMS_COUNT } from '../constants'; -import { logError } from '../logger'; +import { logDebug, logError } from '../logger'; import ApiService from './ApiService'; import StorageService from './StorageService'; @@ -17,11 +17,12 @@ const schema = array( object().shape({ mediaid: string(), }), -); +).nullable(); @injectable() export default class FavoriteService { private PERSIST_KEY_FAVORITES = 'favorites'; + private hasErrors = false; protected readonly apiService; protected readonly storageService; @@ -38,8 +39,13 @@ export default class FavoriteService { } protected validateFavorites(favorites: unknown) { - if (favorites && schema.validateSync(favorites)) { - return favorites as SerializedFavorite[]; + try { + if (favorites && schema.validateSync(favorites)) { + return favorites as SerializedFavorite[]; + } + } catch (error: unknown) { + this.hasErrors = true; + logError('FavoritesService', 'Failed to validate favorites', { error }); } return []; @@ -66,7 +72,11 @@ export default class FavoriteService { } try { - const playlistItems = await this.apiService.getMediaByWatchlist({ playlistId: favoritesList, mediaIds, language }); + const playlistItems = await this.apiService.getMediaByWatchlist({ + playlistId: favoritesList, + mediaIds, + language, + }); return (playlistItems || []).map((item) => this.createFavorite(item)); } catch (error: unknown) { @@ -81,6 +91,10 @@ export default class FavoriteService { }; persistFavorites = async (favorites: Favorite[], user: Customer | null) => { + if (this.hasErrors) { + return logDebug('FavoritesService', 'persist prevented due to an encountered problem while validating the stored favorites'); + } + if (user) { return this.accountService?.updateFavorites({ favorites: this.serializeFavorites(favorites), diff --git a/packages/common/src/services/WatchHistoryService.ts b/packages/common/src/services/WatchHistoryService.ts index c8beaa396..2ad0e1d07 100644 --- a/packages/common/src/services/WatchHistoryService.ts +++ b/packages/common/src/services/WatchHistoryService.ts @@ -7,7 +7,7 @@ import type { Customer } from '../../types/account'; import { getNamedModule } from '../modules/container'; import { INTEGRATION_TYPE } from '../modules/types'; import { MAX_WATCHLIST_ITEMS_COUNT } from '../constants'; -import { logError } from '../logger'; +import { logDebug, logError } from '../logger'; import ApiService from './ApiService'; import StorageService from './StorageService'; @@ -18,11 +18,12 @@ const schema = array( mediaid: string(), progress: number(), }), -); +).nullable(); @injectable() export default class WatchHistoryService { protected PERSIST_KEY_WATCH_HISTORY = 'history'; + protected hasErrors = false; protected readonly apiService; protected readonly storageService; @@ -40,7 +41,11 @@ export default class WatchHistoryService { // Retrieve watch history media items info using a provided watch list protected getWatchHistoryItems = async (continueWatchingList: string, ids: string[], language?: string): Promise> => { - const watchHistoryItems = await this.apiService.getMediaByWatchlist({ playlistId: continueWatchingList, mediaIds: ids, language }); + const watchHistoryItems = await this.apiService.getMediaByWatchlist({ + playlistId: continueWatchingList, + mediaIds: ids, + language, + }); const watchHistoryItemsDict = Object.fromEntries((watchHistoryItems || []).map((item) => [item.mediaid, item])); return watchHistoryItemsDict; @@ -57,7 +62,11 @@ export default class WatchHistoryService { .map((key) => mediaWithSeries?.[key]?.[0]?.series_id) .filter(Boolean) as string[]; const uniqueSerieIds = [...new Set(seriesIds)]; - const seriesItems = await this.apiService.getMediaByWatchlist({ playlistId: continueWatchingList, mediaIds: uniqueSerieIds, language }); + const seriesItems = await this.apiService.getMediaByWatchlist({ + playlistId: continueWatchingList, + mediaIds: uniqueSerieIds, + language, + }); const seriesItemsDict = Object.keys(mediaWithSeries || {}).reduce((acc, key) => { const seriesItemId = mediaWithSeries?.[key]?.[0]?.series_id; if (seriesItemId) { @@ -70,8 +79,13 @@ export default class WatchHistoryService { }; protected validateWatchHistory(history: unknown) { - if (history && schema.validateSync(history)) { - return history as SerializedWatchHistoryItem[]; + try { + if (history && schema.validateSync(history)) { + return history as SerializedWatchHistoryItem[]; + } + } catch (error: unknown) { + this.hasErrors = true; + logError('WatchHistoryService', 'Failed to validate watch history', { error }); } return []; @@ -127,6 +141,10 @@ export default class WatchHistoryService { })); persistWatchHistory = async (watchHistory: WatchHistoryItem[], user: Customer | null) => { + if (this.hasErrors) { + return logDebug('WatchHistoryService', 'persist prevented due to an encountered problem while validating the stored watch history'); + } + if (user) { await this.accountService?.updateWatchHistory({ history: this.serializeWatchHistory(watchHistory),