diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt index bb2a4e4a..f2f9cd4f 100644 --- a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt +++ b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt @@ -33,19 +33,9 @@ class MPVView @JvmOverloads constructor( // GPU mode setting: 'gpu', 'gpu-next' (default: gpu) var gpuMode: String = "gpu" - // GLSL shaders setting (for upscalers) - private var glslShadersVal: String? = null - // Flag to track if onLoad has been fired (prevents multiple fires for HLS streams) private var hasLoadEventFired: Boolean = false - // Video Equalizer state - private var brightnessVal: Int = 0 - private var contrastVal: Int = 0 - private var saturationVal: Int = 0 - private var gammaVal: Int = 0 - private var hueVal: Int = 0 - // Event listener for React Native var onLoadCallback: ((duration: Double, width: Int, height: Int) -> Unit)? = null var onProgressCallback: ((position: Double, duration: Double) -> Unit)? = null @@ -88,7 +78,6 @@ class MPVView @JvmOverloads constructor( MPVLib.addObserver(this) MPVLib.setPropertyString("android-surface-size", "${width}x${height}") observeProperties() - applyPostInitSettings() isMpvInitialized = true // If a data source was set before surface was ready, load it now @@ -488,69 +477,6 @@ class MPVView @JvmOverloads constructor( } } - // Video Equalizer Methods - - fun setBrightness(value: Int) { - brightnessVal = value - if (isMpvInitialized) { - Log.d(TAG, "Setting brightness: $value") - MPVLib.setPropertyDouble("brightness", value.toDouble()) - } - } - - fun setContrast(value: Int) { - contrastVal = value - if (isMpvInitialized) { - Log.d(TAG, "Setting contrast: $value") - MPVLib.setPropertyDouble("contrast", value.toDouble()) - } - } - - fun setSaturation(value: Int) { - saturationVal = value - if (isMpvInitialized) { - Log.d(TAG, "Setting saturation: $value") - MPVLib.setPropertyDouble("saturation", value.toDouble()) - } - } - - fun setGamma(value: Int) { - gammaVal = value - if (isMpvInitialized) { - Log.d(TAG, "Setting gamma: $value") - MPVLib.setPropertyDouble("gamma", value.toDouble()) - } - } - - fun setHue(value: Int) { - hueVal = value - if (isMpvInitialized) { - Log.d(TAG, "Setting hue: $value") - MPVLib.setPropertyDouble("hue", value.toDouble()) - } - } - - fun setGlslShaders(shaders: String?) { - glslShadersVal = shaders - if (isMpvInitialized) { - Log.d(TAG, "Setting glsl-shaders: $shaders") - MPVLib.setPropertyString("glsl-shaders", shaders ?: "") - } - } - - private fun applyPostInitSettings() { - Log.d(TAG, "Applying post-init settings: B=$brightnessVal, C=$contrastVal, S=$saturationVal, G=$gammaVal, H=$hueVal, Shaders=$glslShadersVal") - MPVLib.setPropertyDouble("brightness", brightnessVal.toDouble()) - MPVLib.setPropertyDouble("contrast", contrastVal.toDouble()) - MPVLib.setPropertyDouble("saturation", saturationVal.toDouble()) - MPVLib.setPropertyDouble("gamma", gammaVal.toDouble()) - MPVLib.setPropertyDouble("hue", hueVal.toDouble()) - - glslShadersVal?.let { - MPVLib.setPropertyString("glsl-shaders", it) - } - } - // MPVLib.EventObserver implementation override fun eventProperty(property: String) { diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt b/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt index 0f9ff291..5054e04c 100644 --- a/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt +++ b/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt @@ -191,11 +191,6 @@ class MpvPlayerViewManager( view.gpuMode = gpuMode ?: "gpu" } - @ReactProp(name = "glslShaders") - fun setGlslShaders(view: MPVView, glslShaders: String?) { - view.setGlslShaders(glslShaders) - } - // Subtitle Styling Props @ReactProp(name = "subtitleSize", defaultInt = 48) @@ -243,31 +238,4 @@ class MpvPlayerViewManager( fun setSubtitleAlignment(view: MPVView, align: String?) { view.setSubtitleAlignment(align ?: "center") } - - // Video Equalizer Props - - @ReactProp(name = "brightness", defaultInt = 0) - fun setBrightness(view: MPVView, brightness: Int) { - view.setBrightness(brightness) - } - - @ReactProp(name = "contrast", defaultInt = 0) - fun setContrast(view: MPVView, contrast: Int) { - view.setContrast(contrast) - } - - @ReactProp(name = "saturation", defaultInt = 0) - fun setSaturation(view: MPVView, saturation: Int) { - view.setSaturation(saturation) - } - - @ReactProp(name = "gamma", defaultInt = 0) - fun setGamma(view: MPVView, gamma: Int) { - view.setGamma(gamma) - } - - @ReactProp(name = "hue", defaultInt = 0) - fun setHue(view: MPVView, hue: Int) { - view.setHue(hue) - } } diff --git a/assets/shaders/shaders_new.zip b/assets/shaders/shaders_new.zip deleted file mode 100644 index 4cd610c7..00000000 Binary files a/assets/shaders/shaders_new.zip and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 4d76900f..bb70659e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,6 @@ "expo-web-browser": "~15.0.8", "i18next": "^25.7.3", "intl-pluralrules": "^2.0.1", - "jszip": "^3.10.1", "lodash": "^4.17.21", "lottie-react-native": "~7.3.1", "posthog-react-native": "^4.4.0", @@ -7767,12 +7766,6 @@ "node": ">=16.x" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -8442,18 +8435,6 @@ "node": ">=0.6.0" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, "node_modules/klaw-sync": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", @@ -8505,15 +8486,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lighthouse-logger": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", diff --git a/package.json b/package.json index 04723a85..e5f102df 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,6 @@ "expo-web-browser": "~15.0.8", "i18next": "^25.7.3", "intl-pluralrules": "^2.0.1", - "jszip": "^3.10.1", "lodash": "^4.17.21", "lottie-react-native": "~7.3.1", "posthog-react-native": "^4.4.0", diff --git a/plugins/mpv-bridge/android/mpv/MPVView.kt b/plugins/mpv-bridge/android/mpv/MPVView.kt index c54af23c..6f5727e5 100644 --- a/plugins/mpv-bridge/android/mpv/MPVView.kt +++ b/plugins/mpv-bridge/android/mpv/MPVView.kt @@ -247,27 +247,6 @@ class MPVView @JvmOverloads constructor( } } - // Video EQ Properties - fun setBrightness(value: Int) { - if (isMpvInitialized) MPVLib.setPropertyInt("brightness", value) - } - - fun setContrast(value: Int) { - if (isMpvInitialized) MPVLib.setPropertyInt("contrast", value) - } - - fun setSaturation(value: Int) { - if (isMpvInitialized) MPVLib.setPropertyInt("saturation", value) - } - - fun setGamma(value: Int) { - if (isMpvInitialized) MPVLib.setPropertyInt("gamma", value) - } - - fun setHue(value: Int) { - if (isMpvInitialized) MPVLib.setPropertyInt("hue", value) - } - fun setSubtitleTrack(trackId: Int) { Log.d(TAG, "setSubtitleTrack called: trackId=$trackId, isMpvInitialized=$isMpvInitialized") if (isMpvInitialized) { @@ -291,21 +270,6 @@ class MPVView @JvmOverloads constructor( } } - fun setGlslShaders(paths: String) { - Log.d(TAG, "setGlslShaders called with paths: $paths") - if (isMpvInitialized) { - if (paths.isEmpty()) { - Log.d(TAG, "Clearing GLSL shaders") - MPVLib.setPropertyString("glsl-shaders", "") - } else { - Log.d(TAG, "Setting GLSL shaders") - // MPV expects a list of paths string like "path1,path2" or specialized list commands - // Using setPropertyString on "glsl-shaders" usually overwrites the list - MPVLib.setPropertyString("glsl-shaders", paths) - } - } - } - fun setResizeMode(mode: String) { Log.d(TAG, "setResizeMode called: mode=$mode, isMpvInitialized=$isMpvInitialized") if (isMpvInitialized) { diff --git a/plugins/mpv-bridge/android/mpv/MpvPlayerViewManager.kt b/plugins/mpv-bridge/android/mpv/MpvPlayerViewManager.kt index 6cec01c6..27d48527 100644 --- a/plugins/mpv-bridge/android/mpv/MpvPlayerViewManager.kt +++ b/plugins/mpv-bridge/android/mpv/MpvPlayerViewManager.kt @@ -180,35 +180,4 @@ class MpvPlayerViewManager( view.setHeaders(null) } } - - // Video EQ Props - @ReactProp(name = "brightness", defaultInt = 0) - fun setBrightness(view: MPVView, value: Int) { - view.setBrightness(value) - } - - @ReactProp(name = "contrast", defaultInt = 0) - fun setContrast(view: MPVView, value: Int) { - view.setContrast(value) - } - - @ReactProp(name = "saturation", defaultInt = 0) - fun setSaturation(view: MPVView, value: Int) { - view.setSaturation(value) - } - - @ReactProp(name = "gamma", defaultInt = 0) - fun setGamma(view: MPVView, value: Int) { - view.setGamma(value) - } - - @ReactProp(name = "hue", defaultInt = 0) - fun setHue(view: MPVView, value: Int) { - view.setHue(value) - } - - @ReactProp(name = "glslShaders") - fun setGlslShaders(view: MPVView, paths: String?) { - view.setGlslShaders(paths ?: "") - } } diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 340c0329..12a843e3 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -40,7 +40,6 @@ import { SourcesModal } from './modals/SourcesModal'; import { EpisodesModal } from './modals/EpisodesModal'; import { EpisodeStreamsModal } from './modals/EpisodeStreamsModal'; import { ErrorModal } from './modals/ErrorModal'; -import VisualEnhancementModal from './modals/VisualEnhancementModal'; import { CustomSubtitles } from './subtitles/CustomSubtitles'; import ParentalGuideOverlay from './overlays/ParentalGuideOverlay'; import SkipIntroButton from './overlays/SkipIntroButton'; @@ -58,8 +57,6 @@ import { styles } from './utils/playerStyles'; import { formatTime, isHlsStream, getHlsHeaders, defaultAndroidHeaders, parseSRT } from './utils/playerUtils'; import { storageService } from '../../services/storageService'; import stremioService from '../../services/stremioService'; -import { shaderService, ShaderMode } from '../../services/shaderService'; -import { visualEnhancementService, VideoSettings } from '../../services/colorProfileService'; import { WyzieSubtitle, SubtitleCue } from './utils/playerTypes'; import { findBestSubtitleTrack, findBestAudioTrack } from './utils/trackSelectionUtils'; import { useTheme } from '../../contexts/ThemeContext'; @@ -84,7 +81,7 @@ const AndroidVideoPlayer: React.FC = () => { const playerState = usePlayerState(); const modals = usePlayerModals(); const speedControl = useSpeedControl(); - const { settings, updateSetting } = useSettings(); + const { settings } = useSettings(); const videoRef = useRef(null); const mpvPlayerRef = useRef(null); @@ -148,72 +145,6 @@ const AndroidVideoPlayer: React.FC = () => { // Subtitle sync modal state const [showSyncModal, setShowSyncModal] = useState(false); - // Shader / Video Enhancement State - const [showEnhancementModal, setShowEnhancementModal] = useState(false); - const [shaderMode, setShaderModeState] = useState(settings.defaultShaderMode || 'none'); - const [glslShaders, setGlslShaders] = useState(''); - - // Color Profile State - const [activeProfile, setActiveProfile] = useState('natural'); - const [customSettings, setCustomSettings] = useState(visualEnhancementService.getCustomSettings()); - const [currentVideoSettings, setCurrentVideoSettings] = useState(visualEnhancementService.getCurrentSettings()); - - // Initialize shader and color services - useEffect(() => { - // Shaders - shaderService.initialize().catch(e => { - logger.error('[AndroidVideoPlayer] Failed to init shader service', e); - }); - - // Visual Enhancements - setActiveProfile(visualEnhancementService.getActiveProfile()); - setCustomSettings(visualEnhancementService.getCustomSettings()); - setCurrentVideoSettings(visualEnhancementService.getCurrentSettings()); - }, []); - - // Initialize shader config from persisted setting. - // Respect global shader toggle at runtime. - useEffect(() => { - if (!settings.enableShaders) { - setGlslShaders(''); - return; - } - - if (settings.defaultShaderMode && settings.defaultShaderMode !== 'none') { - const config = shaderService.getShaderConfig(settings.defaultShaderMode, settings.shaderProfile as any || 'MID-END'); - setGlslShaders(config); - } else { - setGlslShaders(''); - } - }, [settings.enableShaders, settings.defaultShaderMode, settings.shaderProfile]); - - const setShaderMode = useCallback((mode: ShaderMode) => { - setShaderModeState(mode); - updateSetting('defaultShaderMode', mode); // Persist selection - if (!settings.enableShaders) { - setGlslShaders(''); - return; - } - - const config = shaderService.getShaderConfig(mode, settings.shaderProfile as any || 'MID-END'); - setGlslShaders(config); - // Don't close modal here, let user close it - // setShowEnhancementModal(false); - }, [settings.enableShaders, settings.shaderProfile, updateSetting]); - - const handleSetProfile = useCallback(async (profile: string) => { - await visualEnhancementService.setProfile(profile); - setActiveProfile(profile); - setCurrentVideoSettings(visualEnhancementService.getCurrentSettings()); - }, []); - - const handleUpdateCustomSettings = useCallback(async (newSettings: Partial) => { - await visualEnhancementService.updateCustomSettings(newSettings); - setCustomSettings(visualEnhancementService.getCustomSettings()); - setActiveProfile('custom'); - setCurrentVideoSettings(visualEnhancementService.getCurrentSettings()); - }, []); - // Track auto-selection ref to prevent duplicate selections const hasAutoSelectedTracks = useRef(false); @@ -883,13 +814,6 @@ const AndroidVideoPlayer: React.FC = () => { screenDimensions={playerState.screenDimensions} decoderMode={settings.decoderMode} gpuMode={settings.gpuMode} - glslShaders={glslShaders} - // Color Profile Props - brightness={currentVideoSettings.brightness} - contrast={currentVideoSettings.contrast} - saturation={currentVideoSettings.saturation} - gamma={currentVideoSettings.gamma} - hue={currentVideoSettings.hue} // Dual video engine props useExoPlayer={useExoPlayer} onCodecError={handleCodecError} @@ -998,7 +922,6 @@ const AndroidVideoPlayer: React.FC = () => { isSubtitleModalOpen={modals.showSubtitleModal} setShowSourcesModal={modals.setShowSourcesModal} setShowEpisodesModal={type === 'series' ? modals.setShowEpisodesModal : undefined} - setShowEnhancementModal={setShowEnhancementModal} onSliderValueChange={(val) => { playerState.isDragging.current = true; }} onSlidingStart={() => { playerState.isDragging.current = true; }} onSlidingComplete={(val) => { @@ -1175,17 +1098,6 @@ const AndroidVideoPlayer: React.FC = () => { onSelectStream={(stream) => handleSelectStream(stream)} /> - setShowEnhancementModal(false)} - shaderMode={shaderMode} - setShaderMode={setShaderMode} - activeProfile={activeProfile} - setProfile={handleSetProfile} - customSettings={customSettings} - updateCustomSettings={handleUpdateCustomSettings} - /> - void; decoderMode?: 'auto' | 'sw' | 'hw' | 'hw+'; gpuMode?: 'gpu' | 'gpu-next'; - glslShaders?: string; - // Video EQ Props - brightness?: number; - contrast?: number; - saturation?: number; - gamma?: number; - hue?: number; // Subtitle Styling subtitleSize?: number; subtitleColor?: string; @@ -128,12 +121,6 @@ const MpvPlayer = forwardRef((props, ref) => { onTracksChanged={handleTracksChanged} decoderMode={props.decoderMode ?? 'auto'} gpuMode={props.gpuMode ?? 'gpu'} - glslShaders={props.glslShaders} - brightness={props.brightness ?? 0} - contrast={props.contrast ?? 0} - saturation={props.saturation ?? 0} - gamma={props.gamma ?? 0} - hue={props.hue ?? 0} // Subtitle Styling subtitleSize={props.subtitleSize ?? 48} subtitleColor={props.subtitleColor ?? '#FFFFFF'} diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index b033d640..57321d56 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -50,13 +50,6 @@ interface VideoSurfaceProps { selectedTextTrack?: SelectedTrack; decoderMode?: 'auto' | 'sw' | 'hw' | 'hw+'; gpuMode?: 'gpu' | 'gpu-next'; - glslShaders?: string; - // Video EQ Props - brightness?: number; - contrast?: number; - saturation?: number; - gamma?: number; - hue?: number; // Dual Engine Props useExoPlayer?: boolean; @@ -112,12 +105,6 @@ export const VideoSurface: React.FC = ({ selectedTextTrack, decoderMode, gpuMode, - glslShaders, - brightness, - contrast, - saturation, - gamma, - hue, // Dual Engine useExoPlayer = true, onCodecError, @@ -414,12 +401,6 @@ export const VideoSurface: React.FC = ({ onTracksChanged={onTracksChanged} decoderMode={decoderMode} gpuMode={gpuMode} - glslShaders={glslShaders} - brightness={brightness} - contrast={contrast} - saturation={saturation} - gamma={gamma} - hue={hue} subtitleSize={subtitleSize} subtitleColor={subtitleColor} subtitleBackgroundOpacity={subtitleBackgroundOpacity} diff --git a/src/components/player/controls/PlayerControls.tsx b/src/components/player/controls/PlayerControls.tsx index aee40ad7..29861710 100644 --- a/src/components/player/controls/PlayerControls.tsx +++ b/src/components/player/controls/PlayerControls.tsx @@ -45,7 +45,6 @@ interface PlayerControlsProps { isSubtitleModalOpen?: boolean; setShowSourcesModal?: (show: boolean) => void; setShowEpisodesModal?: (show: boolean) => void; - setShowEnhancementModal?: (show: boolean) => void; // Slider-specific props onSliderValueChange: (value: number) => void; onSlidingStart: () => void; @@ -96,7 +95,6 @@ export const PlayerControls: React.FC = ({ isSubtitleModalOpen, setShowSourcesModal, setShowEpisodesModal, - setShowEnhancementModal, onSliderValueChange, onSlidingStart, onSlidingComplete, @@ -393,21 +391,6 @@ export const PlayerControls: React.FC = ({ /> )} - - {/* Video Enhancement Button (Top Access) */} - {playerBackend === 'MPV' && setShowEnhancementModal && settings.enableShaders && ( - setShowEnhancementModal(true)} - > - - - )} - @@ -609,7 +592,7 @@ export const PlayerControls: React.FC = ({ {/* Center Buttons Container with rounded background - wraps all buttons */} - {/* Aspect Ratio Button */} + {/* Left Side: Aspect Ratio Button */} @@ -622,6 +605,16 @@ export const PlayerControls: React.FC = ({ + {/* Change Source Button */} + {setShowSourcesModal && ( + setShowSourcesModal(true)} + > + + + )} + {/* Playback Speed Button */} setShowSpeedModal(true)}> @@ -640,16 +633,6 @@ export const PlayerControls: React.FC = ({ /> - {/* Change Source Button */} - {setShowSourcesModal && ( - setShowSourcesModal(true)} - > - - - )} - {/* Submit Intro Button */} {season !== undefined && episode !== undefined && settings.introSubmitEnabled && settings.introDbApiKey && ( = ({ )} - {/* Episodes Button */} + {/* Right Side: Episodes Button */} {setShowEpisodesModal && ( = ({ - ); diff --git a/src/components/player/modals/VisualEnhancementModal.tsx b/src/components/player/modals/VisualEnhancementModal.tsx deleted file mode 100644 index 8b5263eb..00000000 --- a/src/components/player/modals/VisualEnhancementModal.tsx +++ /dev/null @@ -1,366 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { View, Text, TouchableOpacity, useWindowDimensions, StyleSheet, ScrollView } from 'react-native'; -import Animated, { - FadeIn, - FadeOut, - SlideInDown, - SlideOutDown, - useAnimatedStyle, - withTiming, -} from 'react-native-reanimated'; -import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; -import { useTheme } from '../../../contexts/ThemeContext'; -import { shaderService, ShaderMode, SHADER_PROFILES, ShaderCategory } from '../../../services/shaderService'; -import { visualEnhancementService, COLOR_PROFILES, PROFILE_DESCRIPTIONS, VideoSettings } from '../../../services/colorProfileService'; -import { useSettings } from '../../../hooks/useSettings'; -import Slider from '@react-native-community/slider'; - -interface VisualEnhancementModalProps { - visible: boolean; - onClose: () => void; - // Shader props - shaderMode: ShaderMode; - setShaderMode: (mode: ShaderMode) => void; - // Color props - activeProfile: string; - setProfile: (profile: string) => void; - customSettings: VideoSettings; - updateCustomSettings: (settings: Partial) => void; -} - -const TabButton = ({ label, icon, isSelected, onPress }: any) => { - const { currentTheme } = useTheme(); - return ( - - - - {label} - - {isSelected && ( - - )} - - ); -}; - -const ShaderTab = ({ currentMode, setMode }: { currentMode: string, setMode: (m: string) => void }) => { - const { settings } = useSettings(); - const selectedCategory = (settings?.shaderProfile || 'MID-END') as ShaderCategory; - - const animeModes = SHADER_PROFILES[selectedCategory] ? Object.keys(SHADER_PROFILES[selectedCategory]) : []; - const cinemaModes = SHADER_PROFILES['CINEMA'] ? Object.keys(SHADER_PROFILES['CINEMA']) : []; - - const getModeDescription = (name: string) => { - if (name.includes('Mode A')) return 'Best for high-quality sources.'; - if (name.includes('Mode B')) return 'Soft restore for noisy videos.'; - if (name.includes('Mode C')) return 'Balanced restore and upscale.'; - if (name.includes('FSR')) return 'Sharp upscaling for live-action.'; - if (name.includes('SSimSuperRes')) return 'Natural sharpness and anti-ringing.'; - return ''; - }; - - return ( - - - - GENERAL - - - setMode('none')} - /> - - - - ANIME (ANIME4K) - - - {animeModes.map((mode) => ( - setMode(mode)} - isHQ={selectedCategory === 'HIGH-END'} - /> - ))} - - {cinemaModes.length > 0 && ( - <> - - - CINEMA - - - {cinemaModes.map((mode) => ( - setMode(mode)} - /> - ))} - - )} - - ); -}; - -const PresetsTab = ({ activeProfile, setProfile }: { activeProfile: string, setProfile: (p: string) => void }) => { - const groups = { - 'Anime': ['anime_4k', 'anime', 'anime_vibrant', 'anime_soft'], - 'Cinema': ['cinema', 'cinema_dark', 'cinema_hdr'], - 'Vivid': ['vivid', 'vivid_pop', 'vivid_warm'], - 'Other': ['natural', 'dark', 'warm', 'cool', 'grayscale'], - }; - - return ( - - {Object.entries(groups).map(([group, profiles]) => ( - - - {group.toUpperCase()} - - {profiles.map(profile => ( - setProfile(profile)} - /> - ))} - - ))} - - ); -}; - -const CustomTab = ({ settings, updateSettings, onReset }: any) => { - const { currentTheme } = useTheme(); - const sliders = [ - { key: 'brightness', label: 'Brightness', min: -100, max: 100 }, - { key: 'contrast', label: 'Contrast', min: -100, max: 100 }, - { key: 'saturation', label: 'Saturation', min: -100, max: 100 }, - { key: 'gamma', label: 'Gamma', min: -100, max: 100 }, - { key: 'hue', label: 'Hue', min: -100, max: 100 }, - ]; - - return ( - - - - Fine-tune video properties. Changes are applied immediately. - - - - {sliders.map(({ key, label, min, max }) => ( - - - {label} - - {settings[key]} - - - updateSettings({ [key]: val })} - minimumTrackTintColor={currentTheme.colors.primary} - maximumTrackTintColor="rgba(255,255,255,0.2)" - thumbTintColor="white" - /> - - {min} - {max} - - - ))} - - - - Reset to Default - - - ); -}; - -const PresetItem = ({ label, description, isSelected, onPress, isHQ }: any) => { - return ( - - - - - - {label} - - {isHQ && ( - - HQ - - )} - - {description && ( - - {description} - - )} - - {isSelected && ( - - )} - - - ); -}; - -const VisualEnhancementModal: React.FC = ({ - visible, - onClose, - shaderMode, - setShaderMode, - activeProfile, - setProfile, - customSettings, - updateCustomSettings, -}) => { - const { height, width } = useWindowDimensions(); - const [activeTab, setActiveTab] = useState<'shaders' | 'presets' | 'custom'>('shaders'); - - if (!visible) return null; - - return ( - - - - - - - - {/* Header */} - - - Filters & Appearance - - - - - - {/* Tabs */} - - setActiveTab('shaders')} - /> - setActiveTab('presets')} - /> - setActiveTab('custom')} - /> - - - - {/* Content */} - - {activeTab === 'shaders' && ( - - )} - {activeTab === 'presets' && ( - - )} - {activeTab === 'custom' && ( - setProfile('natural')} - /> - )} - - - - - ); -}; - -const styles = StyleSheet.create({ - sectionHeader: { - flexDirection: 'row', - alignItems: 'center', - marginTop: 10, - marginBottom: 12, - marginLeft: 4, - }, - sectionTitle: { - color: 'rgba(255,255,255,0.4)', - fontSize: 12, - fontWeight: '800', - textTransform: 'uppercase', - letterSpacing: 1, - }, -}); - -export default VisualEnhancementModal; diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index b529286d..befff6bb 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -115,10 +115,6 @@ export interface AppSettings { preferredAudioLanguage: string; // Preferred language for audio tracks (ISO 639-1 code) subtitleSourcePreference: 'internal' | 'external' | 'any'; // Prefer internal (embedded), external (addon), or any enableSubtitleAutoSelect: boolean; // Auto-select subtitles based on preferences - // Upscaler settings - enableShaders: boolean; // Enable/disable real-time upscalers - shaderProfile: 'MID-END' | 'HIGH-END'; // Hardware profile for upscalers - defaultShaderMode: string; // Persisted shader preset (e.g. 'Anime4K: Mode A') } export const DEFAULT_SETTINGS: AppSettings = { @@ -207,10 +203,6 @@ export const DEFAULT_SETTINGS: AppSettings = { preferredAudioLanguage: 'en', // Default to English audio subtitleSourcePreference: 'internal', // Prefer internal/embedded subtitles first enableSubtitleAutoSelect: true, // Auto-select subtitles by default - // Upscaler defaults - enableShaders: false, - shaderProfile: 'MID-END', - defaultShaderMode: 'none', }; const SETTINGS_STORAGE_KEY = 'app_settings'; diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 2d6017aa..ec219b70 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -1924,6 +1924,7 @@ const ConditionalPostHogProvider: React.FC<{ children: React.ReactNode }> = ({ c apiKey="phc_sk6THCtV3thEAn6cTaA9kL2cHuKDBnlYiSL40ywdS6C" options={{ host: "https://us.i.posthog.com", + autocapture: analyticsEnabled, // Start opted out if analytics is disabled defaultOptIn: analyticsEnabled, }} diff --git a/src/screens/settings/PlaybackSettingsScreen.tsx b/src/screens/settings/PlaybackSettingsScreen.tsx index c8eb8962..c3559e3c 100644 --- a/src/screens/settings/PlaybackSettingsScreen.tsx +++ b/src/screens/settings/PlaybackSettingsScreen.tsx @@ -15,8 +15,6 @@ import { useTranslation } from 'react-i18next'; import { SvgXml } from 'react-native-svg'; import { toastService } from '../../services/toastService'; import { introService } from '../../services/introService'; -import { shaderService } from '../../services/shaderService'; -import { Ionicons } from '@expo/vector-icons'; const { width } = Dimensions.get('window'); @@ -71,6 +69,7 @@ interface PlaybackSettingsContentProps { /** * Reusable PlaybackSettingsContent component + * Can be used inline (tablets) or wrapped in a screen (mobile) */ export const PlaybackSettingsContent: React.FC = ({ isTablet = false }) => { const navigation = useNavigation>(); @@ -83,39 +82,8 @@ export const PlaybackSettingsContent: React.FC = ( const [apiKeyInput, setApiKeyInput] = useState(settings?.introDbApiKey || ''); const [isVerifyingKey, setIsVerifyingKey] = useState(false); - // Video Enhancement Assets state - const [isEnhancementDownloaded, setIsEnhancementDownloaded] = useState(false); - const [isDownloadingEnhancement, setIsDownloadingEnhancement] = useState(false); - const [enhancementProgress, setEnhancementProgress] = useState(0); - const isMounted = useRef(true); - const checkEnhancementStatus = useCallback(async () => { - const available = await shaderService.checkAvailability(); - setIsEnhancementDownloaded(available); - }, []); - - useFocusEffect( - useCallback(() => { - checkEnhancementStatus(); - }, [checkEnhancementStatus]) - ); - - const handleDownloadEnhancements = async () => { - setIsDownloadingEnhancement(true); - const success = await shaderService.downloadShaders((p) => setEnhancementProgress(p)); - if (isMounted.current) { - setIsDownloadingEnhancement(false); - if (success) { - shaderService.setInitialized(true); // Force update service state - setIsEnhancementDownloaded(true); - toastService.success(t('settings.enhancement_download_success', { defaultValue: 'Enhancement assets installed!' })); - } else { - toastService.error(t('settings.enhancement_download_failed', { defaultValue: 'Failed to install assets' })); - } - } - }; - useEffect(() => { isMounted.current = true; return () => { @@ -154,10 +122,13 @@ export const PlaybackSettingsContent: React.FC = ( try { const res = await fetch(INTRODB_LOGO_URI); let xml = await res.text(); + // Inline CSS class-based styles because react-native-svg doesn't support