Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix / crash when personal shelf data is not valid #659

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
24 changes: 19 additions & 5 deletions packages/common/src/services/FavoriteService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -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 [];
Expand All @@ -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) {
Expand All @@ -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),
Expand Down
30 changes: 24 additions & 6 deletions packages/common/src/services/WatchHistoryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -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<Record<string, PlaylistItem>> => {
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;
Expand All @@ -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) {
Expand All @@ -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 [];
Expand Down Expand Up @@ -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),
Expand Down
Loading