diff --git a/packages/common/src/controllers/AccessController.ts b/packages/common/src/controllers/AccessController.ts index f310f965d..585922310 100644 --- a/packages/common/src/controllers/AccessController.ts +++ b/packages/common/src/controllers/AccessController.ts @@ -55,7 +55,7 @@ export default class AccessController { * If no access tokens exist, it attempts to generate them, if the passport token is expired, it attempts to refresh them. * If an access token retrieval fails or the user is not entitled to the content, an error is thrown. */ - getMediaById = async (mediaId: string) => { + getMediaById = async (mediaId: string, language?: string) => { const { entitledPlan } = useAccountStore.getState(); if (!this.siteId || !entitledPlan) { @@ -67,13 +67,25 @@ export default class AccessController { if (!accessTokens?.passport) { throw new Error('Failed to get / generate access tokens and retrieve media.'); } - return await this.apiService.getMediaByIdWithPassport({ id: mediaId, siteId: this.siteId, planId: entitledPlan.id, passport: accessTokens.passport }); + return await this.apiService.getMediaByIdWithPassport({ + id: mediaId, + siteId: this.siteId, + planId: entitledPlan.id, + passport: accessTokens.passport, + language, + }); } catch (error: unknown) { if (error instanceof ApiError && error.code === 403) { // If the passport is invalid or expired, refresh the access tokens and try to get the media again. const accessTokens = await this.refreshAccessTokens(); if (accessTokens?.passport) { - return await this.apiService.getMediaByIdWithPassport({ id: mediaId, siteId: this.siteId, planId: entitledPlan.id, passport: accessTokens.passport }); + return await this.apiService.getMediaByIdWithPassport({ + id: mediaId, + siteId: this.siteId, + planId: entitledPlan.id, + passport: accessTokens.passport, + language, + }); } throw new Error('Failed to refresh access tokens and retrieve media.'); @@ -160,7 +172,11 @@ export default class AccessController { * Retrieves the access tokens from local storage (if any) along with their expiration timestamp. */ getAccessTokens = async (): Promise<(AccessTokens & { expires: number }) | null> => { - const accessTokens = await this.storageService.getItem(ACCESS_TOKENS, true, true); + const accessTokens = await this.storageService.getItem< + AccessTokens & { + expires: number; + } + >(ACCESS_TOKENS, true, true); if (accessTokens) { useAccessStore.setState({ passport: accessTokens.passport }); } diff --git a/packages/common/src/controllers/EntitlementController.test.ts b/packages/common/src/controllers/EntitlementController.test.ts index 82577e415..fd4833942 100644 --- a/packages/common/src/controllers/EntitlementController.test.ts +++ b/packages/common/src/controllers/EntitlementController.test.ts @@ -82,10 +82,10 @@ describe('entitlementController', () => { supportedLanguages: [], }); - const result = await entitlementController.getSignedMedia('uB8aRnu6'); + const result = await entitlementController.getSignedMedia('uB8aRnu6', 'en'); expect(result?.mediaid).toEqual('uB8aRnu6'); - expect(getMediaById).toBeCalledWith({ id: 'uB8aRnu6', token: undefined, drmPolicyId: undefined }); + expect(getMediaById).toBeCalledWith({ id: 'uB8aRnu6', token: undefined, drmPolicyId: undefined, language: 'en' }); expect(getMediaByIdAccessController).not.toBeCalled(); expect(getMediaTokenGeneric).not.toBeCalled(); @@ -110,11 +110,11 @@ describe('entitlementController', () => { supportedLanguages: [], }); - const result = await entitlementController.getSignedMedia('uB8aRnu6'); + const result = await entitlementController.getSignedMedia('uB8aRnu6', 'nl'); expect(result?.mediaid).toEqual('uB8aRnu6'); expect(getMediaTokenJwp).toBeCalledWith(jwpSigningConfig, 'uB8aRnu6', 'jwttoken', undefined, undefined); - expect(getMediaById).toBeCalledWith({ id: 'uB8aRnu6', token: 'jwpmediatoken', drmPolicyId: undefined }); + expect(getMediaById).toBeCalledWith({ id: 'uB8aRnu6', token: 'jwpmediatoken', drmPolicyId: undefined, language: 'nl' }); expect(getMediaByIdAccessController).not.toBeCalled(); expect(getMediaTokenGeneric).not.toBeCalled(); @@ -240,10 +240,10 @@ describe('entitlementController', () => { supportedLanguages: [], }); - const result = await entitlementController.getSignedMedia('uB8aRnu6'); + const result = await entitlementController.getSignedMedia('uB8aRnu6', 'de'); expect(result?.mediaid).toEqual('uB8aRnu6'); - expect(getMediaByIdAccessController).toBeCalledWith('uB8aRnu6'); + expect(getMediaByIdAccessController).toBeCalledWith('uB8aRnu6', 'de'); }); }); }); diff --git a/packages/common/src/controllers/EntitlementController.ts b/packages/common/src/controllers/EntitlementController.ts index fc2a3a322..535e3c99d 100644 --- a/packages/common/src/controllers/EntitlementController.ts +++ b/packages/common/src/controllers/EntitlementController.ts @@ -65,7 +65,7 @@ export default class EntitlementController { } }; - getSignedMedia = async (id: string, params?: GetMediaParams) => { + getSignedMedia = async (id: string, language?: string, params?: GetMediaParams) => { const { config, settings } = useConfigStore.getState(); const { custom, contentProtection } = config; @@ -76,13 +76,13 @@ export default class EntitlementController { // signing is handled by access bridge if (isAccessBridgeEnabled) { - signedMediaItem = await this.accessController.getMediaById(id); + signedMediaItem = await this.accessController.getMediaById(id, language); } else { const authData = await this.accountController.getAuthData(); const entitlementService = this.getEntitlementService(); const token = await entitlementService?.getMediaToken(config, id, authData?.jwt, params, drmPolicyId); - signedMediaItem = await this.apiService.getMediaById({ id, token, drmPolicyId }); + signedMediaItem = await this.apiService.getMediaById({ id, token, drmPolicyId, language }); } await this.validateGeoRestriction(signedMediaItem); diff --git a/packages/hooks-react/src/useProtectedMedia.ts b/packages/hooks-react/src/useProtectedMedia.ts index fa5d5b264..a7903659d 100644 --- a/packages/hooks-react/src/useProtectedMedia.ts +++ b/packages/hooks-react/src/useProtectedMedia.ts @@ -1,12 +1,15 @@ import { useQuery } from 'react-query'; +import { useTranslation } from 'react-i18next'; import type { PlaylistItem } from '@jwp/ott-common/types/playlist'; import { getModule } from '@jwp/ott-common/src/modules/container'; import EntitlementController from '@jwp/ott-common/src/controllers/EntitlementController'; export default function useProtectedMedia(item: PlaylistItem) { const entitlementController = getModule(EntitlementController); + const { i18n } = useTranslation(); + const { language } = i18n; - return useQuery(['media-signed', item.mediaid, {}], async () => entitlementController.getSignedMedia(item.mediaid), { + return useQuery(['media-signed', item.mediaid, language], async () => entitlementController.getSignedMedia(item.mediaid, language), { retry: 2, retryDelay: 1000, keepPreviousData: false,