mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-19 07:42:09 +00:00
metasceen update
This commit is contained in:
parent
c54ea1d591
commit
be2435db27
4 changed files with 88 additions and 25 deletions
|
|
@ -208,8 +208,8 @@ const TabletStreamsLayout: React.FC<TabletStreamsLayoutProps> = ({
|
|||
duration: 600,
|
||||
easing: Easing.out(Easing.cubic)
|
||||
}));
|
||||
} else if (!backdropSource?.uri) {
|
||||
// No backdrop available, animate content panels immediately
|
||||
} else if (!backdropSource?.uri || backdropError) {
|
||||
// No backdrop available OR backdrop failed to load - animate content panels immediately
|
||||
leftPanelOpacity.value = withTiming(1, {
|
||||
duration: 600,
|
||||
easing: Easing.out(Easing.cubic)
|
||||
|
|
@ -228,7 +228,7 @@ const TabletStreamsLayout: React.FC<TabletStreamsLayoutProps> = ({
|
|||
easing: Easing.out(Easing.cubic)
|
||||
}));
|
||||
}
|
||||
}, [backdropSource?.uri, backdropLoaded]);
|
||||
}, [backdropSource?.uri, backdropLoaded, backdropError]);
|
||||
|
||||
// Reset animation when episode changes
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -275,8 +275,14 @@ const LibraryScreen = () => {
|
|||
try {
|
||||
const items = await catalogService.getLibraryItems();
|
||||
|
||||
logger.log(`[LibraryScreen] Loaded ${items.length} library items`);
|
||||
|
||||
if (items.length === 0) {
|
||||
logger.warn('[LibraryScreen] Library is empty - this might indicate a loading issue');
|
||||
}
|
||||
|
||||
// Sort by date added (most recent first)
|
||||
const sortedItems = items.sort((a, b) => {
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
const timeA = (a as any).addedToLibraryAt || 0;
|
||||
const timeB = (b as any).addedToLibraryAt || 0;
|
||||
return timeB - timeA; // Descending order (newest first)
|
||||
|
|
@ -309,8 +315,10 @@ const LibraryScreen = () => {
|
|||
|
||||
// Subscribe to library updates
|
||||
const unsubscribe = catalogService.subscribeToLibraryUpdates(async (items) => {
|
||||
logger.log(`[LibraryScreen] Library update received with ${items.length} items`);
|
||||
|
||||
// Sort by date added (most recent first)
|
||||
const sortedItems = items.sort((a, b) => {
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
const timeA = (a as any).addedToLibraryAt || 0;
|
||||
const timeB = (b as any).addedToLibraryAt || 0;
|
||||
return timeB - timeA; // Descending order (newest first)
|
||||
|
|
@ -728,7 +736,7 @@ const LibraryScreen = () => {
|
|||
}
|
||||
|
||||
// Sort by last watched/added date (most recent first) using raw timestamps
|
||||
return items.sort((a, b) => {
|
||||
return [...items].sort((a, b) => {
|
||||
const dateA = a.lastWatched ? new Date(a.lastWatched).getTime() : 0;
|
||||
const dateB = b.lastWatched ? new Date(b.lastWatched).getTime() : 0;
|
||||
return dateB - dateA;
|
||||
|
|
|
|||
|
|
@ -1632,6 +1632,41 @@ export const StreamsScreen = () => {
|
|||
|
||||
// Helper to create gradient colors from dominant color
|
||||
const createGradientColors = useCallback((baseColor: string | null): [string, string, string, string, string] => {
|
||||
// Always use black gradient when backdrop is enabled
|
||||
if (settings.enableStreamsBackdrop) {
|
||||
return ['rgba(0,0,0,0)', 'rgba(0,0,0,0.3)', 'rgba(0,0,0,0.6)', 'rgba(0,0,0,0.85)', 'rgba(0,0,0,0.95)'];
|
||||
}
|
||||
|
||||
// When backdrop is disabled, use theme background gradient
|
||||
const themeBg = colors.darkBackground;
|
||||
|
||||
// Handle hex color format (e.g., #1a1a1a)
|
||||
if (themeBg.startsWith('#')) {
|
||||
const r = parseInt(themeBg.substr(1, 2), 16);
|
||||
const g = parseInt(themeBg.substr(3, 2), 16);
|
||||
const b = parseInt(themeBg.substr(5, 2), 16);
|
||||
return [
|
||||
`rgba(${r},${g},${b},0)`,
|
||||
`rgba(${r},${g},${b},0.3)`,
|
||||
`rgba(${r},${g},${b},0.6)`,
|
||||
`rgba(${r},${g},${b},0.85)`,
|
||||
`rgba(${r},${g},${b},0.95)`,
|
||||
];
|
||||
}
|
||||
|
||||
// Handle rgb color format (e.g., rgb(26, 26, 26))
|
||||
const rgbMatch = themeBg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (rgbMatch) {
|
||||
const [, r, g, b] = rgbMatch;
|
||||
return [
|
||||
`rgba(${r},${g},${b},0)`,
|
||||
`rgba(${r},${g},${b},0.3)`,
|
||||
`rgba(${r},${g},${b},0.6)`,
|
||||
`rgba(${r},${g},${b},0.85)`,
|
||||
`rgba(${r},${g},${b},0.95)`,
|
||||
];
|
||||
}
|
||||
|
||||
if (!baseColor || baseColor === '#1a1a1a') {
|
||||
// Fallback to black gradient with stronger bottom edge
|
||||
return ['rgba(0,0,0,0)', 'rgba(0,0,0,0.3)', 'rgba(0,0,0,0.6)', 'rgba(0,0,0,0.85)', 'rgba(0,0,0,0.95)'];
|
||||
|
|
@ -1650,7 +1685,7 @@ export const StreamsScreen = () => {
|
|||
`rgba(${r},${g},${b},0.85)`,
|
||||
`rgba(${r},${g},${b},0.95)`,
|
||||
];
|
||||
}, []);
|
||||
}, [settings.enableStreamsBackdrop, colors.darkBackground]);
|
||||
|
||||
const gradientColors = useMemo(() =>
|
||||
createGradientColors(dominantColor),
|
||||
|
|
@ -1805,7 +1840,7 @@ export const StreamsScreen = () => {
|
|||
<AndroidBlurView
|
||||
blurAmount={15}
|
||||
blurRadius={8}
|
||||
overlayColor={"rgba(0,0,0,0.8)"}
|
||||
overlayColor={"rgba(0,0,0,0.85)"}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -1819,10 +1854,7 @@ export const StreamsScreen = () => {
|
|||
{Platform.OS === 'ios' && (
|
||||
<View style={[
|
||||
StyleSheet.absoluteFill,
|
||||
{ backgroundColor: dominantColor && dominantColor !== '#1a1a1a'
|
||||
? `${dominantColor}CC` // Add 80% opacity (CC in hex)
|
||||
: 'rgba(0,0,0,0.8)'
|
||||
}
|
||||
{ backgroundColor: 'rgba(0,0,0,0.8)' }
|
||||
]} />
|
||||
)}
|
||||
</View>
|
||||
|
|
@ -1923,19 +1955,19 @@ export const StreamsScreen = () => {
|
|||
)}
|
||||
|
||||
{/* Gradient overlay to blend hero section with streams container */}
|
||||
{metadata?.videos && metadata.videos.length > 1 && selectedEpisode && settings.enableStreamsBackdrop && (
|
||||
{metadata?.videos && metadata.videos.length > 1 && selectedEpisode && (
|
||||
<View style={styles.heroBlendOverlay}>
|
||||
<LinearGradient
|
||||
colors={[
|
||||
dominantColor && dominantColor !== '#1a1a1a'
|
||||
? `rgba(${parseInt(dominantColor.substr(1, 2), 16)},${parseInt(dominantColor.substr(3, 2), 16)},${parseInt(dominantColor.substr(5, 2), 16)},0.95)`
|
||||
: 'rgba(0,0,0,0.95)',
|
||||
dominantColor && dominantColor !== '#1a1a1a'
|
||||
? `rgba(${parseInt(dominantColor.substr(1, 2), 16)},${parseInt(dominantColor.substr(3, 2), 16)},${parseInt(dominantColor.substr(5, 2), 16)},0.7)`
|
||||
: 'rgba(0,0,0,0.7)',
|
||||
colors={settings.enableStreamsBackdrop ? [
|
||||
'rgba(0,0,0,0.98)',
|
||||
'rgba(0,0,0,0.85)',
|
||||
'transparent'
|
||||
] : [
|
||||
colors.darkBackground,
|
||||
colors.darkBackground,
|
||||
'transparent'
|
||||
]}
|
||||
locations={[0, 0.5, 1]}
|
||||
locations={[0, 0.4, 1]}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
</View>
|
||||
|
|
@ -2388,6 +2420,7 @@ const createStyles = (colors: any) => StyleSheet.create({
|
|||
position: 'relative',
|
||||
backgroundColor: 'transparent',
|
||||
pointerEvents: 'box-none',
|
||||
zIndex: 1,
|
||||
},
|
||||
streamsHeroBackground: {
|
||||
width: '100%',
|
||||
|
|
@ -2728,10 +2761,10 @@ const createStyles = (colors: any) => StyleSheet.create({
|
|||
},
|
||||
heroBlendOverlay: {
|
||||
position: 'absolute',
|
||||
top: 220, // Height of hero container
|
||||
top: 140, // Start at ~64% of hero section, giving 80px of blend within hero
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 60, // Extend gradient 60px into streams area
|
||||
height: Platform.OS === 'android' ? 150 : 180, // Reduce gradient area on Android
|
||||
zIndex: 0,
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -157,10 +157,23 @@ class CatalogService {
|
|||
private libraryRemoveListeners: ((type: string, id: string) => void)[] = [];
|
||||
|
||||
private constructor() {
|
||||
this.initializeScope();
|
||||
this.loadLibrary();
|
||||
this.loadRecentContent();
|
||||
}
|
||||
|
||||
private async initializeScope(): Promise<void> {
|
||||
try {
|
||||
const currentScope = await AsyncStorage.getItem('@user:current');
|
||||
if (!currentScope) {
|
||||
await AsyncStorage.setItem('@user:current', 'local');
|
||||
logger.log('[CatalogService] Initialized @user:current scope to "local"');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[CatalogService] Failed to initialize scope:', error);
|
||||
}
|
||||
}
|
||||
|
||||
static getInstance(): CatalogService {
|
||||
if (!CatalogService.instance) {
|
||||
CatalogService.instance = new CatalogService();
|
||||
|
|
@ -182,9 +195,16 @@ class CatalogService {
|
|||
}
|
||||
if (storedLibrary) {
|
||||
this.library = JSON.parse(storedLibrary);
|
||||
logger.log(`[CatalogService] Library loaded successfully with ${Object.keys(this.library).length} items from scope: ${scope}`);
|
||||
} else {
|
||||
logger.log(`[CatalogService] No library data found for scope: ${scope}`);
|
||||
this.library = {};
|
||||
}
|
||||
// Ensure @user:current is set to prevent future scope issues
|
||||
await AsyncStorage.setItem('@user:current', scope);
|
||||
} catch (error: any) {
|
||||
logger.error('Failed to load library:', error);
|
||||
this.library = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,8 +212,10 @@ class CatalogService {
|
|||
try {
|
||||
const scope = (await AsyncStorage.getItem('@user:current')) || 'local';
|
||||
const scopedKey = `@user:${scope}:stremio-library`;
|
||||
await AsyncStorage.setItem(scopedKey, JSON.stringify(this.library));
|
||||
await AsyncStorage.setItem(this.LEGACY_LIBRARY_KEY, JSON.stringify(this.library));
|
||||
const libraryData = JSON.stringify(this.library);
|
||||
await AsyncStorage.setItem(scopedKey, libraryData);
|
||||
await AsyncStorage.setItem(this.LEGACY_LIBRARY_KEY, libraryData);
|
||||
logger.log(`[CatalogService] Library saved successfully with ${Object.keys(this.library).length} items to scope: ${scope}`);
|
||||
} catch (error: any) {
|
||||
logger.error('Failed to save library:', error);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue