mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
Merge branch 'feature/subtitle-customizations' into develop
This commit is contained in:
@@ -22,6 +22,12 @@ import { BITRATES } from "@/components/BitrateSelector";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Loader } from "@/components/Loader";
|
||||
import { Controls } from "@/components/video-player/controls/Controls";
|
||||
import {
|
||||
OUTLINE_THICKNESS,
|
||||
OutlineThickness,
|
||||
VLC_COLORS,
|
||||
VLCColor,
|
||||
} from "@/constants/SubtitleConstants";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { usePlaybackManager } from "@/hooks/usePlaybackManager";
|
||||
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
|
||||
@@ -98,7 +104,7 @@ export default function page() {
|
||||
/** Playback position in ticks. */
|
||||
playbackPosition?: string;
|
||||
}>();
|
||||
useSettings();
|
||||
const { settings } = useSettings();
|
||||
|
||||
const offline = offlineStr === "true";
|
||||
const playbackManager = usePlaybackManager();
|
||||
@@ -557,8 +563,34 @@ export default function page() {
|
||||
? allSubs.indexOf(chosenSubtitleTrack)
|
||||
: [...textSubs].reverse().indexOf(chosenSubtitleTrack);
|
||||
initOptions.push(`--sub-track=${finalIndex}`);
|
||||
}
|
||||
|
||||
// Add VLC subtitle styling options from settings
|
||||
const textColor = (settings.vlcTextColor ?? "White") as VLCColor;
|
||||
const backgroundColor = (settings.vlcBackgroundColor ??
|
||||
"Black") as VLCColor;
|
||||
const outlineColor = (settings.vlcOutlineColor ?? "Black") as VLCColor;
|
||||
const outlineThickness = (settings.vlcOutlineThickness ??
|
||||
"Normal") as OutlineThickness;
|
||||
const backgroundOpacity = settings.vlcBackgroundOpacity ?? 128;
|
||||
const outlineOpacity = settings.vlcOutlineOpacity ?? 255;
|
||||
const isBold = settings.vlcIsBold ?? false;
|
||||
// Add subtitle styling options
|
||||
initOptions.push(`--freetype-color=${VLC_COLORS[textColor]}`);
|
||||
initOptions.push(`--freetype-background-opacity=${backgroundOpacity}`);
|
||||
initOptions.push(
|
||||
`--freetype-background-color=${VLC_COLORS[backgroundColor]}`,
|
||||
);
|
||||
initOptions.push(`--freetype-outline-opacity=${outlineOpacity}`);
|
||||
initOptions.push(`--freetype-outline-color=${VLC_COLORS[outlineColor]}`);
|
||||
initOptions.push(
|
||||
`--freetype-outline-thickness=${OUTLINE_THICKNESS[outlineThickness]}`,
|
||||
);
|
||||
initOptions.push(`--sub-text-scale=${settings.subtitleSize}`);
|
||||
initOptions.push("--sub-margin=40");
|
||||
if (isBold) {
|
||||
initOptions.push("--freetype-bold");
|
||||
}
|
||||
}
|
||||
if (notTranscoding && chosenAudioTrack) {
|
||||
initOptions.push(`--audio-track=${allAudio.indexOf(chosenAudioTrack)}`);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export const StorageSettings = () => {
|
||||
};
|
||||
|
||||
const calculatePercentage = (value: number, total: number) => {
|
||||
console.log("usage", value, total);
|
||||
return ((value / total) * 100).toFixed(2);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import { useMedia } from "./MediaContext";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
import { OUTLINE_THICKNESS, VLC_COLORS } from "@/constants/SubtitleConstants";
|
||||
|
||||
export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
const isTv = Platform.isTV;
|
||||
|
||||
@@ -25,6 +27,15 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
const cultures = media.cultures;
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get VLC subtitle settings from the settings system
|
||||
const textColor = pluginSettings?.vlcTextColor ?? "White";
|
||||
const backgroundColor = pluginSettings?.vlcBackgroundColor ?? "Black";
|
||||
const outlineColor = pluginSettings?.vlcOutlineColor ?? "Black";
|
||||
const outlineThickness = pluginSettings?.vlcOutlineThickness ?? "Normal";
|
||||
const backgroundOpacity = pluginSettings?.vlcBackgroundOpacity ?? 128;
|
||||
const outlineOpacity = pluginSettings?.vlcOutlineOpacity ?? 255;
|
||||
const isBold = pluginSettings?.vlcIsBold ?? false;
|
||||
|
||||
if (isTv) return null;
|
||||
if (!settings) return null;
|
||||
|
||||
@@ -147,6 +158,148 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
onUpdate={(subtitleSize) => updateSettings({ subtitleSize })}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.text_color")}>
|
||||
<Dropdown
|
||||
data={Object.keys(VLC_COLORS)}
|
||||
keyExtractor={(item) => item}
|
||||
titleExtractor={(item) =>
|
||||
t(`home.settings.subtitles.colors.${item}`)
|
||||
}
|
||||
title={
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{t(`home.settings.subtitles.colors.${textColor}`)}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.subtitles.text_color")}
|
||||
onSelected={(value) => updateSettings({ vlcTextColor: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.background_color")}>
|
||||
<Dropdown
|
||||
data={Object.keys(VLC_COLORS)}
|
||||
keyExtractor={(item) => item}
|
||||
titleExtractor={(item) =>
|
||||
t(`home.settings.subtitles.colors.${item}`)
|
||||
}
|
||||
title={
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{t(`home.settings.subtitles.colors.${backgroundColor}`)}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.subtitles.background_color")}
|
||||
onSelected={(value) =>
|
||||
updateSettings({ vlcBackgroundColor: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.outline_color")}>
|
||||
<Dropdown
|
||||
data={Object.keys(VLC_COLORS)}
|
||||
keyExtractor={(item) => item}
|
||||
titleExtractor={(item) =>
|
||||
t(`home.settings.subtitles.colors.${item}`)
|
||||
}
|
||||
title={
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{t(`home.settings.subtitles.colors.${outlineColor}`)}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.subtitles.outline_color")}
|
||||
onSelected={(value) => updateSettings({ vlcOutlineColor: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.outline_thickness")}>
|
||||
<Dropdown
|
||||
data={Object.keys(OUTLINE_THICKNESS)}
|
||||
keyExtractor={(item) => item}
|
||||
titleExtractor={(item) =>
|
||||
t(`home.settings.subtitles.thickness.${item}`)
|
||||
}
|
||||
title={
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{t(`home.settings.subtitles.thickness.${outlineThickness}`)}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.subtitles.outline_thickness")}
|
||||
onSelected={(value) =>
|
||||
updateSettings({ vlcOutlineThickness: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.background_opacity")}>
|
||||
<Dropdown
|
||||
data={[0, 32, 64, 96, 128, 160, 192, 224, 255]}
|
||||
keyExtractor={String}
|
||||
titleExtractor={(item) => `${Math.round((item / 255) * 100)}%`}
|
||||
title={
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>{`${Math.round((backgroundOpacity / 255) * 100)}%`}</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.subtitles.background_opacity")}
|
||||
onSelected={(value) =>
|
||||
updateSettings({ vlcBackgroundOpacity: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.outline_opacity")}>
|
||||
<Dropdown
|
||||
data={[0, 32, 64, 96, 128, 160, 192, 224, 255]}
|
||||
keyExtractor={String}
|
||||
titleExtractor={(item) => `${Math.round((item / 255) * 100)}%`}
|
||||
title={
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>{`${Math.round((outlineOpacity / 255) * 100)}%`}</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.subtitles.outline_opacity")}
|
||||
onSelected={(value) => updateSettings({ vlcOutlineOpacity: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem title={t("home.settings.subtitles.bold_text")}>
|
||||
<Switch
|
||||
value={isBold}
|
||||
onValueChange={(value) => updateSettings({ vlcIsBold: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
</View>
|
||||
);
|
||||
|
||||
45
constants/SubtitleConstants.ts
Normal file
45
constants/SubtitleConstants.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export type VLCColor =
|
||||
| "Black"
|
||||
| "Gray"
|
||||
| "Silver"
|
||||
| "White"
|
||||
| "Maroon"
|
||||
| "Red"
|
||||
| "Fuchsia"
|
||||
| "Yellow"
|
||||
| "Olive"
|
||||
| "Green"
|
||||
| "Teal"
|
||||
| "Lime"
|
||||
| "Purple"
|
||||
| "Navy"
|
||||
| "Blue"
|
||||
| "Aqua";
|
||||
|
||||
export type OutlineThickness = "None" | "Thin" | "Normal" | "Thick";
|
||||
|
||||
export const VLC_COLORS: Record<VLCColor, number> = {
|
||||
Black: 0,
|
||||
Gray: 8421504,
|
||||
Silver: 12632256,
|
||||
White: 16777215,
|
||||
Maroon: 8388608,
|
||||
Red: 16711680,
|
||||
Fuchsia: 16711935,
|
||||
Yellow: 16776960,
|
||||
Olive: 8421376,
|
||||
Green: 32768,
|
||||
Teal: 32896,
|
||||
Lime: 65280,
|
||||
Purple: 8388736,
|
||||
Navy: 128,
|
||||
Blue: 255,
|
||||
Aqua: 65535,
|
||||
};
|
||||
|
||||
export const OUTLINE_THICKNESS: Record<OutlineThickness, number> = {
|
||||
None: 0,
|
||||
Thin: 2,
|
||||
Normal: 4,
|
||||
Thick: 6,
|
||||
};
|
||||
@@ -111,11 +111,11 @@
|
||||
},
|
||||
"subtitles": {
|
||||
"subtitle_title": "Subtitles",
|
||||
"subtitle_language": "Subtitle Language",
|
||||
"subtitle_hint": "Configure how subtitles look and behave.",
|
||||
"subtitle_language": "Subtitle language",
|
||||
"subtitle_mode": "Subtitle Mode",
|
||||
"set_subtitle_track": "Set Subtitle Track From Previous Item",
|
||||
"subtitle_size": "Subtitle Size",
|
||||
"subtitle_hint": "Configure Subtitle Preference.",
|
||||
"none": "None",
|
||||
"language": "Language",
|
||||
"loading": "Loading",
|
||||
@@ -124,7 +124,38 @@
|
||||
"Smart": "Smart",
|
||||
"Always": "Always",
|
||||
"None": "None",
|
||||
"OnlyForced": "Only Forced"
|
||||
"OnlyForced": "OnlyForced"
|
||||
},
|
||||
"text_color": "Text Color",
|
||||
"background_color": "Background Color",
|
||||
"outline_color": "Outline Color",
|
||||
"outline_thickness": "Outline Thickness",
|
||||
"background_opacity": "Background Opacity",
|
||||
"outline_opacity": "Outline Opacity",
|
||||
"bold_text": "Bold Text",
|
||||
"colors": {
|
||||
"Black": "Black",
|
||||
"Gray": "Gray",
|
||||
"Silver": "Silver",
|
||||
"White": "White",
|
||||
"Maroon": "Maroon",
|
||||
"Red": "Red",
|
||||
"Fuchsia": "Fuchsia",
|
||||
"Yellow": "Yellow",
|
||||
"Olive": "Olive",
|
||||
"Green": "Green",
|
||||
"Teal": "Teal",
|
||||
"Lime": "Lime",
|
||||
"Purple": "Purple",
|
||||
"Navy": "Navy",
|
||||
"Blue": "Blue",
|
||||
"Aqua": "Aqua"
|
||||
},
|
||||
"thickness": {
|
||||
"None": "None",
|
||||
"Thin": "Thin",
|
||||
"Normal": "Normal",
|
||||
"Thick": "Thick"
|
||||
}
|
||||
},
|
||||
"other": {
|
||||
|
||||
@@ -168,6 +168,13 @@ export type Settings = {
|
||||
defaultPlayer: VideoPlayer;
|
||||
maxAutoPlayEpisodeCount: MaxAutoPlayEpisodeCount;
|
||||
autoPlayEpisodeCount: number;
|
||||
vlcTextColor?: string;
|
||||
vlcBackgroundColor?: string;
|
||||
vlcOutlineColor?: string;
|
||||
vlcOutlineThickness?: string;
|
||||
vlcBackgroundOpacity?: number;
|
||||
vlcOutlineOpacity?: number;
|
||||
vlcIsBold?: boolean;
|
||||
// Gesture controls
|
||||
enableHorizontalSwipeSkip: boolean;
|
||||
enableLeftSideBrightnessSwipe: boolean;
|
||||
@@ -229,6 +236,13 @@ export const defaultValues: Settings = {
|
||||
defaultPlayer: VideoPlayer.VLC_3, // ios-only setting. does not matter what this is for android
|
||||
maxAutoPlayEpisodeCount: { key: "3", value: 3 },
|
||||
autoPlayEpisodeCount: 0,
|
||||
vlcTextColor: undefined,
|
||||
vlcBackgroundColor: undefined,
|
||||
vlcOutlineColor: undefined,
|
||||
vlcOutlineThickness: undefined,
|
||||
vlcBackgroundOpacity: undefined,
|
||||
vlcOutlineOpacity: undefined,
|
||||
vlcIsBold: undefined,
|
||||
// Gesture controls
|
||||
enableHorizontalSwipeSkip: true,
|
||||
enableLeftSideBrightnessSwipe: true,
|
||||
|
||||
Reference in New Issue
Block a user