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:
WithoutPants
2021-02-23 12:56:01 +11:00
committed by GitHub
parent 7fbb92d071
commit f7a8899d90
10 changed files with 107 additions and 12 deletions

View File

@@ -33,15 +33,15 @@ input GeneratePreviewOptionsInput {
input ScanMetadataInput {
paths: [String!]
"""Set name, date, details from metadata (if present)"""
useFileMetadata: Boolean!
useFileMetadata: Boolean
"""Strip file extension from title"""
stripFileExtension: Boolean!
stripFileExtension: Boolean
"""Generate previews during scan"""
scanGeneratePreviews: Boolean!
scanGeneratePreviews: Boolean
"""Generate image previews during scan"""
scanGenerateImagePreviews: Boolean!
scanGenerateImagePreviews: Boolean
"""Generate sprites during scan"""
scanGenerateSprites: Boolean!
scanGenerateSprites: Boolean
}
input CleanMetadataInput {

View File

@@ -59,6 +59,13 @@ func ZipFilename(zipFilename, filenameInZip string) string {
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 {
src io.ReadCloser
zrc *zip.ReadCloser

View File

@@ -215,13 +215,13 @@ func (s *singleton) Scan(input models.ScanMetadataInput) {
task := ScanTask{
TxnManager: s.TxnManager,
FilePath: path,
UseFileMetadata: input.UseFileMetadata,
StripFileExtension: input.StripFileExtension,
UseFileMetadata: utils.IsTrue(input.UseFileMetadata),
StripFileExtension: utils.IsTrue(input.StripFileExtension),
fileNamingAlgorithm: fileNamingAlgo,
calculateMD5: calculateMD5,
GeneratePreview: input.ScanGeneratePreviews,
GenerateImagePreview: input.ScanGenerateImagePreviews,
GenerateSprite: input.ScanGenerateSprites,
GeneratePreview: utils.IsTrue(input.ScanGeneratePreviews),
GenerateImagePreview: utils.IsTrue(input.ScanGenerateImagePreviews),
GenerateSprite: utils.IsTrue(input.ScanGenerateSprites),
}
go task.Start(&wg)

View File

@@ -1044,6 +1044,12 @@ func walkFilesToScan(s *models.StashConfig, f filepath.WalkFunc) error {
excludeVidRegex := generateRegexps(config.GetExcludes())
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()
return utils.SymWalk(s.Path, func(path string, info os.FileInfo, err error) error {

View File

@@ -7,3 +7,8 @@ func Btoi(b bool) int {
}
return 0
}
// IsTrue returns true if the bool pointer is not nil and true.
func IsTrue(b *bool) bool {
return b != nil && *b
}

View File

@@ -8,6 +8,7 @@ import V021 from "./versions/v021.md";
import V030 from "./versions/v030.md";
import V040 from "./versions/v040.md";
import V050 from "./versions/v050.md";
import V060 from "./versions/v060.md";
import { MarkdownPage } from "../Shared/MarkdownPage";
const Changelog: React.FC = () => {
@@ -37,11 +38,19 @@ const Changelog: React.FC = () => {
<>
<h1 className="mb-4">Changelog:</h1>
<Version
version={stashVersion || "v0.5.0"}
version={stashVersion || "v0.6.0"}
date={buildDate}
openState={openState}
setOpenState={setVersionOpenState}
defaultOpen
>
<MarkdownPage page={V060} />
</Version>
<Version
version="v0.5.0"
date="2021-02-23"
openState={openState}
setOpenState={setVersionOpenState}
>
<MarkdownPage page={V050} />
</Version>

View File

@@ -0,0 +1,2 @@
### 🎨 Improvements
* Added Rescan button to scene, image, gallery details overflow button.

View File

@@ -1,7 +1,11 @@
import { Tab, Nav, Dropdown } from "react-bootstrap";
import React, { useEffect, useState } from "react";
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 { TextUtils } from "src/utils";
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);
function onDeleteDialogClosed(deleted: boolean) {
@@ -92,6 +108,15 @@ export const Gallery: React.FC = () => {
<Icon icon="ellipsis-v" />
</Dropdown.Toggle>
<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
key="delete-gallery"
className="bg-secondary text-white"

View File

@@ -7,6 +7,7 @@ import {
useImageDecrementO,
useImageResetO,
useImageUpdate,
mutateMetadataScan,
} from "src/core/StashService";
import { ErrorMessage, LoadingIndicator, Icon } from "src/components/Shared";
import { useToast } from "src/hooks";
@@ -43,6 +44,18 @@ export const Image: React.FC = () => {
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 () => {
try {
setOrganizedLoading(true);
@@ -121,6 +134,13 @@ export const Image: React.FC = () => {
<Icon icon="ellipsis-v" />
</Dropdown.Toggle>
<Dropdown.Menu className="bg-secondary text-white">
<Dropdown.Item
key="rescan"
className="bg-secondary text-white"
onClick={() => onRescan()}
>
Rescan
</Dropdown.Item>
<Dropdown.Item
key="delete-image"
className="bg-secondary text-white"

View File

@@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react";
import { useParams, useLocation, useHistory, Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import {
mutateMetadataScan,
useFindScene,
useSceneIncrementO,
useSceneDecrementO,
@@ -51,6 +52,7 @@ export const Scene: React.FC = () => {
error: streamableError,
loading: streamableLoading,
} = useSceneStreams(id);
const [oLoading, setOLoading] = useState(false);
const [incrementO] = useSceneIncrementO(scene?.id ?? "0");
const [decrementO] = useSceneDecrementO(scene?.id ?? "0");
@@ -130,6 +132,18 @@ export const Scene: React.FC = () => {
setTimestamp(marker.seconds);
}
async function onRescan() {
if (!scene) {
return;
}
await mutateMetadataScan({
paths: [scene.path],
});
Toast.success({ content: "Rescanning scene" });
}
async function onGenerateScreenshot(at?: number) {
if (!scene) {
return;
@@ -184,6 +198,13 @@ export const Scene: React.FC = () => {
<Icon icon="ellipsis-v" />
</Dropdown.Toggle>
<Dropdown.Menu className="bg-secondary text-white">
<Dropdown.Item
key="rescan"
className="bg-secondary text-white"
onClick={() => onRescan()}
>
Rescan
</Dropdown.Item>
<Dropdown.Item
key="generate"
className="bg-secondary text-white"