many major fixes

This commit is contained in:
tapframe 2025-09-09 00:33:30 +05:30
parent 6bb4d927ed
commit c92dfb149c
14 changed files with 130 additions and 51 deletions

View file

@ -129,14 +129,16 @@ const UpdatePopup: React.FC<UpdatePopupProps> = ({
]}> ]}>
Version: Version:
</Text> </Text>
<Text style={[ <Text
styles.infoValue, style={[
{ color: currentTheme.colors.highEmphasis } styles.infoValue,
]}> { color: currentTheme.colors.highEmphasis }
{updateInfo.manifest?.id ? ]}
`${updateInfo.manifest.id.substring(0, 8)}...` : numberOfLines={1}
'Latest' ellipsizeMode="middle"
} selectable
>
{updateInfo.manifest?.id || 'Latest'}
</Text> </Text>
</View> </View>

View file

@ -102,7 +102,7 @@ const OptimizedImage: React.FC<OptimizedImageProps> = ({
if (mountedRef.current) { if (mountedRef.current) {
setIsVisible(true); setIsVisible(true);
} }
}, priority === 'high' ? 100 : priority === 'normal' ? 300 : 500); }, priority === 'high' ? 200 : priority === 'normal' ? 500 : 1000);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
@ -124,8 +124,8 @@ const OptimizedImage: React.FC<OptimizedImageProps> = ({
} }
}, 10000); // 10 second timeout }, 10000); // 10 second timeout
// Prefetch the image // Skip prefetch to reduce memory pressure and heating
await ExpoImage.prefetch(cachedUrl); // await ExpoImage.prefetch(cachedUrl);
if (mountedRef.current) { if (mountedRef.current) {
setIsLoaded(true); setIsLoaded(true);

View file

@ -1080,19 +1080,19 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
}; };
}, [imageOpacity, imageLoadOpacity, shimmerOpacity, trailerOpacity, thumbnailOpacity, actionButtonsOpacity, titleCardTranslateY, genreOpacity, watchProgressOpacity, buttonsOpacity, buttonsTranslateY, logoOpacity, heroOpacity, heroHeight]); }, [imageOpacity, imageLoadOpacity, shimmerOpacity, trailerOpacity, thumbnailOpacity, actionButtonsOpacity, titleCardTranslateY, genreOpacity, watchProgressOpacity, buttonsOpacity, buttonsTranslateY, logoOpacity, heroOpacity, heroHeight]);
// Development-only performance monitoring // Disabled performance monitoring to reduce CPU overhead in production
useEffect(() => { // useEffect(() => {
if (__DEV__) { // if (__DEV__) {
const startTime = Date.now(); // const startTime = Date.now();
const timer = setTimeout(() => { // const timer = setTimeout(() => {
const renderTime = Date.now() - startTime; // const renderTime = Date.now() - startTime;
if (renderTime > 100) { // if (renderTime > 100) {
console.warn(`[HeroSection] Slow render detected: ${renderTime}ms`); // console.warn(`[HeroSection] Slow render detected: ${renderTime}ms`);
} // }
}, 0); // }, 0);
return () => clearTimeout(timer); // return () => clearTimeout(timer);
} // }
}); // });

View file

@ -557,8 +557,8 @@ export function useFeaturedContent() {
} }
}; };
// Increased rotation interval from 15s to 45s to reduce heating // Further increased rotation interval to 90s to reduce CPU cycles
const intervalId = setInterval(rotateContent, 45000); const intervalId = setInterval(rotateContent, 90000);
return () => clearInterval(intervalId); return () => clearInterval(intervalId);
}, [allFeaturedContent]); }, [allFeaturedContent]);

View file

@ -608,6 +608,28 @@ const AddonsScreen = () => {
const [communityLoading, setCommunityLoading] = useState(true); const [communityLoading, setCommunityLoading] = useState(true);
const [communityError, setCommunityError] = useState<string | null>(null); const [communityError, setCommunityError] = useState<string | null>(null);
// Promotional addon: Nuvio Streams
const PROMO_ADDON_URL = 'https://nuviostreams.hayd.uk/manifest.json';
const promoAddon: ExtendedManifest = {
id: 'org.nuvio.streams',
name: 'Nuvio Streams | Elfhosted',
version: '0.5.0',
description: 'Stremio addon for high-quality streaming links.',
// @ts-ignore - logo not in base manifest type
logo: 'https://raw.githubusercontent.com/tapframe/NuvioStreaming/refs/heads/appstore/assets/titlelogo.png',
types: ['movie', 'series'],
catalogs: [],
behaviorHints: { configurable: true },
// help handleConfigureAddon derive configure URL from the transport
transport: PROMO_ADDON_URL,
} as ExtendedManifest;
const isPromoInstalled = addons.some(a =>
a.id === 'org.nuvio.streams' ||
(typeof a.id === 'string' && a.id.includes('nuviostreams.hayd.uk')) ||
(typeof a.transport === 'string' && a.transport.includes('nuviostreams.hayd.uk')) ||
(typeof (a as any).url === 'string' && (a as any).url.includes('nuviostreams.hayd.uk'))
);
useEffect(() => { useEffect(() => {
loadAddons(); loadAddons();
loadCommunityAddons(); loadCommunityAddons();
@ -1129,6 +1151,7 @@ const AddonsScreen = () => {
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
contentInsetAdjustmentBehavior="automatic" contentInsetAdjustmentBehavior="automatic"
> >
{/* Overview Section */} {/* Overview Section */}
<View style={styles.section}> <View style={styles.section}>
<Text style={styles.sectionTitle}>OVERVIEW</Text> <Text style={styles.sectionTitle}>OVERVIEW</Text>
@ -1195,6 +1218,65 @@ const AddonsScreen = () => {
{/* Separator */} {/* Separator */}
<View style={styles.sectionSeparator} /> <View style={styles.sectionSeparator} />
{/* Promotional Addon Section (hidden if installed) */}
{!isPromoInstalled && (
<View style={styles.section}>
<Text style={styles.sectionTitle}>OFFICIAL ADDON</Text>
<View style={styles.addonList}>
<View style={styles.addonItem}>
<View style={styles.addonHeader}>
{promoAddon.logo ? (
<ExpoImage
source={{ uri: promoAddon.logo }}
style={styles.addonIcon}
contentFit="contain"
/>
) : (
<View style={styles.addonIconPlaceholder}>
<MaterialIcons name="extension" size={22} color={colors.mediumGray} />
</View>
)}
<View style={styles.addonTitleContainer}>
<Text style={styles.addonName}>{promoAddon.name}</Text>
<View style={styles.addonMetaContainer}>
<Text style={styles.addonVersion}>v{promoAddon.version}</Text>
<Text style={styles.addonDot}></Text>
<Text style={styles.addonCategory}>{promoAddon.types?.map(t => t.charAt(0).toUpperCase() + t.slice(1)).join(' • ')}</Text>
</View>
</View>
<View style={styles.addonActions}>
{promoAddon.behaviorHints?.configurable && (
<TouchableOpacity
style={styles.configButton}
onPress={() => handleConfigureAddon(promoAddon, PROMO_ADDON_URL)}
>
<MaterialIcons name="settings" size={20} color={colors.primary} />
</TouchableOpacity>
)}
<TouchableOpacity
style={styles.installButton}
onPress={() => handleAddAddon(PROMO_ADDON_URL)}
disabled={installing}
>
{installing ? (
<ActivityIndicator size="small" color={colors.white} />
) : (
<MaterialIcons name="add" size={20} color={colors.white} />
)}
</TouchableOpacity>
</View>
</View>
<Text style={styles.addonDescription}>
{promoAddon.description}
</Text>
<Text style={[styles.addonDescription, { marginTop: 4, opacity: 0.9 }]}>
Configure and install for full functionality.
</Text>
</View>
</View>
</View>
)}
{/* Community Addons Section */} {/* Community Addons Section */}
<View style={styles.section}> <View style={styles.section}>
<Text style={styles.sectionTitle}>COMMUNITY ADDONS</Text> <Text style={styles.sectionTitle}>COMMUNITY ADDONS</Text>

View file

@ -93,7 +93,7 @@ const UpdateScreen: React.FC = () => {
if (info.isAvailable) { if (info.isAvailable) {
setUpdateStatus('available'); setUpdateStatus('available');
setLastOperation(`Update available: ${info.manifest?.id?.substring(0, 8) || 'unknown'}...`); setLastOperation(`Update available: ${info.manifest?.id || 'unknown'}`);
} else { } else {
setUpdateStatus('idle'); setUpdateStatus('idle');
setLastOperation('No updates available'); setLastOperation('No updates available');
@ -484,8 +484,9 @@ const UpdateScreen: React.FC = () => {
<MaterialIcons name="verified" size={14} color={currentTheme.colors.primary} /> <MaterialIcons name="verified" size={14} color={currentTheme.colors.primary} />
</View> </View>
<Text style={[styles.infoLabel, { color: currentTheme.colors.mediumEmphasis }]}>Current version:</Text> <Text style={[styles.infoLabel, { color: currentTheme.colors.mediumEmphasis }]}>Current version:</Text>
<Text style={[styles.infoValue, { color: currentTheme.colors.highEmphasis }]}> <Text style={[styles.infoValue, { color: currentTheme.colors.highEmphasis }]}
{currentInfo?.manifest?.id ? `${currentInfo.manifest.id.substring(0, 8)}...` : (currentInfo?.isEmbeddedLaunch === false ? 'Unknown' : 'Embedded')} selectable>
{currentInfo?.manifest?.id || (currentInfo?.isEmbeddedLaunch === false ? 'Unknown' : 'Embedded')}
</Text> </Text>
</View> </View>

View file

@ -213,7 +213,7 @@ class SyncService {
} catch (e) { } catch (e) {
// silent // silent
} }
}, 14400000); }, 21600000); // Increased from 4 hours to 6 hours to reduce background CPU
}; };
unsubscribeRealtime = (): void => { unsubscribeRealtime = (): void => {

View file

@ -482,11 +482,11 @@ class CatalogService {
async getContentDetails(type: string, id: string, preferredAddonId?: string): Promise<StreamingContent | null> { async getContentDetails(type: string, id: string, preferredAddonId?: string): Promise<StreamingContent | null> {
try { try {
// Try up to 3 times with increasing delays // Try up to 2 times with increasing delays to reduce CPU load
let meta = null; let meta = null;
let lastError = null; let lastError = null;
for (let i = 0; i < 3; i++) { for (let i = 0; i < 2; i++) {
try { try {
meta = await stremioService.getMetaDetails(type, id, preferredAddonId); meta = await stremioService.getMetaDetails(type, id, preferredAddonId);
if (meta) break; if (meta) break;

View file

@ -189,9 +189,7 @@ class ImageCacheService {
removedCount++; removedCount++;
} }
if (removedCount > 0) { // Skip verbose memory eviction logging to reduce CPU load
logger.log(`[ImageCache] Evicted ${removedCount} entries to free memory. Current usage: ${(this.currentMemoryUsage / 1024 / 1024).toFixed(1)}MB`);
}
} }
/** /**
@ -236,9 +234,7 @@ class ImageCacheService {
const finalSize = this.cache.size; const finalSize = this.cache.size;
const finalMemory = this.currentMemoryUsage; const finalMemory = this.currentMemoryUsage;
if (initialSize !== finalSize || Math.abs(initialMemory - finalMemory) > 1024 * 1024) { // Skip verbose cleanup logging to reduce CPU load
logger.log(`[ImageCache] Cleanup completed: ${initialSize}${finalSize} entries, ${(initialMemory / 1024 / 1024).toFixed(1)}${(finalMemory / 1024 / 1024).toFixed(1)}MB`);
}
} }
/** /**

View file

@ -977,7 +977,7 @@ class LocalScraperService {
throw new Error(`No code found for scraper ${scraper.id}`); throw new Error(`No code found for scraper ${scraper.id}`);
} }
logger.log('[LocalScraperService] Executing scraper:', scraper.name); // Skip verbose logging to reduce CPU load
// Load per-scraper settings // Load per-scraper settings
const scraperSettings = await this.getScraperSettings(scraper.id); const scraperSettings = await this.getScraperSettings(scraper.id);
@ -999,7 +999,7 @@ class LocalScraperService {
callback(streams, scraper.id, scraper.name, null); callback(streams, scraper.id, scraper.name, null);
} }
logger.log('[LocalScraperService] Scraper', scraper.name, 'returned', streams.length, 'streams'); // Skip verbose logging to reduce CPU load
} catch (error) { } catch (error) {
logger.error('[LocalScraperService] Scraper', scraper.name, 'failed:', error); logger.error('[LocalScraperService] Scraper', scraper.name, 'failed:', error);

View file

@ -285,14 +285,14 @@ class NotificationService {
// Setup background sync for notifications // Setup background sync for notifications
private setupBackgroundSync(): void { private setupBackgroundSync(): void {
// Sync notifications every 6 hours // Sync notifications every 12 hours to reduce background CPU usage
this.backgroundSyncInterval = setInterval(async () => { this.backgroundSyncInterval = setInterval(async () => {
if (this.settings.enabled) { if (this.settings.enabled) {
// Reduced logging verbosity // Reduced logging verbosity
// logger.log('[NotificationService] Running background notification sync'); // logger.log('[NotificationService] Running background notification sync');
await this.performBackgroundSync(); await this.performBackgroundSync();
} }
}, 6 * 60 * 60 * 1000); // 6 hours }, 12 * 60 * 60 * 1000); // 12 hours
} }
// Setup app state handling for foreground sync // Setup app state handling for foreground sync
@ -326,8 +326,8 @@ class NotificationService {
for (const series of seriesItems) { for (const series of seriesItems) {
await this.updateNotificationsForSeries(series.id); await this.updateNotificationsForSeries(series.id);
// Small delay to prevent overwhelming the API // Longer delay to prevent overwhelming the API and reduce heating
await new Promise(resolve => setTimeout(resolve, 100)); await new Promise(resolve => setTimeout(resolve, 500));
} }
// Reduced logging verbosity // Reduced logging verbosity

View file

@ -17,7 +17,7 @@ interface TraktCollections {
const THIS_WEEK_CACHE_KEY = 'this_week_episodes_cache'; const THIS_WEEK_CACHE_KEY = 'this_week_episodes_cache';
const CALENDAR_CACHE_KEY = 'calendar_data_cache'; const CALENDAR_CACHE_KEY = 'calendar_data_cache';
const CACHE_DURATION_MS = 15 * 60 * 1000; // 15 minutes const CACHE_DURATION_MS = 30 * 60 * 1000; // 30 minutes (increased to reduce API calls)
const ERROR_CACHE_DURATION_MS = 5 * 60 * 1000; // 5 minutes for error recovery const ERROR_CACHE_DURATION_MS = 5 * 60 * 1000; // 5 minutes for error recovery
class RobustCalendarCache { class RobustCalendarCache {

View file

View file

@ -260,7 +260,7 @@ export class TraktService {
// Rate limiting // Rate limiting
private lastApiCall: number = 0; private lastApiCall: number = 0;
private readonly MIN_API_INTERVAL = 1000; // Minimum 1 second between API calls private readonly MIN_API_INTERVAL = 2000; // Minimum 2 seconds between API calls (reduce heating)
private requestQueue: Array<() => Promise<any>> = []; private requestQueue: Array<() => Promise<any>> = [];
private isProcessingQueue: boolean = false; private isProcessingQueue: boolean = false;
@ -272,11 +272,11 @@ export class TraktService {
// Track currently watching sessions to avoid duplicate starts// Sync debouncing // Track currently watching sessions to avoid duplicate starts// Sync debouncing
private currentlyWatching: Set<string> = new Set(); private currentlyWatching: Set<string> = new Set();
private lastSyncTimes: Map<string, number> = new Map(); private lastSyncTimes: Map<string, number> = new Map();
private readonly SYNC_DEBOUNCE_MS = 1000; // 1 second for immediate sync private readonly SYNC_DEBOUNCE_MS = 15000; // 15 seconds to align with player save interval
// Debounce for stop calls // Debounce for stop calls
private lastStopCalls: Map<string, number> = new Map(); private lastStopCalls: Map<string, number> = new Map();
private readonly STOP_DEBOUNCE_MS = 1000; // 1 second debounce for immediate stop calls private readonly STOP_DEBOUNCE_MS = 3000; // 3 seconds to avoid duplicate stop calls
// Default completion threshold (overridden by user settings) // Default completion threshold (overridden by user settings)
private readonly DEFAULT_COMPLETION_THRESHOLD = 80; // 80% private readonly DEFAULT_COMPLETION_THRESHOLD = 80; // 80%
@ -356,9 +356,7 @@ export class TraktService {
} }
} }
if (cleanupCount > 0) { // Skip verbose cleanup logging to reduce CPU load
logger.log(`[TraktService] Cleaned up ${cleanupCount} old tracking entries`);
}
} }
public static getInstance(): TraktService { public static getInstance(): TraktService {