Generate cover image (#376)

* Make mutating metadata ops mutation
* Implement scene generate screenshot
* Remove fetch policy on metadata mutations
* Port UI changes to v2.5
* Set generated image in database
This commit is contained in:
WithoutPants
2020-03-12 08:34:04 +11:00
committed by GitHub
parent 34d829338d
commit 3de6955a9e
28 changed files with 442 additions and 168 deletions

View File

@@ -18,7 +18,7 @@ export const PerformerOperationsPanel: React.FC<IPerformerOperationsProps> = ({
return;
}
try {
await StashService.queryMetadataAutoTag({ performers: [performer.id] });
await StashService.mutateMetadataAutoTag({ performers: [performer.id] });
Toast.success({ content: "Started auto tagging" });
} catch (e) {
Toast.error(e);

View File

@@ -14,6 +14,7 @@ import { SceneFileInfoPanel } from "./SceneFileInfoPanel";
import { SceneEditPanel } from "./SceneEditPanel";
import { SceneDetailPanel } from "./SceneDetailPanel";
import { OCounterButton } from "./OCounterButton";
import { SceneOperationsPanel } from "./SceneOperationsPanel";
export const Scene: React.FC = () => {
const { id = "new" } = useParams();
@@ -144,6 +145,11 @@ export const Scene: React.FC = () => {
onDelete={() => history.push("/scenes")}
/>
</Tab>
<Tab eventKey="scene-operations-panel" title="Operations">
<SceneOperationsPanel
scene={scene}
/>
</Tab>
</Tabs>
</div>
</>

View File

@@ -0,0 +1,36 @@
import { Button } from "react-bootstrap";
import React, { FunctionComponent } from "react";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
import { useToast } from "src/hooks";
import { JWUtils } from "src/utils";
interface IOperationsPanelProps {
scene: GQL.SceneDataFragment;
}
export const SceneOperationsPanel: FunctionComponent<IOperationsPanelProps> = (props: IOperationsPanelProps) => {
const Toast = useToast();
const [generateScreenshot] = StashService.useSceneGenerateScreenshot();
async function onGenerateScreenshot(at?: number) {
await generateScreenshot({
variables: {
id: props.scene.id,
at: at
}
});
Toast.success({ content: "Generating screenshot" });
}
return (
<>
<Button className="edit-button" onClick={() => onGenerateScreenshot(JWUtils.getPlayer().getPosition())}>
Generate thumbnail from current
</Button>
<Button className="edit-button" onClick={() => onGenerateScreenshot()}>
Generate default thumbnail
</Button>
</>
);
};

View File

@@ -12,7 +12,7 @@ export const GenerateButton: React.FC = () => {
async function onGenerate() {
try {
await StashService.queryMetadataGenerate({
await StashService.mutateMetadataGenerate({
sprites,
previews,
markers,

View File

@@ -68,7 +68,7 @@ export const SettingsTasksPanel: React.FC = () => {
function onImport() {
setIsImportAlertOpen(false);
StashService.queryMetadataImport().then(() => {
StashService.mutateMetadataImport().then(() => {
jobStatus.refetch();
});
}
@@ -91,7 +91,7 @@ export const SettingsTasksPanel: React.FC = () => {
function onClean() {
setIsCleanAlertOpen(false);
StashService.queryMetadataClean().then(() => {
StashService.mutateMetadataClean().then(() => {
jobStatus.refetch();
});
}
@@ -115,7 +115,7 @@ export const SettingsTasksPanel: React.FC = () => {
async function onScan() {
try {
await StashService.queryMetadataScan({ useFileMetadata });
await StashService.mutateMetadataScan({ useFileMetadata });
Toast.success({ content: "Started scan" });
jobStatus.refetch();
} catch (e) {
@@ -134,7 +134,7 @@ export const SettingsTasksPanel: React.FC = () => {
async function onAutoTag() {
try {
await StashService.queryMetadataAutoTag(getAutoTagInput());
await StashService.mutateMetadataAutoTag(getAutoTagInput());
Toast.success({ content: "Started auto tagging" });
jobStatus.refetch();
} catch (e) {
@@ -153,7 +153,7 @@ export const SettingsTasksPanel: React.FC = () => {
id="stop"
variant="danger"
onClick={() =>
StashService.queryStopJob().then(() => jobStatus.refetch())
StashService.mutateStopJob().then(() => jobStatus.refetch())
}
>
Stop
@@ -277,7 +277,7 @@ export const SettingsTasksPanel: React.FC = () => {
variant="secondary"
type="submit"
onClick={() =>
StashService.queryMetadataExport().then(() => {
StashService.mutateMetadataExport().then(() => {
jobStatus.refetch();
})
}

View File

@@ -115,7 +115,7 @@ export const Studio: React.FC = () => {
async function onAutoTag() {
if (!studio.id) return;
try {
await StashService.queryMetadataAutoTag({ studios: [studio.id] });
await StashService.mutateMetadataAutoTag({ studios: [studio.id] });
Toast.success({ content: "Started auto tagging" });
} catch (e) {
Toast.error(e);

View File

@@ -62,7 +62,7 @@ export const TagList: React.FC = () => {
async function onAutoTag(tag: GQL.TagDataFragment) {
if (!tag) return;
try {
await StashService.queryMetadataAutoTag({ tags: [tag.id] });
await StashService.mutateMetadataAutoTag({ tags: [tag.id] });
Toast.success({ content: "Started auto tagging" });
} catch (e) {
Toast.error(e);

View File

@@ -407,6 +407,12 @@ export class StashService {
});
}
public static useSceneGenerateScreenshot() {
return GQL.useSceneGenerateScreenshotMutation({
update: () => StashService.invalidateQueries(["findScenes"]),
});
}
private static studioMutationImpactedQueries = [
"findStudios",
"findScenes",
@@ -506,10 +512,9 @@ export class StashService {
});
}
public static queryStopJob() {
return StashService.client.query<GQL.StopJobQuery>({
query: GQL.StopJobDocument,
fetchPolicy: "network-only"
public static mutateStopJob() {
return StashService.client.mutate<GQL.StopJobMutation>({
mutation: GQL.StopJobDocument,
});
}
@@ -566,48 +571,42 @@ export class StashService {
});
}
public static queryMetadataScan(input: GQL.ScanMetadataInput) {
return StashService.client.query<GQL.MetadataScanQuery>({
query: GQL.MetadataScanDocument,
public static mutateMetadataScan(input: GQL.ScanMetadataInput) {
return StashService.client.mutate<GQL.MetadataScanMutation>({
mutation: GQL.MetadataScanDocument,
variables: { input },
fetchPolicy: "network-only"
});
}
public static queryMetadataAutoTag(input: GQL.AutoTagMetadataInput) {
return StashService.client.query<GQL.MetadataAutoTagQuery>({
query: GQL.MetadataAutoTagDocument,
public static mutateMetadataAutoTag(input: GQL.AutoTagMetadataInput) {
return StashService.client.mutate<GQL.MetadataAutoTagMutation>({
mutation: GQL.MetadataAutoTagDocument,
variables: { input },
fetchPolicy: "network-only"
});
}
public static queryMetadataGenerate(input: GQL.GenerateMetadataInput) {
return StashService.client.query<GQL.MetadataGenerateQuery>({
query: GQL.MetadataGenerateDocument,
public static mutateMetadataGenerate(input: GQL.GenerateMetadataInput) {
return StashService.client.mutate<GQL.MetadataGenerateMutation>({
mutation: GQL.MetadataGenerateDocument,
variables: { input },
fetchPolicy: "network-only"
});
}
public static queryMetadataClean() {
return StashService.client.query<GQL.MetadataCleanQuery>({
query: GQL.MetadataCleanDocument,
fetchPolicy: "network-only"
public static mutateMetadataClean() {
return StashService.client.mutate<GQL.MetadataCleanMutation>({
mutation: GQL.MetadataCleanDocument,
});
}
public static queryMetadataExport() {
return StashService.client.query<GQL.MetadataExportQuery>({
query: GQL.MetadataExportDocument,
fetchPolicy: "network-only"
public static mutateMetadataExport() {
return StashService.client.mutate<GQL.MetadataExportMutation>({
mutation: GQL.MetadataExportDocument,
});
}
public static queryMetadataImport() {
return StashService.client.query<GQL.MetadataImportQuery>({
query: GQL.MetadataImportDocument,
fetchPolicy: "network-only"
public static mutateMetadataImport() {
return StashService.client.mutate<GQL.MetadataImportMutation>({
mutation: GQL.MetadataImportDocument,
});
}

View File

@@ -18,7 +18,7 @@ export const GenerateButton: FunctionComponent<IProps> = () => {
async function onGenerate() {
try {
await StashService.queryMetadataGenerate({sprites, previews, markers, transcodes});
await StashService.mutateMetadataGenerate({sprites, previews, markers, transcodes});
ToastUtils.success("Started generating");
} catch (e) {
ErrorUtils.handle(e);

View File

@@ -78,7 +78,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
function onImport() {
setIsImportAlertOpen(false);
StashService.queryMetadataImport().then(() => { jobStatus.refetch()});
StashService.mutateMetadataImport().then(() => { jobStatus.refetch()});
}
function renderImportAlert() {
@@ -102,7 +102,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
function onClean() {
setIsCleanAlertOpen(false);
StashService.queryMetadataClean().then(() => { jobStatus.refetch()});
StashService.mutateMetadataClean().then(() => { jobStatus.refetch()});
}
function renderCleanAlert() {
@@ -127,7 +127,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
async function onScan() {
try {
await StashService.queryMetadataScan({useFileMetadata: useFileMetadata});
await StashService.mutateMetadataScan({useFileMetadata: useFileMetadata});
ToastUtils.success("Started scan");
jobStatus.refetch();
} catch (e) {
@@ -146,7 +146,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
async function onAutoTag() {
try {
await StashService.queryMetadataAutoTag(getAutoTagInput());
await StashService.mutateMetadataAutoTag(getAutoTagInput());
ToastUtils.success("Started auto tagging");
jobStatus.refetch();
} catch (e) {
@@ -162,7 +162,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
return (
<>
<FormGroup>
<Button id="stop" text="Stop" intent="danger" onClick={() => StashService.queryStopJob().then(() => jobStatus.refetch())} />
<Button id="stop" text="Stop" intent="danger" onClick={() => StashService.mutateStopJob().then(() => jobStatus.refetch())} />
</FormGroup>
</>
);
@@ -256,7 +256,7 @@ export const SettingsTasksPanel: FunctionComponent<IProps> = (props: IProps) =>
labelFor="export"
inline={true}
>
<Button id="export" text="Export" onClick={() => StashService.queryMetadataExport().then(() => { jobStatus.refetch()})} />
<Button id="export" text="Export" onClick={() => StashService.mutateMetadataExport().then(() => { jobStatus.refetch()})} />
</FormGroup>
<FormGroup

View File

@@ -109,7 +109,7 @@ export const Studio: FunctionComponent<IProps> = (props: IProps) => {
return;
}
try {
await StashService.queryMetadataAutoTag({ studios: [studio.id]});
await StashService.mutateMetadataAutoTag({ studios: [studio.id]});
ToastUtils.success("Started auto tagging");
} catch (e) {
ErrorUtils.handle(e);

View File

@@ -76,7 +76,7 @@ export const TagList: FunctionComponent<IProps> = (props: IProps) => {
return;
}
try {
await StashService.queryMetadataAutoTag({ tags: [tag.id]});
await StashService.mutateMetadataAutoTag({ tags: [tag.id]});
ToastUtils.success("Started auto tagging");
} catch (e) {
ErrorUtils.handle(e);

View File

@@ -18,7 +18,7 @@ export const PerformerOperationsPanel: FunctionComponent<IPerformerOperationsPro
return;
}
try {
await StashService.queryMetadataAutoTag({ performers: [props.performer.id]});
await StashService.mutateMetadataAutoTag({ performers: [props.performer.id]});
ToastUtils.success("Started auto tagging");
} catch (e) {
ErrorUtils.handle(e);

View File

@@ -19,6 +19,7 @@ import { ScenePerformerPanel } from "./ScenePerformerPanel";
import { SceneMoviePanel } from "./SceneMoviePanel";
import { ErrorUtils } from "../../../utils/errors";
import { IOCounterButtonProps, OCounterButton } from "../OCounterButton";
import { SceneOperationsPanel } from "./SceneOperationsPanel";
interface ISceneProps extends IBaseProps {}
@@ -160,6 +161,14 @@ export const Scene: FunctionComponent<ISceneProps> = (props: ISceneProps) => {
onDelete={() => props.history.push("/scenes")}
/>}
/>
<Tab
id="scene-operations-panel"
title="Operations"
panel={
<SceneOperationsPanel
scene={modifiedScene}
/>}
/>
<Tabs.Expander />
<OCounterButton

View File

@@ -0,0 +1,47 @@
import {
Button,
} from "@blueprintjs/core";
import React, { FunctionComponent } from "react";
import * as GQL from "../../../core/generated-graphql";
import { StashService } from "../../../core/StashService";
import { SceneHelpers } from "../helpers";
import { ToastUtils } from "../../../utils/toasts";
interface IOperationsPanelProps {
scene: GQL.SceneDataFragment;
}
export const SceneOperationsPanel: FunctionComponent<IOperationsPanelProps> = (props: IOperationsPanelProps) => {
const jwplayer = SceneHelpers.getPlayer();
const generateScreenshot = StashService.useSceneGenerateScreenshot();
async function onGenerateScreenshot() {
let position = jwplayer.getPosition();
await generateScreenshot({
variables: {
id: props.scene.id,
at: position
}
});
ToastUtils.success("Generating screenshot");
}
async function onGenerateDefaultScreenshot() {
await generateScreenshot({
variables: {
id: props.scene.id,
}
});
ToastUtils.success("Generating screenshot");
}
return (
<>
<Button className="edit-button" text="Generate thumbnail from current" onClick={() => onGenerateScreenshot()}/>
<Button className="edit-button" text="Generate default thumbnail" onClick={() => onGenerateDefaultScreenshot()}/>
</>
);
};

View File

@@ -355,6 +355,12 @@ export class StashService {
});
}
public static useSceneGenerateScreenshot() {
return GQL.useSceneGenerateScreenshot({
update: () => StashService.invalidateQueries(["findScenes"]),
});
}
private static studioMutationImpactedQueries = [
"findStudios",
"findScenes",
@@ -467,10 +473,9 @@ export class StashService {
});
}
public static queryStopJob() {
return StashService.client.query<GQL.StopJobQuery>({
query: GQL.StopJobDocument,
fetchPolicy: "network-only",
public static mutateStopJob() {
return StashService.client.mutate<GQL.StopJobMutation>({
mutation: GQL.StopJobDocument,
});
}
@@ -521,48 +526,42 @@ export class StashService {
});
}
public static queryMetadataScan(input: GQL.ScanMetadataInput) {
return StashService.client.query<GQL.MetadataScanQuery>({
query: GQL.MetadataScanDocument,
public static mutateMetadataScan(input: GQL.ScanMetadataInput) {
return StashService.client.mutate<GQL.MetadataScanMutation>({
mutation: GQL.MetadataScanDocument,
variables: { input },
fetchPolicy: "network-only",
});
}
public static queryMetadataAutoTag(input: GQL.AutoTagMetadataInput) {
return StashService.client.query<GQL.MetadataAutoTagQuery>({
query: GQL.MetadataAutoTagDocument,
public static mutateMetadataAutoTag(input: GQL.AutoTagMetadataInput) {
return StashService.client.mutate<GQL.MetadataAutoTagMutation>({
mutation: GQL.MetadataAutoTagDocument,
variables: { input },
fetchPolicy: "network-only",
});
}
public static queryMetadataGenerate(input: GQL.GenerateMetadataInput) {
return StashService.client.query<GQL.MetadataGenerateQuery>({
query: GQL.MetadataGenerateDocument,
public static mutateMetadataGenerate(input: GQL.GenerateMetadataInput) {
return StashService.client.mutate<GQL.MetadataGenerateMutation>({
mutation: GQL.MetadataGenerateDocument,
variables: { input },
fetchPolicy: "network-only",
});
}
public static queryMetadataClean() {
return StashService.client.query<GQL.MetadataCleanQuery>({
query: GQL.MetadataCleanDocument,
fetchPolicy: "network-only",
public static mutateMetadataClean() {
return StashService.client.mutate<GQL.MetadataCleanMutation>({
mutation: GQL.MetadataCleanDocument,
});
}
public static queryMetadataExport() {
return StashService.client.query<GQL.MetadataExportQuery>({
query: GQL.MetadataExportDocument,
fetchPolicy: "network-only",
public static mutateMetadataExport() {
return StashService.client.mutate<GQL.MetadataExportMutation>({
mutation: GQL.MetadataExportDocument,
});
}
public static queryMetadataImport() {
return StashService.client.query<GQL.MetadataImportQuery>({
query: GQL.MetadataImportDocument,
fetchPolicy: "network-only",
public static mutateMetadataImport() {
return StashService.client.mutate<GQL.MetadataImportMutation>({
mutation: GQL.MetadataImportDocument,
});
}