NuvioStreaming_backup_24-10-25/src/hooks/useWatchProgress.ts
tapframe 671861c207 Enhance HeroSection and watch progress management with Trakt integration
This update improves the HeroSection component by adding functionality to determine if content has been watched based on Trakt progress. The ActionButtons now reflect the watched state with updated styles and indicators. Additionally, the useWatchProgress hook has been modified to always show current episode progress, ensuring accurate display of watched status. The useTraktIntegration hook has also been enhanced to fetch and merge watched movies, improving synchronization with Trakt.
2025-06-19 23:26:11 +05:30

217 lines
No EOL
7.3 KiB
TypeScript

import { useState, useCallback, useEffect } from 'react';
import { useFocusEffect } from '@react-navigation/native';
import { useTraktContext } from '../contexts/TraktContext';
import { logger } from '../utils/logger';
import { storageService } from '../services/storageService';
interface WatchProgressData {
currentTime: number;
duration: number;
lastUpdated: number;
episodeId?: string;
traktSynced?: boolean;
traktProgress?: number;
}
export const useWatchProgress = (
id: string,
type: 'movie' | 'series',
episodeId?: string,
episodes: any[] = []
) => {
const [watchProgress, setWatchProgress] = useState<WatchProgressData | null>(null);
const { isAuthenticated: isTraktAuthenticated } = useTraktContext();
// Function to get episode details from episodeId
const getEpisodeDetails = useCallback((episodeId: string): { seasonNumber: string; episodeNumber: string; episodeName: string } | null => {
// Try to parse from format "seriesId:season:episode"
const parts = episodeId.split(':');
if (parts.length === 3) {
const [, seasonNum, episodeNum] = parts;
// Find episode in our local episodes array
const episode = episodes.find(
ep => ep.season_number === parseInt(seasonNum) &&
ep.episode_number === parseInt(episodeNum)
);
if (episode) {
return {
seasonNumber: seasonNum,
episodeNumber: episodeNum,
episodeName: episode.name
};
}
}
// If not found by season/episode, try stremioId
const episodeByStremioId = episodes.find(ep => ep.stremioId === episodeId);
if (episodeByStremioId) {
return {
seasonNumber: episodeByStremioId.season_number.toString(),
episodeNumber: episodeByStremioId.episode_number.toString(),
episodeName: episodeByStremioId.name
};
}
return null;
}, [episodes]);
// Enhanced load watch progress with Trakt integration
const loadWatchProgress = useCallback(async () => {
try {
if (id && type) {
if (type === 'series') {
const allProgress = await storageService.getAllWatchProgress();
// Function to get episode number from episodeId
const getEpisodeNumber = (epId: string) => {
const parts = epId.split(':');
if (parts.length === 3) {
return {
season: parseInt(parts[1]),
episode: parseInt(parts[2])
};
}
return null;
};
// Get all episodes for this series with progress
const seriesProgresses = Object.entries(allProgress)
.filter(([key]) => key.includes(`${type}:${id}:`))
.map(([key, value]) => ({
episodeId: key.split(`${type}:${id}:`)[1],
progress: value
}))
.filter(({ episodeId, progress }) => {
const progressPercent = (progress.currentTime / progress.duration) * 100;
return progressPercent > 0;
});
// If we have a specific episodeId in route params
if (episodeId) {
const progress = await storageService.getWatchProgress(id, type, episodeId);
if (progress) {
// Always show the current episode progress when viewing it specifically
// This allows HeroSection to properly display watched state
setWatchProgress({
...progress,
episodeId,
traktSynced: progress.traktSynced,
traktProgress: progress.traktProgress
});
} else {
setWatchProgress(null);
}
} else {
// Find the first unfinished episode
const unfinishedEpisode = episodes.find(ep => {
const epId = ep.stremioId || `${id}:${ep.season_number}:${ep.episode_number}`;
const progress = seriesProgresses.find(p => p.episodeId === epId);
if (!progress) return true;
const percent = (progress.progress.currentTime / progress.progress.duration) * 100;
return percent < 95;
});
if (unfinishedEpisode) {
const epId = unfinishedEpisode.stremioId ||
`${id}:${unfinishedEpisode.season_number}:${unfinishedEpisode.episode_number}`;
const progress = await storageService.getWatchProgress(id, type, epId);
if (progress) {
setWatchProgress({
...progress,
episodeId: epId,
traktSynced: progress.traktSynced,
traktProgress: progress.traktProgress
});
} else {
setWatchProgress({
currentTime: 0,
duration: 0,
lastUpdated: Date.now(),
episodeId: epId,
traktSynced: false
});
}
} else {
setWatchProgress(null);
}
}
} else {
// For movies
const progress = await storageService.getWatchProgress(id, type, episodeId);
if (progress && progress.currentTime > 0) {
// Always show progress data, even if watched (≥95%)
// The HeroSection will handle the "watched" state display
setWatchProgress({
...progress,
episodeId,
traktSynced: progress.traktSynced,
traktProgress: progress.traktProgress
});
} else {
setWatchProgress(null);
}
}
}
} catch (error) {
logger.error('[useWatchProgress] Error loading watch progress:', error);
setWatchProgress(null);
}
}, [id, type, episodeId, episodes]);
// Enhanced function to get play button text with Trakt awareness
const getPlayButtonText = useCallback(() => {
if (!watchProgress || watchProgress.currentTime <= 0) {
return 'Play';
}
// Consider episode complete if progress is >= 95%
const progressPercent = (watchProgress.currentTime / watchProgress.duration) * 100;
if (progressPercent >= 95) {
return 'Play';
}
// If we have Trakt data and it differs significantly from local, show "Resume"
// but the UI will show the discrepancy
return 'Resume';
}, [watchProgress]);
// Subscribe to storage changes for real-time updates
useEffect(() => {
const unsubscribe = storageService.subscribeToWatchProgressUpdates(() => {
logger.log('[useWatchProgress] Storage updated, reloading progress');
loadWatchProgress();
});
return unsubscribe;
}, [loadWatchProgress]);
// Initial load
useEffect(() => {
loadWatchProgress();
}, [loadWatchProgress]);
// Refresh when screen comes into focus
useFocusEffect(
useCallback(() => {
loadWatchProgress();
}, [loadWatchProgress])
);
// Re-load when Trakt authentication status changes
useEffect(() => {
if (isTraktAuthenticated !== undefined) {
// Small delay to ensure Trakt context is fully initialized
setTimeout(() => {
loadWatchProgress();
}, 100);
}
}, [isTraktAuthenticated, loadWatchProgress]);
return {
watchProgress,
getEpisodeDetails,
getPlayButtonText,
loadWatchProgress
};
};