From 2364b83dd2db227acc56de2ca076179b65e8b426 Mon Sep 17 00:00:00 2001 From: Anton Lantukh Date: Wed, 6 Dec 2023 11:40:53 +0100 Subject: [PATCH 1/3] feat(watchhistory): change max items limit --- docs/features/user-watchlists.md | 12 +++++++++--- src/config.ts | 8 ++++++-- src/services/account.service.ts | 1 + src/services/cleeng.account.service.ts | 2 ++ src/services/favorites.service.ts | 10 ---------- src/services/inplayer.account.service.ts | 2 ++ src/services/watchhistory.service.ts | 12 ++++++++---- src/stores/FavoritesController.ts | 7 +++++-- src/stores/WatchHistoryController.ts | 4 +++- 9 files changed, 36 insertions(+), 22 deletions(-) diff --git a/docs/features/user-watchlists.md b/docs/features/user-watchlists.md index f8928df27..c259bcf3d 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 customers it is possible to add 100 items for Favorites and 100 for Watch History + +#### Cleeng + +Cleeng customer `externalData` attribute has maxsize of 4000 symbols. + +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 4000 / 52 = ~76.92, so 38 for Favorites and 38 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/src/config.ts b/src/config.ts index 0259d9a98..315ffb8bb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -23,8 +23,11 @@ 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 MAX_WATCHLIST_ITEMS_COUNT = { + JWP: 100, + CLEENG: 38, // The externalData attribute of Cleeng can contain max 4000 characters + DEFAULT: 48, // Local storage +}; export const ADYEN_TEST_CLIENT_KEY = 'test_I4OFGUUCEVB5TI222AS3N2Y2LY6PJM3K'; @@ -72,4 +75,5 @@ export const DEFAULT_FEATURES = { hasSocialURLs: false, hasProfiles: false, hasNotifications: false, + watchListSizeLimit: MAX_WATCHLIST_ITEMS_COUNT.DEFAULT, }; diff --git a/src/services/account.service.ts b/src/services/account.service.ts index 52649ad99..ca6853a32 100644 --- a/src/services/account.service.ts +++ b/src/services/account.service.ts @@ -33,6 +33,7 @@ export type AccountServiceFeatures = { readonly hasSocialURLs: boolean; readonly hasProfiles: boolean; readonly hasNotifications: boolean; + readonly watchListSizeLimit: number; }; export default abstract class AccountService { diff --git a/src/services/cleeng.account.service.ts b/src/services/cleeng.account.service.ts index 7d7e57eb0..101c1a193 100644 --- a/src/services/cleeng.account.service.ts +++ b/src/services/cleeng.account.service.ts @@ -33,6 +33,7 @@ import type { UpdatePersonalShelves, NotificationsData, } from '#types/account'; +import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config'; @injectable() export default class CleengAccountService extends AccountService { @@ -51,6 +52,7 @@ export default class CleengAccountService extends AccountService { hasProfiles: false, hasSocialURLs: false, hasNotifications: false, + watchListSizeLimit: MAX_WATCHLIST_ITEMS_COUNT.CLEENG, }); this.cleengService = cleengService; diff --git a/src/services/favorites.service.ts b/src/services/favorites.service.ts index ba9babff1..da6c487db 100644 --- a/src/services/favorites.service.ts +++ b/src/services/favorites.service.ts @@ -5,12 +5,10 @@ import ApiService from './api.service'; import * as persist from '#src/utils/persist'; import type { Favorite, SerializedFavorite } from '#types/favorite'; import type { PlaylistItem } from '#types/playlist'; -import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config'; import type { Customer } from '#types/account'; @injectable() export default class FavoritesService { - private MAX_FAVORITES_COUNT = 48; private PERSIST_KEY_FAVORITES = `favorites${window.configId ? `-${window.configId}` : ''}`; private readonly apiService; @@ -40,14 +38,6 @@ export default class FavoritesService { persist.setItem(this.PERSIST_KEY_FAVORITES, this.serializeFavorites(favorites)); }; - getMaxFavoritesCount = () => { - return this.MAX_FAVORITES_COUNT; - }; - - hasReachedFavoritesLimit = (favorites: Favorite[]) => { - return favorites?.length >= MAX_WATCHLIST_ITEMS_COUNT; - }; - createFavorite = (item: PlaylistItem): Favorite => { return { mediaid: item.mediaid, diff --git a/src/services/inplayer.account.service.ts b/src/services/inplayer.account.service.ts index 7ffaeb7c1..b57094a19 100644 --- a/src/services/inplayer.account.service.ts +++ b/src/services/inplayer.account.service.ts @@ -37,6 +37,7 @@ import type { Config } from '#types/Config'; import type { InPlayerAuthData } from '#types/inplayer'; import type { Favorite } from '#types/favorite'; import type { WatchHistoryItem } from '#types/watchHistory'; +import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config'; enum InPlayerEnv { Development = 'development', @@ -60,6 +61,7 @@ export default class InplayerAccountService extends AccountService { hasNotifications: true, hasProfiles: true, hasSocialURLs: true, + watchListSizeLimit: MAX_WATCHLIST_ITEMS_COUNT.JWP, }); } diff --git a/src/services/watchhistory.service.ts b/src/services/watchhistory.service.ts index ee34a21cd..c5a41e64e 100644 --- a/src/services/watchhistory.service.ts +++ b/src/services/watchhistory.service.ts @@ -10,7 +10,6 @@ import type { Customer } from '#types/account'; @injectable() export default class WatchHistoryService { private PERSIST_KEY_WATCH_HISTORY = `history${window.configId ? `-${window.configId}` : ''}`; - private MAX_WATCH_HISTORY_COUNT = 48; private readonly apiService: ApiService; constructor(apiService: ApiService) { @@ -100,7 +99,13 @@ export default class WatchHistoryService { * 1. Move the element to the continue watching list start * 2. If there are many elements in continue watching state we remove the oldest one */ - saveItem = async (item: PlaylistItem, seriesItem: PlaylistItem | undefined, videoProgress: number | null, watchHistory: WatchHistoryItem[]) => { + saveItem = async ( + item: PlaylistItem, + seriesItem: PlaylistItem | undefined, + videoProgress: number | null, + watchHistory: WatchHistoryItem[], + watchHistoryLimit: number, + ) => { if (!videoProgress) return; const watchHistoryItem = this.createWatchHistoryItem(seriesItem || item, item.mediaid, seriesItem?.mediaid, videoProgress); @@ -110,8 +115,7 @@ export default class WatchHistoryService { }); updatedHistory.unshift(watchHistoryItem); - - updatedHistory.splice(this.MAX_WATCH_HISTORY_COUNT); + updatedHistory.splice(watchHistoryLimit); return updatedHistory; }; diff --git a/src/stores/FavoritesController.ts b/src/stores/FavoritesController.ts index ba27ba585..2a20c55f5 100644 --- a/src/stores/FavoritesController.ts +++ b/src/stores/FavoritesController.ts @@ -11,6 +11,7 @@ import type { Favorite, SerializedFavorite } from '#types/favorite'; import type { Customer } from '#types/account'; import { getNamedModule } from '#src/modules/container'; import type { IntegrationType } from '#types/Config'; +import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config'; @injectable() export default class FavoritesController { @@ -108,9 +109,11 @@ export default class FavoritesController { return; } + const favoritesLimit = this.accountService?.features?.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT.DEFAULT; + // If we exceed the max available number of favorites, we show a warning - if (this.favoritesService.hasReachedFavoritesLimit(favorites)) { - setWarning(i18next.t('video:favorites_warning', { maxCount: this.favoritesService.getMaxFavoritesCount() })); + if (this.accountService && favorites?.length >= favoritesLimit) { + setWarning(i18next.t('video:favorites_warning', { maxCount: MAX_WATCHLIST_ITEMS_COUNT })); return; } diff --git a/src/stores/WatchHistoryController.ts b/src/stores/WatchHistoryController.ts index 2cec5b611..83420ab5a 100644 --- a/src/stores/WatchHistoryController.ts +++ b/src/stores/WatchHistoryController.ts @@ -10,6 +10,7 @@ import type { SerializedWatchHistoryItem, WatchHistoryItem } from '#types/watchH import type { Customer } from '#types/account'; import { getNamedModule } from '#src/modules/container'; import type { IntegrationType } from '#types/Config'; +import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config'; @injectable() export default class WatchHistoryController { @@ -87,7 +88,8 @@ export default class WatchHistoryController { if (!videoProgress) return; - const updatedHistory = await this.watchHistoryService.saveItem(item, seriesItem, videoProgress, watchHistory); + const watchHistoryLimit = this.accountService?.features.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT.DEFAULT; + const updatedHistory = await this.watchHistoryService.saveItem(item, seriesItem, videoProgress, watchHistory, watchHistoryLimit); if (updatedHistory) { useWatchHistoryStore.setState({ watchHistory: updatedHistory }); From 32e051712907f60f667193814ca7d71543605dda Mon Sep 17 00:00:00 2001 From: Anton Lantukh Date: Thu, 7 Dec 2023 12:43:28 +0100 Subject: [PATCH 2/3] feat(watchhistory): change limit for JWP --- docs/features/user-watchlists.md | 2 +- src/config.ts | 6 +++--- src/stores/FavoritesController.ts | 4 ++-- src/stores/WatchHistoryController.ts | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/features/user-watchlists.md b/docs/features/user-watchlists.md index c259bcf3d..8481cb1dc 100644 --- a/docs/features/user-watchlists.md +++ b/docs/features/user-watchlists.md @@ -131,7 +131,7 @@ Example data format #### JWP -For JWP customers it is possible to add 100 items for Favorites and 100 for Watch History +For JWP the limit is 50 items for Favorites and 50 for Watch History. #### Cleeng diff --git a/src/config.ts b/src/config.ts index 315ffb8bb..5b4fbedeb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -24,9 +24,9 @@ export const VideoProgressMinMax = { export const PLAYLIST_LIMIT = 25; export const MAX_WATCHLIST_ITEMS_COUNT = { - JWP: 100, - CLEENG: 38, // The externalData attribute of Cleeng can contain max 4000 characters - DEFAULT: 48, // Local storage + CLEENG: 38, // The 'externalData' attribute of Cleeng can contain max 4000 characters + JWP: 50, // Limit of media_ids length passed to the /apps/watchlists endpoint + DEFAULT: 38, // Local storage }; export const ADYEN_TEST_CLIENT_KEY = 'test_I4OFGUUCEVB5TI222AS3N2Y2LY6PJM3K'; diff --git a/src/stores/FavoritesController.ts b/src/stores/FavoritesController.ts index 2a20c55f5..59492acbd 100644 --- a/src/stores/FavoritesController.ts +++ b/src/stores/FavoritesController.ts @@ -112,8 +112,8 @@ export default class FavoritesController { const favoritesLimit = this.accountService?.features?.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT.DEFAULT; // If we exceed the max available number of favorites, we show a warning - if (this.accountService && favorites?.length >= favoritesLimit) { - setWarning(i18next.t('video:favorites_warning', { maxCount: MAX_WATCHLIST_ITEMS_COUNT })); + if (favorites?.length >= favoritesLimit) { + setWarning(i18next.t('video:favorites_warning', { maxCount: favoritesLimit })); return; } diff --git a/src/stores/WatchHistoryController.ts b/src/stores/WatchHistoryController.ts index 83420ab5a..baf13b096 100644 --- a/src/stores/WatchHistoryController.ts +++ b/src/stores/WatchHistoryController.ts @@ -89,6 +89,7 @@ export default class WatchHistoryController { if (!videoProgress) return; const watchHistoryLimit = this.accountService?.features.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT.DEFAULT; + const updatedHistory = await this.watchHistoryService.saveItem(item, seriesItem, videoProgress, watchHistory, watchHistoryLimit); if (updatedHistory) { From 9933bcf886faf237860fd1326bc42a79855ab9d7 Mon Sep 17 00:00:00 2001 From: Anton Lantukh Date: Thu, 29 Feb 2024 16:43:13 +0100 Subject: [PATCH 3/3] feat(watchhistory): update branch --- docs/features/user-watchlists.md | 4 ++-- packages/common/src/constants.ts | 10 +++------- .../src/controllers/FavoritesController.ts | 6 ++---- packages/common/src/services/FavoriteService.ts | 9 ++------- .../common/src/services/WatchHistoryService.ts | 16 +++++++--------- .../integrations/cleeng/CleengAccountService.ts | 2 ++ .../integrations/jwp/JWPAccountService.ts | 3 ++- .../hooks-react/src/useWatchHistoryListener.ts | 2 +- 8 files changed, 21 insertions(+), 31 deletions(-) diff --git a/docs/features/user-watchlists.md b/docs/features/user-watchlists.md index 8481cb1dc..4aa61c100 100644 --- a/docs/features/user-watchlists.md +++ b/docs/features/user-watchlists.md @@ -131,12 +131,12 @@ Example data format #### JWP -For JWP the limit is 50 items for Favorites and 50 for Watch History. +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 52 symbols, one Favorites object equals to 22 symbols. Taking into account only History objects, we get 4000 / 52 = ~76.92, so 38 for Favorites and 38 for Watch History. We also leave some extra space for possible further updates. +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 c8b2e3b22..bfaf1d4b8 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -23,12 +23,6 @@ export const VideoProgressMinMax = { export const PLAYLIST_LIMIT = 25; -export const MAX_WATCHLIST_ITEMS_COUNT = { - CLEENG: 38, // The 'externalData' attribute of Cleeng can contain max 4000 characters - JWP: 50, // Limit of media_ids length passed to the /apps/watchlists endpoint - DEFAULT: 38, // Local storage -}; - export const ADYEN_TEST_CLIENT_KEY = 'test_I4OFGUUCEVB5TI222AS3N2Y2LY6PJM3K'; export const ADYEN_LIVE_CLIENT_KEY = 'live_BQDOFBYTGZB3XKF62GBYSLPUJ4YW2TPL'; @@ -65,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, @@ -76,7 +72,7 @@ export const DEFAULT_FEATURES = { canShowReceipts: false, hasSocialURLs: false, hasNotifications: false, - watchListSizeLimit: MAX_WATCHLIST_ITEMS_COUNT.DEFAULT, + 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 621512ed7..e871bcf63 100644 --- a/packages/common/src/controllers/FavoritesController.ts +++ b/packages/common/src/controllers/FavoritesController.ts @@ -73,11 +73,9 @@ export default class FavoritesController { return; } - const favoritesLimit = this.accountService?.features?.watchListSizeLimit || MAX_WATCHLIST_ITEMS_COUNT.DEFAULT; - // If we exceed the max available number of favorites, we show a warning - if (favorites?.length >= favoritesLimit) { - setWarning(i18next.t('video:favorites_warning', { maxCount: favoritesLimit })); + 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 1942f683d..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 @@ -153,13 +157,7 @@ export default class WatchHistoryService { * 1. Move the element to the continue watching list start * 2. If there are many elements in continue watching state we remove the oldest one */ - saveItem = async ( - item: PlaylistItem, - seriesItem: PlaylistItem | undefined, - videoProgress: number | null, - watchHistory: WatchHistoryItem[], - watchHistoryLimit: number, - ) => { + saveItem = async (item: PlaylistItem, seriesItem: PlaylistItem | undefined, videoProgress: number | null, watchHistory: WatchHistoryItem[]) => { if (!videoProgress) return; const watchHistoryItem = this.createWatchHistoryItem(seriesItem || item, item.mediaid, seriesItem?.mediaid, videoProgress); @@ -169,7 +167,7 @@ export default class WatchHistoryService { }); updatedHistory.unshift(watchHistoryItem); - updatedHistory.splice(watchHistoryLimit); + updatedHistory.splice(this.getMaxWatchHistoryCount()); return updatedHistory; }; 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 c83031834..bd5c2636b 100644 --- a/packages/common/src/services/integrations/jwp/JWPAccountService.ts +++ b/packages/common/src/services/integrations/jwp/JWPAccountService.ts @@ -68,7 +68,8 @@ export default class JWPAccountService extends AccountService { canDeleteAccount: true, hasNotifications: true, hasSocialURLs: true, - watchListSizeLimit: MAX_WATCHLIST_ITEMS_COUNT.JWP, + // 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 = {