diff --git a/src/components/player/overlays/SkipIntroButton.tsx b/src/components/player/overlays/SkipIntroButton.tsx index 663d86d8..b5329c6a 100644 --- a/src/components/player/overlays/SkipIntroButton.tsx +++ b/src/components/player/overlays/SkipIntroButton.tsx @@ -13,6 +13,7 @@ import { BlurView } from 'expo-blur'; import { introService, SkipInterval, SkipType } from '../../../services/introService'; import { useTheme } from '../../../contexts/ThemeContext'; import { logger } from '../../../utils/logger'; +import { useSettings } from '../../../hooks/useSettings'; interface SkipIntroButtonProps { imdbId: string | undefined; @@ -40,8 +41,11 @@ export const SkipIntroButton: React.FC = ({ controlsFixedOffset = 100, }) => { const { currentTheme } = useTheme(); + const { settings } = useSettings(); const insets = useSafeAreaInsets(); + const skipIntroEnabled = settings.skipIntroEnabled; + // State const [skipIntervals, setSkipIntervals] = useState([]); const [currentInterval, setCurrentInterval] = useState(null); @@ -63,6 +67,14 @@ export const SkipIntroButton: React.FC = ({ useEffect(() => { const episodeKey = `${imdbId}-${season}-${episode}-${malId}-${kitsuId}`; + if (!skipIntroEnabled) { + setSkipIntervals([]); + setCurrentInterval(null); + setIsVisible(false); + fetchedRef.current = false; + return; + } + // Skip if not a series or missing required data (though MAL/Kitsu ID might be enough for some cases, usually need season/ep) if (type !== 'series' || (!imdbId && !malId && !kitsuId) || !season || !episode) { setSkipIntervals([]); @@ -99,7 +111,7 @@ export const SkipIntroButton: React.FC = ({ }; fetchSkipData(); - }, [imdbId, type, season, episode, malId, kitsuId]); + }, [imdbId, type, season, episode, malId, kitsuId, skipIntroEnabled]); // Determine active interval based on current playback position useEffect(() => { @@ -228,6 +240,10 @@ export const SkipIntroButton: React.FC = ({ transform: [{ scale: scale.value }, { translateY: translateY.value }], })); + if (!skipIntroEnabled) { + return null; + } + // Don't render if not visible if (!isVisible) { return null; diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 18e5d9a7..32489de2 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -59,6 +59,7 @@ export interface AppSettings { excludedLanguages: string[]; // Array of language strings to exclude (e.g., ['Spanish', 'German', 'French']) // Playback behavior alwaysResume: boolean; // If true, resume automatically without prompt when progress < 85% + skipIntroEnabled: boolean; // Enable/disable Skip Intro overlay (IntroDB) // Downloads enableDownloads: boolean; // Show Downloads tab and enable saving streams // Theme settings @@ -147,6 +148,7 @@ export const DEFAULT_SETTINGS: AppSettings = { excludedLanguages: [], // No languages excluded by default // Playback behavior defaults alwaysResume: true, + skipIntroEnabled: true, // Downloads enableDownloads: false, useExternalPlayerForDownloads: false, diff --git a/src/screens/settings/PlaybackSettingsScreen.tsx b/src/screens/settings/PlaybackSettingsScreen.tsx index f0ac36fa..8cd69710 100644 --- a/src/screens/settings/PlaybackSettingsScreen.tsx +++ b/src/screens/settings/PlaybackSettingsScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo, useRef } from 'react'; +import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react'; import { View, StyleSheet, ScrollView, StatusBar, Platform, Text, TouchableOpacity, Dimensions } from 'react-native'; import { useNavigation, useFocusEffect } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; @@ -12,9 +12,12 @@ import { useRealtimeConfig } from '../../hooks/useRealtimeConfig'; import { MaterialIcons } from '@expo/vector-icons'; import { BottomSheetModal, BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet'; import { useTranslation } from 'react-i18next'; +import { SvgXml } from 'react-native-svg'; const { width } = Dimensions.get('window'); +const INTRODB_LOGO_URI = 'https://introdb.app/images/logo-vector.svg'; + // Available languages for audio/subtitle selection const AVAILABLE_LANGUAGES = [ { code: 'en', name: 'English' }, @@ -73,6 +76,39 @@ export const PlaybackSettingsContent: React.FC = ( const { t } = useTranslation(); const config = useRealtimeConfig(); + const [introDbLogoXml, setIntroDbLogoXml] = useState(null); + + useEffect(() => { + let cancelled = false; + const load = async () => { + 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