From fde8904c3b6eee1ce16ab1e92aa94b239dc2d3f8 Mon Sep 17 00:00:00 2001 From: tapframe Date: Mon, 7 Jul 2025 16:27:21 +0530 Subject: [PATCH] CP!!! Some changes top trakt --- .../home/ContinueWatchingSection.tsx | 56 +++++++++++++++++++ src/components/metadata/SeriesContent.tsx | 51 +++++++++++++++-- src/services/traktService.ts | 23 ++++++++ 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/src/components/home/ContinueWatchingSection.tsx b/src/components/home/ContinueWatchingSection.tsx index a6fb3e2..d31c9f4 100644 --- a/src/components/home/ContinueWatchingSection.tsx +++ b/src/components/home/ContinueWatchingSection.tsx @@ -254,6 +254,62 @@ const ContinueWatchingSection = React.forwardRef((props, re // Wait for all content to be processed await Promise.all(contentPromises); + // -------------------- TRAKT HISTORY INTEGRATION -------------------- + try { + const traktService = TraktService.getInstance(); + const isAuthed = await traktService.isAuthenticated(); + if (isAuthed) { + const historyItems = await traktService.getWatchedEpisodesHistory(1, 200); + const latestWatchedByShow: Record = {}; + + for (const item of historyItems) { + if (item.type !== 'episode') continue; + const showImdb = item.show?.ids?.imdb ? `tt${item.show.ids.imdb.replace(/^tt/, '')}` : null; + if (!showImdb) continue; + + const season = item.episode?.season; + const epNum = item.episode?.number; + if (season === undefined || epNum === undefined) continue; + const watchedAt = new Date(item.watched_at).getTime(); + + const existing = latestWatchedByShow[showImdb]; + if (!existing || existing.watchedAt < watchedAt) { + latestWatchedByShow[showImdb] = { season, episode: epNum, watchedAt }; + } + } + + // Create placeholders for each show if not already present + 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}`; + + try { + const basicContent = await catalogService.getBasicContentDetails('series', showId); + if (!basicContent) continue; + + const placeholder: ContinueWatchingItem = { + ...basicContent, + id: showId, + type: 'series', + progress: 0, + lastUpdated: info.watchedAt, + season: info.season, + episode: nextEpisode, + episodeTitle: `Episode ${nextEpisode}`, + } as ContinueWatchingItem; + + latestEpisodes[showId] = placeholder; + } catch (err) { + logger.error('Failed to build placeholder from history:', err); + } + } + } + } catch (err) { + logger.error('Error merging Trakt history:', err); + } + // Add the latest episodes for each series to the items list progressItems.push(...Object.values(latestEpisodes)); diff --git a/src/components/metadata/SeriesContent.tsx b/src/components/metadata/SeriesContent.tsx index 76d0f71..8af37b7 100644 --- a/src/components/metadata/SeriesContent.tsx +++ b/src/components/metadata/SeriesContent.tsx @@ -10,6 +10,8 @@ import { tmdbService } from '../../services/tmdbService'; import { storageService } from '../../services/storageService'; import { useFocusEffect } from '@react-navigation/native'; import Animated, { FadeIn } from 'react-native-reanimated'; +import { TraktService } from '../../services/traktService'; +import { logger } from '../../utils/logger'; interface SeriesContentProps { episodes: Episode[]; @@ -66,6 +68,46 @@ export const SeriesContent: React.FC = ({ } }); + // ---------------- Trakt watched-history integration ---------------- + try { + const traktService = TraktService.getInstance(); + const isAuthed = await traktService.isAuthenticated(); + if (isAuthed && metadata?.id) { + const historyItems = await traktService.getWatchedEpisodesHistory(1, 400); + + historyItems.forEach(item => { + if (item.type !== 'episode') return; + + const showImdb = item.show?.ids?.imdb ? `tt${item.show.ids.imdb.replace(/^tt/, '')}` : null; + if (!showImdb || showImdb !== metadata.id) return; + + const season = item.episode?.season; + const epNum = item.episode?.number; + if (season === undefined || epNum === undefined) return; + + const episodeId = `${metadata.id}:${season}:${epNum}`; + const watchedAt = new Date(item.watched_at).getTime(); + + // Mark as 100% completed (use 1/1 to avoid divide-by-zero) + const traktProgressEntry = { + currentTime: 1, + duration: 1, + lastUpdated: watchedAt, + }; + + const existing = progress[episodeId]; + const existingPercent = existing ? (existing.currentTime / existing.duration) * 100 : 0; + + // Prefer local progress if it is already >=85%; otherwise use Trakt data + if (!existing || existingPercent < 85) { + progress[episodeId] = traktProgressEntry; + } + }); + } + } catch (err) { + logger.error('[SeriesContent] Failed to merge Trakt history:', err); + } + setEpisodeProgress(progress); }; @@ -799,8 +841,8 @@ const styles = StyleSheet.create({ }, completedBadge: { position: 'absolute', - bottom: 8, - right: 8, + top: 8, + left: 8, width: 20, height: 20, borderRadius: 10, @@ -808,6 +850,7 @@ const styles = StyleSheet.create({ justifyContent: 'center', borderWidth: 1, borderColor: 'rgba(255,255,255,0.3)', + zIndex: 2, }, // Horizontal Layout Styles @@ -935,8 +978,8 @@ const styles = StyleSheet.create({ }, completedBadgeHorizontal: { position: 'absolute', - bottom: 12, - right: 12, + top: 12, + left: 12, width: 24, height: 24, borderRadius: 12, diff --git a/src/services/traktService.ts b/src/services/traktService.ts index 49b161f..46c875a 100644 --- a/src/services/traktService.ts +++ b/src/services/traktService.ts @@ -1562,6 +1562,29 @@ export class TraktService { return false; } } + + public async getWatchedEpisodesHistory(page: number = 1, limit: number = 100): Promise { + await this.ensureInitialized(); + + const cacheKey = `history_episodes_${page}_${limit}`; + const lastSync = this.lastSyncTimes.get(cacheKey) || 0; + const now = Date.now(); + if (now - lastSync < this.SYNC_DEBOUNCE_MS) { + // Return cached result if we fetched recently + return (this as any)[cacheKey] || []; + } + + const endpoint = `/sync/history/episodes?page=${page}&limit=${limit}`; + try { + const data = await this.apiRequest(endpoint, 'GET'); + (this as any)[cacheKey] = data; + this.lastSyncTimes.set(cacheKey, now); + return data; + } catch (error) { + logger.error('[TraktService] Failed to fetch watched episodes history:', error); + return []; + } + } } // Export a singleton instance