diff --git a/packages/addon/src/addon.ts b/packages/addon/src/addon.ts index 55af8bec..5b2b5e2a 100644 --- a/packages/addon/src/addon.ts +++ b/packages/addon/src/addon.ts @@ -740,6 +740,29 @@ export class AIOStreams { (resolution) => resolution[b.resolution] ) ); + } else if (field === 'regexSort') { + if (!this.config.regexSortPattern) return 0; + + try { + const regex = new RegExp(this.config.regexSortPattern); + const aMatch = a.filename ? regex.test(a.filename) : false; + const bMatch = b.filename ? regex.test(b.filename) : false; + + // If both match or both don't match, they are equal + if ((aMatch && bMatch) || (!aMatch && !bMatch)) return 0; + + // If one matches and the other doesn't, use direction to determine order + const direction = this.config.sortBy.find((sort) => Object.keys(sort)[0] === 'regexSort')?.direction; + if (direction === 'asc') { + // In ascending order, matching files come last + return aMatch ? 1 : -1; + } else { + // In descending order, matching files come first + return aMatch ? -1 : 1; + } + } catch (e) { + return 0; + } } else if (field === 'cached') { let aCanbeCached = a.provider; let bCanbeCached = b.provider; diff --git a/packages/addon/src/config.ts b/packages/addon/src/config.ts index 55eaccf7..547bd16e 100644 --- a/packages/addon/src/config.ts +++ b/packages/addon/src/config.ts @@ -465,5 +465,25 @@ export function validateConfig( } } + if (config.regexSortPattern) { + if (!config.apiKey) { + return createResponse( + false, + 'missingApiKey', + 'Regex sorting requires an API key to be set' + ); + } + + try { + new RegExp(config.regexSortPattern); + } catch (e) { + return createResponse( + false, + 'invalidRegexSortPattern', + 'Invalid regex sort pattern' + ); + } + } + return createResponse(true, null, null); } diff --git a/packages/frontend/src/app/configure/page.tsx b/packages/frontend/src/app/configure/page.tsx index 0ae4246b..211642b5 100644 --- a/packages/frontend/src/app/configure/page.tsx +++ b/packages/frontend/src/app/configure/page.tsx @@ -118,6 +118,7 @@ const defaultSortCriteria: SortBy[] = [ { quality: false }, { seeders: false, direction: 'desc' }, { addon: false }, + { regexSort: false, direction: 'desc' }, ]; const defaultResolutions: Resolution[] = [ @@ -219,6 +220,7 @@ export default function Configure() { excludePattern?: string; includePattern?: string; }>({}); + const [regexSortPattern, setRegexSortPattern] = useState(''); useEffect(() => { // get config from the server @@ -284,6 +286,7 @@ export default function Configure() { excludePattern: regexFilters.excludePattern || undefined, includePattern: regexFilters.includePattern || undefined } : undefined, + regexSortPattern: regexSortPattern, }; return config; }; @@ -553,6 +556,7 @@ export default function Configure() { })) || [] ); setRegexFilters(decodedConfig.regexFilters || {}); + setRegexSortPattern(decodedConfig.regexSortPattern || ''); setServices(loadValidServices(decodedConfig.services)); setMaxMovieSize( @@ -1064,6 +1068,27 @@ export default function Configure() { )} + {showApiKeyInput && ( +
+

Regex Sort Pattern

+

+ Enter a regex pattern to sort streams. This will sort streams based on whether their filename matches the pattern. Matching files will come first in descending order, and last in ascending order. +

+ {/*
*/} + setRegexSortPattern(e.target.value)} + placeholder="Example: \b(3L|BiZKiT|BLURANiUM)" + style={{ + width: '97.5%', + padding: '5px', + marginLeft: '5px', + }} + className={styles.input} + /> +
+ )}
diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 7f5166ce..6764bd03 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -181,6 +181,7 @@ export interface Config { enabled: boolean; credentials: { [key: string]: string }; }[]; + regexSortPattern?: string; } interface BaseOptionDetail {