mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-29 13:59:58 +00:00
added sorting
This commit is contained in:
parent
a7e7c86f18
commit
f8192f53ab
3 changed files with 127 additions and 9 deletions
|
|
@ -42,6 +42,7 @@ export interface AppSettings {
|
|||
scraperTimeout: number; // Timeout for scraper execution in seconds
|
||||
enableScraperUrlValidation: boolean; // Enable/disable URL validation for scrapers
|
||||
streamDisplayMode: 'separate' | 'grouped'; // How to display streaming links - separately by provider or grouped under one name
|
||||
streamSortMode: 'scraper-then-quality' | 'quality-then-scraper'; // How to sort streams - by scraper first or quality first
|
||||
// Quality filtering settings
|
||||
excludedQualities: string[]; // Array of quality strings to exclude (e.g., ['2160p', '4K', '1080p', '720p'])
|
||||
}
|
||||
|
|
@ -68,6 +69,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
|||
scraperTimeout: 60, // 60 seconds timeout
|
||||
enableScraperUrlValidation: true, // Enable URL validation by default
|
||||
streamDisplayMode: 'separate', // Default to separate display by provider
|
||||
streamSortMode: 'scraper-then-quality', // Default to current behavior (scraper first, then quality)
|
||||
// Quality filtering defaults
|
||||
excludedQualities: [], // No qualities excluded by default
|
||||
};
|
||||
|
|
|
|||
|
|
@ -796,12 +796,34 @@ const PluginsScreen: React.FC = () => {
|
|||
</View>
|
||||
<Switch
|
||||
value={settings.streamDisplayMode === 'grouped'}
|
||||
onValueChange={(value) => updateSetting('streamDisplayMode', value ? 'grouped' : 'separate')}
|
||||
onValueChange={(value) => {
|
||||
updateSetting('streamDisplayMode', value ? 'grouped' : 'separate');
|
||||
// Auto-disable quality sorting when grouping is disabled
|
||||
if (!value && settings.streamSortMode === 'quality-then-scraper') {
|
||||
updateSetting('streamSortMode', 'scraper-then-quality');
|
||||
}
|
||||
}}
|
||||
trackColor={{ false: colors.elevation3, true: colors.primary }}
|
||||
thumbColor={settings.streamDisplayMode === 'grouped' ? colors.white : '#f4f3f4'}
|
||||
disabled={!settings.enableLocalScrapers}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.settingRow}>
|
||||
<View style={styles.settingInfo}>
|
||||
<Text style={[styles.settingTitle, (!settings.enableLocalScrapers || settings.streamDisplayMode !== 'grouped') && styles.disabledText]}>Sort by Quality First</Text>
|
||||
<Text style={[styles.settingDescription, (!settings.enableLocalScrapers || settings.streamDisplayMode !== 'grouped') && styles.disabledText]}>
|
||||
When enabled, streams are sorted by quality first, then by scraper. When disabled, streams are sorted by scraper first, then by quality. Only available when grouping is enabled.
|
||||
</Text>
|
||||
</View>
|
||||
<Switch
|
||||
value={settings.streamSortMode === 'quality-then-scraper'}
|
||||
onValueChange={(value) => updateSetting('streamSortMode', value ? 'quality-then-scraper' : 'scraper-then-quality')}
|
||||
trackColor={{ false: colors.elevation3, true: colors.primary }}
|
||||
thumbColor={settings.streamSortMode === 'quality-then-scraper' ? colors.white : '#f4f3f4'}
|
||||
disabled={!settings.enableLocalScrapers || settings.streamDisplayMode !== 'grouped'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Quality Filtering */}
|
||||
|
|
|
|||
|
|
@ -537,6 +537,84 @@ export const StreamsScreen = () => {
|
|||
});
|
||||
}, [settings.excludedQualities]);
|
||||
|
||||
// Helper function to sort streams based on user preference
|
||||
const sortStreams = useCallback((streams: Stream[]) => {
|
||||
const installedAddons = stremioService.getInstalledAddons();
|
||||
|
||||
// Helper function to extract quality as number
|
||||
const getQualityNumeric = (title: string | undefined): number => {
|
||||
if (!title) return 0;
|
||||
|
||||
// Check for 4K first (treat as 2160p)
|
||||
if (/\b4k\b/i.test(title)) {
|
||||
return 2160;
|
||||
}
|
||||
|
||||
const matchWithP = title.match(/(\d+)p/i);
|
||||
if (matchWithP) return parseInt(matchWithP[1], 10);
|
||||
|
||||
const qualityPatterns = [
|
||||
/\b(240|360|480|720|1080|1440|2160|4320|8000)\b/i
|
||||
];
|
||||
|
||||
for (const pattern of qualityPatterns) {
|
||||
const match = title.match(pattern);
|
||||
if (match) {
|
||||
const quality = parseInt(match[1], 10);
|
||||
if (quality >= 240 && quality <= 8000) return quality;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Provider priority (higher number = higher priority)
|
||||
const getProviderPriority = (stream: Stream): number => {
|
||||
const addonId = stream.addonId || stream.addonName || '';
|
||||
const addonIndex = installedAddons.findIndex(addon => addon.id === addonId);
|
||||
|
||||
if (addonIndex !== -1) {
|
||||
// Higher priority for addons installed earlier (reverse index)
|
||||
return 50 - addonIndex;
|
||||
}
|
||||
|
||||
return 0; // Unknown providers get lowest priority
|
||||
};
|
||||
|
||||
return [...streams].sort((a, b) => {
|
||||
const qualityA = getQualityNumeric(a.name || a.title);
|
||||
const qualityB = getQualityNumeric(b.name || b.title);
|
||||
const providerPriorityA = getProviderPriority(a);
|
||||
const providerPriorityB = getProviderPriority(b);
|
||||
const isCachedA = a.behaviorHints?.cached || false;
|
||||
const isCachedB = b.behaviorHints?.cached || false;
|
||||
|
||||
// Always prioritize cached/debrid streams first
|
||||
if (isCachedA !== isCachedB) {
|
||||
return isCachedA ? -1 : 1;
|
||||
}
|
||||
|
||||
if (settings.streamSortMode === 'quality-then-scraper') {
|
||||
// Sort by quality first, then by provider
|
||||
if (qualityA !== qualityB) {
|
||||
return qualityB - qualityA; // Higher quality first
|
||||
}
|
||||
if (providerPriorityA !== providerPriorityB) {
|
||||
return providerPriorityB - providerPriorityA; // Better provider first
|
||||
}
|
||||
} else {
|
||||
// Default: Sort by provider first, then by quality
|
||||
if (providerPriorityA !== providerPriorityB) {
|
||||
return providerPriorityB - providerPriorityA; // Better provider first
|
||||
}
|
||||
if (qualityA !== qualityB) {
|
||||
return qualityB - qualityA; // Higher quality first
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}, [settings.excludedQualities, settings.streamSortMode]);
|
||||
|
||||
// Function to determine the best stream based on quality, provider priority, and other factors
|
||||
const getBestStream = useCallback((streamsData: typeof groupedStreams): Stream | null => {
|
||||
if (!streamsData || Object.keys(streamsData).length === 0) {
|
||||
|
|
@ -546,6 +624,12 @@ export const StreamsScreen = () => {
|
|||
// Helper function to extract quality as number
|
||||
const getQualityNumeric = (title: string | undefined): number => {
|
||||
if (!title) return 0;
|
||||
|
||||
// Check for 4K first (treat as 2160p)
|
||||
if (/\b4k\b/i.test(title)) {
|
||||
return 2160;
|
||||
}
|
||||
|
||||
const matchWithP = title.match(/(\d+)p/i);
|
||||
if (matchWithP) return parseInt(matchWithP[1], 10);
|
||||
|
||||
|
|
@ -997,16 +1081,17 @@ export const StreamsScreen = () => {
|
|||
filteredEntries.forEach(([addonId, { addonName, streams: providerStreams }]) => {
|
||||
const isInstalledAddon = installedAddons.some(addon => addon.id === addonId);
|
||||
|
||||
// Apply quality filtering to streams
|
||||
// Apply quality filtering and sorting to streams
|
||||
const filteredStreams = filterStreamsByQuality(providerStreams);
|
||||
const sortedStreams = sortStreams(filteredStreams);
|
||||
|
||||
if (isInstalledAddon) {
|
||||
addonStreams.push(...filteredStreams);
|
||||
addonStreams.push(...sortedStreams);
|
||||
if (!addonNames.includes(addonName)) {
|
||||
addonNames.push(addonName);
|
||||
}
|
||||
} else {
|
||||
pluginStreams.push(...filteredStreams);
|
||||
pluginStreams.push(...sortedStreams);
|
||||
if (!pluginNames.includes(addonName)) {
|
||||
pluginNames.push(addonName);
|
||||
}
|
||||
|
|
@ -1015,17 +1100,25 @@ export const StreamsScreen = () => {
|
|||
|
||||
const sections = [];
|
||||
if (addonStreams.length > 0) {
|
||||
// Apply final sorting to the combined addon streams for quality-first mode
|
||||
const finalSortedAddonStreams = settings.streamSortMode === 'quality-then-scraper' ?
|
||||
sortStreams(addonStreams) : addonStreams;
|
||||
|
||||
sections.push({
|
||||
title: addonNames.join(', '),
|
||||
addonId: 'grouped-addons',
|
||||
data: addonStreams
|
||||
data: finalSortedAddonStreams
|
||||
});
|
||||
}
|
||||
if (pluginStreams.length > 0) {
|
||||
// Apply final sorting to the combined plugin streams for quality-first mode
|
||||
const finalSortedPluginStreams = settings.streamSortMode === 'quality-then-scraper' ?
|
||||
sortStreams(pluginStreams) : pluginStreams;
|
||||
|
||||
sections.push({
|
||||
title: localScraperService.getRepositoryName(),
|
||||
addonId: 'grouped-plugins',
|
||||
data: pluginStreams
|
||||
data: finalSortedPluginStreams
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1033,17 +1126,18 @@ export const StreamsScreen = () => {
|
|||
} else {
|
||||
// Use separate sections for each provider (current behavior)
|
||||
return filteredEntries.map(([addonId, { addonName, streams: providerStreams }]) => {
|
||||
// Apply quality filtering to streams
|
||||
// Apply quality filtering and sorting to streams
|
||||
const filteredStreams = filterStreamsByQuality(providerStreams);
|
||||
const sortedStreams = sortStreams(filteredStreams);
|
||||
|
||||
return {
|
||||
title: addonName,
|
||||
addonId,
|
||||
data: filteredStreams
|
||||
data: sortedStreams
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [selectedProvider, type, episodeStreams, groupedStreams, settings.streamDisplayMode, filterStreamsByQuality]);
|
||||
}, [selectedProvider, type, episodeStreams, groupedStreams, settings.streamDisplayMode, filterStreamsByQuality, sortStreams]);
|
||||
|
||||
const episodeImage = useMemo(() => {
|
||||
if (episodeThumbnail) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue