From 9b936e169c6df1a7b24cc2a76f7051a668ae8b8c Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Sat, 21 Mar 2026 13:43:03 +0530 Subject: [PATCH 1/6] fix unusual type content streams fetching, use addon provided type --- src/services/catalog/search.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/services/catalog/search.ts b/src/services/catalog/search.ts index 95407c8e..ffd75805 100644 --- a/src/services/catalog/search.ts +++ b/src/services/catalog/search.ts @@ -325,12 +325,21 @@ async function searchAddonCatalog( // meta addon by ID prefix matching. Setting it here causes 404s when two addons // are installed and one returns IDs the other can't serve metadata for. - const normalizedCatalogType = type ? type.toLowerCase() : type; - if (normalizedCatalogType && content.type !== normalizedCatalogType) { - content.type = normalizedCatalogType; - } else if (content.type) { + // Always lowercase the item's own type first + if (content.type) { content.type = content.type.toLowerCase(); } + + // Only stamp the catalog type if the item doesn't already have a standard type. + // Prevents catalog type "other" from overwriting correct types like "movie"/"series" + // that the addon already set on individual items. + const normalizedCatalogType = type ? type.toLowerCase() : type; + const STANDARD_TYPES = new Set(['movie', 'series', 'anime.movie', 'anime.series', 'anime', 'tv', 'channel']); + if (normalizedCatalogType && !STANDARD_TYPES.has(content.type) && STANDARD_TYPES.has(normalizedCatalogType)) { + content.type = normalizedCatalogType; + } else if (normalizedCatalogType && !content.type) { + content.type = normalizedCatalogType; + } return content; }); From 792c8a9187fcacef0c754d637ed1840db4562164 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Sat, 21 Mar 2026 14:00:58 +0530 Subject: [PATCH 2/6] fix streams fetching for unusual type and use addon's meta response --- src/hooks/useMetadata.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/useMetadata.ts b/src/hooks/useMetadata.ts index 2049b701..f0231d7d 100644 --- a/src/hooks/useMetadata.ts +++ b/src/hooks/useMetadata.ts @@ -1576,7 +1576,9 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat let tmdbId; let stremioId = id; // Use TMDB-resolved type if available — handles "other", "Movie", etc. - let effectiveStreamType: string = resolvedTypeRef.current || normalizedType; + // Use metadata.type first (most reliable — comes directly from the addon's meta response), + // then fall back to TMDB-resolved type, then normalizedType. + let effectiveStreamType: string = metadata?.type || resolvedTypeRef.current || normalizedType; if (id.startsWith('tmdb:')) { tmdbId = id.split(':')[1]; From 8265fe1eac5571ea5cab34455e41dc139f330cff Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Sat, 21 Mar 2026 14:41:04 +0530 Subject: [PATCH 3/6] minor scrobble fix --- src/hooks/useTraktAutosync.ts | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/hooks/useTraktAutosync.ts b/src/hooks/useTraktAutosync.ts index 4c2f681a..f4cbe5a7 100644 --- a/src/hooks/useTraktAutosync.ts +++ b/src/hooks/useTraktAutosync.ts @@ -309,14 +309,6 @@ export function useTraktAutosync(options: TraktAutosyncOptions) { return; } - // Skip if session was already stopped (e.g. after background/pause). - // Without this, the fallback "force start" block inside handleProgressUpdate - // would fire a new /scrobble/start on the first periodic save after a remount, - // bypassing the hasStopped guard in handlePlaybackStart entirely. - if (hasStopped.current) { - return; - } - try { const rawProgress = (currentTime / duration) * 100; const progressPercent = Math.min(100, Math.max(0, rawProgress)); @@ -340,6 +332,19 @@ export function useTraktAutosync(options: TraktAutosyncOptions) { lastSyncTime.current = now; lastSyncProgress.current = progressPercent; + // If this update crossed the completion threshold, Trakt will have silently + // scrobbled it. Mark complete now so unmount/background don't fire a second + // /scrobble/stop above threshold and create a duplicate history entry. + if (progressPercent >= autosyncSettings.completionThreshold) { + isSessionComplete.current = true; + const ck = getContentKey(options); + const existing = recentlyStoppedSessions.get(ck); + if (existing) { + recentlyStoppedSessions.set(ck, { ...existing, isComplete: true, progress: progressPercent }); + } + logger.log(`[TraktAutosync] Threshold reached via immediate progress update (${progressPercent.toFixed(1)}%), marking session complete`); + } + // Update local storage sync status await storageService.updateTraktSyncStatus( options.id, @@ -375,6 +380,19 @@ export function useTraktAutosync(options: TraktAutosyncOptions) { lastSyncTime.current = now; lastSyncProgress.current = progressPercent; + // If this periodic update crossed the completion threshold, Trakt will have + // silently scrobbled it. Mark complete now so unmount/background don't fire + // a second /scrobble/stop above threshold and create a duplicate history entry. + if (progressPercent >= autosyncSettings.completionThreshold) { + isSessionComplete.current = true; + const ck = getContentKey(options); + const existing = recentlyStoppedSessions.get(ck); + if (existing) { + recentlyStoppedSessions.set(ck, { ...existing, isComplete: true, progress: progressPercent }); + } + logger.log(`[TraktAutosync] Threshold reached via progress update (${progressPercent.toFixed(1)}%), marking session complete to prevent duplicate scrobble`); + } + // Update local storage sync status await storageService.updateTraktSyncStatus( options.id, @@ -385,7 +403,6 @@ export function useTraktAutosync(options: TraktAutosyncOptions) { currentTime ); - // Progress sync logging removed logger.log(`[TraktAutosync] Trakt: Progress updated to ${progressPercent.toFixed(1)}%`); } } From abdbffbc7ff1b82ae794ebca2076dafdb5a83fe4 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Sat, 21 Mar 2026 20:42:28 +0530 Subject: [PATCH 4/6] fix duplicate declaration introduced in the mal PR --- src/components/player/AndroidVideoPlayer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 5933a1ef..6623f9d9 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -271,7 +271,6 @@ const AndroidVideoPlayer: React.FC = () => { const nextEpisodeHook = useNextEpisode(type, season, episode, groupedEpisodes, (metadataResult as any)?.groupedEpisodes, episodeId); - const currentTmdbId = (metadata as any)?.tmdbId || (metadata as any)?.external_ids?.tmdb_id; const { segments: skipIntervals, outroSegment } = useSkipSegments({ imdbId: resolvedImdbId || (id?.startsWith('tt') ? id : undefined), From 7f7f6afc81b574871dfb4c585916a7bc33ccf9b3 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Sat, 21 Mar 2026 22:07:32 +0530 Subject: [PATCH 5/6] minor fix --- src/hooks/useMetadata.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/useMetadata.ts b/src/hooks/useMetadata.ts index f0231d7d..0b7db54c 100644 --- a/src/hooks/useMetadata.ts +++ b/src/hooks/useMetadata.ts @@ -1576,8 +1576,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat let tmdbId; let stremioId = id; // Use TMDB-resolved type if available — handles "other", "Movie", etc. - // Use metadata.type first (most reliable — comes directly from the addon's meta response), - // then fall back to TMDB-resolved type, then normalizedType. + // Use metadata.type first (from addon meta response), then TMDB-resolved, then normalized let effectiveStreamType: string = metadata?.type || resolvedTypeRef.current || normalizedType; if (id.startsWith('tmdb:')) { @@ -1633,7 +1632,8 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat const allStremioAddons = await stremioService.getInstalledAddons(); const localScrapers = await localScraperService.getInstalledScrapers(); - const requestedStreamType = type; + // Use the best available type — not raw type which may be "other" + const requestedStreamType = metadata?.type || resolvedTypeRef.current || normalizedType; const pickEligibleStreamAddons = (requestType: string) => allStremioAddons.filter(addon => { From 2d8a07472bd1f07df167c20c0c89bdc6dfb85b98 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Sun, 22 Mar 2026 01:00:15 +0530 Subject: [PATCH 6/6] minor fix --- src/services/catalog/search.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/services/catalog/search.ts b/src/services/catalog/search.ts index ffd75805..b9142a48 100644 --- a/src/services/catalog/search.ts +++ b/src/services/catalog/search.ts @@ -393,9 +393,17 @@ function dedupeAndStampResults(results: StreamingContent[], catalogType: string) } } - return Array.from(bestById.values()).map(item => - catalogType && item.type !== catalogType ? { ...item, type: catalogType } : item - ); + const normalizedCatalogType = catalogType ? catalogType.toLowerCase() : catalogType; + const STANDARD_TYPES = new Set(['movie', 'series', 'anime.movie', 'anime.series', 'anime', 'tv', 'channel']); + + return Array.from(bestById.values()).map(item => { + // Only stamp catalog type if item doesn't already have a standard type. + // Prevents "other" from overwriting correct types like "movie"/"series". + if (normalizedCatalogType && !STANDARD_TYPES.has(item.type) && STANDARD_TYPES.has(normalizedCatalogType)) { + return { ...item, type: normalizedCatalogType }; + } + return item; + }); } function buildSectionName(