Merge pull request #1184 from Stremio/feat/player-subtitles-sort-default-language
Some checks are pending
Build / build (push) Waiting to run

Player: Sort subtitles menu languages by language settings
This commit is contained in:
Tim 2026-04-01 22:50:45 +02:00 committed by GitHub
commit eb23d3e4db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 35 additions and 55 deletions

View file

@ -2,6 +2,8 @@
const CHROMECAST_RECEIVER_APP_ID = '1634F54B';
const DEFAULT_STREAMING_SERVER_URL = 'http://127.0.0.1:11470/';
const DEFAULT_SUBTITLES_LANGUAGE = 'eng';
const LOCAL_SUBTITLES_LANGUAGE = 'local';
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
const SUBTITLES_FONTS = ['PlusJakartaSans', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
const SEEK_TIME_DURATIONS = [3000, 5000, 10000, 15000, 20000, 30000];
@ -121,6 +123,8 @@ const PROTOCOL = 'stremio:';
module.exports = {
CHROMECAST_RECEIVER_APP_ID,
DEFAULT_STREAMING_SERVER_URL,
DEFAULT_SUBTITLES_LANGUAGE,
LOCAL_SUBTITLES_LANGUAGE,
SUBTITLES_SIZES,
SUBTITLES_FONTS,
SEEK_TIME_DURATIONS,

View file

@ -1,25 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const comparatorWithPriorities = (priorities) => {
return (a, b) => {
if (isNaN(priorities[a]) && isNaN(priorities[b])) {
return a.localeCompare(b);
} else if (isNaN(priorities[a])) {
if (priorities[b] === Number.NEGATIVE_INFINITY) {
return -1;
} else {
return 1;
}
} else if (isNaN(priorities[b])) {
if (priorities[a] === Number.NEGATIVE_INFINITY) {
return 1;
} else {
return -1;
}
} else {
return priorities[b] - priorities[a];
}
};
};
module.exports = comparatorWithPriorities;

View file

@ -5,7 +5,6 @@ const { PlatformProvider, usePlatform } = require('./Platform');
const { ToastProvider, useToast } = require('./Toast');
const { TooltipProvider, Tooltip } = require('./Tooltips');
const { ShortcutsProvider, useShortcuts, onShortcut } = require('./Shortcuts');
const comparatorWithPriorities = require('./comparatorWithPriorities');
const CONSTANTS = require('./CONSTANTS');
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
const getVisibleChildrenRange = require('./getVisibleChildrenRange');
@ -43,7 +42,6 @@ module.exports = {
useToast,
TooltipProvider,
Tooltip,
comparatorWithPriorities,
CONSTANTS,
withCoreSuspender,
useCoreSuspender,

View file

@ -1022,6 +1022,8 @@ const Player = ({ urlParams, queryParams }) => {
subtitlesMenuOpen ?
<SubtitlesMenu
className={classnames(styles['layer'], styles['menu-layer'])}
subtitlesLanguage={settings.subtitlesLanguage}
interfaceLanguage={settings.interfaceLanguage}
subtitlesTracks={video.state.subtitlesTracks}
selectedSubtitlesTrackId={video.state.selectedSubtitlesTrackId}
subtitlesOffset={video.state.subtitlesOffset}

View file

@ -3,28 +3,33 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { comparatorWithPriorities, languages } = require('stremio/common');
const { SUBTITLES_SIZES } = require('stremio/common/CONSTANTS');
const { languages } = require('stremio/common');
const { SUBTITLES_SIZES, DEFAULT_SUBTITLES_LANGUAGE, LOCAL_SUBTITLES_LANGUAGE } = require('stremio/common/CONSTANTS');
const { Button } = require('stremio/components');
const styles = require('./styles');
const { t } = require('i18next');
const { default: Stepper } = require('./Stepper');
const ORIGIN_PRIORITIES = {
'LOCAL': 3,
'EMBEDDED': 2,
'EXCLUSIVE': 1,
};
const LANGUAGE_PRIORITIES = {
'local': 2,
'eng': 1,
};
const ORIGIN_PRIORITIES = [
'LOCAL',
'EMBEDDED',
'EXCLUSIVE',
];
const normalizeTracksLang = (tracks) => tracks.map((track) => ({
...track,
lang: languages.toCode(track.lang),
}));
const sortByValues = (items, values) => items.sort((a, b) => {
const left = values.indexOf(a);
const right = values.indexOf(b);
if (left === -1 && right === -1) return 0;
if (left === -1) return 1;
if (right === -1) return -1;
return left - right;
});
const SubtitlesMenu = React.memo((props) => {
const subtitlesTracks = React.useMemo(() => {
return normalizeTracksLang(Array.isArray(props.subtitlesTracks) ? props.subtitlesTracks : []);
@ -39,16 +44,12 @@ const SubtitlesMenu = React.memo((props) => {
}, [subtitlesTracks, extraSubtitlesTracks]);
const subtitlesLanguages = React.useMemo(() => {
return allSubtitles
.reduce((subtitlesLanguages, { lang }) => {
if (!subtitlesLanguages.includes(lang)) {
subtitlesLanguages.push(lang);
}
return subtitlesLanguages;
}, [])
.sort(comparatorWithPriorities(LANGUAGE_PRIORITIES));
}, [allSubtitles]);
const userLanguage = languages.toCode(props.subtitlesLanguage) ?? DEFAULT_SUBTITLES_LANGUAGE;
const interfaceLanguage = languages.toCode(props.interfaceLanguage) ?? DEFAULT_SUBTITLES_LANGUAGE;
const priorities = [LOCAL_SUBTITLES_LANGUAGE, userLanguage, interfaceLanguage];
const langs = Object.keys(Object.groupBy(allSubtitles, ({ lang }) => lang)).sort((a, b) => a.localeCompare(b));
return sortByValues(langs, priorities);
}, [allSubtitles, props.subtitlesLanguage, props.interfaceLanguage]);
const selectedSubtitlesLanguage = React.useMemo(() => {
return typeof props.selectedSubtitlesTrackId === 'string' ?
@ -74,18 +75,16 @@ const SubtitlesMenu = React.memo((props) => {
null;
}, [subtitlesTracks, extraSubtitlesTracks, props.selectedSubtitlesTrackId, props.selectedExtraSubtitlesTrackId]);
const subtitlesTracksForLanguage = React.useMemo(() => {
return allSubtitles
.filter(({ lang }) => lang === selectedSubtitlesLanguage)
.sort((t1, t2) => comparatorWithPriorities(ORIGIN_PRIORITIES)(t1.origin, t2.origin));
const tracks = allSubtitles.filter(({ lang }) => lang === selectedSubtitlesLanguage);
return sortByValues(tracks, ORIGIN_PRIORITIES);
}, [allSubtitles, selectedSubtitlesLanguage]);
const onMouseDown = React.useCallback((event) => {
event.nativeEvent.subtitlesMenuClosePrevented = true;
}, []);
const subtitlesLanguageOnClick = React.useCallback((event) => {
const track = allSubtitles
.filter(({ lang }) => lang === event.currentTarget.dataset.lang)
.sort((t1, t2) => comparatorWithPriorities(ORIGIN_PRIORITIES)(t1.origin, t2.origin))
.shift();
const tracks = allSubtitles.filter(({ lang }) => lang === event.currentTarget.dataset.lang);
const track = sortByValues(tracks, ORIGIN_PRIORITIES).shift();
if (!track) {
if (typeof props.onSubtitlesTrackSelected === 'function') {
props.onSubtitlesTrackSelected(null);
@ -262,6 +261,8 @@ SubtitlesMenu.displayName = 'MainNavBars';
SubtitlesMenu.propTypes = {
className: PropTypes.string,
subtitlesLanguage: PropTypes.string,
interfaceLanguage: PropTypes.string,
subtitlesTracks: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
lang: PropTypes.string.isRequired,