some sentry fixes maximum update limti reached

This commit is contained in:
tapframe 2025-12-14 23:50:00 +05:30
parent c01528b309
commit f05366ae45
6 changed files with 186 additions and 85 deletions

View file

@ -48,8 +48,8 @@ RCT_EXTERN_METHOD(showAirPlayPicker:(nonnull NSNumber *)node)
@interface RCT_EXTERN_MODULE(KSPlayerModule, RCTEventEmitter)
RCT_EXTERN_METHOD(getTracks:(nonnull NSNumber *)nodeTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(getAirPlayState:(nonnull NSNumber *)nodeTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(showAirPlayPicker:(nonnull NSNumber *)nodeTag)
RCT_EXTERN_METHOD(getTracks:(NSNumber *)nodeTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(getAirPlayState:(NSNumber *)nodeTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(showAirPlayPicker:(NSNumber *)nodeTag)
@end

View file

@ -25,7 +25,11 @@ class KSPlayerModule: RCTEventEmitter {
]
}
@objc func getTracks(_ nodeTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
@objc func getTracks(_ nodeTag: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
guard let nodeTag = nodeTag else {
reject("INVALID_ARGUMENT", "nodeTag must not be nil", nil)
return
}
DispatchQueue.main.async {
if let viewManager = self.bridge.module(for: KSPlayerViewManager.self) as? KSPlayerViewManager {
viewManager.getTracks(nodeTag, resolve: resolve, reject: reject)
@ -35,7 +39,11 @@ class KSPlayerModule: RCTEventEmitter {
}
}
@objc func getAirPlayState(_ nodeTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
@objc func getAirPlayState(_ nodeTag: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
guard let nodeTag = nodeTag else {
reject("INVALID_ARGUMENT", "nodeTag must not be nil", nil)
return
}
DispatchQueue.main.async {
if let viewManager = self.bridge.module(for: KSPlayerViewManager.self) as? KSPlayerViewManager {
viewManager.getAirPlayState(nodeTag, resolve: resolve, reject: reject)
@ -45,7 +53,11 @@ class KSPlayerModule: RCTEventEmitter {
}
}
@objc func showAirPlayPicker(_ nodeTag: NSNumber) {
@objc func showAirPlayPicker(_ nodeTag: NSNumber?) {
guard let nodeTag = nodeTag else {
print("[KSPlayerModule] showAirPlayPicker called with nil nodeTag")
return
}
print("[KSPlayerModule] showAirPlayPicker called for nodeTag: \(nodeTag)")
DispatchQueue.main.async {
if let viewManager = self.bridge.module(for: KSPlayerViewManager.self) as? KSPlayerViewManager {

View file

@ -543,7 +543,11 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
const seasons = Object.keys(groupedEpisodes).map(Number).sort((a, b) => a - b);
const seasons = Object.keys(groupedEpisodes).map(Number).sort((a, b) => {
if (a === 0) return 1;
if (b === 0) return -1;
return a - b;
});
return (
<View style={[
@ -660,7 +664,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
{ color: currentTheme.colors.highEmphasis }
]
]} numberOfLines={1}>
Season {season}
{season === 0 ? 'Specials' : `Season ${season}`}
</Text>
</TouchableOpacity>
</View>
@ -723,7 +727,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
]
]}
>
Season {season}
{season === 0 ? 'Specials' : `Season ${season}`}
</Text>
</TouchableOpacity>
</View>

View file

@ -130,8 +130,10 @@ const KSPlayer = forwardRef<KSPlayerRef, KSPlayerProps>((props, ref) => {
getTracks: async () => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
if (node) {
return await KSPlayerModule.getTracks(node);
}
}
return { audioTracks: [], textTracks: [] };
},
setAllowsExternalPlayback: (allows: boolean) => {
@ -153,15 +155,21 @@ const KSPlayer = forwardRef<KSPlayerRef, KSPlayerProps>((props, ref) => {
getAirPlayState: async () => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
if (node) {
return await KSPlayerModule.getAirPlayState(node);
}
}
return { allowsExternalPlayback: false, usesExternalPlaybackWhileExternalScreenIsActive: false, isExternalPlaybackActive: false };
},
showAirPlayPicker: () => {
if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current);
if (node) {
console.log('[KSPlayerComponent] Calling showAirPlayPicker with node:', node);
KSPlayerModule.showAirPlayPicker(node);
} else {
console.warn('[KSPlayerComponent] Cannot call showAirPlayPicker: node is null');
}
} else {
console.log('[KSPlayerComponent] nativeRef.current is null');
}

View file

@ -1181,14 +1181,59 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
// Determine initial season only once per series
const seasons = Object.keys(groupedAddonEpisodes).map(Number);
const firstSeason = Math.min(...seasons);
const nonZeroSeasons = seasons.filter(s => s !== 0);
const firstSeason = nonZeroSeasons.length > 0 ? Math.min(...nonZeroSeasons) : Math.min(...seasons);
if (!initializedSeasonRef.current) {
const nextSeason = firstSeason;
if (selectedSeason !== nextSeason) {
logger.log(`📺 Setting season ${nextSeason} as selected (${groupedAddonEpisodes[nextSeason]?.length || 0} episodes)`);
setSelectedSeason(nextSeason);
// Check for watch progress to auto-select season
let selectedSeasonNumber = firstSeason;
try {
const allProgress = await storageService.getAllWatchProgress();
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;
}
setEpisodes(groupedAddonEpisodes[nextSeason] || []);
}
});
if (mostRecentEpisodeId) {
// Try to parse season from ID or find matching episode
const parts = mostRecentEpisodeId.split(':');
if (parts.length === 3) {
// Format: showId:season:episode
const watchProgressSeason = parseInt(parts[1], 10);
if (groupedAddonEpisodes[watchProgressSeason]) {
selectedSeasonNumber = watchProgressSeason;
logger.log(`[useMetadata] Auto-selected season ${selectedSeasonNumber} based on most recent watch progress for ${mostRecentEpisodeId}`);
}
} else {
// Try to find by stremioId
const allEpisodesList = Object.values(groupedAddonEpisodes).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, try persistent storage
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);
selectedSeasonNumber = getSeason(id, firstSeason);
}
if (selectedSeason !== selectedSeasonNumber) {
logger.log(`📺 Setting season ${selectedSeasonNumber} as selected`);
setSelectedSeason(selectedSeasonNumber);
}
setEpisodes(groupedAddonEpisodes[selectedSeasonNumber] || []);
initializedSeasonRef.current = true;
} else {
// Keep current selection; refresh episode list for selected season
@ -1238,8 +1283,10 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
setGroupedEpisodes(transformedEpisodes);
// Get the first available season as fallback
const firstSeason = Math.min(...Object.keys(allEpisodes).map(Number));
// Get the first available season as fallback (preferring non-zero seasons)
const availableSeasons = Object.keys(allEpisodes).map(Number);
const nonZeroSeasons = availableSeasons.filter(s => s !== 0);
const firstSeason = nonZeroSeasons.length > 0 ? Math.min(...nonZeroSeasons) : Math.min(...availableSeasons);
if (!initializedSeasonRef.current) {
// Check for watch progress to auto-select season

View file

@ -158,8 +158,14 @@ const HomeScreen = () => {
refreshFeatured
} = useFeaturedContent();
// Guard to prevent overlapping fetch calls
const isFetchingRef = useRef(false);
// Progressive catalog loading function with performance optimizations
const loadCatalogsProgressively = useCallback(async () => {
if (isFetchingRef.current) return;
isFetchingRef.current = true;
setCatalogsLoading(true);
setCatalogs([]);
setLoadedCatalogCount(0);
@ -296,6 +302,10 @@ const HomeScreen = () => {
if (prev === 0) {
setCatalogsLoading(false);
}
// ** Crucial: If all catalogs processed, release the fetch guard **
if (next >= totalCatalogsRef.current) {
isFetchingRef.current = false;
}
return next;
});
});
@ -311,6 +321,13 @@ const HomeScreen = () => {
totalCatalogsRef.current = catalogIndex;
// If no catalogs to load, release locks immediately
if (catalogIndex === 0) {
setCatalogsLoading(false);
isFetchingRef.current = false;
return;
}
// Initialize catalogs array with proper length - ensure on main thread
InteractionManager.runAfterInteractions(() => {
setCatalogs(new Array(catalogIndex).fill(null));
@ -323,6 +340,7 @@ const HomeScreen = () => {
InteractionManager.runAfterInteractions(() => {
setCatalogsLoading(false);
});
isFetchingRef.current = false;
}
}, []);
@ -555,19 +573,31 @@ const HomeScreen = () => {
}
}, []);
// Use refs to track state for event listeners without triggering re-effects
const catalogsLengthRef = useRef(catalogs.length);
const catalogsLoadingRef = useRef(catalogsLoading);
useEffect(() => {
catalogsLengthRef.current = catalogs.length;
}, [catalogs.length]);
useEffect(() => {
catalogsLoadingRef.current = catalogsLoading;
}, [catalogsLoading]);
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// Only refresh continue watching section on focus
refreshContinueWatching();
// Don't reload catalogs unless they haven't been loaded yet
// Catalogs will be refreshed through context updates when addons change
if (catalogs.length === 0 && !catalogsLoading) {
// Uses refs to avoid re-binding the listener on every state change
if (catalogsLengthRef.current === 0 && !catalogsLoadingRef.current) {
loadCatalogsProgressively();
}
});
return unsubscribe;
}, [navigation, refreshContinueWatching, loadCatalogsProgressively, catalogs.length, catalogsLoading]);
}, [navigation, refreshContinueWatching, loadCatalogsProgressively]);
// Memoize the loading screen to prevent unnecessary re-renders
const renderLoadingScreen = useMemo(() => {