feat: add delete icon to preferred list, only load valid values, fix password requirement check for new logins, fix spellings and add types

This commit is contained in:
Viren070
2025-06-12 16:25:55 +01:00
parent 9771c7be7f
commit d845c0ce8b
4 changed files with 81 additions and 17 deletions
+39 -3
View File
@@ -1,10 +1,47 @@
import { AUDIO_TAGS, QUALITIES, RESOLUTIONS } from '../utils/constants';
import { VISUAL_TAGS } from '../utils/constants';
import { ENCODES } from '../utils/constants';
import { LANGUAGES } from '../utils/constants';
import { AUDIO_CHANNELS } from '../utils/constants';
const createRegex = (pattern: string): RegExp =>
new RegExp(`(?<![^\\s\\[(_\\-.,])(${pattern})(?=[\\s\\)\\]_.\\-,]|$)`, 'i');
const createLanguageRegex = (pattern: string): RegExp =>
createRegex(`${pattern}(?![ .\\-_]?sub(title)?s?)`);
export const PARSE_REGEX = {
type PARSE_REGEX = {
resolutions: Omit<Record<(typeof RESOLUTIONS)[number], RegExp>, 'Unknown'> & {
Unknown?: RegExp;
};
qualities: Omit<Record<(typeof QUALITIES)[number], RegExp>, 'Unknown'> & {
Unknown?: RegExp;
};
visualTags: Omit<
Record<(typeof VISUAL_TAGS)[number], RegExp>,
'Unknown' | 'HDR+DV'
> & {
Unknown?: RegExp;
'HDR+DV'?: RegExp;
};
audioTags: Omit<Record<(typeof AUDIO_TAGS)[number], RegExp>, 'Unknown'> & {
Unknown?: RegExp;
};
audioChannels: Omit<
Record<(typeof AUDIO_CHANNELS)[number], RegExp>,
'Unknown'
> & {
Unknown?: RegExp;
};
languages: Omit<Record<(typeof LANGUAGES)[number], RegExp>, 'Unknown'> & {
Unknown?: RegExp;
};
encodes: Omit<Record<(typeof ENCODES)[number], RegExp>, 'Unknown'> & {
Unknown?: RegExp;
};
releaseGroup: RegExp;
};
export const PARSE_REGEX: PARSE_REGEX = {
resolutions: {
'2160p': createRegex(
'(bd|hd|m)?(4k|2160(p|i)?)|u(ltra)?[ .\\-_]?hd|3840\s?x\s?(\d{4})'
@@ -20,7 +57,6 @@ export const PARSE_REGEX = {
'480p': createRegex('(bd|hd|m)?(480(p|i)?)|sd'),
'360p': createRegex('(bd|hd|m)?(360(p|i)?)'),
'240p': createRegex('(bd|hd|m)?(240(p|i)?)'),
'180p': createRegex('(bd|hd|m)?(180(p|i)?)'),
'144p': createRegex('(bd|hd|m)?(144(p|i)?)'),
},
qualities: {
@@ -86,7 +122,7 @@ export const PARSE_REGEX = {
HEVC: createRegex('hevc[ .\\-_]?(10)?|[xh][ .\\-_]?265'),
AVC: createRegex('avc|[xh][ .\\-_]?264'),
AV1: createRegex('av1'),
Xvid: createRegex('xvid'),
XviD: createRegex('xvid'),
DivX: createRegex('divx|dvix'),
'H-OU': createRegex('h?(alf)?[ .\\-_]?(ou|over[ .\\-_]?under)'),
'H-SBS': createRegex('h?(alf)?[ .\\-_]?(sbs|side[ .\\-_]?by[ .\\-_]?side)'),
+3 -2
View File
@@ -506,8 +506,8 @@ const RESOLUTIONS = [
] as const;
const QUALITIES = [
'Bluray REMUX',
'Bluray',
'BluRay REMUX',
'BluRay',
'WEB-DL',
'WEBRip',
'HDRip',
@@ -532,6 +532,7 @@ const VISUAL_TAGS = [
'IMAX',
'AI',
'SDR',
'Unknown',
] as const;
const AUDIO_TAGS = [
@@ -99,8 +99,8 @@ const defaultPreferredResolutions: Resolution[] = [
];
const defaultPreferredQualities: Quality[] = [
'Bluray REMUX',
'Bluray',
'BluRay REMUX',
'BluRay',
'WEB-DL',
'WEBRip',
'HDRip',
@@ -1831,11 +1831,15 @@ function FilterSettings<T extends string>({
const [included, setIncluded] = useState<T[]>(includedOptions);
const [isDragging, setIsDragging] = useState(false);
const filterToAllowedValues = (filter: T[]) => {
return filter.filter((value) => options.some((opt) => opt.value === value));
};
useEffect(() => {
setRequired(requiredOptions);
setExcluded(excludedOptions);
setPreferred(preferredOptions);
setIncluded(includedOptions);
setRequired(filterToAllowedValues(requiredOptions));
setExcluded(filterToAllowedValues(excludedOptions));
setPreferred(filterToAllowedValues(preferredOptions));
setIncluded(filterToAllowedValues(includedOptions));
}, [requiredOptions, excludedOptions, preferredOptions, includedOptions]);
// DND logic
@@ -2003,6 +2007,11 @@ function FilterSettings<T extends string>({
name={
options.find((opt) => opt.value === value)?.name || value
}
onDelete={() => {
const newPreferred = preferred.filter((v) => v !== value);
setPreferred(newPreferred);
onPreferredChange(newPreferred);
}}
/>
))}
</div>
@@ -2014,7 +2023,15 @@ function FilterSettings<T extends string>({
);
}
function SortableFilterItem({ id, name }: { id: string; name: string }) {
function SortableFilterItem({
id,
name,
onDelete,
}: {
id: string;
name: string;
onDelete: () => void;
}) {
const {
attributes,
listeners,
@@ -2041,6 +2058,19 @@ function SortableFilterItem({ id, name }: { id: string; name: string }) {
<div className="flex-1 flex flex-col justify-center min-w-0">
<span className="font-mono text-base truncate">{name}</span>
</div>
<div className="flex-shrink-0 ml-auto">
<IconButton
size="sm"
rounded
icon={<FaRegTrashAlt />}
intent="alert-subtle"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onDelete();
}}
/>
</div>
</div>
</div>
);
@@ -53,15 +53,12 @@ function Content() {
React.useEffect(() => {
const requirements: string[] = [];
console.log(uuid, password);
// already created a config
if (uuid && password) {
setPasswordRequirements([]);
return;
}
console.log(newPassword);
console.log('checking requirements');
if (newPassword.length < 8) {
requirements.push('Password must be at least 8 characters long');
}
@@ -84,7 +81,7 @@ function Content() {
}
setPasswordRequirements(requirements);
}, [newPassword]);
}, [newPassword, uuid, password]);
const handleSave = async (e?: React.FormEvent<HTMLFormElement>) => {
e?.preventDefault();