Merge pull request #1115 from Stremio/feat/player-remember-selected-tracks
Some checks are pending
Build / build (push) Waiting to run

Player: Remember selected tracks
This commit is contained in:
Tim 2026-01-21 10:07:33 +01:00 committed by GitHub
commit cd111942a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 79 additions and 19 deletions

View file

@ -29,6 +29,9 @@ const styles = require('./styles');
const Video = require('./Video');
const { default: Indicator } = require('./Indicator/Indicator');
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
const findTrackById = (tracks, id) => tracks.find((track) => track.id === id);
const Player = ({ urlParams, queryParams }) => {
const { t } = useTranslation();
const services = useServices();
@ -37,7 +40,7 @@ const Player = ({ urlParams, queryParams }) => {
return queryParams.has('forceTranscoding');
}, [queryParams]);
const profile = useProfile();
const [player, videoParamsChanged, timeChanged, seek, pausedChanged, ended, nextVideo] = usePlayer(urlParams);
const [player, videoParamsChanged, streamStateChanged, timeChanged, seek, pausedChanged, ended, nextVideo] = usePlayer(urlParams);
const [settings, updateSettings] = useSettings();
const streamingServer = useStreamingServer();
const statistics = useStatistics(player, streamingServer);
@ -224,15 +227,32 @@ const Player = ({ urlParams, queryParams }) => {
const onSubtitlesTrackSelected = React.useCallback((id) => {
video.setSubtitlesTrack(id);
}, []);
streamStateChanged({
subtitleTrack: {
id,
embedded: true,
},
});
}, [streamStateChanged]);
const onExtraSubtitlesTrackSelected = React.useCallback((id) => {
video.setExtraSubtitlesTrack(id);
}, []);
streamStateChanged({
subtitleTrack: {
id,
embedded: false,
},
});
}, [streamStateChanged]);
const onAudioTrackSelected = React.useCallback((id) => {
video.setProp('selectedAudioTrackId', id);
}, []);
streamStateChanged({
audioTrack: {
id,
},
});
}, [streamStateChanged]);
const onExtraSubtitlesDelayChanged = React.useCallback((delay) => {
video.setProp('extraSubtitlesDelay', delay);
@ -444,41 +464,49 @@ const Player = ({ urlParams, queryParams }) => {
}
}, [player.nextVideo, video.state.time, video.state.duration]);
// Auto subtitles track selection
React.useEffect(() => {
if (!defaultSubtitlesSelected.current) {
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
if (settings.subtitlesLanguage === null) {
onSubtitlesTrackSelected(null);
onExtraSubtitlesTrackSelected(null);
video.setSubtitlesTrack(null);
video.setExtraSubtitlesTrack(null);
defaultSubtitlesSelected.current = true;
return;
}
const subtitlesTrack = findTrackByLang(video.state.subtitlesTracks, settings.subtitlesLanguage);
const extraSubtitlesTrack = findTrackByLang(video.state.extraSubtitlesTracks, settings.subtitlesLanguage);
const savedTrackId = player.streamState?.subtitleTrack?.id;
const subtitlesTrack = savedTrackId ?
findTrackById(video.state.subtitlesTracks, savedTrackId) :
findTrackByLang(video.state.subtitlesTracks, settings.subtitlesLanguage);
const extraSubtitlesTrack = savedTrackId ?
findTrackById(video.state.extraSubtitlesTracks, savedTrackId) :
findTrackByLang(video.state.extraSubtitlesTracks, settings.subtitlesLanguage);
if (subtitlesTrack && subtitlesTrack.id) {
onSubtitlesTrackSelected(subtitlesTrack.id);
video.setSubtitlesTrack(subtitlesTrack.id);
defaultSubtitlesSelected.current = true;
} else if (extraSubtitlesTrack && extraSubtitlesTrack.id) {
onExtraSubtitlesTrackSelected(extraSubtitlesTrack.id);
video.setExtraSubtitlesTrack(extraSubtitlesTrack.id);
defaultSubtitlesSelected.current = true;
}
}
}, [video.state.subtitlesTracks, video.state.extraSubtitlesTracks]);
}, [video.state.subtitlesTracks, video.state.extraSubtitlesTracks, player.streamState]);
// Auto audio track selection
React.useEffect(() => {
if (!defaultAudioTrackSelected.current) {
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
const audioTrack = findTrackByLang(video.state.audioTracks, settings.audioLanguage);
const savedTrackId = player.streamState?.audioTrack?.id;
const audioTrack = savedTrackId ?
findTrackById(video.state.audioTracks, savedTrackId) :
findTrackByLang(video.state.audioTracks, settings.audioLanguage);
if (audioTrack && audioTrack.id) {
onAudioTrackSelected(audioTrack.id);
video.setProp('selectedAudioTrackId', audioTrack.id);
defaultAudioTrackSelected.current = true;
}
}
}, [video.state.audioTracks]);
}, [video.state.audioTracks, player.streamState]);
React.useEffect(() => {
defaultSubtitlesSelected.current = false;

View file

@ -86,6 +86,9 @@ const usePlayer = (urlParams) => {
};
}
}, [urlParams]);
const player = useModelState({ model: 'player', action, map });
const videoParamsChanged = React.useCallback((videoParams) => {
core.transport.dispatch({
action: 'Player',
@ -153,8 +156,22 @@ const usePlayer = (urlParams) => {
}, 'player');
}, []);
const player = useModelState({ model: 'player', action, map });
return [player, videoParamsChanged, timeChanged, seek, pausedChanged, ended, nextVideo];
const streamStateChanged = React.useCallback((partialStreamState) => {
return core.transport.dispatch({
action: 'Player',
args: {
action: 'StreamStateChanged',
args: {
state: {
...player.streamState,
...partialStreamState,
},
},
},
}, 'player');
}, [player.streamState]);
return [player, videoParamsChanged, streamStateChanged, timeChanged, seek, pausedChanged, ended, nextVideo];
};
module.exports = usePlayer;

View file

@ -30,6 +30,20 @@ type SeriesInfo = {
season: number,
};
type SubtitlesTrackState = {
id: string,
embedded: boolean,
};
type AudioTrackState = {
id: string,
};
type StreamState = {
subtitleTrack?: SubtitlesTrackState,
audioTrack?: AudioTrackState,
};
type Player = {
addon: Addon | null,
libraryItem: LibraryItemPlayer | null,
@ -42,6 +56,7 @@ type Player = {
subtitlesPath: ResourceRequestPath,
} | null,
seriesInfo: SeriesInfo | null,
streamState: StreamState | null,
subtitles: Subtitle[],
title: string | null,
};