diff --git a/graphql/documents/data/file.graphql b/graphql/documents/data/file.graphql index da02f00d6..7acb95feb 100644 --- a/graphql/documents/data/file.graphql +++ b/graphql/documents/data/file.graphql @@ -7,6 +7,7 @@ fragment VideoFileData on VideoFile { id path size + mod_time duration video_codec audio_codec @@ -24,6 +25,7 @@ fragment ImageFileData on ImageFile { id path size + mod_time width height fingerprints { @@ -36,6 +38,7 @@ fragment GalleryFileData on GalleryFile { id path size + mod_time fingerprints { type value diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx index 1cf4f6c46..422e45d4e 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/Gallery.tsx @@ -9,7 +9,12 @@ import { useFindGallery, useGalleryUpdate, } from "src/core/StashService"; -import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared"; +import { + ErrorMessage, + LoadingIndicator, + Icon, + Counter, +} from "src/components/Shared"; import Mousetrap from "mousetrap"; import { useToast } from "src/hooks"; import { OrganizedButton } from "src/components/Scenes/SceneDetails/OrganizedButton"; @@ -174,6 +179,9 @@ export const GalleryPage: React.FC = ({ gallery }) => { + {gallery.files.length > 1 && ( + + )} ) : undefined} diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryFileInfoPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryFileInfoPanel.tsx index 94456aba4..5fd71c1ac 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryFileInfoPanel.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useState } from "react"; import { Accordion, Button, Card } from "react-bootstrap"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, FormattedTime } from "react-intl"; import { TruncatedText } from "src/components/Shared"; import DeleteFilesDialog from "src/components/Shared/DeleteFilesDialog"; import * as GQL from "src/core/generated-graphql"; @@ -44,6 +44,15 @@ const FileInfoPanel: React.FC = ( value={`file://${path}`} truncate /> + {props.file && ( + + + + )} {props.ofMany && props.onSetPrimaryFile && !props.primary && (
diff --git a/ui/v2.5/src/components/Images/ImageDetails/Image.tsx b/ui/v2.5/src/components/Images/ImageDetails/Image.tsx index b394f3af6..72d88bad9 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/Image.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/Image.tsx @@ -11,7 +11,12 @@ import { useImageUpdate, mutateMetadataScan, } from "src/core/StashService"; -import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared"; +import { + ErrorMessage, + LoadingIndicator, + Icon, + Counter, +} from "src/components/Shared"; import { useToast } from "src/hooks"; import * as Mousetrap from "mousetrap"; import { OCounterButton } from "src/components/Scenes/SceneDetails/OCounterButton"; @@ -178,6 +183,9 @@ export const Image: React.FC = () => { + {image.files.length > 1 && ( + + )} diff --git a/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx b/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx index a8377f12c..9d0991775 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Accordion, Button, Card } from "react-bootstrap"; -import { FormattedMessage, FormattedNumber } from "react-intl"; +import { FormattedMessage, FormattedNumber, FormattedTime } from "react-intl"; import { TruncatedText } from "src/components/Shared"; import DeleteFilesDialog from "src/components/Shared/DeleteFilesDialog"; import * as GQL from "src/core/generated-graphql"; @@ -65,6 +65,13 @@ const FileInfoPanel: React.FC = ( truncate /> {renderFileSize()} + + + { ); } + function maybeRenderFileCount(scene: GQL.SlimSceneDataFragment) { + if (scene.files.length <= 1) return; + + const popoverContent = ( + + ); + + return ( + + + + ); + } + function maybeRenderOrganized(scene: GQL.SlimSceneDataFragment) { if (scene.organized) { return ( @@ -313,6 +334,7 @@ export const SceneDuplicateChecker: React.FC = () => { scene.scene_markers.length > 0 || scene?.o_counter || scene.galleries.length > 0 || + scene.files.length > 1 || scene.organized ) { return ( @@ -324,6 +346,7 @@ export const SceneDuplicateChecker: React.FC = () => { {maybeRenderSceneMarkerPopoverButton(scene)} {maybeRenderOCounter(scene)} {maybeRenderGallery(scene)} + {maybeRenderFileCount(scene)} {maybeRenderOrganized(scene)} diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx index 0d1024cb9..1e7a8163f 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx @@ -58,6 +58,7 @@ const DeleteScenesDialog = lazy(() => import("../DeleteScenesDialog")); const GenerateDialog = lazy(() => import("../../Dialogs/GenerateDialog")); const SceneVideoFilterPanel = lazy(() => import("./SceneVideoFilterPanel")); import { objectPath, objectTitle } from "src/core/files"; +import { Counter } from "src/components/Shared"; interface IProps { scene: GQL.SceneDataFragment; @@ -367,6 +368,9 @@ const ScenePage: React.FC = ({ + {scene.files.length > 1 && ( + + )} diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx index 804c228b4..7e4a9aefe 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx @@ -1,6 +1,11 @@ import React, { useMemo, useState } from "react"; import { Accordion, Button, Card } from "react-bootstrap"; -import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; +import { + FormattedMessage, + FormattedNumber, + FormattedTime, + useIntl, +} from "react-intl"; import { useHistory } from "react-router-dom"; import { TruncatedText } from "src/components/Shared"; import DeleteFilesDialog from "src/components/Shared/DeleteFilesDialog"; @@ -87,6 +92,13 @@ const FileInfoPanel: React.FC = ( truncate /> {renderFileSize()} + + +