diff --git a/src/components/Player/Player.test.tsx b/src/components/Player/Player.test.tsx index 8eb16c4cf..d92f1a62b 100644 --- a/src/components/Player/Player.test.tsx +++ b/src/components/Player/Player.test.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import { render } from '@testing-library/react'; import Player from './Player'; import type { PlaylistItem } from '#types/playlist'; +import { renderWithRouter } from '#test/testUtils'; describe('', () => { test('renders and matches snapshot', () => { @@ -24,7 +24,7 @@ describe('', () => { title: 'Test item title', tracks: [], } as PlaylistItem; - const { container } = render( null} onPause={() => null} />); + const { container } = renderWithRouter( null} onPause={() => null} />); expect(container).toMatchSnapshot(); }); diff --git a/src/components/Player/Player.tsx b/src/components/Player/Player.tsx index b53591f1b..0b85c7f29 100644 --- a/src/components/Player/Player.tsx +++ b/src/components/Player/Player.tsx @@ -10,6 +10,7 @@ import useEventCallback from '#src/hooks/useEventCallback'; import useOttAnalytics from '#src/hooks/useOttAnalytics'; import { logDev, testId } from '#src/utils/common'; import { useConfigStore } from '#src/stores/ConfigStore'; +import { useMediaAds } from '#src/hooks/useMediaAds'; type Props = { playerId: string; @@ -59,7 +60,9 @@ const Player: React.FC = ({ const startTimeRef = useRef(startTime); const setPlayer = useOttAnalytics(item, feedId); - const { adScheduleData } = useConfigStore((s) => s); + const adScheduleId = useConfigStore((s) => s.config.adSchedule); + const { preRollUrl, midRollUrl, postRollUrl } = item; + const { data: adScheduleData, isLoading: isAdScheduleLoading } = useMediaAds(adScheduleId, item?.mediaid, { preRollUrl, midRollUrl, postRollUrl }); const handleBeforePlay = useEventCallback(onBeforePlay); const handlePlay = useEventCallback(onPlay); @@ -201,10 +204,10 @@ const Player: React.FC = ({ return loadPlaylist(); } - if (libLoaded) { + if (libLoaded && !isAdScheduleLoading) { initializePlayer(); } - }, [libLoaded, item, detachEvents, attachEvents, playerId, setPlayer, autostart, adScheduleData, playerLicenseKey, feedId]); + }, [libLoaded, item, detachEvents, attachEvents, playerId, setPlayer, autostart, adScheduleData, playerLicenseKey, feedId, isAdScheduleLoading]); useEffect(() => { return () => { diff --git a/src/hooks/useAdSchedule.ts b/src/hooks/useAdSchedule.ts new file mode 100644 index 000000000..ff5b74a96 --- /dev/null +++ b/src/hooks/useAdSchedule.ts @@ -0,0 +1,22 @@ +import { useQuery } from 'react-query'; + +import { getAdSchedule } from '#src/services/api.service'; + +const CACHE_TIME = 60 * 1000 * 60 * 8; + +export const useAdSchedule = (adScheduleId: string | null | undefined) => { + const { isLoading, data } = useQuery( + ['ad-schedule'], + async () => { + const adSchedule = await getAdSchedule(adScheduleId); + + return adSchedule; + }, + { enabled: Boolean(adScheduleId), cacheTime: CACHE_TIME, staleTime: CACHE_TIME }, + ); + + return { + isLoading, + data, + }; +}; diff --git a/src/hooks/useMediaAds.ts b/src/hooks/useMediaAds.ts new file mode 100644 index 000000000..d9a078349 --- /dev/null +++ b/src/hooks/useMediaAds.ts @@ -0,0 +1,25 @@ +import { useQuery } from 'react-query'; + +import { getMediaAdSchedule } from '#src/services/api.service'; +import { useAdSchedule } from '#src/hooks/useAdSchedule'; +import type { AdTagUrls } from '#types/ad-schedule'; + +// 204 code +export const useMediaAds = (adScheduleId: string | null | undefined, mediaId: string, urls: AdTagUrls) => { + const { data: adSchedule, isLoading: isAdScheduleLoading } = useAdSchedule(adScheduleId); + + const { isLoading: isPerMediaAdSchedule, data: perMediaAds } = useQuery( + ['per-media-ad-schedule', mediaId], + async () => { + const adSchedule = await getMediaAdSchedule(mediaId, urls, adScheduleId); + + return adSchedule; + }, + { enabled: Boolean(mediaId) }, + ); + + return { + isLoading: isAdScheduleLoading || isPerMediaAdSchedule, + data: perMediaAds || adSchedule, + }; +}; diff --git a/src/services/api.service.ts b/src/services/api.service.ts index a348247e3..6705ff0d6 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -6,7 +6,7 @@ import { addQueryParams } from '#src/utils/formatting'; import { getDataOrThrow } from '#src/utils/api'; import { filterMediaOffers } from '#src/utils/entitlements'; import type { GetPlaylistParams, Playlist, PlaylistItem } from '#types/playlist'; -import type { AdSchedule } from '#types/ad-schedule'; +import type { AdSchedule, AdTagUrls } from '#types/ad-schedule'; import type { EpisodesRes, EpisodesWithPagination, GetSeriesParams, Series, EpisodeInSeries } from '#types/series'; import { useConfigStore as ConfigStore } from '#src/stores/ConfigStore'; @@ -92,6 +92,7 @@ export const getPlaylistById = async (id?: string, params: GetPlaylistParams = { /** * Get watchlist by playlistId * @param {string} playlistId + * @param {string[]} mediaIds * @param {string} [token] */ export const getMediaByWatchlist = async (playlistId: string, mediaIds: string[], token?: string): Promise => { @@ -247,3 +248,25 @@ export const getAdSchedule = async (id: string | undefined | null): Promise => { + const { preRollUrl, midRollUrl, postRollUrl } = urls; + const pathname = `/v2/advertising/media/${id}/schedule.json`; + const url = addQueryParams(`${import.meta.env.APP_API_BASE_URL}${pathname}`, { + preroll_url: preRollUrl, + midroll_url: midRollUrl, + postroll_url: postRollUrl, + fallback_ad_schedule: fallbackAdSchedule, + }); + + const response = await fetch(url); + + // No media ads set up + if (response.status === 204) { + return; + } + + const data = (await getDataOrThrow(response)) as { timings: AdSchedule }; + + return data.timings; +}; diff --git a/types/ad-schedule.d.ts b/types/ad-schedule.d.ts index d4d18f525..705ad31f5 100644 --- a/types/ad-schedule.d.ts +++ b/types/ad-schedule.d.ts @@ -3,3 +3,9 @@ export type AdSchedule = { client: string; schedule: string; }; + +export type AdTagUrls = { + preRollUrl: string | undefined; + midRollUrl: string | undefined; + postRollUrl: string | undefined; +}; diff --git a/types/playlist.d.ts b/types/playlist.d.ts index 3d601f89c..651770853 100644 --- a/types/playlist.d.ts +++ b/types/playlist.d.ts @@ -57,6 +57,9 @@ export type PlaylistItem = { scheduledStart?: Date; scheduledEnd?: Date; markdown?: string; + preRollUrl?: string; + midRollUrl?: string; + postRollUrl?: string; [key: string]: unknown; };