diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index b7681174..1aab148a 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -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)}`); } 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 59ec1570..f01ab200 100644 --- a/components/settings/SubtitleToggles.tsx +++ b/components/settings/SubtitleToggles.tsx @@ -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 }) => { const isTv = Platform.isTV; @@ -25,6 +27,15 @@ export const SubtitleToggles: React.FC = ({ ...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 }) => { 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={(value) => updateSettings({ vlcTextColor: value })} + /> + + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.colors.${item}`) + } + title={ + + + {t(`home.settings.subtitles.colors.${backgroundColor}`)} + + + + } + label={t("home.settings.subtitles.background_color")} + onSelected={(value) => + updateSettings({ vlcBackgroundColor: value }) + } + /> + + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.colors.${item}`) + } + title={ + + + {t(`home.settings.subtitles.colors.${outlineColor}`)} + + + + } + label={t("home.settings.subtitles.outline_color")} + onSelected={(value) => updateSettings({ vlcOutlineColor: value })} + /> + + + item} + titleExtractor={(item) => + t(`home.settings.subtitles.thickness.${item}`) + } + title={ + + + {t(`home.settings.subtitles.thickness.${outlineThickness}`)} + + + + } + label={t("home.settings.subtitles.outline_thickness")} + onSelected={(value) => + updateSettings({ vlcOutlineThickness: value }) + } + /> + + + `${Math.round((item / 255) * 100)}%`} + title={ + + {`${Math.round((backgroundOpacity / 255) * 100)}%`} + + + } + label={t("home.settings.subtitles.background_opacity")} + onSelected={(value) => + updateSettings({ vlcBackgroundOpacity: value }) + } + /> + + + `${Math.round((item / 255) * 100)}%`} + title={ + + {`${Math.round((outlineOpacity / 255) * 100)}%`} + + + } + label={t("home.settings.subtitles.outline_opacity")} + onSelected={(value) => updateSettings({ vlcOutlineOpacity: value })} + /> + + + updateSettings({ vlcIsBold: value })} + /> + ); 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, +}; diff --git a/translations/en.json b/translations/en.json index 82723427..98d1daa7 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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": { 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,