mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat: use sliders for seeder ranges and fix some options not being multi-option
This commit is contained in:
@@ -278,24 +278,16 @@ export const UserDataSchema = z.object({
|
||||
enhanceResults: z.boolean().optional(),
|
||||
enhancePosters: z.boolean().optional(),
|
||||
|
||||
excludedSeeders: z
|
||||
.object({
|
||||
min: z.number().optional(),
|
||||
max: z.number().optional(),
|
||||
})
|
||||
excludeSeederRange: z
|
||||
.tuple([z.number().min(0), z.number().min(0)])
|
||||
.optional(),
|
||||
includedSeeders: z
|
||||
.object({
|
||||
min: z.number().optional(),
|
||||
max: z.number().optional(),
|
||||
})
|
||||
includeSeederRange: z
|
||||
.tuple([z.number().min(0), z.number().min(0)])
|
||||
.optional(),
|
||||
requiredSeeders: z
|
||||
.object({
|
||||
min: z.number().optional(),
|
||||
max: z.number().optional(),
|
||||
})
|
||||
requiredSeederRange: z
|
||||
.tuple([z.number().min(0), z.number().min(0)])
|
||||
.optional(),
|
||||
seederRangeTypes: z.array(z.enum(['p2p', 'cached', 'uncached'])).optional(),
|
||||
excludeCached: z.boolean().optional(),
|
||||
excludeCachedFromAddons: z.array(z.string().min(1)).optional(),
|
||||
excludeCachedFromServices: z.array(z.string().min(1)).optional(),
|
||||
|
||||
+115
-67
@@ -1350,6 +1350,50 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
}
|
||||
};
|
||||
|
||||
const normaliseRange = (
|
||||
range: [number, number] | undefined,
|
||||
defaults: { min: number; max: number }
|
||||
): [number | undefined, number | undefined] | undefined => {
|
||||
if (!range) return undefined;
|
||||
const [min, max] = range;
|
||||
const normMin = min === defaults.min ? undefined : min;
|
||||
const normMax = max === defaults.max ? undefined : max;
|
||||
return normMin === undefined && normMax === undefined
|
||||
? undefined
|
||||
: [normMin, normMax];
|
||||
};
|
||||
|
||||
const normaliseSeederRange = (
|
||||
seederRange: [number, number] | undefined
|
||||
) => {
|
||||
return normaliseRange(seederRange, {
|
||||
min: constants.MIN_SEEDERS,
|
||||
max: constants.MAX_SEEDERS,
|
||||
});
|
||||
};
|
||||
|
||||
const normaliseSizeRange = (sizeRange: [number, number] | undefined) => {
|
||||
return normaliseRange(sizeRange, {
|
||||
min: constants.MIN_SIZE,
|
||||
max: constants.MAX_SIZE,
|
||||
});
|
||||
};
|
||||
|
||||
const getStreamType = (
|
||||
stream: ParsedStream
|
||||
): 'p2p' | 'cached' | 'uncached' | undefined => {
|
||||
switch (stream.type) {
|
||||
case 'debrid':
|
||||
return stream.service?.cached ? 'cached' : 'uncached';
|
||||
case 'usenet':
|
||||
return stream.service?.cached ? 'cached' : 'uncached';
|
||||
case 'p2p':
|
||||
return 'p2p';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const shouldKeepStream = async (stream: ParsedStream): Promise<boolean> => {
|
||||
const file = stream.parsedFile;
|
||||
|
||||
@@ -1433,23 +1477,42 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
return true;
|
||||
}
|
||||
|
||||
const includedSeederRange = normaliseSeederRange(
|
||||
this.userData.includeSeederRange
|
||||
);
|
||||
const excludedSeederRange = normaliseSeederRange(
|
||||
this.userData.excludeSeederRange
|
||||
);
|
||||
const requiredSeederRange = normaliseSeederRange(
|
||||
this.userData.requiredSeederRange
|
||||
);
|
||||
|
||||
const typeForSeederRange = getStreamType(stream);
|
||||
|
||||
if (
|
||||
this.userData.includedSeeders?.min &&
|
||||
stream.torrent?.seeders &&
|
||||
stream.torrent.seeders > this.userData.includedSeeders.min
|
||||
includedSeederRange &&
|
||||
(!this.userData.seederRangeTypes ||
|
||||
(typeForSeederRange &&
|
||||
this.userData.seederRangeTypes.includes(typeForSeederRange)))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
this.userData.includedSeeders?.max &&
|
||||
stream.torrent?.seeders &&
|
||||
stream.torrent.seeders < this.userData.includedSeeders.max
|
||||
) {
|
||||
return true;
|
||||
if (
|
||||
stream.torrent?.seeders &&
|
||||
includedSeederRange[0] &&
|
||||
stream.torrent.seeders > includedSeederRange[0]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
stream.torrent?.seeders &&
|
||||
includedSeederRange[1] &&
|
||||
stream.torrent.seeders < includedSeederRange[1]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Track stream type exclusions
|
||||
if (this.userData.excludedStreamTypes?.includes(stream.type)) {
|
||||
// Track stream type exclusions
|
||||
skipReasons.excludedStreamType.total++;
|
||||
skipReasons.excludedStreamType.details[stream.type] =
|
||||
(skipReasons.excludedStreamType.details[stream.type] || 0) + 1;
|
||||
@@ -1780,37 +1843,47 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
}
|
||||
|
||||
if (
|
||||
this.userData.requiredSeeders?.min &&
|
||||
stream.torrent?.seeders &&
|
||||
stream.torrent.seeders < this.userData.requiredSeeders.min
|
||||
requiredSeederRange &&
|
||||
(!this.userData.seederRangeTypes ||
|
||||
(typeForSeederRange &&
|
||||
this.userData.seederRangeTypes.includes(typeForSeederRange)))
|
||||
) {
|
||||
skipReasons.requiredSeeders.total++;
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.userData.requiredSeeders?.max &&
|
||||
stream.torrent?.seeders &&
|
||||
stream.torrent.seeders > this.userData.requiredSeeders.max
|
||||
) {
|
||||
skipReasons.requiredSeeders.total++;
|
||||
return false;
|
||||
if (
|
||||
stream.torrent?.seeders &&
|
||||
requiredSeederRange[0] &&
|
||||
stream.torrent.seeders < requiredSeederRange[0]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
stream.torrent?.seeders &&
|
||||
requiredSeederRange[1] &&
|
||||
stream.torrent.seeders > requiredSeederRange[1]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.userData.excludedSeeders?.min &&
|
||||
stream.torrent?.seeders &&
|
||||
stream.torrent.seeders < this.userData.excludedSeeders.min
|
||||
excludedSeederRange &&
|
||||
(!this.userData.seederRangeTypes ||
|
||||
(typeForSeederRange &&
|
||||
this.userData.seederRangeTypes.includes(typeForSeederRange)))
|
||||
) {
|
||||
skipReasons.excludedSeeders.total++;
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.userData.excludedSeeders?.max &&
|
||||
stream.torrent?.seeders &&
|
||||
stream.torrent.seeders > this.userData.excludedSeeders.max
|
||||
) {
|
||||
skipReasons.excludedSeeders.total++;
|
||||
return false;
|
||||
if (
|
||||
stream.torrent?.seeders &&
|
||||
excludedSeederRange[0] &&
|
||||
stream.torrent.seeders > excludedSeederRange[0]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
stream.torrent?.seeders &&
|
||||
excludedSeederRange[1] &&
|
||||
stream.torrent.seeders < excludedSeederRange[1]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!performTitleMatch(stream)) {
|
||||
@@ -1836,19 +1909,6 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
return false;
|
||||
}
|
||||
|
||||
const useMinMax = (
|
||||
minMax: [number, number] | undefined,
|
||||
defaults: { min: number; max: number }
|
||||
): [number | undefined, number | undefined] | undefined => {
|
||||
if (!minMax) return undefined;
|
||||
const [min, max] = minMax;
|
||||
const normMin = min === defaults.min ? undefined : min;
|
||||
const normMax = max === defaults.max ? undefined : max;
|
||||
return normMin === undefined && normMax === undefined
|
||||
? undefined
|
||||
: [normMin, normMax];
|
||||
};
|
||||
|
||||
const global = this.userData.size?.global;
|
||||
const resolution = stream.parsedFile?.resolution
|
||||
? this.userData.size?.resolution?.[
|
||||
@@ -1860,24 +1920,12 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
let minMax: [number | undefined, number | undefined] | undefined;
|
||||
if (type === 'movie') {
|
||||
minMax =
|
||||
useMinMax(resolution?.movies, {
|
||||
min: constants.MIN_SIZE,
|
||||
max: constants.MAX_SIZE,
|
||||
}) ||
|
||||
useMinMax(global?.movies, {
|
||||
min: constants.MIN_SIZE,
|
||||
max: constants.MAX_SIZE,
|
||||
});
|
||||
normaliseSizeRange(resolution?.movies) ||
|
||||
normaliseSizeRange(global?.movies);
|
||||
} else {
|
||||
minMax =
|
||||
useMinMax(resolution?.series, {
|
||||
min: constants.MIN_SIZE,
|
||||
max: constants.MAX_SIZE,
|
||||
}) ||
|
||||
useMinMax(global?.series, {
|
||||
min: constants.MIN_SIZE,
|
||||
max: constants.MAX_SIZE,
|
||||
});
|
||||
normaliseSizeRange(resolution?.series) ||
|
||||
normaliseSizeRange(global?.series);
|
||||
}
|
||||
|
||||
if (minMax) {
|
||||
|
||||
@@ -585,6 +585,9 @@ const SORT_CRITERIA = [
|
||||
export const MIN_SIZE = 0;
|
||||
export const MAX_SIZE = 100 * 1000 * 1000 * 1000; // 100GB
|
||||
|
||||
export const MIN_SEEDERS = 0;
|
||||
export const MAX_SEEDERS = 1000;
|
||||
|
||||
export const DEFAULT_POSTERS = [
|
||||
'aHR0cHM6Ly93d3cucG5nbWFydC5jb20vZmlsZXMvMTEvUmlja3JvbGxpbmctUE5HLVBpYy5wbmc=',
|
||||
];
|
||||
|
||||
@@ -59,6 +59,10 @@ import {
|
||||
TYPES,
|
||||
DEDUPLICATOR_KEYS,
|
||||
AUDIO_CHANNELS,
|
||||
MIN_SIZE,
|
||||
MAX_SIZE,
|
||||
MIN_SEEDERS,
|
||||
MAX_SEEDERS,
|
||||
} from '../../../../core/src/utils/constants';
|
||||
import { PageControls } from '../shared/page-controls';
|
||||
import { Switch } from '../ui/switch';
|
||||
@@ -82,9 +86,6 @@ type AudioTag = (typeof AUDIO_TAGS)[number];
|
||||
type AudioChannel = (typeof AUDIO_CHANNELS)[number];
|
||||
type Language = (typeof LANGUAGES)[number];
|
||||
|
||||
const MIN_SIZE = 0;
|
||||
const MAX_SIZE = 100 * 1000 * 1000 * 1000; // 100GB
|
||||
|
||||
const defaultPreferredResolutions: Resolution[] = [
|
||||
'2160p',
|
||||
'1440p',
|
||||
@@ -853,100 +854,251 @@ function Content() {
|
||||
description="Configure required, excluded, and included seeder ranges"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<NumberInput
|
||||
label="Required Minimum Seeders"
|
||||
help="Streams with fewer seeders than this will be excluded"
|
||||
value={userData.requiredSeeders?.min}
|
||||
min={0}
|
||||
onValueChange={(value) => {
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
requiredSeeders: {
|
||||
...prev.requiredSeeders,
|
||||
min: value,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Required Maximum Seeders"
|
||||
help="Streams with more seeders than this will be excluded"
|
||||
value={userData.requiredSeeders?.max}
|
||||
min={0}
|
||||
onValueChange={(value) => {
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
requiredSeeders: {
|
||||
...prev.requiredSeeders,
|
||||
max: value,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="flex-1 min-w-0">
|
||||
<Slider
|
||||
min={MIN_SEEDERS}
|
||||
max={MAX_SEEDERS}
|
||||
defaultValue={[MIN_SEEDERS, MAX_SEEDERS]}
|
||||
value={
|
||||
userData.requiredSeederRange || [
|
||||
MIN_SEEDERS,
|
||||
MAX_SEEDERS,
|
||||
]
|
||||
}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
newValue?.[0] !== undefined &&
|
||||
newValue?.[1] !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
requiredSeederRange: [newValue[0], newValue[1]],
|
||||
}))
|
||||
}
|
||||
minStepsBetweenThumbs={1}
|
||||
label="Required Seeder Range"
|
||||
help="Streams with seeders outside this range will be excluded"
|
||||
/>
|
||||
<div className="flex justify-between mt-1 text-xs text-[--muted]">
|
||||
<span>
|
||||
{userData.requiredSeederRange?.[0] || MIN_SEEDERS}
|
||||
</span>
|
||||
<span>
|
||||
{userData.requiredSeederRange?.[1] || MAX_SEEDERS}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 md:w-[240px] shrink-0">
|
||||
<NumberInput
|
||||
label="Min"
|
||||
value={
|
||||
userData.requiredSeederRange?.[0] || MIN_SEEDERS
|
||||
}
|
||||
min={MIN_SEEDERS}
|
||||
max={userData.requiredSeederRange?.[1] || MAX_SEEDERS}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
requiredSeederRange: [
|
||||
newValue,
|
||||
prev.requiredSeederRange?.[1] || MAX_SEEDERS,
|
||||
],
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Max"
|
||||
value={
|
||||
userData.requiredSeederRange?.[1] || MAX_SEEDERS
|
||||
}
|
||||
min={userData.requiredSeederRange?.[0] || MIN_SEEDERS}
|
||||
max={MAX_SEEDERS}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
requiredSeederRange: [
|
||||
prev.requiredSeederRange?.[0] || MIN_SEEDERS,
|
||||
newValue,
|
||||
],
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="flex-1 min-w-0">
|
||||
<Slider
|
||||
min={MIN_SEEDERS}
|
||||
max={MAX_SEEDERS}
|
||||
defaultValue={[MIN_SEEDERS, MAX_SEEDERS]}
|
||||
value={
|
||||
userData.excludeSeederRange || [
|
||||
MIN_SEEDERS,
|
||||
MAX_SEEDERS,
|
||||
]
|
||||
}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
newValue?.[0] !== undefined &&
|
||||
newValue?.[1] !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
excludeSeederRange: [newValue[0], newValue[1]],
|
||||
}))
|
||||
}
|
||||
minStepsBetweenThumbs={1}
|
||||
label="Excluded Seeder Range"
|
||||
help="Streams with seeders in this range will be excluded"
|
||||
/>
|
||||
<div className="flex justify-between mt-1 text-xs text-[--muted]">
|
||||
<span>
|
||||
{userData.excludeSeederRange?.[0] || MIN_SEEDERS}
|
||||
</span>
|
||||
<span>
|
||||
{userData.excludeSeederRange?.[1] || MAX_SEEDERS}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 md:w-[240px] shrink-0">
|
||||
<NumberInput
|
||||
label="Min"
|
||||
value={
|
||||
userData.excludeSeederRange?.[0] || MIN_SEEDERS
|
||||
}
|
||||
min={MIN_SEEDERS}
|
||||
max={userData.excludeSeederRange?.[1] || MAX_SEEDERS}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
excludeSeederRange: [
|
||||
newValue,
|
||||
prev.excludeSeederRange?.[1] || MAX_SEEDERS,
|
||||
],
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Max"
|
||||
value={
|
||||
userData.excludeSeederRange?.[1] || MAX_SEEDERS
|
||||
}
|
||||
min={userData.excludeSeederRange?.[0] || MIN_SEEDERS}
|
||||
max={MAX_SEEDERS}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
excludeSeederRange: [
|
||||
prev.excludeSeederRange?.[0] || MIN_SEEDERS,
|
||||
newValue,
|
||||
],
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="flex-1 min-w-0">
|
||||
<Slider
|
||||
min={MIN_SEEDERS}
|
||||
max={MAX_SEEDERS}
|
||||
defaultValue={[MIN_SEEDERS, MAX_SEEDERS]}
|
||||
value={
|
||||
userData.includeSeederRange || [
|
||||
MIN_SEEDERS,
|
||||
MAX_SEEDERS,
|
||||
]
|
||||
}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
newValue?.[0] !== undefined &&
|
||||
newValue?.[1] !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
includeSeederRange: [newValue[0], newValue[1]],
|
||||
}))
|
||||
}
|
||||
minStepsBetweenThumbs={1}
|
||||
label="Included Seeder Range"
|
||||
help="Streams with seeders in this range will be included, ignoring other filters"
|
||||
/>
|
||||
<div className="flex justify-between mt-1 text-xs text-[--muted]">
|
||||
<span>
|
||||
{userData.includeSeederRange?.[0] || MIN_SEEDERS}
|
||||
</span>
|
||||
<span>
|
||||
{userData.includeSeederRange?.[1] || MAX_SEEDERS}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 md:w-[240px] shrink-0">
|
||||
<NumberInput
|
||||
label="Min"
|
||||
value={
|
||||
userData.includeSeederRange?.[0] || MIN_SEEDERS
|
||||
}
|
||||
min={MIN_SEEDERS}
|
||||
max={userData.includeSeederRange?.[1] || MAX_SEEDERS}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
includeSeederRange: [
|
||||
newValue,
|
||||
prev.includeSeederRange?.[1] || MAX_SEEDERS,
|
||||
],
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Max"
|
||||
value={
|
||||
userData.includeSeederRange?.[1] || MAX_SEEDERS
|
||||
}
|
||||
min={userData.includeSeederRange?.[0] || MIN_SEEDERS}
|
||||
max={MAX_SEEDERS}
|
||||
onValueChange={(newValue) =>
|
||||
newValue !== undefined &&
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
includeSeederRange: [
|
||||
prev.includeSeederRange?.[0] || MIN_SEEDERS,
|
||||
newValue,
|
||||
],
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<NumberInput
|
||||
label="Excluded Minimum Seeders"
|
||||
help="Streams with more seeders than this will be excluded"
|
||||
value={userData.excludedSeeders?.min}
|
||||
min={0}
|
||||
|
||||
<div className="space-y-4">
|
||||
<Combobox
|
||||
label="Stream Types"
|
||||
emptyMessage="There aren't any stream types to choose from..."
|
||||
options={['p2p', 'cached', 'uncached'].map((type) => ({
|
||||
label: type,
|
||||
value: type,
|
||||
}))}
|
||||
value={userData.seederRangeTypes || []}
|
||||
onValueChange={(value) => {
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
excludedSeeders: {
|
||||
...prev.excludedSeeders,
|
||||
min: value,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Excluded Maximum Seeders"
|
||||
help="Streams with fewer seeders than this will be excluded"
|
||||
value={userData.excludedSeeders?.max}
|
||||
min={0}
|
||||
onValueChange={(value) => {
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
excludedSeeders: {
|
||||
...prev.excludedSeeders,
|
||||
max: value,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<NumberInput
|
||||
label="Included Minimum Seeders"
|
||||
help="Streams with more seeders than this will be included, ignoring other filters"
|
||||
value={userData.includedSeeders?.min}
|
||||
min={0}
|
||||
onValueChange={(value) => {
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
includedSeeders: {
|
||||
...prev.includedSeeders,
|
||||
min: value,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Included Maximum Seeders"
|
||||
help="Streams with fewer seeders than this will be included, ignoring other filters"
|
||||
value={userData.includedSeeders?.max}
|
||||
min={0}
|
||||
onValueChange={(value) => {
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
includedSeeders: {
|
||||
...prev.includedSeeders,
|
||||
max: value,
|
||||
},
|
||||
seederRangeTypes: value as (
|
||||
| 'p2p'
|
||||
| 'cached'
|
||||
| 'uncached'
|
||||
)[],
|
||||
}));
|
||||
}}
|
||||
help="Stream types that will use the seeder ranges defined above. Leave blank to apply to all stream types."
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1036,6 +1188,7 @@ function Content() {
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<Combobox
|
||||
multiple
|
||||
disabled={!userData.titleMatching?.enabled}
|
||||
label="Request Types"
|
||||
emptyMessage="There aren't any request types to choose from..."
|
||||
@@ -1057,6 +1210,7 @@ function Content() {
|
||||
}}
|
||||
/>
|
||||
<Combobox
|
||||
multiple
|
||||
disabled={!userData.titleMatching?.enabled}
|
||||
label="Addons"
|
||||
help="Addons that will use strict title matching. Leave blank to apply to all addons."
|
||||
@@ -1103,6 +1257,7 @@ function Content() {
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<Combobox
|
||||
multiple
|
||||
disabled={!userData.seasonEpisodeMatching?.enabled}
|
||||
label="Request Types"
|
||||
help="Request types that will use season/episode matching. Leave blank to apply to all request types."
|
||||
@@ -1124,6 +1279,7 @@ function Content() {
|
||||
}}
|
||||
/>
|
||||
<Combobox
|
||||
multiple
|
||||
disabled={!userData.seasonEpisodeMatching?.enabled}
|
||||
label="Addons"
|
||||
help="Addons that will use season/episode matching. Leave blank to apply to all addons."
|
||||
|
||||
Reference in New Issue
Block a user