Merge pull request #348 from chrisk325/patch-5

This commit is contained in:
Nayif 2026-01-03 10:36:11 +05:30 committed by GitHub
commit 6cb115ed74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 94 additions and 60 deletions

View file

@ -36,6 +36,9 @@ interface ContinueWatchingItem extends StreamingContent {
episode?: number;
episodeTitle?: string;
addonId?: string;
addonPoster?: string;
addonName?: string;
addonDescription?: string;
}
// Define the ref interface
@ -212,9 +215,8 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
const metadataCache = useRef<Record<string, { metadata: any; basicContent: StreamingContent | null; timestamp: number }>>({});
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
// Helper function to get cached or fetch metadata
const getCachedMetadata = useCallback(async (type: string, id: string) => {
const cacheKey = `${type}:${id}`;
const getCachedMetadata = useCallback(async (type: string, id: string, addonId?: string) => {
const cacheKey = `${type}:${id}:${addonId || 'default'}`;
const cached = metadataCache.current[cacheKey];
const now = Date.now();
@ -224,60 +226,67 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
try {
const shouldFetchMeta = await stremioService.isValidContentId(type, id);
const [metadata, basicContent] = await Promise.all([
const [metadata, basicContent, addonContent] = await Promise.all([
shouldFetchMeta ? stremioService.getMetaDetails(type, id) : Promise.resolve(null),
catalogService.getBasicContentDetails(type, id)
catalogService.getBasicContentDetails(type, id),
addonId ? stremioService.getMetaDetails(type, id, addonId).catch(() => null) : Promise.resolve(null)
]);
if (basicContent) {
const result = { metadata, basicContent, timestamp: now };
const finalContent = basicContent ? {
...basicContent,
...(addonContent?.name && { name: addonContent.name }),
...(addonContent?.poster && { poster: addonContent.poster }),
...(addonContent?.description && { description: addonContent.description }),
} : null;
if (finalContent) {
const result = { metadata, basicContent: finalContent, addonContent, timestamp: now };
metadataCache.current[cacheKey] = result;
return result;
}
return null;
} catch (error: any) {
// Skip logging 404 errors to reduce noise
return null;
}
}, []);
// Helper function to find the next episode
const findNextEpisode = useCallback((currentSeason: number, currentEpisode: number, videos: any[]) => {
const findNextEpisode = useCallback((
currentSeason: number,
currentEpisode: number,
videos: any[],
watchedSet?: Set<string>,
showId?: string
) => {
if (!videos || !Array.isArray(videos)) return null;
// Sort videos to ensure correct order
const sortedVideos = [...videos].sort((a, b) => {
if (a.season !== b.season) return a.season - b.season;
return a.episode - b.episode;
});
// Strategy 1: Look for next episode in the same season
let nextEp = sortedVideos.find(v => v.season === currentSeason && v.episode === currentEpisode + 1);
const isAlreadyWatched = (season: number, episode: number): boolean => {
if (!watchedSet || !showId) return false;
const cleanShowId = showId.startsWith('tt') ? showId : `tt${showId}`;
return watchedSet.has(`${cleanShowId}:${season}:${episode}`) ||
watchedSet.has(`${showId}:${season}:${episode}`);
};
// Strategy 2: If not found, look for the first episode of the next season
if (!nextEp) {
nextEp = sortedVideos.find(v => v.season === currentSeason + 1 && v.episode === 1);
}
// Strategy 3: Just find the very next video in the list after the current one
// This handles cases where episode numbering isn't sequential or S+1 E1 isn't the standard start
if (!nextEp) {
const currentIndex = sortedVideos.findIndex(v => v.season === currentSeason && v.episode === currentEpisode);
if (currentIndex !== -1 && currentIndex + 1 < sortedVideos.length) {
const candidate = sortedVideos[currentIndex + 1];
// Ensure we didn't just jump to a random special; check reasonable bounds if needed,
// but generally taking the next sorted item is correct for sequential viewing.
nextEp = candidate;
for (const video of sortedVideos) {
if (video.season < currentSeason) continue;
if (video.season === currentSeason && video.episode <= currentEpisode) continue;
if (isAlreadyWatched(video.season, video.episode)) continue;
if (isEpisodeReleased(video)) {
return video;
}
}
// Verify the found episode is released
if (nextEp && isEpisodeReleased(nextEp)) {
return nextEp;
}
return null;
}, []);
// Modified loadContinueWatching to render incrementally
const loadContinueWatching = useCallback(async (isBackgroundRefresh = false) => {
@ -461,7 +470,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
return;
}
}
const cachedData = await getCachedMetadata(group.type, group.id);
const cachedData = await getCachedMetadata(group.type, group.id, group.episodes[0]?.progress?.addonId);
if (!cachedData?.basicContent) return;
const { metadata, basicContent } = cachedData;
@ -660,7 +669,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
const movieKey = `movie:${imdbId}`;
if (recentlyRemovedRef.current.has(movieKey)) continue;
const cachedData = await getCachedMetadata('movie', imdbId);
const cachedData = await getCachedMetadata('movie', imdbId, item.addonId);
if (!cachedData?.basicContent) continue;
const pausedAt = new Date(item.paused_at).getTime();
@ -692,7 +701,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
continue;
}
const cachedData = await getCachedMetadata('series', showImdb);
const cachedData = await getCachedMetadata('series', showImdb, item.addonId);
if (!cachedData?.basicContent) continue;
traktBatch.push({

View file

@ -559,15 +559,23 @@ export function useTraktIntegration() {
return undefined;
})();
updatePromises.push(
storageService.mergeWithTraktProgress(
id,
type,
item.progress,
item.paused_at,
episodeId,
exactTime
)
// Merge with local progress
await storageService.mergeWithTraktProgress(
id,
type,
item.progress,
item.paused_at,
episodeId,
exactTime
);
// FIX: Mark as already synced so it won't be re-uploaded to Trakt
await storageService.updateTraktSyncStatus(
id,
type,
true, // synced = true
item.progress,
episodeId
);
} catch (error) {
logger.error('[useTraktIntegration] Error preparing Trakt progress update:', error);
@ -581,19 +589,27 @@ export function useTraktIntegration() {
const id = movie.movie.ids.imdb;
const watchedAt = movie.last_watched_at;
updatePromises.push(
storageService.mergeWithTraktProgress(
id,
'movie',
100, // 100% progress for watched items
watchedAt
)
await storageService.mergeWithTraktProgress(
id,
'movie',
100,
watchedAt
);
// FIX: Mark as already synced
await storageService.updateTraktSyncStatus(
id,
'movie',
true,
100
);
}
} catch (error) {
logger.error('[useTraktIntegration] Error preparing watched movie update:', error);
logger.error('[useTraktIntegration] Error preparing watched movie update:', error);
}
}
// Process watched shows (100% completed episodes)
for (const show of watchedShows) {
try {
if (show.show?.ids?.imdb && show.seasons) {
@ -602,14 +618,22 @@ export function useTraktIntegration() {
for (const season of show.seasons) {
for (const episode of season.episodes) {
const episodeId = `${showImdbId}:${season.number}:${episode.number}`;
updatePromises.push(
storageService.mergeWithTraktProgress(
showImdbId,
'series',
100,
episode.last_watched_at,
episodeId
)
await storageService.mergeWithTraktProgress(
showImdbId,
'series',
100,
episode.last_watched_at,
episodeId
);
// FIX: Mark as already synced
await storageService.updateTraktSyncStatus(
showImdbId,
'series',
true,
100,
episodeId
);
}
}
@ -618,6 +642,7 @@ export function useTraktIntegration() {
logger.error('[useTraktIntegration] Error preparing watched show update:', error);
}
}
// Execute all updates in parallel
await Promise.all(updatePromises);