Ios #14
5 changed files with 161 additions and 51 deletions
|
|
@ -393,16 +393,16 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
<View style={styles.episodeRow}>
|
||||
<Text style={[styles.episodeText, { color: currentTheme.colors.mediumEmphasis }]}>
|
||||
Season {item.season}
|
||||
</Text>
|
||||
{item.episodeTitle && (
|
||||
</Text>
|
||||
{item.episodeTitle && (
|
||||
<Text
|
||||
style={[styles.episodeTitle, { color: currentTheme.colors.mediumEmphasis }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.episodeTitle}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
{item.episodeTitle}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
|
|
@ -416,15 +416,15 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
|
|||
{/* Progress Bar */}
|
||||
<View style={styles.wideProgressContainer}>
|
||||
<View style={styles.wideProgressTrack}>
|
||||
<View
|
||||
style={[
|
||||
<View
|
||||
style={[
|
||||
styles.wideProgressBar,
|
||||
{
|
||||
width: `${item.progress}%`,
|
||||
backgroundColor: currentTheme.colors.primary
|
||||
}
|
||||
]}
|
||||
/>
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.progressLabel, { color: currentTheme.colors.textMuted }]}>
|
||||
{Math.round(item.progress)}% watched
|
||||
|
|
|
|||
|
|
@ -40,16 +40,17 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
const { width } = useWindowDimensions();
|
||||
const isTablet = width > 768;
|
||||
const isDarkMode = useColorScheme() === 'dark';
|
||||
const [episodeProgress, setEpisodeProgress] = useState<{ [key: string]: { currentTime: number; duration: number } }>({});
|
||||
const [episodeProgress, setEpisodeProgress] = useState<{ [key: string]: { currentTime: number; duration: number; lastUpdated: number } }>({});
|
||||
|
||||
// Add ref for the season selector ScrollView
|
||||
// Add refs for the scroll views
|
||||
const seasonScrollViewRef = useRef<ScrollView | null>(null);
|
||||
const episodeScrollViewRef = useRef<ScrollView | null>(null);
|
||||
|
||||
const loadEpisodesProgress = async () => {
|
||||
if (!metadata?.id) return;
|
||||
|
||||
const allProgress = await storageService.getAllWatchProgress();
|
||||
const progress: { [key: string]: { currentTime: number; duration: number } } = {};
|
||||
const progress: { [key: string]: { currentTime: number; duration: number; lastUpdated: number } } = {};
|
||||
|
||||
episodes.forEach(episode => {
|
||||
const episodeId = episode.stremioId || `${metadata.id}:${episode.season_number}:${episode.episode_number}`;
|
||||
|
|
@ -57,7 +58,8 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
if (allProgress[key]) {
|
||||
progress[episodeId] = {
|
||||
currentTime: allProgress[key].currentTime,
|
||||
duration: allProgress[key].duration
|
||||
duration: allProgress[key].duration,
|
||||
lastUpdated: allProgress[key].lastUpdated
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -65,6 +67,67 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
setEpisodeProgress(progress);
|
||||
};
|
||||
|
||||
// Function to find and scroll to the most recently watched episode
|
||||
const scrollToMostRecentEpisode = () => {
|
||||
if (!metadata?.id || !episodeScrollViewRef.current || settings.episodeLayoutStyle !== 'horizontal') {
|
||||
console.log('[SeriesContent] Scroll conditions not met:', {
|
||||
hasMetadataId: !!metadata?.id,
|
||||
hasScrollRef: !!episodeScrollViewRef.current,
|
||||
isHorizontal: settings.episodeLayoutStyle === 'horizontal'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSeasonEpisodes = groupedEpisodes[selectedSeason] || [];
|
||||
if (currentSeasonEpisodes.length === 0) {
|
||||
console.log('[SeriesContent] No episodes in current season:', selectedSeason);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the most recently watched episode in the current season
|
||||
let mostRecentEpisodeIndex = -1;
|
||||
let mostRecentTimestamp = 0;
|
||||
let mostRecentEpisodeName = '';
|
||||
|
||||
currentSeasonEpisodes.forEach((episode, index) => {
|
||||
const episodeId = episode.stremioId || `${metadata.id}:${episode.season_number}:${episode.episode_number}`;
|
||||
const progress = episodeProgress[episodeId];
|
||||
|
||||
if (progress && progress.lastUpdated > mostRecentTimestamp && progress.currentTime > 0) {
|
||||
mostRecentTimestamp = progress.lastUpdated;
|
||||
mostRecentEpisodeIndex = index;
|
||||
mostRecentEpisodeName = episode.name;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[SeriesContent] Episode scroll analysis:', {
|
||||
totalEpisodes: currentSeasonEpisodes.length,
|
||||
mostRecentIndex: mostRecentEpisodeIndex,
|
||||
mostRecentEpisode: mostRecentEpisodeName,
|
||||
selectedSeason
|
||||
});
|
||||
|
||||
// Scroll to the most recently watched episode if found
|
||||
if (mostRecentEpisodeIndex >= 0) {
|
||||
const cardWidth = isTablet ? width * 0.4 + 16 : width * 0.85 + 16;
|
||||
const scrollPosition = mostRecentEpisodeIndex * cardWidth;
|
||||
|
||||
console.log('[SeriesContent] Scrolling to episode:', {
|
||||
index: mostRecentEpisodeIndex,
|
||||
cardWidth,
|
||||
scrollPosition,
|
||||
episodeName: mostRecentEpisodeName
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
episodeScrollViewRef.current?.scrollTo({
|
||||
x: scrollPosition,
|
||||
animated: true
|
||||
});
|
||||
}, 500); // Delay to ensure the season has loaded
|
||||
}
|
||||
};
|
||||
|
||||
// Initial load of watch progress
|
||||
useEffect(() => {
|
||||
loadEpisodesProgress();
|
||||
|
|
@ -96,6 +159,13 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
}
|
||||
}, [selectedSeason, groupedEpisodes]);
|
||||
|
||||
// Add effect to scroll to most recently watched episode when season changes or progress loads
|
||||
useEffect(() => {
|
||||
if (Object.keys(episodeProgress).length > 0 && selectedSeason) {
|
||||
scrollToMostRecentEpisode();
|
||||
}
|
||||
}, [selectedSeason, episodeProgress, settings.episodeLayoutStyle, groupedEpisodes]);
|
||||
|
||||
if (loadingSeasons) {
|
||||
return (
|
||||
<View style={styles.centeredContainer}>
|
||||
|
|
@ -480,6 +550,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
|
|||
{settings.episodeLayoutStyle === 'horizontal' ? (
|
||||
// Horizontal Layout (Netflix-style)
|
||||
<ScrollView
|
||||
ref={episodeScrollViewRef}
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={styles.episodeList}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { logger } from '../utils/logger';
|
|||
import { usePersistentSeasons } from './usePersistentSeasons';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { Stream } from '../types/metadata';
|
||||
import { storageService } from '../services/storageService';
|
||||
|
||||
// Constants for timeouts and retries
|
||||
const API_TIMEOUT = 10000; // 10 seconds
|
||||
|
|
@ -589,14 +590,61 @@ export const useMetadata = ({ id, type }: UseMetadataProps): UseMetadataReturn =
|
|||
// Get the first available season as fallback
|
||||
const firstSeason = Math.min(...Object.keys(allEpisodes).map(Number));
|
||||
|
||||
// Get saved season from persistence, fallback to first season if not found
|
||||
const persistedSeason = getSeason(id, firstSeason);
|
||||
// Check for watch progress to auto-select season
|
||||
let selectedSeasonNumber = firstSeason;
|
||||
|
||||
// Set the selected season from persistence
|
||||
setSelectedSeason(persistedSeason);
|
||||
try {
|
||||
// Check watch progress for auto-season selection
|
||||
const allProgress = await storageService.getAllWatchProgress();
|
||||
|
||||
// Find the most recently watched episode for this series
|
||||
let mostRecentEpisodeId = '';
|
||||
let mostRecentTimestamp = 0;
|
||||
|
||||
Object.entries(allProgress).forEach(([key, progress]) => {
|
||||
if (key.includes(`series:${id}:`)) {
|
||||
const episodeId = key.split(`series:${id}:`)[1];
|
||||
if (progress.lastUpdated > mostRecentTimestamp && progress.currentTime > 0) {
|
||||
mostRecentTimestamp = progress.lastUpdated;
|
||||
mostRecentEpisodeId = episodeId;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (mostRecentEpisodeId) {
|
||||
// Parse season number from episode ID
|
||||
const parts = mostRecentEpisodeId.split(':');
|
||||
if (parts.length === 3) {
|
||||
const watchProgressSeason = parseInt(parts[1], 10);
|
||||
if (transformedEpisodes[watchProgressSeason]) {
|
||||
selectedSeasonNumber = watchProgressSeason;
|
||||
logger.log(`[useMetadata] Auto-selected season ${selectedSeasonNumber} based on most recent watch progress for ${mostRecentEpisodeId}`);
|
||||
}
|
||||
} else {
|
||||
// Try to find episode by stremioId to get season
|
||||
const allEpisodesList = Object.values(transformedEpisodes).flat();
|
||||
const episode = allEpisodesList.find(ep => ep.stremioId === mostRecentEpisodeId);
|
||||
if (episode) {
|
||||
selectedSeasonNumber = episode.season_number;
|
||||
logger.log(`[useMetadata] Auto-selected season ${selectedSeasonNumber} based on most recent watch progress for episode with stremioId ${mostRecentEpisodeId}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No watch progress found, use persistent storage as fallback
|
||||
selectedSeasonNumber = getSeason(id, firstSeason);
|
||||
logger.log(`[useMetadata] No watch progress found, using persistent season ${selectedSeasonNumber}`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[useMetadata] Error checking watch progress for season selection:', error);
|
||||
// Fall back to persistent storage
|
||||
selectedSeasonNumber = getSeason(id, firstSeason);
|
||||
}
|
||||
|
||||
// Set the selected season
|
||||
setSelectedSeason(selectedSeasonNumber);
|
||||
|
||||
// Set episodes for the selected season
|
||||
setEpisodes(transformedEpisodes[persistedSeason] || []);
|
||||
setEpisodes(transformedEpisodes[selectedSeasonNumber] || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load episodes:', error);
|
||||
|
|
|
|||
|
|
@ -103,36 +103,27 @@ export const useWatchProgress = (
|
|||
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
|
||||
});
|
||||
}
|
||||
// FIXED: Find the most recently watched episode instead of first unfinished
|
||||
// Sort by lastUpdated timestamp (most recent first)
|
||||
const sortedProgresses = seriesProgresses.sort((a, b) =>
|
||||
b.progress.lastUpdated - a.progress.lastUpdated
|
||||
);
|
||||
|
||||
if (sortedProgresses.length > 0) {
|
||||
// Use the most recently watched episode
|
||||
const mostRecentProgress = sortedProgresses[0];
|
||||
const progress = mostRecentProgress.progress;
|
||||
|
||||
logger.log(`[useWatchProgress] Using most recent progress for ${mostRecentProgress.episodeId}, updated at ${new Date(progress.lastUpdated).toLocaleString()}`);
|
||||
|
||||
setWatchProgress({
|
||||
...progress,
|
||||
episodeId: mostRecentProgress.episodeId,
|
||||
traktSynced: progress.traktSynced,
|
||||
traktProgress: progress.traktProgress
|
||||
});
|
||||
} else {
|
||||
// No watched episodes found
|
||||
setWatchProgress(null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -533,9 +533,9 @@ const HomeScreen = () => {
|
|||
console.log('[HomeScreen] Refreshing continue watching...');
|
||||
if (continueWatchingRef.current) {
|
||||
try {
|
||||
const hasContent = await continueWatchingRef.current.refresh();
|
||||
const hasContent = await continueWatchingRef.current.refresh();
|
||||
console.log(`[HomeScreen] Continue watching has content: ${hasContent}`);
|
||||
setHasContinueWatching(hasContent);
|
||||
setHasContinueWatching(hasContent);
|
||||
|
||||
// Debug: Let's check what's in storage
|
||||
const allProgress = await storageService.getAllWatchProgress();
|
||||
|
|
@ -667,7 +667,7 @@ const HomeScreen = () => {
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<ContinueWatchingSection ref={continueWatchingRef} />
|
||||
<ContinueWatchingSection ref={continueWatchingRef} />
|
||||
|
||||
{catalogs.length > 0 ? (
|
||||
catalogs.map((catalog, index) => (
|
||||
|
|
|
|||
Loading…
Reference in a new issue