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:
</Text>
<Text style={[
styles.infoValue,
{ color: currentTheme.colors.highEmphasis }
]}>
{updateInfo.manifest?.id ?
`${updateInfo.manifest.id.substring(0, 8)}...` :
'Latest'
}
<Text
style={[
styles.infoValue,
{ color: currentTheme.colors.highEmphasis }
]}
numberOfLines={1}
ellipsizeMode="middle"
selectable
>
{updateInfo.manifest?.id || 'Latest'}
</Text>
</View>

View file

@ -102,7 +102,7 @@ const OptimizedImage: React.FC<OptimizedImageProps> = ({
if (mountedRef.current) {
setIsVisible(true);
}
}, priority === 'high' ? 100 : priority === 'normal' ? 300 : 500);
}, priority === 'high' ? 200 : priority === 'normal' ? 500 : 1000);
return () => clearTimeout(timer);
}
@ -124,8 +124,8 @@ const OptimizedImage: React.FC<OptimizedImageProps> = ({
}
}, 10000); // 10 second timeout
// Prefetch the image
await ExpoImage.prefetch(cachedUrl);
// Skip prefetch to reduce memory pressure and heating
// await ExpoImage.prefetch(cachedUrl);
if (mountedRef.current) {
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]);
// Development-only performance monitoring
useEffect(() => {
if (__DEV__) {
const startTime = Date.now();
const timer = setTimeout(() => {
const renderTime = Date.now() - startTime;
if (renderTime > 100) {
console.warn(`[HeroSection] Slow render detected: ${renderTime}ms`);
}
}, 0);
return () => clearTimeout(timer);
}
});
// Disabled performance monitoring to reduce CPU overhead in production
// useEffect(() => {
// if (__DEV__) {
// const startTime = Date.now();
// const timer = setTimeout(() => {
// const renderTime = Date.now() - startTime;
// if (renderTime > 100) {
// console.warn(`[HeroSection] Slow render detected: ${renderTime}ms`);
// }
// }, 0);
// return () => clearTimeout(timer);
// }
// });

View file

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

View file

@ -608,6 +608,28 @@ const AddonsScreen = () => {
const [communityLoading, setCommunityLoading] = useState(true);
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(() => {
loadAddons();
loadCommunityAddons();
@ -1129,6 +1151,7 @@ const AddonsScreen = () => {
showsVerticalScrollIndicator={false}
contentInsetAdjustmentBehavior="automatic"
>
{/* Overview Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>OVERVIEW</Text>
@ -1195,6 +1218,65 @@ const AddonsScreen = () => {
{/* Separator */}
<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 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>COMMUNITY ADDONS</Text>

View file

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

View file

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

View file

@ -482,11 +482,11 @@ class CatalogService {
async getContentDetails(type: string, id: string, preferredAddonId?: string): Promise<StreamingContent | null> {
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 lastError = null;
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 2; i++) {
try {
meta = await stremioService.getMetaDetails(type, id, preferredAddonId);
if (meta) break;

View file

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

View file

@ -977,7 +977,7 @@ class LocalScraperService {
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
const scraperSettings = await this.getScraperSettings(scraper.id);
@ -999,7 +999,7 @@ class LocalScraperService {
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) {
logger.error('[LocalScraperService] Scraper', scraper.name, 'failed:', error);

View file

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

View file

@ -17,7 +17,7 @@ interface TraktCollections {
const THIS_WEEK_CACHE_KEY = 'this_week_episodes_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
class RobustCalendarCache {

View file

View file

@ -260,7 +260,7 @@ export class TraktService {
// Rate limiting
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 isProcessingQueue: boolean = false;
@ -272,11 +272,11 @@ export class TraktService {
// Track currently watching sessions to avoid duplicate starts// Sync debouncing
private currentlyWatching: Set<string> = new Set();
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
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)
private readonly DEFAULT_COMPLETION_THRESHOLD = 80; // 80%
@ -356,9 +356,7 @@ export class TraktService {
}
}
if (cleanupCount > 0) {
logger.log(`[TraktService] Cleaned up ${cleanupCount} old tracking entries`);
}
// Skip verbose cleanup logging to reduce CPU load
}
public static getInstance(): TraktService {