diff --git a/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx b/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx index 5f4f5e44da2..dc4bfa1082f 100644 --- a/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx +++ b/ui/v2.5/src/components/SceneDuplicateChecker/SceneDuplicateChecker.tsx @@ -48,6 +48,7 @@ import { objectTitle } from "src/core/files"; const CLASSNAME = "duplicate-checker"; const defaultDurationDiff = "1"; +const defaultSelectedCodec = "hevc"; export const SceneDuplicateChecker: React.FC = () => { const intl = useIntl(); @@ -59,6 +60,7 @@ export const SceneDuplicateChecker: React.FC = () => { const durationDiff = Number.parseFloat( query.get("durationDiff") ?? defaultDurationDiff ); + let SelectedCodec = query.get("SelectedCodec") ?? defaultSelectedCodec; const [currentPageSize, setCurrentPageSize] = useState(pageSize); const [isMultiDelete, setIsMultiDelete] = useState(false); @@ -177,6 +179,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[]) => { // Get maximum resolution of a scene const sceneResolution = (scene: GQL.SlimSceneDataFragment) => { @@ -259,6 +274,53 @@ export const SceneDuplicateChecker: React.FC = () => { setCheckedScenes(checkedArray); }; + const onSelectSmallestClick = () => { + setSelectedScenes([]); + const checkedArray: Record = {}; + + 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 = {}; + + 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 = () => { setSelectedScenes([]); const checkedArray: Record = {}; @@ -750,6 +812,47 @@ export const SceneDuplicateChecker: React.FC = () => { + + + + + + + + + setQuery({ + SelectedCodec: + e.currentTarget.value === defaultSelectedCodec + ? undefined + : e.currentTarget.value, + page: undefined, + }) + } + defaultValue={SelectedCodec} + className="input-control ml-4" + > + + + + + + + + @@ -762,6 +865,12 @@ export const SceneDuplicateChecker: React.FC = () => { {intl.formatMessage({ id: "dupe_check.select_none" })} + onSelectCodecClick()}> + {intl.formatMessage({ + id: "dupe_check.select_all_but_specified_codec.label", + })} + + onSelectLargestResolutionClick()} > @@ -776,6 +885,12 @@ export const SceneDuplicateChecker: React.FC = () => { })} + onSelectSmallestClick()}> + {intl.formatMessage({ + id: "dupe_check.select_all_but_smallest_file", + })} + + onSelectByAge(true)}> {intl.formatMessage({ id: "dupe_check.select_oldest", diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 74073d1ccfb..f2b769a1596 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -993,7 +993,15 @@ "medium": "Medium" }, "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_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_none": "Select None", "select_oldest": "Select the oldest file in the duplicate group",