mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
added skip intro toggle
This commit is contained in:
parent
0ddc78587b
commit
61a69799d4
3 changed files with 72 additions and 2 deletions
|
|
@ -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<SkipIntroButtonProps> = ({
|
|||
controlsFixedOffset = 100,
|
||||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const { settings } = useSettings();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const skipIntroEnabled = settings.skipIntroEnabled;
|
||||
|
||||
// State
|
||||
const [skipIntervals, setSkipIntervals] = useState<SkipInterval[]>([]);
|
||||
const [currentInterval, setCurrentInterval] = useState<SkipInterval | null>(null);
|
||||
|
|
@ -63,6 +67,14 @@ export const SkipIntroButton: React.FC<SkipIntroButtonProps> = ({
|
|||
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<SkipIntroButtonProps> = ({
|
|||
};
|
||||
|
||||
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<SkipIntroButtonProps> = ({
|
|||
transform: [{ scale: scale.value }, { translateY: translateY.value }],
|
||||
}));
|
||||
|
||||
if (!skipIntroEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't render if not visible
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<PlaybackSettingsContentProps> = (
|
|||
const { t } = useTranslation();
|
||||
const config = useRealtimeConfig();
|
||||
|
||||
const [introDbLogoXml, setIntroDbLogoXml] = useState<string | null>(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 <style> class selectors
|
||||
// Map known classes from the IntroDB logo to equivalent inline attributes
|
||||
xml = xml.replace(/class="cls-4"/g, 'fill="url(#linear-gradient)"');
|
||||
xml = xml.replace(/class="cls-3"/g, 'fill="#141414" opacity=".38"');
|
||||
xml = xml.replace(/class="cls-1"/g, 'fill="url(#linear-gradient-2)" opacity=".53"');
|
||||
xml = xml.replace(/class="cls-2"/g, 'fill="url(#linear-gradient-3)" opacity=".53"');
|
||||
// Remove the <style> block to avoid unsupported CSS
|
||||
xml = xml.replace(/<style>[\s\S]*?<\/style>/, '');
|
||||
if (!cancelled) setIntroDbLogoXml(xml);
|
||||
} catch {
|
||||
if (!cancelled) setIntroDbLogoXml(null);
|
||||
}
|
||||
};
|
||||
load();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const introDbLogoIcon = introDbLogoXml ? (
|
||||
<SvgXml xml={introDbLogoXml} width={28} height={18} />
|
||||
) : (
|
||||
<MaterialIcons name="skip-next" size={18} color={currentTheme.colors.primary} />
|
||||
);
|
||||
|
||||
// Bottom sheet refs
|
||||
const audioLanguageSheetRef = useRef<BottomSheetModal>(null);
|
||||
const subtitleLanguageSheetRef = useRef<BottomSheetModal>(null);
|
||||
|
|
@ -173,6 +209,22 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
</SettingsCard>
|
||||
)}
|
||||
|
||||
<SettingsCard title={t('player.section_playback', { defaultValue: 'Playback' })} isTablet={isTablet}>
|
||||
<SettingItem
|
||||
title={t('player.skip_intro_settings_title', { defaultValue: 'Skip Intro' })}
|
||||
description={t('player.powered_by_introdb', { defaultValue: 'Powered by IntroDB' })}
|
||||
customIcon={introDbLogoIcon}
|
||||
renderControl={() => (
|
||||
<CustomSwitch
|
||||
value={settings?.skipIntroEnabled ?? true}
|
||||
onValueChange={(value) => updateSetting('skipIntroEnabled', value)}
|
||||
/>
|
||||
)}
|
||||
isLast
|
||||
isTablet={isTablet}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
{/* Audio & Subtitle Preferences */}
|
||||
<SettingsCard title={t('settings.sections.audio_subtitles')} isTablet={isTablet}>
|
||||
<SettingItem
|
||||
|
|
|
|||
Loading…
Reference in a new issue