mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Add rescan option to overflow dropdown (#1119)
* Make scan options optional * Add scene rescan * Add image rescan * Add gallery rescan * Add changelog
This commit is contained in:
@@ -33,15 +33,15 @@ input GeneratePreviewOptionsInput {
|
|||||||
input ScanMetadataInput {
|
input ScanMetadataInput {
|
||||||
paths: [String!]
|
paths: [String!]
|
||||||
"""Set name, date, details from metadata (if present)"""
|
"""Set name, date, details from metadata (if present)"""
|
||||||
useFileMetadata: Boolean!
|
useFileMetadata: Boolean
|
||||||
"""Strip file extension from title"""
|
"""Strip file extension from title"""
|
||||||
stripFileExtension: Boolean!
|
stripFileExtension: Boolean
|
||||||
"""Generate previews during scan"""
|
"""Generate previews during scan"""
|
||||||
scanGeneratePreviews: Boolean!
|
scanGeneratePreviews: Boolean
|
||||||
"""Generate image previews during scan"""
|
"""Generate image previews during scan"""
|
||||||
scanGenerateImagePreviews: Boolean!
|
scanGenerateImagePreviews: Boolean
|
||||||
"""Generate sprites during scan"""
|
"""Generate sprites during scan"""
|
||||||
scanGenerateSprites: Boolean!
|
scanGenerateSprites: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input CleanMetadataInput {
|
input CleanMetadataInput {
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ func ZipFilename(zipFilename, filenameInZip string) string {
|
|||||||
return zipFilename + zipSeparator + filenameInZip
|
return zipFilename + zipSeparator + filenameInZip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsZipPath returns true if the path includes the zip separator byte,
|
||||||
|
// indicating it is within a zip file.
|
||||||
|
// TODO - this should be moved to utils
|
||||||
|
func IsZipPath(p string) bool {
|
||||||
|
return strings.Contains(p, zipSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
type imageReadCloser struct {
|
type imageReadCloser struct {
|
||||||
src io.ReadCloser
|
src io.ReadCloser
|
||||||
zrc *zip.ReadCloser
|
zrc *zip.ReadCloser
|
||||||
|
|||||||
@@ -215,13 +215,13 @@ func (s *singleton) Scan(input models.ScanMetadataInput) {
|
|||||||
task := ScanTask{
|
task := ScanTask{
|
||||||
TxnManager: s.TxnManager,
|
TxnManager: s.TxnManager,
|
||||||
FilePath: path,
|
FilePath: path,
|
||||||
UseFileMetadata: input.UseFileMetadata,
|
UseFileMetadata: utils.IsTrue(input.UseFileMetadata),
|
||||||
StripFileExtension: input.StripFileExtension,
|
StripFileExtension: utils.IsTrue(input.StripFileExtension),
|
||||||
fileNamingAlgorithm: fileNamingAlgo,
|
fileNamingAlgorithm: fileNamingAlgo,
|
||||||
calculateMD5: calculateMD5,
|
calculateMD5: calculateMD5,
|
||||||
GeneratePreview: input.ScanGeneratePreviews,
|
GeneratePreview: utils.IsTrue(input.ScanGeneratePreviews),
|
||||||
GenerateImagePreview: input.ScanGenerateImagePreviews,
|
GenerateImagePreview: utils.IsTrue(input.ScanGenerateImagePreviews),
|
||||||
GenerateSprite: input.ScanGenerateSprites,
|
GenerateSprite: utils.IsTrue(input.ScanGenerateSprites),
|
||||||
}
|
}
|
||||||
go task.Start(&wg)
|
go task.Start(&wg)
|
||||||
|
|
||||||
|
|||||||
@@ -1044,6 +1044,12 @@ func walkFilesToScan(s *models.StashConfig, f filepath.WalkFunc) error {
|
|||||||
excludeVidRegex := generateRegexps(config.GetExcludes())
|
excludeVidRegex := generateRegexps(config.GetExcludes())
|
||||||
excludeImgRegex := generateRegexps(config.GetImageExcludes())
|
excludeImgRegex := generateRegexps(config.GetImageExcludes())
|
||||||
|
|
||||||
|
// don't scan zip images directly
|
||||||
|
if image.IsZipPath(s.Path) {
|
||||||
|
logger.Warnf("Cannot rescan zip image %s. Rescan zip gallery instead.", s.Path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
generatedPath := config.GetGeneratedPath()
|
generatedPath := config.GetGeneratedPath()
|
||||||
|
|
||||||
return utils.SymWalk(s.Path, func(path string, info os.FileInfo, err error) error {
|
return utils.SymWalk(s.Path, func(path string, info os.FileInfo, err error) error {
|
||||||
|
|||||||
@@ -7,3 +7,8 @@ func Btoi(b bool) int {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTrue returns true if the bool pointer is not nil and true.
|
||||||
|
func IsTrue(b *bool) bool {
|
||||||
|
return b != nil && *b
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import V021 from "./versions/v021.md";
|
|||||||
import V030 from "./versions/v030.md";
|
import V030 from "./versions/v030.md";
|
||||||
import V040 from "./versions/v040.md";
|
import V040 from "./versions/v040.md";
|
||||||
import V050 from "./versions/v050.md";
|
import V050 from "./versions/v050.md";
|
||||||
|
import V060 from "./versions/v060.md";
|
||||||
import { MarkdownPage } from "../Shared/MarkdownPage";
|
import { MarkdownPage } from "../Shared/MarkdownPage";
|
||||||
|
|
||||||
const Changelog: React.FC = () => {
|
const Changelog: React.FC = () => {
|
||||||
@@ -37,11 +38,19 @@ const Changelog: React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<h1 className="mb-4">Changelog:</h1>
|
<h1 className="mb-4">Changelog:</h1>
|
||||||
<Version
|
<Version
|
||||||
version={stashVersion || "v0.5.0"}
|
version={stashVersion || "v0.6.0"}
|
||||||
date={buildDate}
|
date={buildDate}
|
||||||
openState={openState}
|
openState={openState}
|
||||||
setOpenState={setVersionOpenState}
|
setOpenState={setVersionOpenState}
|
||||||
defaultOpen
|
defaultOpen
|
||||||
|
>
|
||||||
|
<MarkdownPage page={V060} />
|
||||||
|
</Version>
|
||||||
|
<Version
|
||||||
|
version="v0.5.0"
|
||||||
|
date="2021-02-23"
|
||||||
|
openState={openState}
|
||||||
|
setOpenState={setVersionOpenState}
|
||||||
>
|
>
|
||||||
<MarkdownPage page={V050} />
|
<MarkdownPage page={V050} />
|
||||||
</Version>
|
</Version>
|
||||||
|
|||||||
2
ui/v2.5/src/components/Changelog/versions/v060.md
Normal file
2
ui/v2.5/src/components/Changelog/versions/v060.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
### 🎨 Improvements
|
||||||
|
* Added Rescan button to scene, image, gallery details overflow button.
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Tab, Nav, Dropdown } from "react-bootstrap";
|
import { Tab, Nav, Dropdown } from "react-bootstrap";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useParams, useHistory, Link } from "react-router-dom";
|
import { useParams, useHistory, Link } from "react-router-dom";
|
||||||
import { useFindGallery, useGalleryUpdate } from "src/core/StashService";
|
import {
|
||||||
|
mutateMetadataScan,
|
||||||
|
useFindGallery,
|
||||||
|
useGalleryUpdate,
|
||||||
|
} from "src/core/StashService";
|
||||||
import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared";
|
import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared";
|
||||||
import { TextUtils } from "src/utils";
|
import { TextUtils } from "src/utils";
|
||||||
import * as Mousetrap from "mousetrap";
|
import * as Mousetrap from "mousetrap";
|
||||||
@@ -60,6 +64,18 @@ export const Gallery: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function onRescan() {
|
||||||
|
if (!gallery || !gallery.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutateMetadataScan({
|
||||||
|
paths: [gallery.path],
|
||||||
|
});
|
||||||
|
|
||||||
|
Toast.success({ content: "Rescanning image" });
|
||||||
|
}
|
||||||
|
|
||||||
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
function onDeleteDialogClosed(deleted: boolean) {
|
function onDeleteDialogClosed(deleted: boolean) {
|
||||||
@@ -92,6 +108,15 @@ export const Gallery: React.FC = () => {
|
|||||||
<Icon icon="ellipsis-v" />
|
<Icon icon="ellipsis-v" />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu className="bg-secondary text-white">
|
<Dropdown.Menu className="bg-secondary text-white">
|
||||||
|
{gallery?.path ? (
|
||||||
|
<Dropdown.Item
|
||||||
|
key="rescan"
|
||||||
|
className="bg-secondary text-white"
|
||||||
|
onClick={() => onRescan()}
|
||||||
|
>
|
||||||
|
Rescan
|
||||||
|
</Dropdown.Item>
|
||||||
|
) : undefined}
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
key="delete-gallery"
|
key="delete-gallery"
|
||||||
className="bg-secondary text-white"
|
className="bg-secondary text-white"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useImageDecrementO,
|
useImageDecrementO,
|
||||||
useImageResetO,
|
useImageResetO,
|
||||||
useImageUpdate,
|
useImageUpdate,
|
||||||
|
mutateMetadataScan,
|
||||||
} from "src/core/StashService";
|
} from "src/core/StashService";
|
||||||
import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared";
|
import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
@@ -43,6 +44,18 @@ export const Image: React.FC = () => {
|
|||||||
|
|
||||||
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
async function onRescan() {
|
||||||
|
if (!image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutateMetadataScan({
|
||||||
|
paths: [image.path],
|
||||||
|
});
|
||||||
|
|
||||||
|
Toast.success({ content: "Rescanning image" });
|
||||||
|
}
|
||||||
|
|
||||||
const onOrganizedClick = async () => {
|
const onOrganizedClick = async () => {
|
||||||
try {
|
try {
|
||||||
setOrganizedLoading(true);
|
setOrganizedLoading(true);
|
||||||
@@ -121,6 +134,13 @@ export const Image: React.FC = () => {
|
|||||||
<Icon icon="ellipsis-v" />
|
<Icon icon="ellipsis-v" />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu className="bg-secondary text-white">
|
<Dropdown.Menu className="bg-secondary text-white">
|
||||||
|
<Dropdown.Item
|
||||||
|
key="rescan"
|
||||||
|
className="bg-secondary text-white"
|
||||||
|
onClick={() => onRescan()}
|
||||||
|
>
|
||||||
|
Rescan
|
||||||
|
</Dropdown.Item>
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
key="delete-image"
|
key="delete-image"
|
||||||
className="bg-secondary text-white"
|
className="bg-secondary text-white"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { useParams, useLocation, useHistory, Link } from "react-router-dom";
|
import { useParams, useLocation, useHistory, Link } from "react-router-dom";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
|
mutateMetadataScan,
|
||||||
useFindScene,
|
useFindScene,
|
||||||
useSceneIncrementO,
|
useSceneIncrementO,
|
||||||
useSceneDecrementO,
|
useSceneDecrementO,
|
||||||
@@ -51,6 +52,7 @@ export const Scene: React.FC = () => {
|
|||||||
error: streamableError,
|
error: streamableError,
|
||||||
loading: streamableLoading,
|
loading: streamableLoading,
|
||||||
} = useSceneStreams(id);
|
} = useSceneStreams(id);
|
||||||
|
|
||||||
const [oLoading, setOLoading] = useState(false);
|
const [oLoading, setOLoading] = useState(false);
|
||||||
const [incrementO] = useSceneIncrementO(scene?.id ?? "0");
|
const [incrementO] = useSceneIncrementO(scene?.id ?? "0");
|
||||||
const [decrementO] = useSceneDecrementO(scene?.id ?? "0");
|
const [decrementO] = useSceneDecrementO(scene?.id ?? "0");
|
||||||
@@ -130,6 +132,18 @@ export const Scene: React.FC = () => {
|
|||||||
setTimestamp(marker.seconds);
|
setTimestamp(marker.seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onRescan() {
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutateMetadataScan({
|
||||||
|
paths: [scene.path],
|
||||||
|
});
|
||||||
|
|
||||||
|
Toast.success({ content: "Rescanning scene" });
|
||||||
|
}
|
||||||
|
|
||||||
async function onGenerateScreenshot(at?: number) {
|
async function onGenerateScreenshot(at?: number) {
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
return;
|
return;
|
||||||
@@ -184,6 +198,13 @@ export const Scene: React.FC = () => {
|
|||||||
<Icon icon="ellipsis-v" />
|
<Icon icon="ellipsis-v" />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu className="bg-secondary text-white">
|
<Dropdown.Menu className="bg-secondary text-white">
|
||||||
|
<Dropdown.Item
|
||||||
|
key="rescan"
|
||||||
|
className="bg-secondary text-white"
|
||||||
|
onClick={() => onRescan()}
|
||||||
|
>
|
||||||
|
Rescan
|
||||||
|
</Dropdown.Item>
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
key="generate"
|
key="generate"
|
||||||
className="bg-secondary text-white"
|
className="bg-secondary text-white"
|
||||||
|
|||||||
Reference in New Issue
Block a user