This commit is contained in:
tapframe 2025-07-07 16:50:35 +05:30
parent fde8904c3b
commit cf750c9da2
3 changed files with 107 additions and 33 deletions

View file

@ -278,10 +278,8 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
}
}
// Create placeholders for each show if not already present
// Create placeholders (or update) for each show based on Trakt history
for (const [showId, info] of Object.entries(latestWatchedByShow)) {
if (latestEpisodes[showId]) continue; // already handled via progress
const nextEpisode = info.episode + 1;
const nextEpisodeId = `${showId}:${info.season}:${nextEpisode}`;
@ -300,7 +298,30 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
episodeTitle: `Episode ${nextEpisode}`,
} as ContinueWatchingItem;
latestEpisodes[showId] = placeholder;
const existing = latestEpisodes[showId];
if (!existing || existing.lastUpdated < info.watchedAt) {
latestEpisodes[showId] = placeholder;
}
// Persist "watched" progress for the episode that Trakt reported
const watchedEpisodeId = `${showId}:${info.season}:${info.episode}`;
const existingProgress = allProgress[`series:${showId}:${watchedEpisodeId}`];
const existingPercent = existingProgress ? (existingProgress.currentTime / existingProgress.duration) * 100 : 0;
if (!existingProgress || existingPercent < 85) {
await storageService.setWatchProgress(
showId,
'series',
{
currentTime: 1,
duration: 1,
lastUpdated: info.watchedAt,
traktSynced: true,
traktProgress: 100,
} as any,
`${info.season}:${info.episode}`
);
}
} catch (err) {
logger.error('Failed to build placeholder from history:', err);
}

View file

@ -154,18 +154,36 @@ const ActionButtons = React.memo(({
// For series, attempt to show the next episode label (e.g., "Play S02E05")
if (type === 'series' && watchProgress?.episodeId) {
let seasonNum: number | null = null;
let episodeNum: number | null = null;
const parts = watchProgress.episodeId.split(':');
if (parts.length >= 3) {
const seasonNum = parseInt(parts[parts.length - 2], 10);
const episodeNum = parseInt(parts[parts.length - 1], 10);
if (!isNaN(seasonNum) && !isNaN(episodeNum)) {
const nextSeason = seasonNum;
const nextEpisode = episodeNum + 1;
const seasonStr = nextSeason.toString().padStart(2, '0');
const episodeStr = nextEpisode.toString().padStart(2, '0');
return `Play S${seasonStr}E${episodeStr}`;
if (parts.length === 3) {
// Format: showId:season:episode
seasonNum = parseInt(parts[1], 10);
episodeNum = parseInt(parts[2], 10);
} else if (parts.length === 2) {
// Format: season:episode (no show id)
seasonNum = parseInt(parts[0], 10);
episodeNum = parseInt(parts[1], 10);
} else {
// Try pattern s1e2
const match = watchProgress.episodeId.match(/s(\d+)e(\d+)/i);
if (match) {
seasonNum = parseInt(match[1], 10);
episodeNum = parseInt(match[2], 10);
}
}
if (seasonNum !== null && episodeNum !== null && !isNaN(seasonNum) && !isNaN(episodeNum)) {
// For watched episodes, show the NEXT episode number
const nextEpisode = episodeNum + 1;
const seasonStr = seasonNum.toString().padStart(2, '0');
const episodeStr = nextEpisode.toString().padStart(2, '0');
return `Play S${seasonStr}E${episodeStr}`;
}
// Fallback label if parsing fails
return 'Play Next Episode';
}

View file

@ -225,42 +225,77 @@ const MetadataScreen: React.FC = () => {
let targetEpisodeId: string | undefined;
if (progressPercent >= 85 && watchProgress?.episodeId) {
// Try to navigate to next episode
// Try to navigate to next episode support multiple episodeId formats
let currentSeason: number | null = null;
let currentEpisode: number | null = null;
const parts = watchProgress.episodeId.split(':');
if (parts.length >= 3) {
const currentSeason = parseInt(parts[parts.length - 2], 10);
const currentEpisode = parseInt(parts[parts.length - 1], 10);
// Find next episode in episodes array
const sortedEpisodes = [...episodes].sort((a, b) => {
if (a.season_number === b.season_number) {
return a.episode_number - b.episode_number;
}
return a.season_number - b.season_number;
});
const currentIndex = sortedEpisodes.findIndex(ep => ep.season_number === currentSeason && ep.episode_number === currentEpisode);
if (currentIndex !== -1 && currentIndex + 1 < sortedEpisodes.length) {
const nextEp = sortedEpisodes[currentIndex + 1];
targetEpisodeId = buildEpisodeId(nextEp);
if (parts.length === 3) {
// showId:season:episode
currentSeason = parseInt(parts[1], 10);
currentEpisode = parseInt(parts[2], 10);
} else if (parts.length === 2) {
// season:episode
currentSeason = parseInt(parts[0], 10);
currentEpisode = parseInt(parts[1], 10);
} else {
// pattern like s5e01
const match = watchProgress.episodeId.match(/s(\d+)e(\d+)/i);
if (match) {
currentSeason = parseInt(match[1], 10);
currentEpisode = parseInt(match[2], 10);
}
}
if (currentSeason !== null && currentEpisode !== null) {
// DIRECT APPROACH: Just create the next episode ID directly
// This ensures we navigate to the next episode even if it's not yet in our episodes array
const nextEpisodeId = `${id}:${currentSeason}:${currentEpisode + 1}`;
console.log(`[MetadataScreen] Created next episode ID directly: ${nextEpisodeId}`);
// Still try to find the episode in our list to verify it exists
const nextEpisodeExists = episodes.some(ep =>
ep.season_number === currentSeason && ep.episode_number === (currentEpisode + 1)
);
if (nextEpisodeExists) {
console.log(`[MetadataScreen] Verified next episode S${currentSeason}E${currentEpisode + 1} exists in episodes list`);
} else {
console.log(`[MetadataScreen] Warning: Next episode S${currentSeason}E${currentEpisode + 1} not found in episodes list, but proceeding anyway`);
}
targetEpisodeId = nextEpisodeId;
}
}
// Fallback logic: if not finished or nextEp not found
if (!targetEpisodeId) {
targetEpisodeId = watchProgress?.episodeId || episodeId || (episodes.length > 0 ? buildEpisodeId(episodes[0]) : undefined);
console.log(`[MetadataScreen] Using fallback episode ID: ${targetEpisodeId}`);
}
if (targetEpisodeId) {
navigation.navigate('Streams', { id, type, episodeId: targetEpisodeId });
// Ensure the episodeId has showId prefix (id:season:episode)
const epParts = targetEpisodeId.split(':');
let normalizedEpisodeId = targetEpisodeId;
if (epParts.length === 2) {
normalizedEpisodeId = `${id}:${epParts[0]}:${epParts[1]}`;
}
console.log(`[MetadataScreen] Navigating to streams with episodeId: ${normalizedEpisodeId}`);
navigation.navigate('Streams', { id, type, episodeId: normalizedEpisodeId });
return;
}
}
// Default movie or unknown flow
navigation.navigate('Streams', { id, type, episodeId });
// Normalize fallback episodeId too
let fallbackEpisodeId = episodeId;
if (episodeId && episodeId.split(':').length === 2) {
const p = episodeId.split(':');
fallbackEpisodeId = `${id}:${p[0]}:${p[1]}`;
}
console.log(`[MetadataScreen] Navigating with fallback episodeId: ${fallbackEpisodeId}`);
navigation.navigate('Streams', { id, type, episodeId: fallbackEpisodeId });
}, [navigation, id, type, episodes, episodeId, watchProgressData.watchProgress]);
const handleEpisodeSelect = useCallback((episode: Episode) => {