diff --git a/graphql/schema/types/metadata.graphql b/graphql/schema/types/metadata.graphql index faf4d63bc..d8b94d477 100644 --- a/graphql/schema/types/metadata.graphql +++ b/graphql/schema/types/metadata.graphql @@ -9,6 +9,8 @@ input GenerateMetadataInput { markerImagePreviews: Boolean markerScreenshots: Boolean transcodes: Boolean + """Generate transcodes even if not required""" + forceTranscodes: Boolean phashes: Boolean interactiveHeatmapsSpeeds: Boolean diff --git a/pkg/manager/task_generate.go b/pkg/manager/task_generate.go index fbd428fbe..7aaec4fba 100644 --- a/pkg/manager/task_generate.go +++ b/pkg/manager/task_generate.go @@ -252,9 +252,11 @@ func (j *GenerateJob) queueSceneJobs(scene *models.Scene, queue chan<- Task, tot } if utils.IsTrue(j.input.Transcodes) { + forceTranscode := utils.IsTrue(j.input.ForceTranscodes) task := &GenerateTranscodeTask{ Scene: *scene, Overwrite: j.overwrite, + Force: forceTranscode, fileNamingAlgorithm: j.fileNamingAlgo, } if task.isTranscodeNeeded() { diff --git a/pkg/manager/task_transcode.go b/pkg/manager/task_transcode.go index c78b31435..4530f851d 100644 --- a/pkg/manager/task_transcode.go +++ b/pkg/manager/task_transcode.go @@ -15,6 +15,9 @@ type GenerateTranscodeTask struct { Scene models.Scene Overwrite bool fileNamingAlgorithm models.HashAlgorithm + + // is true, generate even if video is browser-supported + Force bool } func (t *GenerateTranscodeTask) GetDescription() string { @@ -49,7 +52,7 @@ func (t *GenerateTranscodeTask) Start(ctc context.Context) { audioCodec = ffmpeg.AudioCodec(t.Scene.AudioCodec.String) } - if ffmpeg.IsStreamable(videoCodec, audioCodec, container) { + if !t.Force && ffmpeg.IsStreamable(videoCodec, audioCodec, container) { return } @@ -95,6 +98,14 @@ func (t *GenerateTranscodeTask) Start(ctc context.Context) { // used only when counting files to generate, doesn't affect the actual transcode generation // if container is missing from DB it is treated as non supported in order not to delay the user func (t *GenerateTranscodeTask) isTranscodeNeeded() bool { + hasTranscode := HasTranscode(&t.Scene, t.fileNamingAlgorithm) + if !t.Overwrite && hasTranscode { + return false + } + + if t.Force { + return true + } videoCodec := t.Scene.VideoCodec.String container := "" @@ -111,9 +122,5 @@ func (t *GenerateTranscodeTask) isTranscodeNeeded() bool { return false } - hasTranscode := HasTranscode(&t.Scene, t.fileNamingAlgorithm) - if !t.Overwrite && hasTranscode { - return false - } return true } diff --git a/ui/v2.5/src/components/Changelog/versions/v0120.md b/ui/v2.5/src/components/Changelog/versions/v0120.md index 35cdb9c2a..103686d79 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0120.md +++ b/ui/v2.5/src/components/Changelog/versions/v0120.md @@ -1,4 +1,5 @@ ### ✨ New Features +* Added option to force generation of transcodes for selected scenes. ([#2126](https://github.com/stashapp/stash/pull/2126)) * Added selective clean task. ([#2125](https://github.com/stashapp/stash/pull/2125)) * Show heatmaps and median stroke speed for interactive scenes on the scenes page. ([#2096](https://github.com/stashapp/stash/pull/2096)) * Save task options when scanning, generating and auto-tagging. ([#1949](https://github.com/stashapp/stash/pull/1949), [#2061](https://github.com/stashapp/stash/pull/2061)) diff --git a/ui/v2.5/src/components/Dialogs/GenerateDialog.tsx b/ui/v2.5/src/components/Dialogs/GenerateDialog.tsx index 5ad142636..1f95d54a7 100644 --- a/ui/v2.5/src/components/Dialogs/GenerateDialog.tsx +++ b/ui/v2.5/src/components/Dialogs/GenerateDialog.tsx @@ -1,15 +1,11 @@ import React, { useState, useEffect, useMemo } from "react"; import { Form, Button } from "react-bootstrap"; -import { - mutateMetadataGenerate, - useConfigureDefaults, -} from "src/core/StashService"; -import { Modal, Icon, OperationButton } from "src/components/Shared"; +import { mutateMetadataGenerate } from "src/core/StashService"; +import { Modal, Icon } from "src/components/Shared"; import { useToast } from "src/hooks"; import * as GQL from "src/core/generated-graphql"; import { FormattedMessage, useIntl } from "react-intl"; import { ConfigurationContext } from "src/hooks/Config"; -// import { DirectorySelectionDialog } from "../Settings/SettingsTasksPanel/DirectorySelectionDialog"; import { Manual } from "../Help/Manual"; import { withoutTypename } from "src/utils"; import { GenerateOptions } from "../Settings/Tasks/GenerateOptions"; @@ -25,7 +21,6 @@ export const GenerateDialog: React.FC = ({ onClose, }) => { const { configuration } = React.useContext(ConfigurationContext); - const [configureDefaults] = useConfigureDefaults(); function getDefaultOptions(): GQL.GenerateMetadataInput { return { @@ -45,10 +40,7 @@ export const GenerateDialog: React.FC = ({ getDefaultOptions() ); const [configRead, setConfigRead] = useState(false); - const [paths /* , setPaths */] = useState([]); const [showManual, setShowManual] = useState(false); - // const [settingPaths, setSettingPaths] = useState(false); - const [savingDefaults, setSavingDefaults] = useState(false); const [animation, setAnimation] = useState(true); const intl = useIntl(); @@ -111,16 +103,7 @@ export const GenerateDialog: React.FC = ({ ); } - const message = paths.length ? ( -
- : -
    - {paths.map((p) => ( -
  • {p}
  • - ))} -
-
- ) : ( + const message = ( = ({ ); - // function onClick() { - // setAnimation(false); - // setSettingPaths(true); - // } - return ( -
- {message} - {/*
- -
*/} -
+
{message}
); - }, [selectedIds, intl, paths]); + }, [selectedIds, intl]); async function onGenerate() { try { @@ -181,58 +149,11 @@ export const GenerateDialog: React.FC = ({ } } - function makeDefaultGenerateInput() { - const ret = options; - // const { paths: _paths, ...withoutSpecifics } = ret; - const { overwrite: _overwrite, ...withoutSpecifics } = ret; - return withoutSpecifics; - } - function onShowManual() { setAnimation(false); setShowManual(true); } - async function setAsDefault() { - try { - setSavingDefaults(true); - await configureDefaults({ - variables: { - input: { - generate: makeDefaultGenerateInput(), - }, - }, - }); - - Toast.success({ - content: intl.formatMessage( - { id: "config.tasks.defaults_set" }, - { action: intl.formatMessage({ id: "actions.generate" }) } - ), - }); - } catch (e) { - Toast.error(e); - } finally { - setSavingDefaults(false); - } - } - - // if (settingPaths) { - // return ( - // { - // if (p) { - // setPaths(p); - // } - // setSettingPaths(false); - // }} - // /> - // ); - // } - if (showManual) { return ( = ({ text: intl.formatMessage({ id: "actions.cancel" }), variant: "secondary", }} - disabled={savingDefaults} - footerButtons={ - - - - } leftFooterButtons={