mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat: implement regex-based stream filtering with API key protection (#155)
This commit is contained in:
@@ -106,6 +106,8 @@ export class AIOStreams {
|
||||
sizeFilters: 0,
|
||||
duplicateStreams: 0,
|
||||
streamLimiters: 0,
|
||||
excludeRegex: 0,
|
||||
requiredRegex: 0,
|
||||
};
|
||||
|
||||
logger.info(
|
||||
@@ -341,6 +343,32 @@ export class AIOStreams {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// apply regex filters if API key is set
|
||||
if (this.config.apiKey && this.config.regexFilters) {
|
||||
const { excludePattern, includePattern } = this.config.regexFilters;
|
||||
|
||||
if (excludePattern) {
|
||||
const regexExclude = new RegExp(excludePattern, 'i');
|
||||
if (parsedStream.filename && regexExclude.test(parsedStream.filename)) {
|
||||
skipReasons.excludeRegex++;
|
||||
return false;
|
||||
}
|
||||
if (parsedStream.indexers && regexExclude.test(parsedStream.indexers)) {
|
||||
skipReasons.excludeRegex++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (includePattern) {
|
||||
const regexInclude = new RegExp(includePattern, 'i');
|
||||
if (!((parsedStream.filename && regexInclude.test(parsedStream.filename)) || (parsedStream.indexers && regexInclude.test(parsedStream.indexers)))) {
|
||||
skipReasons.requiredRegex++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -431,5 +431,39 @@ export function validateConfig(
|
||||
}
|
||||
});
|
||||
|
||||
if (config.regexFilters) {
|
||||
if (!config.apiKey) {
|
||||
return createResponse(
|
||||
false,
|
||||
'missingApiKey',
|
||||
'Regex filtering requires an API key to be set'
|
||||
);
|
||||
}
|
||||
|
||||
if (config.regexFilters.excludePattern) {
|
||||
try {
|
||||
new RegExp(config.regexFilters.excludePattern);
|
||||
} catch (e) {
|
||||
return createResponse(
|
||||
false,
|
||||
'invalidExcludeRegex',
|
||||
'Invalid exclude regex pattern'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.regexFilters.includePattern) {
|
||||
try {
|
||||
new RegExp(config.regexFilters.includePattern);
|
||||
} catch (e) {
|
||||
return createResponse(
|
||||
false,
|
||||
'invalidIncludeRegex',
|
||||
'Invalid include regex pattern'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return createResponse(true, null, null);
|
||||
}
|
||||
|
||||
@@ -231,3 +231,20 @@
|
||||
); /* Slight background highlight on click */
|
||||
border-color: rgba(255, 255, 255, 0.815); /* More visible border on click */
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin: 4px 0;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.helpText {
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
font-family: monospace;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
@@ -215,6 +215,10 @@ export default function Configure() {
|
||||
);
|
||||
const [showApiKeyInput, setShowApiKeyInput] = useState<boolean>(false);
|
||||
const [manifestUrl, setManifestUrl] = useState<string | null>(null);
|
||||
const [regexFilters, setRegexFilters] = useState<{
|
||||
excludePattern?: string;
|
||||
includePattern?: string;
|
||||
}>({});
|
||||
|
||||
useEffect(() => {
|
||||
// get config from the server
|
||||
@@ -276,6 +280,10 @@ export default function Configure() {
|
||||
},
|
||||
addons,
|
||||
services,
|
||||
regexFilters: (regexFilters.excludePattern || regexFilters.includePattern) ? {
|
||||
excludePattern: regexFilters.excludePattern || undefined,
|
||||
includePattern: regexFilters.includePattern || undefined
|
||||
} : undefined,
|
||||
};
|
||||
return config;
|
||||
};
|
||||
@@ -544,6 +552,7 @@ export default function Configure() {
|
||||
value: filter,
|
||||
})) || []
|
||||
);
|
||||
setRegexFilters(decodedConfig.regexFilters || {});
|
||||
|
||||
setServices(loadValidServices(decodedConfig.services));
|
||||
setMaxMovieSize(
|
||||
@@ -998,6 +1007,63 @@ export default function Configure() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showApiKeyInput && (
|
||||
<div className={styles.section}>
|
||||
<div>
|
||||
<h2 style={{ padding: '5px', margin: '0px ' }}>Regex Filtering</h2>
|
||||
<p style={{ margin: '5px 0 12px 5px' }}>
|
||||
Configure regex patterns to filter streams. These filters will be applied in addition to keyword filters.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ marginBottom: '0px' }}>
|
||||
<div className={styles.section}>
|
||||
<h3 style={{ margin: '2px 0 2px 0' }}>Exclude Pattern</h3>
|
||||
<p style={{ margin: '10px 0 10px 0' }}>
|
||||
Enter a regex pattern to exclude streams. Streams will be excluded if their filename OR indexers match this pattern.
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
value={regexFilters.excludePattern || ''}
|
||||
onChange={(e) => setRegexFilters({
|
||||
...regexFilters,
|
||||
excludePattern: e.target.value
|
||||
})}
|
||||
placeholder="Example: \b(0neshot|1XBET)"
|
||||
className={styles.input}
|
||||
/>
|
||||
<p className={styles.helpText}>
|
||||
Example patterns:
|
||||
<br />
|
||||
- \b(0neshot|1XBET|24xHD) (exclude 0neshot, 1XBET, and 24xHD releases)
|
||||
<br />
|
||||
- ^.*Hi10.*$ (exclude Hi10 profile releases)
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.section} style={{ marginBottom: '0px' }}>
|
||||
<h3 style={{ margin: '2px 0 2px 0' }}>Include Pattern</h3>
|
||||
<p style={{ margin: '10px 0 10px 0' }}>
|
||||
Enter a regex pattern to include streams. Only streams whose filename or indexers match this pattern will be included.
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
value={regexFilters.includePattern || ''}
|
||||
onChange={(e) => setRegexFilters({
|
||||
...regexFilters,
|
||||
includePattern: e.target.value
|
||||
})}
|
||||
placeholder="Example: \b(3L|BiZKiT)"
|
||||
className={styles.input}
|
||||
/>
|
||||
<p className={styles.helpText}>
|
||||
Example patterns:
|
||||
<br />
|
||||
- \b(3L|BiZKiT|BLURANiUM) (only include 3L, BiZKiT, and BLURANiUM releases)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.section}>
|
||||
<div className={styles.slidersSetting}>
|
||||
<div>
|
||||
|
||||
@@ -159,6 +159,10 @@ export interface Config {
|
||||
maxResultsPerResolution: number | null;
|
||||
excludeFilters: string[] | null;
|
||||
strictIncludeFilters: string[] | null;
|
||||
regexFilters?: {
|
||||
excludePattern?: string;
|
||||
includePattern?: string;
|
||||
};
|
||||
mediaFlowConfig?: {
|
||||
mediaFlowEnabled: boolean;
|
||||
proxyUrl: string;
|
||||
|
||||
Reference in New Issue
Block a user