diff --git a/docs/features/user-watchlists.md b/docs/features/user-watchlists.md index f8928df27..4aa61c100 100644 --- a/docs/features/user-watchlists.md +++ b/docs/features/user-watchlists.md @@ -127,10 +127,16 @@ Example data format } ``` -### Max 48 items +### Max amount of items -Cleeng customer `externalData` attribute has maxsize of 5000 symbols. +#### JWP -The length of one stringified object of History equals to 52 symbols, one Favorites object equals to 22 symbols. Taking into account only History objects, we get 5000 / 52 = ~96, so 48 for Favorites and 48 for History. We also leave some extra space for possible further updates. +For JWP the limit is 48 items for Favorites and 48 for Watch History. + +#### Cleeng + +Cleeng customer `externalData` attribute has maxsize of 4000 symbols. + +The length of one stringified object of History equals to 41 symbols, one Favorites object equals to 22 symbols. Taking into account only History objects, we get 4000 / 41 = ~97.56, so 48 for Favorites and 48 for Watch History. We also leave some extra space for possible further updates. We rotate the oldest continue watching object to the first item position after its progress property gets a new value. diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index 06c8c0ee1..bfaf1d4b8 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -23,9 +23,6 @@ export const VideoProgressMinMax = { export const PLAYLIST_LIMIT = 25; -// The externalData attribute of Cleeng can contain max 5000 characters -export const MAX_WATCHLIST_ITEMS_COUNT = 48; - export const ADYEN_TEST_CLIENT_KEY = 'test_I4OFGUUCEVB5TI222AS3N2Y2LY6PJM3K'; export const ADYEN_LIVE_CLIENT_KEY = 'live_BQDOFBYTGZB3XKF62GBYSLPUJ4YW2TPL'; @@ -62,6 +59,8 @@ export const STALE_TIME = 60 * 1000 * 20; export const CARD_ASPECT_RATIOS = ['1:1', '2:1', '2:3', '4:3', '5:3', '16:9', '9:13', '9:16'] as const; +export const MAX_WATCHLIST_ITEMS_COUNT = 48; // Default value + export const DEFAULT_FEATURES = { canUpdateEmail: false, canSupportEmptyFullName: false, @@ -73,6 +72,7 @@ export const DEFAULT_FEATURES = { canShowReceipts: false, hasSocialURLs: false, hasNotifications: false, + watchListSizeLimit: MAX_WATCHLIST_ITEMS_COUNT, }; export const simultaneousLoginWarningKey = 'simultaneous_logins'; diff --git a/packages/common/src/controllers/FavoritesController.ts b/packages/common/src/controllers/FavoritesController.ts index 8f8639adb..e871bcf63 100644 --- a/packages/common/src/controllers/FavoritesController.ts +++ b/packages/common/src/controllers/FavoritesController.ts @@ -74,7 +74,7 @@ export default class FavoritesController { } // If we exceed the max available number of favorites, we show a warning - if (this.favoritesService.hasReachedFavoritesLimit(favorites)) { + if (favorites.length > this.favoritesService.getMaxFavoritesCount()) { setWarning(i18next.t('video:favorites_warning', { maxCount: this.favoritesService.getMaxFavoritesCount() })); return; } diff --git a/packages/common/src/services/FavoriteService.ts b/packages/common/src/services/FavoriteService.ts index e2319b07c..ed833889a 100644 --- a/packages/common/src/services/FavoriteService.ts +++ b/packages/common/src/services/FavoriteService.ts @@ -1,13 +1,13 @@ import { inject, injectable } from 'inversify'; import { object, array, string } from 'yup'; -import { MAX_WATCHLIST_ITEMS_COUNT } from '../constants'; import type { Favorite, SerializedFavorite } from '../../types/favorite'; import type { PlaylistItem } from '../../types/playlist'; import type { Customer } from '../../types/account'; import { getNamedModule } from '../modules/container'; import { INTEGRATION_TYPE } from '../modules/types'; import { logDev } from '../utils/common'; +import { MAX_WATCHLIST_ITEMS_COUNT } from '../constants'; import ApiService from './ApiService'; import StorageService from './StorageService'; @@ -21,7 +21,6 @@ const schema = array( @injectable() export default class FavoriteService { - private MAX_FAVORITES_COUNT = 48; private PERSIST_KEY_FAVORITES = 'favorites'; private readonly apiService; @@ -89,11 +88,7 @@ export default class FavoriteService { }; getMaxFavoritesCount = () => { - return this.MAX_FAVORITES_COUNT; - }; - - hasReachedFavoritesLimit = (favorites: Favorite[]) => { - return favorites?.length >= MAX_WATCHLIST_ITEMS_COUNT; + return this.accountService?.features?.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT; }; createFavorite = (item: PlaylistItem): Favorite => { diff --git a/packages/common/src/services/WatchHistoryService.ts b/packages/common/src/services/WatchHistoryService.ts index e5df748d3..62e4408ac 100644 --- a/packages/common/src/services/WatchHistoryService.ts +++ b/packages/common/src/services/WatchHistoryService.ts @@ -7,6 +7,7 @@ import type { Customer } from '../../types/account'; import { getNamedModule } from '../modules/container'; import { INTEGRATION_TYPE } from '../modules/types'; import { logDev } from '../utils/common'; +import { MAX_WATCHLIST_ITEMS_COUNT } from '../constants'; import ApiService from './ApiService'; import StorageService from './StorageService'; @@ -22,7 +23,6 @@ const schema = array( @injectable() export default class WatchHistoryService { private PERSIST_KEY_WATCH_HISTORY = 'history'; - private MAX_WATCH_HISTORY_COUNT = 48; private readonly apiService; private readonly storageService; @@ -145,6 +145,10 @@ export default class WatchHistoryService { } as WatchHistoryItem; }; + getMaxWatchHistoryCount = () => { + return this.accountService?.features?.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT; + }; + /** * If we already have an element with continue watching state, we: * 1. Update the progress @@ -163,8 +167,7 @@ export default class WatchHistoryService { }); updatedHistory.unshift(watchHistoryItem); - - updatedHistory.splice(this.MAX_WATCH_HISTORY_COUNT); + updatedHistory.splice(this.getMaxWatchHistoryCount()); return updatedHistory; }; diff --git a/packages/common/src/services/integrations/AccountService.ts b/packages/common/src/services/integrations/AccountService.ts index b72475b3d..deb186041 100644 --- a/packages/common/src/services/integrations/AccountService.ts +++ b/packages/common/src/services/integrations/AccountService.ts @@ -35,6 +35,7 @@ export type AccountServiceFeatures = { readonly canShowReceipts: boolean; readonly hasSocialURLs: boolean; readonly hasNotifications: boolean; + readonly watchListSizeLimit: number; }; export default abstract class AccountService { diff --git a/packages/common/src/services/integrations/cleeng/CleengAccountService.ts b/packages/common/src/services/integrations/cleeng/CleengAccountService.ts index 40c55a285..701ceb44a 100644 --- a/packages/common/src/services/integrations/cleeng/CleengAccountService.ts +++ b/packages/common/src/services/integrations/cleeng/CleengAccountService.ts @@ -71,6 +71,8 @@ export default class CleengAccountService extends AccountService { canShowReceipts: true, hasSocialURLs: false, hasNotifications: false, + // The 'externalData' attribute of Cleeng can contain max 4000 characters + watchListSizeLimit: 48, }); this.cleengService = cleengService; diff --git a/packages/common/src/services/integrations/jwp/JWPAccountService.ts b/packages/common/src/services/integrations/jwp/JWPAccountService.ts index 379dbb19d..bd5c2636b 100644 --- a/packages/common/src/services/integrations/jwp/JWPAccountService.ts +++ b/packages/common/src/services/integrations/jwp/JWPAccountService.ts @@ -68,6 +68,8 @@ export default class JWPAccountService extends AccountService { canDeleteAccount: true, hasNotifications: true, hasSocialURLs: true, + // Limit of media_ids length passed to the /apps/watchlists endpoint + watchListSizeLimit: 48, }); this.storageService = storageService; diff --git a/packages/hooks-react/src/useWatchHistoryListener.ts b/packages/hooks-react/src/useWatchHistoryListener.ts index ef2d15e9c..d9d2d9002 100644 --- a/packages/hooks-react/src/useWatchHistoryListener.ts +++ b/packages/hooks-react/src/useWatchHistoryListener.ts @@ -57,7 +57,7 @@ export const useWatchHistoryListener = (player: jwplayer.JWPlayer | undefined, i // live streams have a negative duration, we ignore these for now if (event.duration < 0) return; - const progress = event.position / event.duration; + const progress = Number((event.position / event.duration).toFixed(5)); if (!isNaN(progress) && isFinite(progress)) { queuedWatchProgress.current = {