From 621d1644027e9c3ef8aeca69ec8699d0ad35efe4 Mon Sep 17 00:00:00 2001 From: Lance Chant <13349722+lancechant@users.noreply.github.com> Date: Sat, 20 Sep 2025 12:35:00 +0200 Subject: [PATCH 1/5] feat: added more subtitle customization options Subtitles can now be customized with the following extra options: - Colour - background opacity/colour - outline opacity/colour - boldness Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com> --- app/(auth)/player/direct-player.tsx | 76 ++++++- components/settings/SubtitleToggles.tsx | 207 ++++++++++++++++++++ components/settings/VLCSubtitleSettings.tsx | 156 +++++++++++++++ translations/en.json | 33 +++- 4 files changed, 470 insertions(+), 2 deletions(-) create mode 100644 components/settings/VLCSubtitleSettings.tsx diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index ca906a78..10135002 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -37,9 +37,29 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl"; import { writeToLog } from "@/utils/log"; +import { storage } from "@/utils/mmkv"; import { generateDeviceProfile } from "@/utils/profiles/native"; import { msToTicks, ticksToSeconds } from "@/utils/time"; +type VLCColor = + | "Black" + | "Gray" + | "Silver" + | "White" + | "Maroon" + | "Red" + | "Fuchsia" + | "Yellow" + | "Olive" + | "Green" + | "Teal" + | "Lime" + | "Purple" + | "Navy" + | "Blue" + | "Aqua"; +type OutlineThickness = "None" | "Thin" | "Normal" | "Thick"; + export default function page() { const videoRef = useRef(null); const user = useAtomValue(userAtom); @@ -576,8 +596,62 @@ 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 = (storage.getString("vlc.textColor") || + "White") as VLCColor; + const backgroundColor = (storage.getString("vlc.backgroundColor") || + "Black") as VLCColor; + const outlineColor = (storage.getString("vlc.outlineColor") || + "Black") as VLCColor; + const outlineThickness = (storage.getString("vlc.outlineThickness") || + "Normal") as OutlineThickness; + const backgroundOpacity = storage.getNumber("vlc.backgroundOpacity") || 128; + const outlineOpacity = storage.getNumber("vlc.outlineOpacity") || 255; + const isBold = storage.getBoolean("vlc.isBold") || false; + + // VLC color values mapping + const VLC_COLORS: Record = { + 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, + }; + + const OUTLINE_THICKNESS: Record = { + None: 0, + Thin: 2, + Normal: 4, + Thick: 6, + }; + + // 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]}`, + ); + if (isBold) { + initOptions.push("--freetype-bold"); + } + } if (notTranscoding && chosenAudioTrack) { initOptions.push(`--audio-track=${allAudio.indexOf(chosenAudioTrack)}`); } diff --git a/components/settings/SubtitleToggles.tsx b/components/settings/SubtitleToggles.tsx index 59ec1570..e196a599 100644 --- a/components/settings/SubtitleToggles.tsx +++ b/components/settings/SubtitleToggles.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from "react"; import { Platform, TouchableOpacity, View, type ViewProps } from "react-native"; const _DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null; @@ -9,6 +10,7 @@ import { Switch } from "react-native-gesture-handler"; import Dropdown from "@/components/common/Dropdown"; import { Stepper } from "@/components/inputs/Stepper"; import { useSettings } from "@/utils/atoms/settings"; +import { storage } from "@/utils/mmkv"; import { Text } from "../common/Text"; import { ListGroup } from "../list/ListGroup"; import { ListItem } from "../list/ListItem"; @@ -16,6 +18,32 @@ import { useMedia } from "./MediaContext"; interface Props extends ViewProps {} +const VLC_COLORS = { + 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, +}; + +const OUTLINE_THICKNESS = { + None: 0, + Thin: 2, + Normal: 4, + Thick: 6, +}; + export const SubtitleToggles: React.FC = ({ ...props }) => { const isTv = Platform.isTV; @@ -25,6 +53,52 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { const cultures = media.cultures; const { t } = useTranslation(); + // VLC subtitle styling states + const [textColor, setTextColor] = useState( + storage.getString("vlc.textColor") || "White", + ); + const [backgroundColor, setBackgroundColor] = useState( + storage.getString("vlc.backgroundColor") || "Black", + ); + const [outlineColor, setOutlineColor] = useState( + storage.getString("vlc.outlineColor") || "Black", + ); + const [outlineThickness, setOutlineThickness] = useState( + storage.getString("vlc.outlineThickness") || "Normal", + ); + const [backgroundOpacity, setBackgroundOpacity] = useState( + storage.getNumber("vlc.backgroundOpacity") || 128, + ); + const [outlineOpacity, setOutlineOpacity] = useState( + storage.getNumber("vlc.outlineOpacity") || 255, + ); + const [isBold, setIsBold] = useState( + storage.getBoolean("vlc.isBold") || false, + ); + + // VLC settings effects + useEffect(() => { + storage.set("vlc.textColor", textColor); + }, [textColor]); + useEffect(() => { + storage.set("vlc.backgroundColor", backgroundColor); + }, [backgroundColor]); + useEffect(() => { + storage.set("vlc.outlineColor", outlineColor); + }, [outlineColor]); + useEffect(() => { + storage.set("vlc.outlineThickness", outlineThickness); + }, [outlineThickness]); + useEffect(() => { + storage.set("vlc.backgroundOpacity", backgroundOpacity); + }, [backgroundOpacity]); + useEffect(() => { + storage.set("vlc.outlineOpacity", outlineOpacity); + }, [outlineOpacity]); + useEffect(() => { + storage.set("vlc.isBold", isBold); + }, [isBold]); + if (isTv) return null; if (!settings) return null; @@ -147,6 +221,139 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { onUpdate={(subtitleSize) => updateSettings({ subtitleSize })} /> + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.colors.${item}`) + } + title={ + + + {t(`home.settings.subtitles.colors.${textColor}`)} + + + + } + label={t("home.settings.subtitles.text_color")} + onSelected={setTextColor} + /> + + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.colors.${item}`) + } + title={ + + + {t(`home.settings.subtitles.colors.${backgroundColor}`)} + + + + } + label={t("home.settings.subtitles.background_color")} + onSelected={setBackgroundColor} + /> + + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.colors.${item}`) + } + title={ + + + {t(`home.settings.subtitles.colors.${outlineColor}`)} + + + + } + label={t("home.settings.subtitles.outline_color")} + onSelected={setOutlineColor} + /> + + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.thickness.${item}`) + } + title={ + + + {t(`home.settings.subtitles.thickness.${outlineThickness}`)} + + + + } + label={t("home.settings.subtitles.outline_thickness")} + onSelected={setOutlineThickness} + /> + + + `${Math.round((item / 255) * 100)}%`} + title={ + + {`${Math.round((backgroundOpacity / 255) * 100)}%`} + + + } + label={t("home.settings.subtitles.background_opacity")} + onSelected={setBackgroundOpacity} + /> + + + `${Math.round((item / 255) * 100)}%`} + title={ + + {`${Math.round((outlineOpacity / 255) * 100)}%`} + + + } + label={t("home.settings.subtitles.outline_opacity")} + onSelected={setOutlineOpacity} + /> + + + + ); diff --git a/components/settings/VLCSubtitleSettings.tsx b/components/settings/VLCSubtitleSettings.tsx new file mode 100644 index 00000000..919ee14e --- /dev/null +++ b/components/settings/VLCSubtitleSettings.tsx @@ -0,0 +1,156 @@ +import { t } from "i18next"; +import { useEffect, useState } from "react"; +import { View } from "react-native"; +import { ListGroup } from "@/components/list/ListGroup"; +import { ListItem } from "@/components/list/ListItem"; +import { storage } from "@/utils/mmkv"; + +const VLC_COLORS = { + 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, +}; + +const OUTLINE_THICKNESS = { + None: 0, + Thin: 2, + Normal: 4, + Thick: 6, +}; + +export function VLCSubtitleSettings({ + className = "", +}: { + className?: string; +}) { + const [textColor, setTextColor] = useState( + storage.getString("vlc.textColor") || "White", + ); + const [backgroundColor, setBackgroundColor] = useState( + storage.getString("vlc.backgroundColor") || "Black", + ); + const [outlineColor, setOutlineColor] = useState( + storage.getString("vlc.outlineColor") || "Black", + ); + const [outlineThickness, setOutlineThickness] = useState( + storage.getString("vlc.outlineThickness") || "Normal", + ); + const [backgroundOpacity, setBackgroundOpacity] = useState( + storage.getNumber("vlc.backgroundOpacity") || 128, + ); + const [outlineOpacity, setOutlineOpacity] = useState( + storage.getNumber("vlc.outlineOpacity") || 255, + ); + const [isBold, setIsBold] = useState( + storage.getBoolean("vlc.isBold") || false, + ); + + useEffect(() => { + storage.set("vlc.textColor", textColor); + }, [textColor]); + + useEffect(() => { + storage.set("vlc.backgroundColor", backgroundColor); + }, [backgroundColor]); + + useEffect(() => { + storage.set("vlc.outlineColor", outlineColor); + }, [outlineColor]); + + useEffect(() => { + storage.set("vlc.outlineThickness", outlineThickness); + }, [outlineThickness]); + + useEffect(() => { + storage.set("vlc.backgroundOpacity", backgroundOpacity); + }, [backgroundOpacity]); + + useEffect(() => { + storage.set("vlc.outlineOpacity", outlineOpacity); + }, [outlineOpacity]); + + useEffect(() => { + storage.set("vlc.isBold", isBold); + }, [isBold]); + + return ( + + + { + const colors = Object.keys(VLC_COLORS); + const currentIndex = colors.indexOf(textColor); + const nextIndex = (currentIndex + 1) % colors.length; + setTextColor(colors[nextIndex]); + }} + /> + { + const colors = Object.keys(VLC_COLORS); + const currentIndex = colors.indexOf(backgroundColor); + const nextIndex = (currentIndex + 1) % colors.length; + setBackgroundColor(colors[nextIndex]); + }} + /> + { + const colors = Object.keys(VLC_COLORS); + const currentIndex = colors.indexOf(outlineColor); + const nextIndex = (currentIndex + 1) % colors.length; + setOutlineColor(colors[nextIndex]); + }} + /> + { + const thicknesses = Object.keys(OUTLINE_THICKNESS); + const currentIndex = thicknesses.indexOf(outlineThickness); + const nextIndex = (currentIndex + 1) % thicknesses.length; + setOutlineThickness(thicknesses[nextIndex]); + }} + /> + { + const newOpacity = (backgroundOpacity + 32) % 256; + setBackgroundOpacity(newOpacity); + }} + /> + { + const newOpacity = (outlineOpacity + 32) % 256; + setOutlineOpacity(newOpacity); + }} + /> + setIsBold(!isBold)} + /> + + + ); +} diff --git a/translations/en.json b/translations/en.json index b1ccbb80..af6caf18 100644 --- a/translations/en.json +++ b/translations/en.json @@ -106,11 +106,11 @@ }, "subtitles": { "subtitle_title": "Subtitles", + "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", @@ -120,6 +120,37 @@ "Always": "Always", "None": "None", "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": { From 388f65b44335ebaadd42c471b7dd1fd12cccae9c Mon Sep 17 00:00:00 2001 From: Lance Chant <13349722+lancechant@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:03:07 +0200 Subject: [PATCH 2/5] chore: moved constant values to a file Reduced duplication and removed constants to a new file Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com> --- app/(auth)/player/direct-player.tsx | 52 +++------------------ components/settings/StorageSettings.tsx | 1 - components/settings/SubtitleToggles.tsx | 26 +---------- components/settings/VLCSubtitleSettings.tsx | 27 +---------- constants/SubtitleConstants.ts | 45 ++++++++++++++++++ 5 files changed, 53 insertions(+), 98 deletions(-) create mode 100644 constants/SubtitleConstants.ts diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index 10135002..7f310ec8 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -21,6 +21,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"; @@ -41,25 +47,6 @@ import { storage } from "@/utils/mmkv"; import { generateDeviceProfile } from "@/utils/profiles/native"; import { msToTicks, ticksToSeconds } from "@/utils/time"; -type VLCColor = - | "Black" - | "Gray" - | "Silver" - | "White" - | "Maroon" - | "Red" - | "Fuchsia" - | "Yellow" - | "Olive" - | "Green" - | "Teal" - | "Lime" - | "Purple" - | "Navy" - | "Blue" - | "Aqua"; -type OutlineThickness = "None" | "Thin" | "Normal" | "Thick"; - export default function page() { const videoRef = useRef(null); const user = useAtomValue(userAtom); @@ -610,33 +597,6 @@ export default function page() { const outlineOpacity = storage.getNumber("vlc.outlineOpacity") || 255; const isBold = storage.getBoolean("vlc.isBold") || false; - // VLC color values mapping - const VLC_COLORS: Record = { - 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, - }; - - const OUTLINE_THICKNESS: Record = { - None: 0, - Thin: 2, - Normal: 4, - Thick: 6, - }; - // Add subtitle styling options initOptions.push(`--freetype-color=${VLC_COLORS[textColor]}`); initOptions.push(`--freetype-background-opacity=${backgroundOpacity}`); diff --git a/components/settings/StorageSettings.tsx b/components/settings/StorageSettings.tsx index 89f6a2e9..117152fc 100644 --- a/components/settings/StorageSettings.tsx +++ b/components/settings/StorageSettings.tsx @@ -40,7 +40,6 @@ export const StorageSettings = () => { }; const calculatePercentage = (value: number, total: number) => { - console.log("usage", value, total); return ((value / total) * 100).toFixed(2); }; diff --git a/components/settings/SubtitleToggles.tsx b/components/settings/SubtitleToggles.tsx index e196a599..d1d30274 100644 --- a/components/settings/SubtitleToggles.tsx +++ b/components/settings/SubtitleToggles.tsx @@ -18,31 +18,7 @@ import { useMedia } from "./MediaContext"; interface Props extends ViewProps {} -const VLC_COLORS = { - 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, -}; - -const OUTLINE_THICKNESS = { - None: 0, - Thin: 2, - Normal: 4, - Thick: 6, -}; +import { OUTLINE_THICKNESS, VLC_COLORS } from "@/constants/SubtitleConstants"; export const SubtitleToggles: React.FC = ({ ...props }) => { const isTv = Platform.isTV; diff --git a/components/settings/VLCSubtitleSettings.tsx b/components/settings/VLCSubtitleSettings.tsx index 919ee14e..4596f781 100644 --- a/components/settings/VLCSubtitleSettings.tsx +++ b/components/settings/VLCSubtitleSettings.tsx @@ -3,34 +3,9 @@ import { useEffect, useState } from "react"; import { View } from "react-native"; import { ListGroup } from "@/components/list/ListGroup"; import { ListItem } from "@/components/list/ListItem"; +import { OUTLINE_THICKNESS, VLC_COLORS } from "@/constants/SubtitleConstants"; import { storage } from "@/utils/mmkv"; -const VLC_COLORS = { - 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, -}; - -const OUTLINE_THICKNESS = { - None: 0, - Thin: 2, - Normal: 4, - Thick: 6, -}; - export function VLCSubtitleSettings({ className = "", }: { diff --git a/constants/SubtitleConstants.ts b/constants/SubtitleConstants.ts new file mode 100644 index 00000000..7fc7a8e6 --- /dev/null +++ b/constants/SubtitleConstants.ts @@ -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 = { + 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 = { + None: 0, + Thin: 2, + Normal: 4, + Thick: 6, +}; From 28f6729ae2f0f4c436e0a22577470d86946100ca Mon Sep 17 00:00:00 2001 From: lance chant <13349722+lancechant@users.noreply.github.com> Date: Thu, 25 Sep 2025 07:58:36 +0200 Subject: [PATCH 3/5] Update direct-player.tsx Added text scaling --- app/(auth)/player/direct-player.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index d0f2885b..1200abc2 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -105,7 +105,7 @@ export default function page() { /** Playback position in ticks. */ playbackPosition?: string; }>(); - useSettings(); + const { settings } = useSettings(); const offline = offlineStr === "true"; const playbackManager = usePlaybackManager(); @@ -589,6 +589,8 @@ export default function page() { 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"); } From d250295e3610643dded7eb721b88a621f15e69e3 Mon Sep 17 00:00:00 2001 From: lance chant <13349722+lancechant@users.noreply.github.com> Date: Thu, 25 Sep 2025 09:11:24 +0200 Subject: [PATCH 4/5] Update direct-player.tsx Fixed syntax --- app/(auth)/player/direct-player.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index 1200abc2..a8caf201 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -589,8 +589,8 @@ export default function page() { initOptions.push( `--freetype-outline-thickness=${OUTLINE_THICKNESS[outlineThickness]}`, ); - initOptions.push(`--sub-text-scale=${settings.subtitleSize}`), - initOptions.push("--sub-margin=40"), + initOptions.push(`--sub-text-scale=${settings.subtitleSize}`); + initOptions.push("--sub-margin=40"); if (isBold) { initOptions.push("--freetype-bold"); } From 1eba074ebd1db09ea2ea860ea61c2f2cdb40481c Mon Sep 17 00:00:00 2001 From: Lance Chant <13349722+lancechant@users.noreply.github.com> Date: Tue, 30 Sep 2025 11:18:36 +0200 Subject: [PATCH 5/5] chore: rework logic Changed logic to use the settings component to store the changed values rather than referencing storage directly Deleted an unused file Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com> --- app/(auth)/player/direct-player.tsx | 18 ++- components/settings/SubtitleToggles.tsx | 78 ++++-------- components/settings/VLCSubtitleSettings.tsx | 131 -------------------- utils/atoms/settings.ts | 14 +++ 4 files changed, 45 insertions(+), 196 deletions(-) delete mode 100644 components/settings/VLCSubtitleSettings.tsx diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index a8caf201..1aab148a 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -44,7 +44,6 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl"; import { writeToLog } from "@/utils/log"; -import { storage } from "@/utils/mmkv"; import { generateDeviceProfile } from "@/utils/profiles/native"; import { msToTicks, ticksToSeconds } from "@/utils/time"; @@ -566,18 +565,15 @@ export default function page() { initOptions.push(`--sub-track=${finalIndex}`); // Add VLC subtitle styling options from settings - const textColor = (storage.getString("vlc.textColor") || - "White") as VLCColor; - const backgroundColor = (storage.getString("vlc.backgroundColor") || + const textColor = (settings.vlcTextColor ?? "White") as VLCColor; + const backgroundColor = (settings.vlcBackgroundColor ?? "Black") as VLCColor; - const outlineColor = (storage.getString("vlc.outlineColor") || - "Black") as VLCColor; - const outlineThickness = (storage.getString("vlc.outlineThickness") || + const outlineColor = (settings.vlcOutlineColor ?? "Black") as VLCColor; + const outlineThickness = (settings.vlcOutlineThickness ?? "Normal") as OutlineThickness; - const backgroundOpacity = storage.getNumber("vlc.backgroundOpacity") || 128; - const outlineOpacity = storage.getNumber("vlc.outlineOpacity") || 255; - const isBold = storage.getBoolean("vlc.isBold") || false; - + 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}`); diff --git a/components/settings/SubtitleToggles.tsx b/components/settings/SubtitleToggles.tsx index d1d30274..f01ab200 100644 --- a/components/settings/SubtitleToggles.tsx +++ b/components/settings/SubtitleToggles.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from "react"; import { Platform, TouchableOpacity, View, type ViewProps } from "react-native"; const _DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null; @@ -10,7 +9,6 @@ import { Switch } from "react-native-gesture-handler"; import Dropdown from "@/components/common/Dropdown"; import { Stepper } from "@/components/inputs/Stepper"; import { useSettings } from "@/utils/atoms/settings"; -import { storage } from "@/utils/mmkv"; import { Text } from "../common/Text"; import { ListGroup } from "../list/ListGroup"; import { ListItem } from "../list/ListItem"; @@ -29,51 +27,14 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { const cultures = media.cultures; const { t } = useTranslation(); - // VLC subtitle styling states - const [textColor, setTextColor] = useState( - storage.getString("vlc.textColor") || "White", - ); - const [backgroundColor, setBackgroundColor] = useState( - storage.getString("vlc.backgroundColor") || "Black", - ); - const [outlineColor, setOutlineColor] = useState( - storage.getString("vlc.outlineColor") || "Black", - ); - const [outlineThickness, setOutlineThickness] = useState( - storage.getString("vlc.outlineThickness") || "Normal", - ); - const [backgroundOpacity, setBackgroundOpacity] = useState( - storage.getNumber("vlc.backgroundOpacity") || 128, - ); - const [outlineOpacity, setOutlineOpacity] = useState( - storage.getNumber("vlc.outlineOpacity") || 255, - ); - const [isBold, setIsBold] = useState( - storage.getBoolean("vlc.isBold") || false, - ); - - // VLC settings effects - useEffect(() => { - storage.set("vlc.textColor", textColor); - }, [textColor]); - useEffect(() => { - storage.set("vlc.backgroundColor", backgroundColor); - }, [backgroundColor]); - useEffect(() => { - storage.set("vlc.outlineColor", outlineColor); - }, [outlineColor]); - useEffect(() => { - storage.set("vlc.outlineThickness", outlineThickness); - }, [outlineThickness]); - useEffect(() => { - storage.set("vlc.backgroundOpacity", backgroundOpacity); - }, [backgroundOpacity]); - useEffect(() => { - storage.set("vlc.outlineOpacity", outlineOpacity); - }, [outlineOpacity]); - useEffect(() => { - storage.set("vlc.isBold", isBold); - }, [isBold]); + // 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; @@ -217,7 +178,7 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { } label={t("home.settings.subtitles.text_color")} - onSelected={setTextColor} + onSelected={(value) => updateSettings({ vlcTextColor: value })} /> @@ -240,7 +201,9 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { } label={t("home.settings.subtitles.background_color")} - onSelected={setBackgroundColor} + onSelected={(value) => + updateSettings({ vlcBackgroundColor: value }) + } /> @@ -263,7 +226,7 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { } label={t("home.settings.subtitles.outline_color")} - onSelected={setOutlineColor} + onSelected={(value) => updateSettings({ vlcOutlineColor: value })} /> @@ -286,7 +249,9 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { } label={t("home.settings.subtitles.outline_thickness")} - onSelected={setOutlineThickness} + onSelected={(value) => + updateSettings({ vlcOutlineThickness: value }) + } /> @@ -305,7 +270,9 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { } label={t("home.settings.subtitles.background_opacity")} - onSelected={setBackgroundOpacity} + onSelected={(value) => + updateSettings({ vlcBackgroundOpacity: value }) + } /> @@ -324,11 +291,14 @@ export const SubtitleToggles: React.FC = ({ ...props }) => { } label={t("home.settings.subtitles.outline_opacity")} - onSelected={setOutlineOpacity} + onSelected={(value) => updateSettings({ vlcOutlineOpacity: value })} /> - + updateSettings({ vlcIsBold: value })} + /> diff --git a/components/settings/VLCSubtitleSettings.tsx b/components/settings/VLCSubtitleSettings.tsx deleted file mode 100644 index 4596f781..00000000 --- a/components/settings/VLCSubtitleSettings.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { t } from "i18next"; -import { useEffect, useState } from "react"; -import { View } from "react-native"; -import { ListGroup } from "@/components/list/ListGroup"; -import { ListItem } from "@/components/list/ListItem"; -import { OUTLINE_THICKNESS, VLC_COLORS } from "@/constants/SubtitleConstants"; -import { storage } from "@/utils/mmkv"; - -export function VLCSubtitleSettings({ - className = "", -}: { - className?: string; -}) { - const [textColor, setTextColor] = useState( - storage.getString("vlc.textColor") || "White", - ); - const [backgroundColor, setBackgroundColor] = useState( - storage.getString("vlc.backgroundColor") || "Black", - ); - const [outlineColor, setOutlineColor] = useState( - storage.getString("vlc.outlineColor") || "Black", - ); - const [outlineThickness, setOutlineThickness] = useState( - storage.getString("vlc.outlineThickness") || "Normal", - ); - const [backgroundOpacity, setBackgroundOpacity] = useState( - storage.getNumber("vlc.backgroundOpacity") || 128, - ); - const [outlineOpacity, setOutlineOpacity] = useState( - storage.getNumber("vlc.outlineOpacity") || 255, - ); - const [isBold, setIsBold] = useState( - storage.getBoolean("vlc.isBold") || false, - ); - - useEffect(() => { - storage.set("vlc.textColor", textColor); - }, [textColor]); - - useEffect(() => { - storage.set("vlc.backgroundColor", backgroundColor); - }, [backgroundColor]); - - useEffect(() => { - storage.set("vlc.outlineColor", outlineColor); - }, [outlineColor]); - - useEffect(() => { - storage.set("vlc.outlineThickness", outlineThickness); - }, [outlineThickness]); - - useEffect(() => { - storage.set("vlc.backgroundOpacity", backgroundOpacity); - }, [backgroundOpacity]); - - useEffect(() => { - storage.set("vlc.outlineOpacity", outlineOpacity); - }, [outlineOpacity]); - - useEffect(() => { - storage.set("vlc.isBold", isBold); - }, [isBold]); - - return ( - - - { - const colors = Object.keys(VLC_COLORS); - const currentIndex = colors.indexOf(textColor); - const nextIndex = (currentIndex + 1) % colors.length; - setTextColor(colors[nextIndex]); - }} - /> - { - const colors = Object.keys(VLC_COLORS); - const currentIndex = colors.indexOf(backgroundColor); - const nextIndex = (currentIndex + 1) % colors.length; - setBackgroundColor(colors[nextIndex]); - }} - /> - { - const colors = Object.keys(VLC_COLORS); - const currentIndex = colors.indexOf(outlineColor); - const nextIndex = (currentIndex + 1) % colors.length; - setOutlineColor(colors[nextIndex]); - }} - /> - { - const thicknesses = Object.keys(OUTLINE_THICKNESS); - const currentIndex = thicknesses.indexOf(outlineThickness); - const nextIndex = (currentIndex + 1) % thicknesses.length; - setOutlineThickness(thicknesses[nextIndex]); - }} - /> - { - const newOpacity = (backgroundOpacity + 32) % 256; - setBackgroundOpacity(newOpacity); - }} - /> - { - const newOpacity = (outlineOpacity + 32) % 256; - setOutlineOpacity(newOpacity); - }} - /> - setIsBold(!isBold)} - /> - - - ); -} diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts index c0780462..d7edeb20 100644 --- a/utils/atoms/settings.ts +++ b/utils/atoms/settings.ts @@ -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,