Skip to content

Commit 6df2b0f

Browse files
Added media source ID handling in Player and Details components
1 parent 1ff6235 commit 6df2b0f

4 files changed

Lines changed: 80 additions & 8 deletions

File tree

packages/app/src/App/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ const AppContent = (props) => {
715715
<Player
716716
item={playingItem}
717717
resume={isResume}
718+
initialMediaSourceId={playbackOptions?.mediaSourceId}
718719
initialAudioIndex={playbackOptions?.audioStreamIndex}
719720
initialSubtitleIndex={playbackOptions?.subtitleStreamIndex}
720721
audioPlaylist={playbackOptions?.audioPlaylist}

packages/app/src/views/Details/Details.js

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
148148
const [artistAlbums, setArtistAlbums] = useState([]);
149149
const [playlistItems, setPlaylistItems] = useState([]);
150150
const [isLoading, setIsLoading] = useState(true);
151+
const [selectedVersionIndex, setSelectedVersionIndex] = useState(0);
151152
const [selectedAudioIndex, setSelectedAudioIndex] = useState(0);
152153
const [selectedSubtitleIndex, setSelectedSubtitleIndex] = useState(-1);
153154
const [showMediaInfo, setShowMediaInfo] = useState(false);
@@ -178,6 +179,7 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
178179
const data = await effectiveApi.getItem(itemId);
179180
setItem(tagWithServerInfo(data));
180181

182+
setSelectedVersionIndex(0);
181183
const ms = data.MediaSources?.[0];
182184
if (ms) {
183185
const audioStreams = ms.MediaStreams?.filter(s => s.Type === 'Audio') || [];
@@ -316,12 +318,13 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
316318

317319
let playbackOptions = {};
318320
if (supportsSelection) {
319-
const playMediaSource = item.MediaSources[0];
321+
const playMediaSource = item.MediaSources[selectedVersionIndex] || item.MediaSources[0];
320322
const audioStreamsList = playMediaSource?.MediaStreams?.filter(s => s.Type === 'Audio') || [];
321323
const subtitleStreamsList = playMediaSource?.MediaStreams?.filter(s => s.Type === 'Subtitle') || [];
322324
const selectedAudio = audioStreamsList[selectedAudioIndex];
323325
const subtitleStream = selectedSubtitleIndex >= 0 ? subtitleStreamsList[selectedSubtitleIndex] : null;
324326
playbackOptions = {
327+
mediaSourceId: playMediaSource.Id,
325328
audioStreamIndex: selectedAudio?.Index ?? selectedAudioIndex,
326329
subtitleStreamIndex: subtitleStream?.Index ?? selectedSubtitleIndex
327330
};
@@ -354,7 +357,7 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
354357
} else {
355358
onPlay?.(item, false, playbackOptions);
356359
}
357-
}, [item, episodes, nextUp, seasons, albumTracks, playlistItems, onPlay, onSelectItem, selectedAudioIndex, selectedSubtitleIndex]);
360+
}, [item, episodes, nextUp, seasons, albumTracks, playlistItems, onPlay, onSelectItem, selectedAudioIndex, selectedSubtitleIndex, selectedVersionIndex]);
358361

359362
const handleResume = useCallback(() => {
360363
if (!item) return;
@@ -365,19 +368,20 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
365368

366369
let playbackOptions = {};
367370
if (supportsSelection) {
368-
const resumeMediaSource = item.MediaSources[0];
371+
const resumeMediaSource = item.MediaSources[selectedVersionIndex] || item.MediaSources[0];
369372
const audioStreamsList = resumeMediaSource?.MediaStreams?.filter(s => s.Type === 'Audio') || [];
370373
const subtitleStreamsList = resumeMediaSource?.MediaStreams?.filter(s => s.Type === 'Subtitle') || [];
371374
const selectedAudio = audioStreamsList[selectedAudioIndex];
372375
const subtitleStream = selectedSubtitleIndex >= 0 ? subtitleStreamsList[selectedSubtitleIndex] : null;
373376
playbackOptions = {
377+
mediaSourceId: resumeMediaSource.Id,
374378
audioStreamIndex: selectedAudio?.Index ?? selectedAudioIndex,
375379
subtitleStreamIndex: subtitleStream?.Index ?? selectedSubtitleIndex
376380
};
377381
}
378382

379383
onPlay?.(item, true, playbackOptions);
380-
}, [item, onPlay, selectedAudioIndex, selectedSubtitleIndex]);
384+
}, [item, onPlay, selectedAudioIndex, selectedSubtitleIndex, selectedVersionIndex]);
381385

382386
const handleShuffle = useCallback(() => {
383387
if (item) {
@@ -505,6 +509,7 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
505509

506510
const handleOpenAudioModal = useCallback(() => openModal('audio'), [openModal]);
507511
const handleOpenSubtitleModal = useCallback(() => openModal('subtitle'), [openModal]);
512+
const handleOpenVersionModal = useCallback(() => openModal('version'), [openModal]);
508513

509514
const closeModal = useCallback(() => {
510515
setActiveModal(null);
@@ -524,6 +529,28 @@ const Details = ({itemId, initialItem, onPlay, onSelectItem, onSelectPerson, bac
524529
closeModal();
525530
}, [closeModal]);
526531

532+
const handleSelectVersion = useCallback((e) => {
533+
const index = parseInt(e.currentTarget.dataset.index, 10);
534+
if (isNaN(index) || !item?.MediaSources?.[index]) return;
535+
setSelectedVersionIndex(index);
536+
const ms = item.MediaSources[index];
537+
const audioStreams = ms.MediaStreams?.filter(s => s.Type === 'Audio') || [];
538+
const subtitleStreams = ms.MediaStreams?.filter(s => s.Type === 'Subtitle') || [];
539+
if (ms.DefaultAudioStreamIndex != null) {
540+
const idx = audioStreams.findIndex(s => s.Index === ms.DefaultAudioStreamIndex);
541+
setSelectedAudioIndex(idx >= 0 ? idx : 0);
542+
} else {
543+
setSelectedAudioIndex(0);
544+
}
545+
if (ms.DefaultSubtitleStreamIndex != null) {
546+
const idx = subtitleStreams.findIndex(s => s.Index === ms.DefaultSubtitleStreamIndex);
547+
setSelectedSubtitleIndex(idx >= 0 ? idx : -1);
548+
} else {
549+
setSelectedSubtitleIndex(-1);
550+
}
551+
closeModal();
552+
}, [item, closeModal]);
553+
527554
const handleSeasonSelect = useCallback((ev) => {
528555
const seasonId = ev.currentTarget.dataset.seasonId;
529556
const season = seasons.find(s => s.Id === seasonId);
@@ -877,12 +904,13 @@ const handleSectionKeyDown = useCallback((ev) => {
877904
const seasonCount = item.ChildCount || seasons.length || 0;
878905

879906
// Media source info
880-
const mediaSource = item.MediaSources?.[0];
907+
const mediaSource = item.MediaSources?.[selectedVersionIndex] || item.MediaSources?.[0];
881908
const audioStreams = mediaSource?.MediaStreams?.filter(s => s.Type === 'Audio') || [];
882909
const subtitleStreams = mediaSource?.MediaStreams?.filter(s => s.Type === 'Subtitle') || [];
883910
const supportsMediaSourceSelection = item.MediaType === 'Video' &&
884911
item.MediaSources?.length > 0 &&
885912
item.MediaSources[0].Type !== 'Placeholder';
913+
const hasMultipleVersions = supportsMediaSourceSelection && (item.MediaSources?.length || 0) > 1;
886914
const hasMultipleAudio = supportsMediaSourceSelection && audioStreams.length > 1;
887915
const hasSubtitles = supportsMediaSourceSelection && subtitleStreams.length > 0;
888916
const currentAudioStream = audioStreams[selectedAudioIndex];
@@ -1016,6 +1044,17 @@ const handleSectionKeyDown = useCallback((ev) => {
10161044
<span className={css.btnLabel}>Shuffle</span>
10171045
</SpottableDiv>
10181046
)}
1047+
{hasMultipleVersions && (
1048+
<SpottableDiv className={css.btnWrapper} onClick={handleOpenVersionModal}>
1049+
<div className={css.btnAction}>
1050+
<svg className={css.btnIcon} viewBox="0 -960 960 960" fill="currentColor">
1051+
<path d="M320-240h320v-80H320v80Zm0-160h320v-80H320v80ZM240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T740-80H240Zm280-520v-200H240v640h500v-440H520ZM240-800v200-200 640-640Z"/>
1052+
</svg>
1053+
</div>
1054+
<span className={css.btnLabel}>Version</span>
1055+
<span className={css.btnDetail}>{mediaSource?.Name || `Version ${selectedVersionIndex + 1}`}</span>
1056+
</SpottableDiv>
1057+
)}
10191058
{hasMultipleAudio && (
10201059
<SpottableDiv className={css.btnWrapper} onClick={handleOpenAudioModal}>
10211060
<div className={css.btnAction}>
@@ -1944,7 +1983,33 @@ const handleSectionKeyDown = useCallback((ev) => {
19441983
</div>
19451984
</Scroller>
19461985

1947-
{/* Audio/Subtitle Track Modals */}
1986+
{/* Version / Audio / Subtitle Track Modals */}
1987+
{activeModal === 'version' && (
1988+
<div className={css.trackModal} onClick={closeModal}>
1989+
<ModalContainer className={css.trackModalPanel} onClick={handleStopPropagation} data-modal="version" spotlightId="version-modal">
1990+
<h2 className={css.trackModalTitle}>Select Version</h2>
1991+
<div className={css.trackList}>
1992+
{item.MediaSources.map((source, i) => {
1993+
const video = source.MediaStreams?.find(s => s.Type === 'Video');
1994+
const resLabel = video?.Width >= 3800 ? '4K' : video?.Width >= 1900 ? '1080p' : video?.Width >= 1260 ? '720p' : video?.Width ? `${video.Width}p` : '';
1995+
return (
1996+
<SpottableButton
1997+
key={source.Id}
1998+
className={`${css.trackItem} ${i === selectedVersionIndex ? css.selected : ''}`}
1999+
data-index={i}
2000+
data-selected={i === selectedVersionIndex ? 'true' : undefined}
2001+
onClick={handleSelectVersion}
2002+
>
2003+
<span className={css.trackName}>{source.Name || `Version ${i + 1}`}</span>
2004+
{resLabel && <span className={css.trackInfo}>{resLabel}</span>}
2005+
</SpottableButton>
2006+
);
2007+
})}
2008+
</div>
2009+
<p className={css.trackModalFooter}>Press BACK to close</p>
2010+
</ModalContainer>
2011+
</div>
2012+
)}
19482013
{activeModal === 'audio' && (
19492014
<div className={css.trackModal} onClick={closeModal}>
19502015
<ModalContainer className={css.trackModalPanel} onClick={handleStopPropagation} data-modal="audio" spotlightId="audio-modal">

packages/app/src/views/Player/TizenPlayer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import css from './TizenPlayer.module.less';
2727
* playback. AVPlay renders on a platform multimedia layer BEHIND the web engine;
2828
* the web layer must be transparent in the video area for the content to show through.
2929
*/
30-
const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded, onBack, onPlayNext, audioPlaylist}) => {
30+
const Player = ({item, resume, initialMediaSourceId, initialAudioIndex, initialSubtitleIndex, onEnded, onBack, onPlayNext, audioPlaylist}) => {
3131
const {settings} = useSettings();
3232

3333
const [isLoading, setIsLoading] = useState(true);
@@ -331,6 +331,7 @@ const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded,
331331
maxBitrate: effectiveBitrate,
332332
preferTranscode: settings.preferTranscode,
333333
item: item,
334+
mediaSourceId: initialMediaSourceId,
334335
audioStreamIndex: initialAudioIndex != null ? initialAudioIndex : undefined
335336
});
336337

@@ -721,6 +722,7 @@ const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded,
721722
enableDirectPlay: false,
722723
enableDirectStream: false,
723724
enableTranscoding: true,
725+
mediaSourceId: mediaSourceId,
724726
item: item
725727
});
726728

packages/app/src/views/Player/WebOSPlayer.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626

2727
import css from './WebOSPlayer.module.less';
2828

29-
const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded, onBack, onPlayNext, audioPlaylist}) => {
29+
const Player = ({item, resume, initialMediaSourceId, initialAudioIndex, initialSubtitleIndex, onEnded, onBack, onPlayNext, audioPlaylist}) => {
3030
const {settings} = useSettings();
3131

3232
const [mediaUrl, setMediaUrl] = useState(null);
@@ -267,6 +267,7 @@ const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded,
267267
enableDirectPlay: !settings.preferTranscode,
268268
enableDirectStream: !settings.preferTranscode,
269269
forceDirectPlay: settings.forceDirectPlay,
270+
mediaSourceId: initialMediaSourceId,
270271
audioStreamIndex: initialAudioIndex,
271272
subtitleStreamIndex: initialSubtitleIndex,
272273
item: item
@@ -532,6 +533,7 @@ const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded,
532533
enableDirectPlay: false,
533534
enableDirectStream: false,
534535
enableTranscoding: true,
536+
mediaSourceId: mediaSourceId,
535537
item: item
536538
});
537539

@@ -1080,6 +1082,7 @@ const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded,
10801082
enableDirectPlay: false,
10811083
enableDirectStream: false,
10821084
enableTranscoding: true,
1085+
mediaSourceId: mediaSourceId,
10831086
item: item
10841087
});
10851088

@@ -1115,6 +1118,7 @@ const Player = ({item, resume, initialAudioIndex, initialSubtitleIndex, onEnded,
11151118
enableDirectStream: false,
11161119
enableTranscoding: true,
11171120
deviceProfile: h264Profile,
1121+
mediaSourceId: mediaSourceId,
11181122
item: item
11191123
});
11201124

0 commit comments

Comments
 (0)