sub bg fix

This commit is contained in:
tapframe 2025-11-04 20:47:03 +05:30
parent 2ba7c3c057
commit 5fe1db24c1
4 changed files with 55 additions and 17 deletions

View file

@ -56,6 +56,32 @@ const HeroCarousel: React.FC<HeroCarouselProps> = ({ items, loading = false }) =
const scrollX = useSharedValue(0);
const interval = CARD_WIDTH + 16;
// Parallel image prefetch: start fetching banners and logos as soon as data arrives
const itemsToPreload = useMemo(() => data.slice(0, 8), [data]);
useEffect(() => {
if (!itemsToPreload.length) return;
try {
const sources = itemsToPreload.flatMap((it) => {
const result: { uri: string; priority?: any }[] = [];
const bannerOrPoster = it.banner || it.poster;
if (bannerOrPoster) {
result.push({ uri: bannerOrPoster, priority: (FastImage as any).priority?.low });
}
if (it.logo) {
result.push({ uri: it.logo, priority: (FastImage as any).priority?.normal });
}
return result;
});
// de-duplicate by uri
const uniqueSources = Array.from(new Map(sources.map((s) => [s.uri, s])).values());
if (uniqueSources.length && (FastImage as any).preload) {
(FastImage as any).preload(uniqueSources);
}
} catch {
// no-op: prefetch is best-effort
}
}, [itemsToPreload]);
// Comprehensive reset when component mounts/remounts to prevent glitching
useEffect(() => {
scrollX.value = 0;

View file

@ -1,5 +1,5 @@
import React from 'react';
import { View, Text, Dimensions } from 'react-native';
import { View, Text } from 'react-native';
import Svg, { Text as SvgText, TSpan } from 'react-native-svg';
import { styles } from '../utils/playerStyles';
import { SubtitleSegment } from '../utils/playerTypes';
@ -85,13 +85,11 @@ export const CustomSubtitles: React.FC<CustomSubtitlesProps> = ({
const displayFontSize = subtitleSize * inverseScale;
const displayLineHeight = subtitleSize * lineHeightMultiplier * inverseScale;
const svgHeight = lines.length * displayLineHeight;
// Estimate text width to keep background from spanning full screen when using SVG
const windowWidth = Math.min(Dimensions.get('window').width, Dimensions.get('window').height);
const longestLineChars = Math.max(1, ...lines.map(l => l.length));
const estimatedContentWidth = Math.min(
Math.max(100, Math.ceil(longestLineChars * displayFontSize * 0.6)),
Math.max(140, windowWidth - 40)
);
// Roughly estimate text width to size SVG snugly (avoids overly wide background)
const charWidthFactor = 0.48; // even tighter average width per character
const estimatedLineWidths = lines.map(line => Math.max(1, line.length * displayFontSize * charWidthFactor));
const maxEstimatedLineWidth = estimatedLineWidths.length > 0 ? Math.max(...estimatedLineWidths) : displayFontSize * 2;
const svgWidth = Math.max(displayFontSize * 2, Math.ceil(maxEstimatedLineWidth + displayFontSize * 0.25));
// Helper to render formatted segments
const renderFormattedText = (segments: SubtitleSegment[], lineIndex: number, keyPrefix: string, isRTL?: boolean, customLetterSpacing?: number) => {
@ -148,15 +146,18 @@ export const CustomSubtitles: React.FC<CustomSubtitlesProps> = ({
position: 'relative',
alignItems: 'center',
alignSelf: 'center',
maxWidth: windowWidth - 40,
maxWidth: '90%',
paddingHorizontal: 4,
paddingVertical: 4,
transform: [{ scale: inverseScale }],
}
]}>
{useCrispSvgOutline ? (
// Crisp outline using react-native-svg (stroke under, fill on top)
<Svg
width={estimatedContentWidth}
width={svgWidth}
height={svgHeight}
viewBox={`0 0 ${estimatedContentWidth} ${svgHeight}`}
viewBox={`0 0 ${svgWidth} ${svgHeight}`}
preserveAspectRatio="xMidYMax meet"
>
{(() => {
@ -168,10 +169,10 @@ export const CustomSubtitles: React.FC<CustomSubtitlesProps> = ({
if (isRTL) {
// For RTL, always use 'end' anchor to position from right edge
anchor = 'end';
x = estimatedContentWidth;
x = svgWidth;
} else {
anchor = align === 'center' ? 'middle' : align === 'left' ? 'start' : 'end';
x = align === 'center' ? (estimatedContentWidth / 2) : (align === 'left' ? 0 : estimatedContentWidth);
x = align === 'center' ? svgWidth / 2 : (align === 'left' ? 0 : svgWidth);
}
const baseFontSize = displayFontSize;
@ -257,7 +258,6 @@ export const CustomSubtitles: React.FC<CustomSubtitlesProps> = ({
letterSpacing: effectiveLetterSpacing,
fontSize: subtitleSize * inverseScale,
lineHeight: subtitleSize * lineHeightMultiplier * inverseScale,
transform: [{ scale: inverseScale }],
},
shadowStyle,
]}>

View file

@ -668,6 +668,8 @@ export const styles = StyleSheet.create({
customSubtitleWrapper: {
padding: 10,
borderRadius: 5,
alignSelf: 'center',
maxWidth: '90%',
},
customSubtitleText: {
color: 'white',

View file

@ -904,22 +904,32 @@ class LocalScraperService {
}
}
// Normalize media type for plugin compatibility (treat 'series'/'other' as 'tv')
const media: 'movie' | 'tv' = (type === 'series' || type === 'other') ? 'tv' : (type as 'movie' | 'tv');
// Get available scrapers from manifest (respects manifestEnabled)
const availableScrapers = await this.getAvailableScrapers();
const enabledScrapers = availableScrapers
.filter(scraper =>
scraper.enabled &&
scraper.manifestEnabled !== false &&
scraper.supportedTypes.includes(type as 'movie' | 'tv')
scraper.supportedTypes.includes(media)
);
logger.log(`[LocalScraperService] Media normalized '${type}' -> '${media}'. Enabled scrapers for this media: ${enabledScrapers.length}`);
if (enabledScrapers.length > 0) {
try {
logger.log('[LocalScraperService] Enabled scrapers:', enabledScrapers.map(s => s.name).join(', '));
} catch {}
}
if (enabledScrapers.length === 0) {
logger.log('[LocalScraperService] No enabled scrapers found for type:', type);
// No callback needed here since this is after filtering - scrapers weren't added to UI yet
return;
}
logger.log(`[LocalScraperService] Executing ${enabledScrapers.length} scrapers for ${type}:${tmdbId}`, {
logger.log(`[LocalScraperService] Executing ${enabledScrapers.length} scrapers for ${media}:${tmdbId}`, {
scrapers: enabledScrapers.map(s => s.name)
});
@ -928,7 +938,7 @@ class LocalScraperService {
// Execute all enabled scrapers
for (const scraper of enabledScrapers) {
this.executeScraper(scraper, type, tmdbId, season, episode, callback, requestId);
this.executeScraper(scraper, media, tmdbId, season, episode, callback, requestId);
}
}