diff --git a/src/components/Card/Card.test.tsx b/src/components/Card/Card.test.tsx index 87da64b7a..5c948ea3d 100644 --- a/src/components/Card/Card.test.tsx +++ b/src/components/Card/Card.test.tsx @@ -6,7 +6,7 @@ import Card from './Card'; import type { PlaylistItem } from '#types/playlist'; const item = { title: 'aa', duration: 120 } as PlaylistItem; -const itemWithImage = { title: 'This is a movie', duration: 120, shelfImage: { image: 'http://movie.jpg' } } as PlaylistItem; +const itemWithImage = { title: 'This is a movie', duration: 120, cardImage: 'http://movie.jpg' } as PlaylistItem; describe('', () => { it('renders card with video title', () => { @@ -35,27 +35,4 @@ describe('', () => { expect(getByAltText('This is a movie')).toHaveStyle({ opacity: 1 }); }); - - it('uses the fallback image when the image fails to load', () => { - const itemWithFallbackImage = { - title: 'This is a movie', - duration: 120, - shelfImage: { - image: 'http://movie.jpg', - fallbackImage: 'http://fallback.jpg', - }, - } as PlaylistItem; - - const { getByAltText } = render( ''} />); - - fireEvent.error(getByAltText('This is a movie')); - - expect(getByAltText('This is a movie')).toHaveAttribute('src', 'http://fallback.jpg?width=320'); - expect(getByAltText('This is a movie')).toHaveStyle({ opacity: 0 }); - - fireEvent.load(getByAltText('This is a movie')); - - expect(getByAltText('This is a movie')).toHaveAttribute('src', 'http://fallback.jpg?width=320'); - expect(getByAltText('This is a movie')).toHaveStyle({ opacity: 1 }); - }); }); diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 5a92080ac..43592d783 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -43,7 +43,7 @@ function Card({ isLocked = true, currentLabel, }: CardProps): JSX.Element { - const { title, duration, episodeNumber, seasonNumber, shelfImage: image, mediaStatus, scheduledStart } = item; + const { title, duration, episodeNumber, seasonNumber, cardImage: image, mediaStatus, scheduledStart } = item; const { t, i18n: { language }, diff --git a/src/components/CardGrid/CardGrid.tsx b/src/components/CardGrid/CardGrid.tsx index b028d4a39..5a62a0217 100644 --- a/src/components/CardGrid/CardGrid.tsx +++ b/src/components/CardGrid/CardGrid.tsx @@ -55,7 +55,7 @@ function CardGrid({ onCardHover, }: CardGridProps) { const breakpoint: Breakpoint = useBreakpoint(); - const posterAspect = parseAspectRatio(playlist.shelfImageAspectRatio); + const posterAspect = parseAspectRatio(playlist.cardImageAspectRatio || playlist.shelfImageAspectRatio); const visibleTiles = cols[breakpoint] + parseTilesDelta(posterAspect); const [rowCount, setRowCount] = useState(INITIAL_ROW_COUNT); diff --git a/src/components/Hero/Hero.tsx b/src/components/Hero/Hero.tsx index 7584c987e..766cc66bc 100644 --- a/src/components/Hero/Hero.tsx +++ b/src/components/Hero/Hero.tsx @@ -3,12 +3,11 @@ import React from 'react'; import styles from './Hero.module.scss'; import Image from '#components/Image/Image'; -import type { ImageData } from '#types/playlist'; type Props = { title: string; description: string; - image?: ImageData; + image?: string; }; const Hero = ({ title, description, image }: Props) => { diff --git a/src/components/Image/Image.test.tsx b/src/components/Image/Image.test.tsx index 4ce1a4f04..9cf8e9a38 100644 --- a/src/components/Image/Image.test.tsx +++ b/src/components/Image/Image.test.tsx @@ -5,97 +5,22 @@ import Image from './Image'; describe('', () => { test('uses the src attribute when valid', () => { - const { getByAltText } = render(image); + const { getByAltText } = render(image); expect(getByAltText('image')).toHaveAttribute('src', 'http://image.jpg?width=640'); }); - test('tries the fallbackSrc when the image fails to load', () => { - const { getByAltText } = render( - image, - ); - - fireEvent.error(getByAltText('image')); - - expect(getByAltText('image')).toHaveAttribute('src', 'http://fallback.jpg?width=640'); - }); - - test('updates the src attribute when changed', () => { - const { getByAltText, rerender } = render(image); - - expect(getByAltText('image')).toHaveAttribute('src', 'http://image.jpg?width=640'); - - rerender(image); - - expect(getByAltText('image')).toHaveAttribute('src', 'http://otherimage.jpg?width=640'); - }); - - test('updates the src attribute when changed with the fallback image', () => { - const { getByAltText, rerender } = render(image); - - expect(getByAltText('image')).toHaveAttribute('src', 'http://image.jpg?width=640'); - - rerender( - image, - ); - - fireEvent.error(getByAltText('image')); - - expect(getByAltText('image')).toHaveAttribute('src', 'http://otherfallback.jpg?width=640'); - }); - test('fires the onLoad callback when the image is loaded', () => { const onLoad = vi.fn(); - const { getByAltText } = render(image); - - fireEvent.load(getByAltText('image')); - - expect(onLoad).toHaveBeenCalledTimes(1); - }); - - test('fires the onLoad callback when the fallback image is loaded', () => { - const onLoad = vi.fn(); - const { getByAltText } = render( - image, - ); + const { getByAltText } = render(image); - fireEvent.error(getByAltText('image')); fireEvent.load(getByAltText('image')); - expect(getByAltText('image')).toHaveAttribute('src', 'http://fallback.jpg?width=640'); expect(onLoad).toHaveBeenCalledTimes(1); }); test('changes the image width based on the given width', () => { - const { getByAltText } = render( - image, - ); + const { getByAltText } = render(image); expect(getByAltText('image')).toHaveAttribute('src', 'http://image.jpg?width=1280'); }); diff --git a/src/components/Image/Image.tsx b/src/components/Image/Image.tsx index 2673793ff..4155ea610 100644 --- a/src/components/Image/Image.tsx +++ b/src/components/Image/Image.tsx @@ -1,14 +1,13 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import classNames from 'classnames'; import styles from './Image.module.scss'; import { addQueryParams } from '#src/utils/formatting'; -import type { ImageData } from '#types/playlist'; type Props = { className?: string; - image?: ImageData; + image?: string; onLoad?: () => void; alt?: string; width?: number; @@ -19,25 +18,13 @@ const setWidth = (url: string, width: number) => { }; const Image = ({ className, image, onLoad, alt = '', width = 640 }: Props) => { - const [imgSrc, setImgSrc] = useState(image?.image); - const handleLoad = () => { if (onLoad) onLoad(); }; - const handleError = () => { - if (image?.fallbackImage && image.fallbackImage !== image.image) { - setImgSrc(image?.fallbackImage); - } - }; - - useEffect(() => { - setImgSrc(image?.image); - }, [image]); - - if (!imgSrc) return null; + if (!image) return null; - return {alt}; + return {alt}; }; export default React.memo(Image); diff --git a/src/components/VideoDetails/VideoDetails.test.tsx b/src/components/VideoDetails/VideoDetails.test.tsx index a343a050d..22847cf73 100644 --- a/src/components/VideoDetails/VideoDetails.test.tsx +++ b/src/components/VideoDetails/VideoDetails.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; +import { render } from '@testing-library/react'; import VideoDetails from './VideoDetails'; @@ -11,7 +11,7 @@ describe('', () => { description="Video description" primaryMetadata="Primary metadata string" secondaryMetadata={Secondary metadata string} - image={{ image: 'http://image.jpg' }} + image="http://image.jpg" startWatchingButton={} shareButton={} favoriteButton={} @@ -29,7 +29,7 @@ describe('', () => { description="Video description" primaryMetadata="Primary metadata string" secondaryMetadata={Secondary metadata string} - image={{ image: 'http://image.jpg' }} + image="http://image.jpg" startWatchingButton={} shareButton={} favoriteButton={} @@ -39,26 +39,4 @@ describe('', () => { expect(getByAltText('Test video')).toHaveAttribute('src', 'http://image.jpg?width=1280'); }); - - test('renders the fallback image when the image fails to load', () => { - const { getByAltText } = render( - Secondary metadata string} - image={{ image: 'http://image.jpg', fallbackImage: 'http://fallback.jpg' }} - startWatchingButton={} - shareButton={} - favoriteButton={} - trailerButton={} - />, - ); - - expect(getByAltText('Test video')).toHaveAttribute('src', 'http://image.jpg?width=1280'); - - fireEvent.error(getByAltText('Test video')); - - expect(getByAltText('Test video')).toHaveAttribute('src', 'http://fallback.jpg?width=1280'); - }); }); diff --git a/src/components/VideoDetails/VideoDetails.tsx b/src/components/VideoDetails/VideoDetails.tsx index 3fe1f8385..f694d5f1f 100644 --- a/src/components/VideoDetails/VideoDetails.tsx +++ b/src/components/VideoDetails/VideoDetails.tsx @@ -6,7 +6,6 @@ import styles from './VideoDetails.module.scss'; import CollapsibleText from '#components/CollapsibleText/CollapsibleText'; import useBreakpoint, { Breakpoint } from '#src/hooks/useBreakpoint'; import Image from '#components/Image/Image'; -import type { ImageData } from '#types/playlist'; import { testId } from '#src/utils/common'; type Props = { @@ -14,7 +13,7 @@ type Props = { description: string; primaryMetadata: React.ReactNode; secondaryMetadata?: React.ReactNode; - image?: ImageData; + image?: string; startWatchingButton: React.ReactNode; shareButton: React.ReactNode; favoriteButton: React.ReactNode; diff --git a/src/components/VideoLayout/VideoLayout.tsx b/src/components/VideoLayout/VideoLayout.tsx index 137278ddd..3325cf11f 100644 --- a/src/components/VideoLayout/VideoLayout.tsx +++ b/src/components/VideoLayout/VideoLayout.tsx @@ -10,7 +10,7 @@ import VideoDetailsInline from '#components/VideoDetailsInline/VideoDetailsInlin import VideoList from '#components/VideoList/VideoList'; import useBreakpoint, { Breakpoint } from '#src/hooks/useBreakpoint'; import { testId } from '#src/utils/common'; -import type { ImageData, Playlist, PlaylistItem } from '#types/playlist'; +import type { Playlist, PlaylistItem } from '#types/playlist'; import type { AccessModel } from '#types/Config'; type FilterProps = { @@ -29,7 +29,7 @@ type LoadMoreProps = { type VideoDetailsProps = { title: string; description: string; - image?: ImageData; + image?: string; primaryMetadata: React.ReactNode; secondaryMetadata?: React.ReactNode; shareButton: React.ReactNode; diff --git a/src/components/VideoListItem/VideoListItem.tsx b/src/components/VideoListItem/VideoListItem.tsx index de37da5b5..016130eb1 100644 --- a/src/components/VideoListItem/VideoListItem.tsx +++ b/src/components/VideoListItem/VideoListItem.tsx @@ -25,7 +25,7 @@ type VideoListItemProps = { }; function VideoListItem({ onClick, onHover, progress, activeLabel, item, loading = false, isActive = false, isLocked = true }: VideoListItemProps): JSX.Element { - const { title, duration, seasonNumber, episodeNumber, shelfImage: image, mediaStatus, scheduledStart } = item; + const { title, duration, seasonNumber, episodeNumber, cardImage: image, mediaStatus, scheduledStart } = item; const { t, diff --git a/src/containers/ShelfList/ShelfList.tsx b/src/containers/ShelfList/ShelfList.tsx index eddb5213b..80984e4e3 100644 --- a/src/containers/ShelfList/ShelfList.tsx +++ b/src/containers/ShelfList/ShelfList.tsx @@ -60,7 +60,7 @@ const ShelfList = ({ rows }: Props) => { {({ playlist, error, isLoading, style }) => { const title = row?.title || playlist.title; - const posterAspect = parseAspectRatio(playlist.shelfImageAspectRatio); + const posterAspect = parseAspectRatio(playlist.cardImageAspectRatio || playlist.shelfImageAspectRatio); const visibleTilesDelta = parseTilesDelta(posterAspect); return ( diff --git a/src/hooks/usePlanByEpg.ts b/src/hooks/usePlanByEpg.ts index fdaf6abc3..d132da02c 100644 --- a/src/hooks/usePlanByEpg.ts +++ b/src/hooks/usePlanByEpg.ts @@ -13,24 +13,24 @@ const isBaseTimeFormat = is12HourClock(); const usePlanByEpg = (channels: EpgChannel[], sidebarWidth: number, itemHeight: number, highlightColor?: string | null, backgroundColor?: string | null) => { const [epgChannels, epgPrograms] = useMemo(() => { return [ - channels.map((channel) => ({ - uuid: channel.id, - logo: channel.channelLogoImage?.image || '', - channelLogoImage: channel.channelLogoImage, - backgroundImage: channel.backgroundImage, + channels.map(({ id, channelLogoImage, backgroundImage }) => ({ + uuid: id, + logo: channelLogoImage, + channelLogoImage: channelLogoImage, + backgroundImage: backgroundImage, })), channels.flatMap((channel) => - channel.programs.map((program) => ({ + channel.programs.map(({ id, title, cardImage, backgroundImage, description, endTime, startTime }) => ({ channelUuid: channel.id, - id: program.id, - title: program.title, - image: program.shelfImage?.image || '', - // programs have the same shelfImage/backgroundImage (different API) - shelfImage: program.shelfImage, - backgroundImage: program.backgroundImage, - description: program.description || '', - till: program.endTime, - since: program.startTime, + id: id, + title, + image: cardImage || '', + // programs have the same cardImage/backgroundImage (different API) + cardImage: cardImage || '', + backgroundImage: backgroundImage || '', + description: description || '', + till: endTime, + since: startTime, })), ), ]; diff --git a/src/pages/LegacySeries/LegacySeries.tsx b/src/pages/LegacySeries/LegacySeries.tsx index c2b85c542..03fdd3f42 100644 --- a/src/pages/LegacySeries/LegacySeries.tsx +++ b/src/pages/LegacySeries/LegacySeries.tsx @@ -24,7 +24,7 @@ import ShareButton from '#components/ShareButton/ShareButton'; import FavoriteButton from '#src/containers/FavoriteButton/FavoriteButton'; import Button from '#components/Button/Button'; import PlayTrailer from '#src/icons/PlayTrailer'; -import type { PlaylistItem, ImageData } from '#types/playlist'; +import type { PlaylistItem } from '#types/playlist'; import useQueryParam from '#src/hooks/useQueryParam'; import Loading from '#src/pages/Loading/Loading'; import usePlaylist from '#src/hooks/usePlaylist'; @@ -108,7 +108,7 @@ const LegacySeries = () => { const pageTitle = `${selectedItem.title} - ${siteName}`; const pageDescription = selectedItem?.description || ''; const canonicalUrl = `${window.location.origin}${legacySeriesURL({ episodeId: episode?.mediaid, seriesId })}`; - const backgroundImage = (selectedItem.backgroundImage as ImageData) || undefined; + const backgroundImage = (selectedItem.backgroundImage as string) || undefined; const primaryMetadata = episode ? formatVideoMetaString(episode, t('video:total_episodes', { count: seriesPlaylist?.playlist?.length })) diff --git a/src/services/api.service.ts b/src/services/api.service.ts index fc7fd2e16..b5626dcc0 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -9,31 +9,33 @@ import type { GetPlaylistParams, Playlist, PlaylistItem } from '#types/playlist' import type { AdSchedule } from '#types/ad-schedule'; import type { EpisodesRes, EpisodesWithPagination, GetSeriesParams, Series, EpisodeInSeries } from '#types/series'; import { useConfigStore as ConfigStore } from '#src/stores/ConfigStore'; -import { generateImageData } from '#src/utils/image'; // change the values below to change the property used to look up the alternate image enum ImageProperty { - SHELF = 'shelfImage', - BACKGROUND = 'backgroundImage', - CHANNEL_LOGO = 'channelLogoImage', + CARD = 'card', + BACKGROUND = 'background', + CHANNEL_LOGO = 'channel_logo', } const PAGE_LIMIT = 20; +const generateAlternateImageURL = (item: PlaylistItem, label: string) => + `https://img.jwplayer.com/v1/media/${item.mediaid}/images/${label}.webp?poster_fallback=1`; + /** * Transform incoming media items * - Parses productId into MediaOffer[] for all cleeng offers */ -export const transformMediaItem = (item: PlaylistItem, playlist?: Playlist) => { +export const transformMediaItem = (item: PlaylistItem) => { const config = ConfigStore.getState().config; const offerKeys = Object.keys(config?.integrations)[0]; const transformedMediaItem = { ...item, - shelfImage: generateImageData(config, ImageProperty.SHELF, item, playlist), - backgroundImage: generateImageData(config, ImageProperty.BACKGROUND, item), - channelLogoImage: generateImageData(config, ImageProperty.CHANNEL_LOGO, item), + cardImage: generateAlternateImageURL(item, ImageProperty.CARD), + backgroundImage: generateAlternateImageURL(item, ImageProperty.BACKGROUND), + channelLogoImage: generateAlternateImageURL(item, ImageProperty.CHANNEL_LOGO), mediaOffers: item.productIds ? filterMediaOffers(offerKeys, item.productIds) : undefined, scheduledStart: item['VCH.ScheduledStart'] ? parseISO(item['VCH.ScheduledStart'] as string) : undefined, scheduledEnd: item['VCH.ScheduledEnd'] ? parseISO(item['VCH.ScheduledEnd'] as string) : undefined, @@ -52,7 +54,7 @@ export const transformMediaItem = (item: PlaylistItem, playlist?: Playlist) => { * @param relatedMediaId */ export const transformPlaylist = (playlist: Playlist, relatedMediaId?: string) => { - playlist.playlist = playlist.playlist.map((item) => transformMediaItem(item, playlist)); + playlist.playlist = playlist.playlist.map((item) => transformMediaItem(item)); // remove the related media item (when this is a recommendations playlist) if (relatedMediaId) playlist.playlist.filter((item) => item.mediaid !== relatedMediaId); @@ -96,7 +98,7 @@ export const getMediaByWatchlist = async (playlistId: string, mediaIds: string[] if (!data) throw new Error(`The data was not found using the watchlist ${playlistId}`); - return (data.playlist || []).map((item) => transformMediaItem(item, data)); + return (data.playlist || []).map((item) => transformMediaItem(item)); }; /** diff --git a/src/services/epg.service.test.ts b/src/services/epg.service.test.ts index 803a8ae60..04cae444d 100644 --- a/src/services/epg.service.test.ts +++ b/src/services/epg.service.test.ts @@ -313,7 +313,7 @@ describe('epgService', () => { endTime: '2022-07-19T15:00:00Z', description: undefined, image: undefined, - shelfImage: undefined, + cardImage: undefined, backgroundImage: undefined, }); @@ -324,7 +324,7 @@ describe('epgService', () => { endTime: '2022-07-19T15:00:00Z', description: undefined, image: undefined, - shelfImage: undefined, + cardImage: undefined, backgroundImage: undefined, }); @@ -334,8 +334,8 @@ describe('epgService', () => { startTime: '2022-07-19T12:00:00Z', endTime: '2022-07-19T15:00:00Z', description: 'A description', - shelfImage: { image: 'https://cdn.jwplayer/logo.jpg' }, - backgroundImage: { image: 'https://cdn.jwplayer/logo.jpg' }, + cardImage: 'https://cdn.jwplayer/logo.jpg', + backgroundImage: 'https://cdn.jwplayer/logo.jpg', }); }); }); diff --git a/src/services/epg.service.ts b/src/services/epg.service.ts index 6f8ee44b2..fea96f910 100644 --- a/src/services/epg.service.ts +++ b/src/services/epg.service.ts @@ -1,7 +1,7 @@ import { array, object, string } from 'yup'; import { addDays, differenceInDays, endOfDay, isValid, startOfDay, subDays } from 'date-fns'; -import type { ImageData, PlaylistItem } from '#types/playlist'; +import type { PlaylistItem } from '#types/playlist'; import { getDataOrThrow } from '#src/utils/api'; import { logDev } from '#src/utils/common'; @@ -20,8 +20,8 @@ export type EpgChannel = { id: string; title: string; description: string; - channelLogoImage: ImageData; - backgroundImage: ImageData; + channelLogoImage: string; + backgroundImage: string; programs: EpgProgram[]; catchupHours: number; }; @@ -32,8 +32,8 @@ export type EpgProgram = { startTime: string; endTime: string; description?: string; - shelfImage?: ImageData; - backgroundImage?: ImageData; + cardImage?: string; + backgroundImage?: string; }; const epgProgramSchema = object().shape({ @@ -64,7 +64,7 @@ class EpgService { description, startTime: subDays(startOfDay(new Date()), 1).toJSON(), endTime: addDays(endOfDay(new Date()), 1).toJSON(), - shelfImage: undefined, + cardImage: undefined, backgroundImage: undefined, }; } @@ -96,16 +96,15 @@ class EpgService { */ async transformProgram(data: unknown): Promise { const program = await epgProgramSchema.validate(data); - const image = program.chapterPointCustomProperties?.find((item) => item.key === 'image')?.value || ''; - const imageData = image ? { image } : undefined; + const image = program.chapterPointCustomProperties?.find((item) => item.key === 'image')?.value || undefined; return { id: program.id, title: program.title, startTime: program.startTime, endTime: program.endTime, - shelfImage: imageData, - backgroundImage: imageData, + cardImage: image, + backgroundImage: image, description: program.chapterPointCustomProperties?.find((item) => item.key === 'description')?.value || undefined, }; } diff --git a/src/utils/collection.ts b/src/utils/collection.ts index 805044c2e..cdd02e44c 100644 --- a/src/utils/collection.ts +++ b/src/utils/collection.ts @@ -40,18 +40,9 @@ const generatePlaylistPlaceholder = (playlistLength: number = 15): Playlist => ( feedid: '', image: '', images: [], - shelfImage: { - image: '', - fallbackImage: '', - }, - backgroundImage: { - image: '', - fallbackImage: '', - }, - channelLogoImage: { - image: '', - fallbackImage: '', - }, + cardImage: '', + backgroundImage: '', + channelLogoImage: '', link: '', genre: '', mediaid: `placeholder_${index}`, diff --git a/src/utils/image.test.ts b/src/utils/image.test.ts deleted file mode 100644 index 1af57efe1..000000000 --- a/src/utils/image.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { generateAlternateImageURL, generateImageData } from './image'; - -import configFixture from '#test/fixtures/config.json'; -import playlistFixture from '#test/fixtures/playlist.json'; -import type { Config } from '#types/Config'; -import type { Playlist } from '#types/playlist'; - -const config = configFixture as Config; -const playlist = playlistFixture as Playlist; - -describe('image utils', () => { - describe('generateImageData', () => { - test('should only return the poster image URL when no alternate image is set', () => { - const images = generateImageData(config, 'shelfImage', playlist.playlist[0], playlist); - - expect(images).toEqual({ image: 'https://cdn.jwplayer.com/v2/media/uB8aRnu6/poster.jpg?width=640' }); - }); - - test('should return the alternate image and poster image url when alternate image is set', () => { - const playlistWithProperty = Object.assign({ shelfImage: 'playlist_label' }, playlist); - const images = generateImageData(config, 'shelfImage', playlist.playlist[0], playlistWithProperty); - - expect(images).toEqual({ - image: 'https://img.jwplayer.com/v1/media/uB8aRnu6/images/playlist_label.webp?width=640', - fallbackImage: 'https://cdn.jwplayer.com/v2/media/uB8aRnu6/poster.jpg?width=640', - }); - }); - - test('should use the width param for the generated image URLs', () => { - const playlistWithProperty = Object.assign({ shelfImage: 'playlist_label' }, playlist); - const images = generateImageData(config, 'shelfImage', playlist.playlist[0], playlistWithProperty, 1280); - - expect(images).toEqual({ - image: 'https://img.jwplayer.com/v1/media/uB8aRnu6/images/playlist_label.webp?width=1280', - fallbackImage: 'https://cdn.jwplayer.com/v2/media/uB8aRnu6/poster.jpg?width=1280', - }); - }); - }); - - describe('generateAlternateImageURL', () => { - test('generates a correct image URL', () => { - expect(generateAlternateImageURL('123456', 'label', 640)).toEqual('https://img.jwplayer.com/v1/media/123456/images/label.webp?width=640'); - expect(generateAlternateImageURL('654321', 'shelf', 1280)).toEqual('https://img.jwplayer.com/v1/media/654321/images/shelf.webp?width=1280'); - }); - }); -}); diff --git a/src/utils/image.ts b/src/utils/image.ts deleted file mode 100644 index b6f598ebe..000000000 --- a/src/utils/image.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Config } from '#types/Config'; -import type { Playlist, PlaylistItem } from '#types/playlist'; -import { findPlaylistImageForWidth } from '#src/utils/collection'; - -export const generateAlternateImageURL = (mediaid: string, imageLabel: string, width: number) => - `https://img.jwplayer.com/v1/media/${mediaid}/images/${imageLabel}.webp?width=${width}`; - -export const generateImageData = (config: Config, propertyName: string, item: PlaylistItem, playlist?: Playlist, width = 640) => { - const posterImage = findPlaylistImageForWidth(item, width); - // when a playlist is given, get the alternate image label from the playlist instead - const contentImageLabel = playlist ? playlist[propertyName] : item[propertyName]; - const configImageLabel = config.custom?.[propertyName]; - const imageLabel = contentImageLabel || configImageLabel; - - return { - image: typeof imageLabel === 'string' ? generateAlternateImageURL(item.mediaid, imageLabel, width) : posterImage, - fallbackImage: imageLabel ? posterImage : undefined, - }; -}; diff --git a/test-e2e/tests/home_test.ts b/test-e2e/tests/home_test.ts index 64375061d..fe7b36ce3 100644 --- a/test-e2e/tests/home_test.ts +++ b/test-e2e/tests/home_test.ts @@ -127,24 +127,6 @@ Scenario('I can slide within non-featured shelves', async ({ I }) => { I.dontSee('Agent 327'); }); -Scenario('I can see alternate shelf images for the `All Films` shelf', async ({ I }) => { - // scroll to shelf to make it visible and for screenshot - await I.scrollToShelf(ShelfId.allFilms); - await I.seeCardImageSrc('Agent 327', ShelfId.allFilms, 'https://img.jwplayer.com/v1/media/uB8aRnu6/images/shelf.webp?width=320'); - await I.seeCardImageSrc('Big Buck Bunny', ShelfId.allFilms, 'https://img.jwplayer.com/v1/media/awWEFyPu/images/shelf.webp?width=320'); -}); - -Scenario('I can see poster images for the `All courses` shelf', async ({ I }) => { - // scroll to shelf to make it visible and for screenshot - await I.scrollToShelf(ShelfId.allCourses); - await I.seeCardImageSrc('Primitive Animals', ShelfId.allCourses, `https://cdn.jwplayer.com/v2/media/${constants.primitiveAnimalsId}/poster.jpg?width=320`); - await I.seeCardImageSrc( - 'Fantasy Vehicle Creation', - ShelfId.allCourses, - `https://cdn.jwplayer.com/v2/media/${constants.fantasyVehicleId}/poster.jpg?width=320`, - ); -}); - Scenario('I can see the footer', ({ I }) => { I.scrollPageToBottom(); I.see('© JW Player'); diff --git a/test-e2e/tests/live_channel_test.ts b/test-e2e/tests/live_channel_test.ts index 10104358e..9c31166cd 100644 --- a/test-e2e/tests/live_channel_test.ts +++ b/test-e2e/tests/live_channel_test.ts @@ -227,24 +227,24 @@ Scenario('I can navigate through the epg', async ({ I }) => { await isLiveProgram(I, channel2LiveProgramLocator, 'channel 2'); }); -Scenario('I can see an alternate channel logo for Channel 1', async ({ I }) => { +Scenario('I can see the channel logo for Channel 1', async ({ I }) => { await I.openVideoCard('Channel 1'); - await I.seeEpgChannelLogoImage('Uh7zcqVm', 'https://img.jwplayer.com/v1/media/Uh7zcqVm/images/channel_logo.webp?width=320'); + await I.seeEpgChannelLogoImage('Uh7zcqVm', 'https://img.jwplayer.com/v1/media/Uh7zcqVm/images/channel_logo.webp?poster_fallback=1&width=320'); }); -Scenario('I can see the default channel logo for Channel 2', async ({ I }) => { +Scenario('I can see the channel logo for Channel 2', async ({ I }) => { await I.openVideoCard('Channel 2'); - await I.seeEpgChannelLogoImage('Z2evecey', 'https://cdn.jwplayer.com/v2/media/Z2evecey/poster.jpg?width=320'); + await I.seeEpgChannelLogoImage('Z2evecey', 'https://img.jwplayer.com/v1/media/Z2evecey/images/channel_logo.webp?poster_fallback=1&width=320'); }); -Scenario('I can see an alternate background image for Channel 3', async ({ I }) => { +Scenario('I can see the background image for Channel 3', async ({ I }) => { await I.openVideoCard('Channel 3'); - await I.seeVideoDetailsBackgroundImage('Channel 3', 'https://img.jwplayer.com/v1/media/wewsVyR7/images/background.webp?width=1280'); + await I.seeVideoDetailsBackgroundImage('Channel 3', 'https://img.jwplayer.com/v1/media/wewsVyR7/images/background.webp?poster_fallback=1&width=1280'); }); -Scenario('I can see the default background image for Channel 4', async ({ I }) => { +Scenario('I can see the background image for Channel 4', async ({ I }) => { await I.openVideoCard('Channel 4'); - await I.seeVideoDetailsBackgroundImage('Channel 4', 'https://cdn.jwplayer.com/v2/media/kH7LozaK/poster.jpg?width=1280'); + await I.seeVideoDetailsBackgroundImage('Channel 4', 'https://img.jwplayer.com/v1/media/kH7LozaK/images/background.webp?poster_fallback=1&width=1280'); }); async function isSelectedProgram(I: CodeceptJS.I, locator: CodeceptJS.Locator, channel: string) { diff --git a/test-e2e/tests/series_test.ts b/test-e2e/tests/series_test.ts index ea260378c..b09c9f1b0 100644 --- a/test-e2e/tests/series_test.ts +++ b/test-e2e/tests/series_test.ts @@ -90,9 +90,3 @@ Scenario('I can play other episodes from the series', async ({ I }) => { assert.strictEqual((await I.grabPageScrollPosition()).y, 0); I.see('Placing the lights and creating the environment then finishes up this workshop!'); }); - -Scenario('I can see an alternate background image for Fantasy Vehicle Creation', async ({ I }) => { - await I.openVideoCard(constants.fantasyVehicleTitle, ShelfId.allCourses); - I.see('Fantasy Vehicle Creation'); - await I.seeVideoDetailsBackgroundImage('Fantasy Vehicle Creation', 'https://img.jwplayer.com/v1/media/lsFXY5xn/images/background.webp?width=1280'); -}); diff --git a/test-e2e/tests/video_detail_test.ts b/test-e2e/tests/video_detail_test.ts index 7b9b387a2..7adea6485 100644 --- a/test-e2e/tests/video_detail_test.ts +++ b/test-e2e/tests/video_detail_test.ts @@ -34,16 +34,6 @@ function runTestSuite(config: typeof testConfigs.svod, providerName: string) { I.see('11 min', { css: 'div[aria-label="Play Elephants Dream"]' }); }); - Scenario(`I can see an alternate background image for Agent 327 - ${providerName}`, async ({ I }) => { - await I.openVideoCard('Agent 327'); - await I.seeVideoDetailsBackgroundImage('Agent 327', 'https://img.jwplayer.com/v1/media/uB8aRnu6/images/background.webp?width=1280'); - }); - - Scenario(`I can see the default background image for Elephants Dream - ${providerName}`, async ({ I }) => { - await I.openVideoCard('Elephants Dream'); - await I.seeVideoDetailsBackgroundImage('Elephants Dream', 'https://cdn.jwplayer.com/v2/media/eFPH2tVG/poster.jpg?width=1280'); - }); - Scenario(`I can expand the description (@mobile-only) - ${providerName}`, async ({ I }) => { await I.openVideoCard('Agent 327'); diff --git a/test/fixtures/epgChannels.json b/test/fixtures/epgChannels.json index 785111d86..d02fc539f 100644 --- a/test/fixtures/epgChannels.json +++ b/test/fixtures/epgChannels.json @@ -4,36 +4,24 @@ "title": "Channel 1", "description": "Description for Channel 1", "image": "", - "channelLogoImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - }, + "channelLogoImage": "", + "backgroundImage": "", "programs": [ { "id": "program1", "title": "Program 1", "startTime": "2022-07-15T10:00:00Z", "endTime": "2022-07-15T10:30:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" }, { "id": "program2", "title": "Program 2", "startTime": "2022-07-15T10:30:00Z", "endTime": "2022-07-15T11:00:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" } ], "catchupHours": 2 @@ -43,36 +31,24 @@ "title": "Channel 2", "description": "Description for Channel 2", "image": "", - "channelLogoImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - }, + "channelLogoImage": "", + "backgroundImage": "", "programs": [ { "id": "program3", "title": "Program 3", "startTime": "2022-07-15T10:00:00Z", "endTime": "2022-07-15T10:30:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" }, { "id": "program4", "title": "Program 4", "startTime": "2022-07-15T10:30:00Z", "endTime": "2022-07-15T11:00:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" } ], "catchupHours": 2 diff --git a/test/fixtures/epgChannelsUpdate.json b/test/fixtures/epgChannelsUpdate.json index 51bdff9f7..4c51cb601 100644 --- a/test/fixtures/epgChannelsUpdate.json +++ b/test/fixtures/epgChannelsUpdate.json @@ -4,48 +4,32 @@ "title": "Channel 1", "description": "Description for Channel 1", "image": "", - "channelLogoImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - }, + "channelLogoImage": "", + "backgroundImage": "", "programs": [ { "id": "program1", "title": "Program 1", "startTime": "2022-07-15T10:00:00Z", "endTime": "2022-07-15T10:45:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" }, { "id": "program2", "title": "Program 2", "startTime": "2022-07-15T10:45:00Z", "endTime": "2022-07-15T11:00:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" }, { "id": "program5", "title": "Program 5", "startTime": "2022-07-15T11:00:00Z", "endTime": "2022-07-15T11:30:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" } ], "catchupHours": 2 @@ -55,48 +39,32 @@ "title": "Channel 2", "description": "Description for Channel 2", "image": "", - "channelLogoImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - }, + "channelLogoImage": "", + "backgroundImage": "", "programs": [ { "id": "program3", "title": "Program 3", "startTime": "2022-07-15T10:00:00Z", "endTime": "2022-07-15T10:45:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" }, { "id": "program4", "title": "Program 4", "startTime": "2022-07-15T10:45:00Z", "endTime": "2022-07-15T11:00:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" }, { "id": "program6", "title": "Program 6", "startTime": "2022-07-15T11:30:00Z", "endTime": "2022-07-15T12:00:00Z", - "shelfImage": { - "image": "" - }, - "backgroundImage": { - "image": "" - } + "cardImage": "", + "backgroundImage": "" } ], "catchupHours": 2 diff --git a/types/playlist.d.ts b/types/playlist.d.ts index 1958c07d1..893334003 100644 --- a/types/playlist.d.ts +++ b/types/playlist.d.ts @@ -2,11 +2,6 @@ import type { MediaOffer } from '#types/media'; export type GetPlaylistParams = { page_limit?: string; related_media_id?: string; token?: string; search?: string }; -export type ImageData = { - image: string; - fallbackImage?: string; -}; - export type Image = { src: string; type: string; @@ -30,9 +25,9 @@ export type PlaylistItem = { feedid: string; image: string; images: Image[]; - shelfImage?: ImageData; - backgroundImage?: ImageData; - channelLogoImage?: ImageData; + cardImage?: string; + backgroundImage?: string; + channelLogoImage?: string; link: string; genre?: string; mediaid: string; @@ -79,5 +74,10 @@ export type Playlist = { playlist: PlaylistItem[]; title: string; contentType?: string; + /** + * @deprecated Use {@link Playlist.cardImageAspectRatio} instead. + */ + shelfImageAspectRatio?: string; + cardImageAspectRatio?: string; [key: string]: unknown; };