mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-04 17:29:07 +00:00
heating optimization
This commit is contained in:
parent
02bfd85b5a
commit
6bb4d927ed
8 changed files with 34 additions and 47 deletions
|
|
@ -164,10 +164,11 @@ const ContentItem = ({ item, onPress }: ContentItemProps) => {
|
||||||
style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, borderRadius: posterRadius }]}
|
style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, borderRadius: posterRadius }]}
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
cachePolicy="memory-disk" // Use both memory and disk cache
|
cachePolicy="memory-disk" // Use both memory and disk cache
|
||||||
transition={200} // Add smooth transition
|
transition={0} // Disable transition to reduce GPU work
|
||||||
placeholder={{ blurhash: PLACEHOLDER_BLURHASH } as any}
|
placeholder={{ blurhash: PLACEHOLDER_BLURHASH } as any}
|
||||||
placeholderContentFit="cover"
|
placeholderContentFit="cover"
|
||||||
allowDownscaling
|
allowDownscaling
|
||||||
|
priority="low" // Deprioritize decode for long lists
|
||||||
onLoad={() => {
|
onLoad={() => {
|
||||||
setImageLoaded(true);
|
setImageLoaded(true);
|
||||||
setImageError(false);
|
setImageError(false);
|
||||||
|
|
@ -183,7 +184,6 @@ const ContentItem = ({ item, onPress }: ContentItemProps) => {
|
||||||
setImageError(true);
|
setImageError(true);
|
||||||
setImageLoaded(false);
|
setImageLoaded(false);
|
||||||
}}
|
}}
|
||||||
priority="normal" // Increase priority for better loading
|
|
||||||
recyclingKey={item.id} // Add recycling key for better performance
|
recyclingKey={item.id} // Add recycling key for better performance
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -239,11 +239,11 @@ const styles = StyleSheet.create({
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
elevation: Platform.OS === 'android' ? 2 : 0,
|
elevation: Platform.OS === 'android' ? 1 : 0,
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 1 },
|
shadowOffset: { width: 0, height: 1 },
|
||||||
shadowOpacity: 0.1,
|
shadowOpacity: 0.05,
|
||||||
shadowRadius: 2,
|
shadowRadius: 1,
|
||||||
borderWidth: 0.5,
|
borderWidth: 0.5,
|
||||||
borderColor: 'rgba(255,255,255,0.12)',
|
borderColor: 'rgba(255,255,255,0.12)',
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
|
|
|
||||||
|
|
@ -157,10 +157,10 @@ const HeroCarousel: React.FC<HeroCarouselProps> = ({ items, loading = false }) =
|
||||||
source={{ uri: item.banner || item.poster }}
|
source={{ uri: item.banner || item.poster }}
|
||||||
style={styles.backgroundImage as ImageStyle}
|
style={styles.backgroundImage as ImageStyle}
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
blurRadius={Platform.OS === 'android' ? 12 : 20}
|
blurRadius={Platform.OS === 'android' ? 8 : 12}
|
||||||
cachePolicy="memory-disk"
|
cachePolicy="memory-disk"
|
||||||
transition={200}
|
transition={0}
|
||||||
priority="high"
|
priority="low"
|
||||||
/>
|
/>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={["rgba(0,0,0,0.45)", "rgba(0,0,0,0.75)"]}
|
colors={["rgba(0,0,0,0.45)", "rgba(0,0,0,0.75)"]}
|
||||||
|
|
@ -270,7 +270,7 @@ const CarouselCard: React.FC<CarouselCardProps> = memo(({ item, colors, logoFail
|
||||||
source={{ uri: item.banner || item.poster }}
|
source={{ uri: item.banner || item.poster }}
|
||||||
style={styles.banner as ImageStyle}
|
style={styles.banner as ImageStyle}
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
transition={300}
|
transition={0}
|
||||||
cachePolicy="memory-disk"
|
cachePolicy="memory-disk"
|
||||||
/>
|
/>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
|
|
@ -285,7 +285,7 @@ const CarouselCard: React.FC<CarouselCardProps> = memo(({ item, colors, logoFail
|
||||||
source={{ uri: item.logo }}
|
source={{ uri: item.logo }}
|
||||||
style={styles.logo as ImageStyle}
|
style={styles.logo as ImageStyle}
|
||||||
contentFit="contain"
|
contentFit="contain"
|
||||||
transition={250}
|
transition={0}
|
||||||
cachePolicy="memory-disk"
|
cachePolicy="memory-disk"
|
||||||
onError={onLogoError}
|
onError={onLogoError}
|
||||||
/>
|
/>
|
||||||
|
|
@ -360,11 +360,11 @@ const styles = StyleSheet.create({
|
||||||
height: CARD_HEIGHT,
|
height: CARD_HEIGHT,
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
elevation: 6,
|
elevation: 2,
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 6 },
|
shadowOffset: { width: 0, height: 2 },
|
||||||
shadowOpacity: 0.3,
|
shadowOpacity: 0.15,
|
||||||
shadowRadius: 12,
|
shadowRadius: 4,
|
||||||
},
|
},
|
||||||
skeletonCard: {
|
skeletonCard: {
|
||||||
width: CARD_WIDTH,
|
width: CARD_WIDTH,
|
||||||
|
|
|
||||||
|
|
@ -515,8 +515,8 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
clearInterval(progressSaveInterval);
|
clearInterval(progressSaveInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMMEDIATE SYNC: Reduce sync interval to 5 seconds for near real-time sync
|
// HEATING FIX: Increase sync interval to 15 seconds to reduce CPU load
|
||||||
const syncInterval = 5000; // 5 seconds for immediate sync
|
const syncInterval = 15000; // 15 seconds to prevent heating
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
saveWatchProgress();
|
saveWatchProgress();
|
||||||
|
|
@ -2032,7 +2032,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
playWhenInactive={false}
|
playWhenInactive={false}
|
||||||
ignoreSilentSwitch="ignore"
|
ignoreSilentSwitch="ignore"
|
||||||
mixWithOthers="inherit"
|
mixWithOthers="inherit"
|
||||||
progressUpdateInterval={250}
|
progressUpdateInterval={1000}
|
||||||
bufferConfig={{
|
bufferConfig={{
|
||||||
minBufferMs: 15000,
|
minBufferMs: 15000,
|
||||||
maxBufferMs: 50000,
|
maxBufferMs: 50000,
|
||||||
|
|
|
||||||
|
|
@ -547,8 +547,8 @@ const VideoPlayer: React.FC = () => {
|
||||||
clearInterval(progressSaveInterval);
|
clearInterval(progressSaveInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMMEDIATE SYNC: Reduce sync interval to 5 seconds for near real-time sync
|
// HEATING FIX: Increase sync interval to 15 seconds to reduce CPU load
|
||||||
const syncInterval = 5000; // 5 seconds for immediate sync
|
const syncInterval = 15000; // 15 seconds to prevent heating
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
saveWatchProgress();
|
saveWatchProgress();
|
||||||
|
|
|
||||||
|
|
@ -870,8 +870,8 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check completion periodically
|
// Check completion less frequently to reduce CPU load
|
||||||
const completionInterval = setInterval(checkScrapersCompletion, 1000);
|
const completionInterval = setInterval(checkScrapersCompletion, 2000);
|
||||||
|
|
||||||
// Fallback timeout after 30 seconds
|
// Fallback timeout after 30 seconds
|
||||||
const fallbackTimeout = setTimeout(() => {
|
const fallbackTimeout = setTimeout(() => {
|
||||||
|
|
@ -1039,8 +1039,8 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check completion periodically
|
// Check completion less frequently to reduce CPU load
|
||||||
const episodeCompletionInterval = setInterval(checkEpisodeScrapersCompletion, 1000);
|
const episodeCompletionInterval = setInterval(checkEpisodeScrapersCompletion, 2000);
|
||||||
|
|
||||||
// Fallback timeout after 30 seconds
|
// Fallback timeout after 30 seconds
|
||||||
const episodeFallbackTimeout = setTimeout(() => {
|
const episodeFallbackTimeout = setTimeout(() => {
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) =
|
||||||
// Use single progress value for all header animations
|
// Use single progress value for all header animations
|
||||||
if (headerProgress.value !== progress) {
|
if (headerProgress.value !== progress) {
|
||||||
headerProgress.value = withTiming(progress, {
|
headerProgress.value = withTiming(progress, {
|
||||||
duration: progress ? 200 : 150,
|
duration: progress ? 150 : 100,
|
||||||
easing: easings.ultraFast
|
easing: easings.ultraFast
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ const HomeScreen = () => {
|
||||||
let catalogIndex = 0;
|
let catalogIndex = 0;
|
||||||
|
|
||||||
// Limit concurrent catalog loading to prevent overwhelming the system
|
// Limit concurrent catalog loading to prevent overwhelming the system
|
||||||
const MAX_CONCURRENT_CATALOGS = 3; // Lower concurrency to reduce CPU/network spikes
|
const MAX_CONCURRENT_CATALOGS = 2; // Very low concurrency to reduce heating
|
||||||
let activeCatalogLoads = 0;
|
let activeCatalogLoads = 0;
|
||||||
const catalogQueue: (() => Promise<void>)[] = [];
|
const catalogQueue: (() => Promise<void>)[] = [];
|
||||||
|
|
||||||
|
|
@ -169,8 +169,8 @@ const HomeScreen = () => {
|
||||||
activeCatalogLoads++;
|
activeCatalogLoads++;
|
||||||
catalogLoader().finally(async () => {
|
catalogLoader().finally(async () => {
|
||||||
activeCatalogLoads--;
|
activeCatalogLoads--;
|
||||||
// Yield to event loop to avoid JS thread starvation
|
// Yield to event loop to avoid JS thread starvation and reduce heating
|
||||||
await new Promise(resolve => setTimeout(resolve, 10));
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
processCatalogQueue(); // Process next in queue
|
processCatalogQueue(); // Process next in queue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -605,14 +605,7 @@ const HomeScreen = () => {
|
||||||
|
|
||||||
// Add memory cleanup on scroll end
|
// Add memory cleanup on scroll end
|
||||||
const handleScrollEnd = useCallback(() => {
|
const handleScrollEnd = useCallback(() => {
|
||||||
// Clear memory cache after scroll settles to free up RAM
|
// No-op; avoid clearing image memory cache here to prevent decode thrash/heating
|
||||||
setTimeout(() => {
|
|
||||||
try {
|
|
||||||
ExpoImage.clearMemoryCache();
|
|
||||||
} catch (error) {
|
|
||||||
// Ignore errors
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Memoize individual section components to prevent re-renders
|
// Memoize individual section components to prevent re-renders
|
||||||
|
|
@ -765,7 +758,6 @@ const HomeScreen = () => {
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
ListHeaderComponent={memoizedHeader}
|
ListHeaderComponent={memoizedHeader}
|
||||||
ListFooterComponent={ListFooterComponent}
|
ListFooterComponent={ListFooterComponent}
|
||||||
onMomentumScrollEnd={handleScrollEnd}
|
|
||||||
onEndReached={handleLoadMoreCatalogs}
|
onEndReached={handleLoadMoreCatalogs}
|
||||||
onEndReachedThreshold={0.6}
|
onEndReachedThreshold={0.6}
|
||||||
scrollEventThrottle={32}
|
scrollEventThrottle={32}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ class ImageCacheService {
|
||||||
private cleanupInterval: NodeJS.Timeout | null = null;
|
private cleanupInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Start cleanup interval every 10 minutes
|
// Start cleanup interval every 30 minutes (less churn)
|
||||||
this.cleanupInterval = setInterval(() => {
|
this.cleanupInterval = setInterval(() => {
|
||||||
this.performCleanup();
|
this.performCleanup();
|
||||||
}, 10 * 60 * 1000);
|
}, 30 * 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,13 +40,13 @@ class ImageCacheService {
|
||||||
// Update access tracking
|
// Update access tracking
|
||||||
cached.accessCount++;
|
cached.accessCount++;
|
||||||
cached.lastAccessed = Date.now();
|
cached.lastAccessed = Date.now();
|
||||||
logger.log(`[ImageCache] Retrieved from cache: ${originalUrl.substring(0, 50)}...`);
|
// Skip verbose logging to reduce CPU load
|
||||||
return cached.localPath;
|
return cached.localPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check memory pressure before adding new entries (more lenient)
|
// Check memory pressure before adding new entries (more lenient)
|
||||||
if (this.cache.size >= this.MAX_CACHE_SIZE * 0.95) {
|
if (this.cache.size >= this.MAX_CACHE_SIZE * 0.95) {
|
||||||
logger.log(`[ImageCache] Skipping cache due to size limit`);
|
// Skip verbose logging to reduce CPU load
|
||||||
return originalUrl;
|
return originalUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ class ImageCacheService {
|
||||||
this.currentMemoryUsage += estimatedSize;
|
this.currentMemoryUsage += estimatedSize;
|
||||||
this.enforceMemoryLimits();
|
this.enforceMemoryLimits();
|
||||||
|
|
||||||
logger.log(`[ImageCache] ✅ NEW CACHE ENTRY: ${originalUrl.substring(0, 50)}... (Cache: ${this.cache.size}/${this.MAX_CACHE_SIZE}, Memory: ${(this.currentMemoryUsage / 1024 / 1024).toFixed(1)}MB)`);
|
// Skip verbose logging to reduce CPU load
|
||||||
return cachedImage.localPath;
|
return cachedImage.localPath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[ImageCache] Failed to cache image:', error);
|
logger.error('[ImageCache] Failed to cache image:', error);
|
||||||
|
|
@ -231,12 +231,7 @@ class ImageCacheService {
|
||||||
this.enforceMemoryLimits();
|
this.enforceMemoryLimits();
|
||||||
this.enforceMaxCacheSize();
|
this.enforceMaxCacheSize();
|
||||||
|
|
||||||
// Clear Expo image memory cache periodically
|
// Avoid clearing Expo's global memory cache to prevent re-decode churn
|
||||||
try {
|
|
||||||
ExpoImage.clearMemoryCache();
|
|
||||||
} catch (error) {
|
|
||||||
// Ignore errors from clearing memory cache
|
|
||||||
}
|
|
||||||
|
|
||||||
const finalSize = this.cache.size;
|
const finalSize = this.cache.size;
|
||||||
const finalMemory = this.currentMemoryUsage;
|
const finalMemory = this.currentMemoryUsage;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue