Merge pull request #1339 from Stremio/feat/ass-subtitle-controls
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Timothy Z. 2026-08-12 11:30:54 +03:00 committed by GitHub
commit 1748dc1a3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 65 additions and 24 deletions

View file

@ -23,7 +23,7 @@
"@stremio/stremio-colors": "5.2.0",
"@stremio/stremio-core-web": "0.61.0",
"@stremio/stremio-icons": "5.10.0",
"@stremio/stremio-video": "0.0.90",
"@stremio/stremio-video": "0.0.93",
"a-color-picker": "1.2.1",
"bowser": "2.14.1",
"buffer": "6.0.3",

View file

@ -24,8 +24,8 @@ importers:
specifier: 5.10.0
version: 5.10.0
'@stremio/stremio-video':
specifier: 0.0.90
version: 0.0.90
specifier: 0.0.93
version: 0.0.93
a-color-picker:
specifier: 1.2.1
version: 1.2.1
@ -1417,6 +1417,10 @@ packages:
'@sinonjs/fake-timers@10.3.0':
resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
'@stremio/libass-wasm@https://github.com/Stremio/JavascriptSubtitlesOctopus/releases/download/v4.2.6/stremio-libass-wasm-4.2.6.tgz':
resolution: {integrity: sha512-oHIud2ukVu4/tfrQNrd8vzd6Su0zV8FCatLjuEznf533AW+hj59Eg1g9y2mllpd+GmXrSzf/DNr9jJhYQAg5Gg==, tarball: https://github.com/Stremio/JavascriptSubtitlesOctopus/releases/download/v4.2.6/stremio-libass-wasm-4.2.6.tgz}
version: 4.2.6
'@stremio/stremio-colors@5.2.0':
resolution: {integrity: sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==}
@ -1426,8 +1430,8 @@ packages:
'@stremio/stremio-icons@5.10.0':
resolution: {integrity: sha512-Zw/vGC3D2yeQfk8xv/tfMJTDvbCPOI91tBg4XpR2+EgbZSX8Xvm7Vz457PIhFPhTAwdOPHp0VX0M3gzjbt0zOg==}
'@stremio/stremio-video@0.0.90':
resolution: {integrity: sha512-5I4T6GRFaK9fbPwz98bBNScBNpWBSrczkFt+qmhEJ8Tq9INdaah5OQd+hsZTEJIs7dR28M5nvMHpB4owx1UWDg==}
'@stremio/stremio-video@0.0.93':
resolution: {integrity: sha512-/R6rHh/lekHK4ZjCeMb4O27iOXQo36jek0kfzbbpYw/3yXf9yWxX+LWK5hajBFeZWitOm8GMSnDlkb31LIeIeg==}
'@stylistic/eslint-plugin-jsx@4.4.1':
resolution: {integrity: sha512-83SInq4u7z71vWwGG+6ViOtlOmZ6tSrDkMPhrvdBBTGMLA0gs22WSdhQ4vZP3oJ5Xg4ythvqeUiFSedvVxzhyA==}
@ -6483,6 +6487,8 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
'@stremio/libass-wasm@https://github.com/Stremio/JavascriptSubtitlesOctopus/releases/download/v4.2.6/stremio-libass-wasm-4.2.6.tgz': {}
'@stremio/stremio-colors@5.2.0': {}
'@stremio/stremio-core-web@0.61.0':
@ -6491,8 +6497,9 @@ snapshots:
'@stremio/stremio-icons@5.10.0': {}
'@stremio/stremio-video@0.0.90':
'@stremio/stremio-video@0.0.93':
dependencies:
'@stremio/libass-wasm': https://github.com/Stremio/JavascriptSubtitlesOctopus/releases/download/v4.2.6/stremio-libass-wasm-4.2.6.tgz
buffer: 6.0.3
color: 4.2.3
deep-freeze: 0.0.1

View file

@ -23,6 +23,7 @@ interface Shell {
type ShellCapabilities = {
gpuVideoProcessing: boolean;
nativeAssSubtitles: boolean;
};
type ShellState = {

View file

@ -43,6 +43,7 @@ const useShell = (): Shell => {
});
const [capabilities, setCapabilities] = useState<ShellCapabilities>({
gpuVideoProcessing: false,
nativeAssSubtitles: false,
});
const on = (name: string, listener: (arg: any) => void) => events.on(name, listener);
@ -91,15 +92,18 @@ const useShell = (): Shell => {
if (event.type === ShellEventType.INIT) {
const { data } = event as ShellEventInit;
const [, [,,, version], [,,, gpuVideoProcessing] = []] = data.transport.properties;
const shellProperties = Object.fromEntries(data.transport.properties
.filter((property) => property.length >= 4 && property[1])
.map(([, name,, value]) => [name, value]));
setState((state) => ({
...state,
initialized: true,
version,
version: shellProperties.shellVersion ?? null,
}));
setCapabilities({
gpuVideoProcessing: gpuVideoProcessing === 'true',
gpuVideoProcessing: shellProperties.gpuVideoProcessing === 'true',
nativeAssSubtitles: shellProperties.nativeAssSubtitles === 'true',
});
}

View file

@ -21,8 +21,16 @@
flex-direction: column;
gap: 0.25rem;
.variant-title {
display: flex;
align-items: center;
gap: 0.5rem;
min-width: 0;
}
.variant-label {
flex: 1;
min-width: 0;
font-size: 1.1rem;
line-height: 1.5rem;
color: var(--primary-foreground-color);
@ -31,6 +39,16 @@
overflow: hidden;
}
.ass-badge {
flex: none;
padding: 0.1rem 0.3rem;
border: 1px solid var(--color-placeholder-text);
border-radius: 0.25rem;
font-size: 0.65rem;
line-height: 0.8rem;
color: var(--color-placeholder-text);
}
.variant-origin {
font-size: 0.9rem;
color: var(--color-placeholder-text);

View file

@ -19,6 +19,7 @@ type SubtitlesTrack = {
embedded?: boolean,
local?: boolean,
exclusive?: boolean,
ass?: boolean,
};
type Props = {
@ -28,6 +29,7 @@ type Props = {
};
const hasValidLabel = (label?: string) => label && label.length > 0 && !label.startsWith('http');
const ASS_BADGE = 'ASS';
const SubtitleVariant = ({ track, selected, onSelect }: Props) => {
const { t } = useTranslation();
@ -73,8 +75,11 @@ const SubtitleVariant = ({ track, selected, onSelect }: Props) => {
className={classNames(styles['variant-option'], { 'selected': selected })}
>
<div className={styles['info']}>
<div className={styles['variant-label']}>
{variantLabel}
<div className={styles['variant-title']}>
<div className={styles['variant-label']}>
{variantLabel}
</div>
{track.ass ? <div className={styles['ass-badge']}>{ASS_BADGE}</div> : null}
</div>
<div className={styles['variant-origin']}>
{t(track.origin)}

View file

@ -226,7 +226,7 @@ const SubtitlesMenu = React.memo(React.forwardRef((props, ref) => {
step={25}
min={SUBTITLES_SIZES[0]}
max={SUBTITLES_SIZES[SUBTITLES_SIZES.length - 1]}
disabled={(props.selectedSubtitlesTrackId && props.subtitlesSize === null) || (props.selectedExtraSubtitlesTrackId && props.extraSubtitlesSize === null)}
disabled={props.assSubtitlesStylingActive || (props.selectedSubtitlesTrackId && props.subtitlesSize === null) || (props.selectedExtraSubtitlesTrackId && props.extraSubtitlesSize === null)}
onChange={onSubtitlesSizeChanged}
/>
<Stepper
@ -237,7 +237,7 @@ const SubtitlesMenu = React.memo(React.forwardRef((props, ref) => {
step={1}
min={0}
max={100}
disabled={(props.selectedSubtitlesTrackId && props.subtitlesOffset === null) || (props.selectedExtraSubtitlesTrackId && props.extraSubtitlesOffset === null)}
disabled={props.assSubtitlesStylingActive || (props.selectedSubtitlesTrackId && props.subtitlesOffset === null) || (props.selectedExtraSubtitlesTrackId && props.extraSubtitlesOffset === null)}
onChange={onSubtitlesOffsetChanged}
/>
</div>
@ -255,7 +255,8 @@ SubtitlesMenu.propTypes = {
subtitlesTracks: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
lang: PropTypes.string.isRequired,
origin: PropTypes.string.isRequired
origin: PropTypes.string.isRequired,
ass: PropTypes.bool
})),
selectedSubtitlesTrackId: PropTypes.string,
subtitlesOffset: PropTypes.number,
@ -268,12 +269,14 @@ SubtitlesMenu.propTypes = {
url: PropTypes.string,
embedded: PropTypes.bool,
local: PropTypes.bool,
exclusive: PropTypes.bool
exclusive: PropTypes.bool,
ass: PropTypes.bool
})),
selectedExtraSubtitlesTrackId: PropTypes.string,
extraSubtitlesOffset: PropTypes.number,
extraSubtitlesDelay: PropTypes.number,
extraSubtitlesSize: PropTypes.number,
assSubtitlesStylingActive: PropTypes.bool,
onSubtitlesTrackSelected: PropTypes.func,
onExtraSubtitlesTrackSelected: PropTypes.func,
onSubtitlesOffsetChanged: PropTypes.func,

View file

@ -10,6 +10,7 @@ type SubtitleTrack = {
embedded?: boolean,
local?: boolean,
exclusive?: boolean,
ass?: boolean,
buffer?: ArrayBuffer,
};
@ -30,6 +31,7 @@ type VideoSubtitleState = {
extraSubtitlesOffset: number | null,
extraSubtitlesDelay: number | null,
extraSubtitlesSize: number | null,
assSubtitlesStylingActive: boolean,
};
type VideoEvents = {
@ -76,6 +78,7 @@ type SubtitlesMenuProps = {
extraSubtitlesOffset: number | null,
extraSubtitlesDelay: number | null,
extraSubtitlesSize: number | null,
assSubtitlesStylingActive: boolean,
onSubtitlesTrackSelected: (track: SubtitleTrack | null) => void,
onExtraSubtitlesTrackSelected: (track: SubtitleTrack | null) => void,
onSubtitlesOffsetChanged: (offset: number) => void,

View file

@ -560,6 +560,7 @@ const useSubtitles = ({
extraSubtitlesOffset: video.state.extraSubtitlesOffset,
extraSubtitlesDelay: video.state.extraSubtitlesDelay,
extraSubtitlesSize: video.state.extraSubtitlesSize,
assSubtitlesStylingActive: video.state.assSubtitlesStylingActive,
onSubtitlesTrackSelected: selectEmbeddedTrack,
onExtraSubtitlesTrackSelected: selectExtraTrack,
onSubtitlesOffsetChanged: changeOffset,
@ -579,6 +580,7 @@ const useSubtitles = ({
video.state.extraSubtitlesOffset,
video.state.extraSubtitlesSize,
video.state.extraSubtitlesTracks,
video.state.assSubtitlesStylingActive,
video.state.selectedExtraSubtitlesTrackId,
video.state.selectedSubtitlesTrackId,
video.state.subtitlesOffset,

View file

@ -40,6 +40,7 @@ const useVideo = () => {
extraSubtitlesTextColor: null,
extraSubtitlesBackgroundColor: null,
extraSubtitlesOutlineColor: null,
assSubtitlesStylingActive: false,
fullscreen: null,
});

View file

@ -66,6 +66,12 @@ const Player = forwardRef<HTMLDivElement, Props>(({ profile }: Props, ref) => {
{...subtitlesOutlineColorInput}
/>
</Option>
<Option label={'SETTINGS_ASS_SUBTITLES_STYLING'}>
<Toggle
tabIndex={-1}
{...assSubtitlesStylingToggle}
/>
</Option>
</Category>
<Category icon={'volume-medium'} label={'SETTINGS_SECTION_AUDIO'}>
<Option label={'SETTINGS_DEFAULT_AUDIO_TRACK'}>
@ -159,15 +165,6 @@ const Player = forwardRef<HTMLDivElement, Props>(({ profile }: Props, ref) => {
/>
</Option>
}
{
shell.active &&
<Option label={'SETTINGS_ASS_SUBTITLES_STYLING'}>
<Toggle
tabIndex={-1}
{...assSubtitlesStylingToggle}
/>
</Option>
}
</Category>
</Section>
);