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 {
|
||||
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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
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 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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user