mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Merge b8d11b5788 into 39fd8a6550
This commit is contained in:
commit
d81243503d
2 changed files with 123 additions and 0 deletions
|
|
@ -49,6 +49,7 @@ import { FileSize } from "../Shared/FileSize";
|
||||||
const CLASSNAME = "duplicate-checker";
|
const CLASSNAME = "duplicate-checker";
|
||||||
|
|
||||||
const defaultDurationDiff = "1";
|
const defaultDurationDiff = "1";
|
||||||
|
const defaultSelectedCodec = "hevc";
|
||||||
|
|
||||||
export const SceneDuplicateChecker: React.FC = () => {
|
export const SceneDuplicateChecker: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
@ -60,6 +61,7 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||||
const durationDiff = Number.parseFloat(
|
const durationDiff = Number.parseFloat(
|
||||||
query.get("durationDiff") ?? defaultDurationDiff
|
query.get("durationDiff") ?? defaultDurationDiff
|
||||||
);
|
);
|
||||||
|
let SelectedCodec = query.get("SelectedCodec") ?? defaultSelectedCodec;
|
||||||
|
|
||||||
const [currentPageSize, setCurrentPageSize] = useState(pageSize);
|
const [currentPageSize, setCurrentPageSize] = useState(pageSize);
|
||||||
const [isMultiDelete, setIsMultiDelete] = useState(false);
|
const [isMultiDelete, setIsMultiDelete] = useState(false);
|
||||||
|
|
@ -195,6 +197,19 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findSmallestScene = (group: GQL.SlimSceneDataFragment[]) => {
|
||||||
|
// Get maximum file size of a scene
|
||||||
|
const totalSize = (scene: GQL.SlimSceneDataFragment) => {
|
||||||
|
return scene.files.reduce((prev: number, f) => Math.max(prev, f.size), 0);
|
||||||
|
};
|
||||||
|
// Find scene object with minimum total size
|
||||||
|
return group.reduce((smallest, scene) => {
|
||||||
|
const smallestSize = totalSize(smallest);
|
||||||
|
const currentSize = totalSize(scene);
|
||||||
|
return currentSize < smallestSize ? scene : smallest;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const findLargestResolutionScene = (group: GQL.SlimSceneDataFragment[]) => {
|
const findLargestResolutionScene = (group: GQL.SlimSceneDataFragment[]) => {
|
||||||
// Get maximum resolution of a scene
|
// Get maximum resolution of a scene
|
||||||
const sceneResolution = (scene: GQL.SlimSceneDataFragment) => {
|
const sceneResolution = (scene: GQL.SlimSceneDataFragment) => {
|
||||||
|
|
@ -277,6 +292,53 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||||
setCheckedScenes(checkedArray);
|
setCheckedScenes(checkedArray);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSelectSmallestClick = () => {
|
||||||
|
setSelectedScenes([]);
|
||||||
|
const checkedArray: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
filteredScenes.forEach((group) => {
|
||||||
|
if (chkSafeSelect && !checkSameCodec(group)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Find smallest scene in group a
|
||||||
|
const smallest = findSmallestScene(group);
|
||||||
|
group.forEach((scene) => {
|
||||||
|
if (scene !== smallest) {
|
||||||
|
checkedArray[scene.id] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setCheckedScenes(checkedArray);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelectCodecClick = () => {
|
||||||
|
setSelectedScenes([]);
|
||||||
|
const checkedArray: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
filteredScenes.forEach((group) => {
|
||||||
|
if (chkSafeSelect && !checkSameCodec(group)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// select scenes where codec is not preferred
|
||||||
|
let hasCodec = false;
|
||||||
|
group.forEach((scene) => {
|
||||||
|
if (scene.files[0]?.video_codec != SelectedCodec) {
|
||||||
|
checkedArray[scene.id] = true;
|
||||||
|
} else if (scene.files[0]?.video_codec == SelectedCodec) {
|
||||||
|
hasCodec = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!hasCodec) {
|
||||||
|
// when no scenes with preferred codec are in the group select nothing
|
||||||
|
group.forEach((scene) => {
|
||||||
|
checkedArray[scene.id] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setCheckedScenes(checkedArray);
|
||||||
|
};
|
||||||
|
|
||||||
const onSelectLargestResolutionClick = () => {
|
const onSelectLargestResolutionClick = () => {
|
||||||
setSelectedScenes([]);
|
setSelectedScenes([]);
|
||||||
const checkedArray: Record<string, boolean> = {};
|
const checkedArray: Record<string, boolean> = {};
|
||||||
|
|
@ -755,6 +817,47 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group>
|
||||||
|
<Row noGutters>
|
||||||
|
<Form.Label>
|
||||||
|
<FormattedMessage id="dupe_check.select_all_but_specified_codec.title" />
|
||||||
|
</Form.Label>
|
||||||
|
<Col xs="auto">
|
||||||
|
<Form.Control
|
||||||
|
as="select"
|
||||||
|
onChange={(e) =>
|
||||||
|
setQuery({
|
||||||
|
SelectedCodec:
|
||||||
|
e.currentTarget.value === defaultSelectedCodec
|
||||||
|
? undefined
|
||||||
|
: e.currentTarget.value,
|
||||||
|
page: undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defaultValue={SelectedCodec}
|
||||||
|
className="input-control ml-4"
|
||||||
|
>
|
||||||
|
<option value={"av1"}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "dupe_check.select_all_but_specified_codec.av1",
|
||||||
|
})}
|
||||||
|
</option>
|
||||||
|
<option value={"hevc"}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "dupe_check.select_all_but_specified_codec.hevc",
|
||||||
|
})}
|
||||||
|
</option>
|
||||||
|
<option value={"h264"}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "dupe_check.select_all_but_specified_codec.h264",
|
||||||
|
})}
|
||||||
|
</option>
|
||||||
|
</Form.Control>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Row noGutters>
|
<Row noGutters>
|
||||||
<Col xs="12">
|
<Col xs="12">
|
||||||
|
|
@ -767,6 +870,12 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||||
{intl.formatMessage({ id: "dupe_check.select_none" })}
|
{intl.formatMessage({ id: "dupe_check.select_none" })}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
|
|
||||||
|
<Dropdown.Item onClick={() => onSelectCodecClick()}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "dupe_check.select_all_but_specified_codec.label",
|
||||||
|
})}
|
||||||
|
</Dropdown.Item>
|
||||||
|
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
onClick={() => onSelectLargestResolutionClick()}
|
onClick={() => onSelectLargestResolutionClick()}
|
||||||
>
|
>
|
||||||
|
|
@ -781,6 +890,12 @@ export const SceneDuplicateChecker: React.FC = () => {
|
||||||
})}
|
})}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
|
|
||||||
|
<Dropdown.Item onClick={() => onSelectSmallestClick()}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "dupe_check.select_all_but_smallest_file",
|
||||||
|
})}
|
||||||
|
</Dropdown.Item>
|
||||||
|
|
||||||
<Dropdown.Item onClick={() => onSelectByAge(true)}>
|
<Dropdown.Item onClick={() => onSelectByAge(true)}>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: "dupe_check.select_oldest",
|
id: "dupe_check.select_oldest",
|
||||||
|
|
|
||||||
|
|
@ -1054,7 +1054,15 @@
|
||||||
"medium": "Medium"
|
"medium": "Medium"
|
||||||
},
|
},
|
||||||
"search_accuracy_label": "Search Accuracy",
|
"search_accuracy_label": "Search Accuracy",
|
||||||
|
"select_all_but_specified_codec": {
|
||||||
|
"title": "Preferred video codec",
|
||||||
|
"label": "Select every file in each group, except for the file with the preferred codec",
|
||||||
|
"av1": "av1",
|
||||||
|
"hevc": "hevc",
|
||||||
|
"h264": "h264"
|
||||||
|
},
|
||||||
"select_all_but_largest_file": "Select every file in each duplicated group, except the largest file",
|
"select_all_but_largest_file": "Select every file in each duplicated group, except the largest file",
|
||||||
|
"select_all_but_smallest_file": "Select every file in each duplicated group, except the smallest file",
|
||||||
"select_all_but_largest_resolution": "Select every file in each duplicated group, except the file with highest resolution",
|
"select_all_but_largest_resolution": "Select every file in each duplicated group, except the file with highest resolution",
|
||||||
"select_none": "Select None",
|
"select_none": "Select None",
|
||||||
"select_oldest": "Select the oldest file in the duplicate group",
|
"select_oldest": "Select the oldest file in the duplicate group",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue