mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
Add auto-play feature for first available stream in MetadataScreen
This update introduces a new setting in the app settings to enable auto-playing the first available stream without showing the stream selection screen. The MetadataScreen has been modified to check this setting and automatically load the first stream if enabled, enhancing user experience by streamlining the viewing process. Additionally, the SettingsScreen has been updated to include a toggle for this new feature.
This commit is contained in:
parent
9c73af1d47
commit
4deb343c5f
3 changed files with 140 additions and 2 deletions
|
|
@ -35,6 +35,7 @@ export interface AppSettings {
|
|||
logoSourcePreference: 'metahub' | 'tmdb'; // Preferred source for title logos
|
||||
tmdbLanguagePreference: string; // Preferred language for TMDB logos (ISO 639-1 code)
|
||||
enableInternalProviders: boolean; // Toggle for internal providers like HDRezka
|
||||
autoPlayFirstStream: boolean; // Auto-play first stream without showing streams selection
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: AppSettings = {
|
||||
|
|
@ -52,6 +53,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
|||
logoSourcePreference: 'metahub', // Default to Metahub as first source
|
||||
tmdbLanguagePreference: 'en', // Default to English
|
||||
enableInternalProviders: true, // Enable internal providers by default
|
||||
autoPlayFirstStream: false, // Default to false to maintain existing behavior
|
||||
};
|
||||
|
||||
const SETTINGS_STORAGE_KEY = 'app_settings';
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
|||
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import * as ScreenOrientation from 'expo-screen-orientation';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { useMetadata } from '../hooks/useMetadata';
|
||||
import { CastSection } from '../components/metadata/CastSection';
|
||||
|
|
@ -20,6 +21,7 @@ import { MovieContent } from '../components/metadata/MovieContent';
|
|||
import { MoreLikeThisSection } from '../components/metadata/MoreLikeThisSection';
|
||||
import { RatingsSection } from '../components/metadata/RatingsSection';
|
||||
import { RouteParams, Episode } from '../types/metadata';
|
||||
import { Stream, GroupedStreams } from '../types/streams';
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
interpolate,
|
||||
|
|
@ -75,6 +77,10 @@ const MetadataScreen: React.FC = () => {
|
|||
loadingRecommendations,
|
||||
setMetadata,
|
||||
imdbId,
|
||||
loadStreams,
|
||||
loadEpisodeStreams,
|
||||
groupedStreams,
|
||||
episodeStreams,
|
||||
} = useMetadata({ id, type });
|
||||
|
||||
// Optimized hooks with memoization
|
||||
|
|
@ -107,8 +113,127 @@ const MetadataScreen: React.FC = () => {
|
|||
handleSeasonChange(seasonNumber);
|
||||
}, [handleSeasonChange]);
|
||||
|
||||
const handleShowStreams = useCallback(() => {
|
||||
// Helper function to get the first available stream from grouped streams
|
||||
const getFirstAvailableStream = useCallback((streams: GroupedStreams): Stream | null => {
|
||||
const providers = Object.values(streams);
|
||||
for (const provider of providers) {
|
||||
if (provider.streams && provider.streams.length > 0) {
|
||||
// Try to find a cached stream first
|
||||
const cachedStream = provider.streams.find(stream =>
|
||||
stream.behaviorHints?.cached === true
|
||||
);
|
||||
if (cachedStream) {
|
||||
return cachedStream;
|
||||
}
|
||||
|
||||
// Otherwise return the first stream
|
||||
return provider.streams[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
const handleShowStreams = useCallback(async () => {
|
||||
const { watchProgress } = watchProgressData;
|
||||
|
||||
// Check if auto-play is enabled
|
||||
if (settings.autoPlayFirstStream) {
|
||||
try {
|
||||
console.log('Auto-play enabled, attempting to load streams...');
|
||||
|
||||
// Determine the target episode for series
|
||||
let targetEpisodeId: string | undefined;
|
||||
if (type === 'series') {
|
||||
targetEpisodeId = watchProgress?.episodeId || episodeId || (episodes.length > 0 ?
|
||||
(episodes[0].stremioId || `${id}:${episodes[0].season_number}:${episodes[0].episode_number}`) : undefined);
|
||||
}
|
||||
|
||||
// Load streams without locking orientation yet
|
||||
let streamsLoaded = false;
|
||||
if (type === 'series' && targetEpisodeId) {
|
||||
console.log('Loading episode streams for:', targetEpisodeId);
|
||||
await loadEpisodeStreams(targetEpisodeId);
|
||||
streamsLoaded = true;
|
||||
} else if (type === 'movie') {
|
||||
console.log('Loading movie streams...');
|
||||
await loadStreams();
|
||||
streamsLoaded = true;
|
||||
}
|
||||
|
||||
if (streamsLoaded) {
|
||||
// Wait a bit longer for streams to be processed and state to update
|
||||
console.log('Waiting for streams to be processed...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// Check if we have any streams available
|
||||
const availableStreams = type === 'series' ? episodeStreams : groupedStreams;
|
||||
console.log('Available streams:', Object.keys(availableStreams));
|
||||
|
||||
const firstStream = getFirstAvailableStream(availableStreams);
|
||||
|
||||
if (firstStream) {
|
||||
console.log('Found stream, navigating to player:', firstStream);
|
||||
|
||||
// Now lock orientation to landscape before navigation
|
||||
await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
if (type === 'series' && targetEpisodeId) {
|
||||
// Get episode details for navigation
|
||||
const targetEpisode = episodes.find(ep =>
|
||||
ep.stremioId === targetEpisodeId ||
|
||||
`${id}:${ep.season_number}:${ep.episode_number}` === targetEpisodeId
|
||||
);
|
||||
|
||||
// Navigate directly to player with the first stream
|
||||
navigation.navigate('Player', {
|
||||
uri: firstStream.url,
|
||||
title: metadata?.name || 'Unknown',
|
||||
season: targetEpisode?.season_number,
|
||||
episode: targetEpisode?.episode_number,
|
||||
episodeTitle: targetEpisode?.name,
|
||||
quality: firstStream.title?.match(/(\d+)p/)?.[1] || 'Unknown',
|
||||
year: metadata?.year,
|
||||
streamProvider: firstStream.name || 'Unknown',
|
||||
id,
|
||||
type,
|
||||
episodeId: targetEpisodeId,
|
||||
imdbId: imdbId || id,
|
||||
});
|
||||
return;
|
||||
} else if (type === 'movie') {
|
||||
// Navigate directly to player with the first stream
|
||||
navigation.navigate('Player', {
|
||||
uri: firstStream.url,
|
||||
title: metadata?.name || 'Unknown',
|
||||
quality: firstStream.title?.match(/(\d+)p/)?.[1] || 'Unknown',
|
||||
year: metadata?.year,
|
||||
streamProvider: firstStream.name || 'Unknown',
|
||||
id,
|
||||
type,
|
||||
imdbId: imdbId || id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.log('No streams found after waiting, disabling auto-play for this session');
|
||||
// Don't fall back to streams screen, just show an alert
|
||||
alert('No streams available for auto-play. Please try selecting streams manually.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Auto-play failed, falling back to manual selection');
|
||||
} catch (error) {
|
||||
console.error('Auto-play failed with error:', error);
|
||||
// Don't fall back on error, just show alert
|
||||
alert('Auto-play failed. Please try selecting streams manually.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal behavior: navigate to streams screen (only if auto-play is disabled or not attempted)
|
||||
console.log('Navigating to streams screen (normal flow)');
|
||||
if (type === 'series') {
|
||||
const targetEpisodeId = watchProgress?.episodeId || episodeId || (episodes.length > 0 ?
|
||||
(episodes[0].stremioId || `${id}:${episodes[0].season_number}:${episodes[0].episode_number}`) : undefined);
|
||||
|
|
@ -119,7 +244,7 @@ const MetadataScreen: React.FC = () => {
|
|||
}
|
||||
}
|
||||
navigation.navigate('Streams', { id, type, episodeId });
|
||||
}, [navigation, id, type, episodes, episodeId, watchProgressData.watchProgress]);
|
||||
}, [settings.autoPlayFirstStream, navigation, id, type, episodes, episodeId, watchProgressData, metadata, loadEpisodeStreams, loadStreams, episodeStreams, groupedStreams, imdbId, getFirstAvailableStream]);
|
||||
|
||||
const handleEpisodeSelect = useCallback((episode: Episode) => {
|
||||
const episodeId = episode.stremioId || `${id}:${episode.season_number}:${episode.episode_number}`;
|
||||
|
|
|
|||
|
|
@ -462,6 +462,17 @@ const SettingsScreen: React.FC = () => {
|
|||
icon="play-arrow"
|
||||
renderControl={ChevronRight}
|
||||
onPress={() => navigation.navigate('PlayerSettings')}
|
||||
/>
|
||||
<SettingItem
|
||||
title="Auto-play First Stream"
|
||||
description="Automatically play the first available stream without showing stream selection"
|
||||
icon="auto-fix-high"
|
||||
renderControl={() => (
|
||||
<CustomSwitch
|
||||
value={settings.autoPlayFirstStream}
|
||||
onValueChange={(value) => updateSetting('autoPlayFirstStream', value)}
|
||||
/>
|
||||
)}
|
||||
isLast={true}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
|
|
|||
Loading…
Reference in a new issue