mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Make audio stream optional for preview generation (#1454)
This commit is contained in:
@@ -10,6 +10,7 @@ fragment ConfigGeneralData on ConfigGeneralResult {
|
|||||||
calculateMD5
|
calculateMD5
|
||||||
videoFileNamingAlgorithm
|
videoFileNamingAlgorithm
|
||||||
parallelTasks
|
parallelTasks
|
||||||
|
previewAudio
|
||||||
previewSegments
|
previewSegments
|
||||||
previewSegmentDuration
|
previewSegmentDuration
|
||||||
previewExcludeStart
|
previewExcludeStart
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ input ConfigGeneralInput {
|
|||||||
videoFileNamingAlgorithm: HashAlgorithm!
|
videoFileNamingAlgorithm: HashAlgorithm!
|
||||||
"""Number of parallel tasks to start during scan/generate"""
|
"""Number of parallel tasks to start during scan/generate"""
|
||||||
parallelTasks: Int
|
parallelTasks: Int
|
||||||
|
"""Include audio stream in previews"""
|
||||||
|
previewAudio: Boolean!
|
||||||
"""Number of segments in a preview file"""
|
"""Number of segments in a preview file"""
|
||||||
previewSegments: Int
|
previewSegments: Int
|
||||||
"""Preview segment duration, in seconds"""
|
"""Preview segment duration, in seconds"""
|
||||||
@@ -116,6 +118,8 @@ type ConfigGeneralResult {
|
|||||||
videoFileNamingAlgorithm: HashAlgorithm!
|
videoFileNamingAlgorithm: HashAlgorithm!
|
||||||
"""Number of parallel tasks to start during scan/generate"""
|
"""Number of parallel tasks to start during scan/generate"""
|
||||||
parallelTasks: Int!
|
parallelTasks: Int!
|
||||||
|
"""Include audio stream in previews"""
|
||||||
|
previewAudio: Boolean!
|
||||||
"""Number of segments in a preview file"""
|
"""Number of segments in a preview file"""
|
||||||
previewSegments: Int!
|
previewSegments: Int!
|
||||||
"""Preview segment duration, in seconds"""
|
"""Preview segment duration, in seconds"""
|
||||||
|
|||||||
@@ -88,6 +88,9 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co
|
|||||||
if input.ParallelTasks != nil {
|
if input.ParallelTasks != nil {
|
||||||
c.Set(config.ParallelTasks, *input.ParallelTasks)
|
c.Set(config.ParallelTasks, *input.ParallelTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Set(config.PreviewAudio, input.PreviewAudio)
|
||||||
|
|
||||||
if input.PreviewSegments != nil {
|
if input.PreviewSegments != nil {
|
||||||
c.Set(config.PreviewSegments, *input.PreviewSegments)
|
c.Set(config.PreviewSegments, *input.PreviewSegments)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ func makeConfigGeneralResult() *models.ConfigGeneralResult {
|
|||||||
CalculateMd5: config.IsCalculateMD5(),
|
CalculateMd5: config.IsCalculateMD5(),
|
||||||
VideoFileNamingAlgorithm: config.GetVideoFileNamingAlgorithm(),
|
VideoFileNamingAlgorithm: config.GetVideoFileNamingAlgorithm(),
|
||||||
ParallelTasks: config.GetParallelTasks(),
|
ParallelTasks: config.GetParallelTasks(),
|
||||||
|
PreviewAudio: config.GetPreviewAudio(),
|
||||||
PreviewSegments: config.GetPreviewSegments(),
|
PreviewSegments: config.GetPreviewSegments(),
|
||||||
PreviewSegmentDuration: config.GetPreviewSegmentDuration(),
|
PreviewSegmentDuration: config.GetPreviewSegmentDuration(),
|
||||||
PreviewExcludeStart: config.GetPreviewExcludeStart(),
|
PreviewExcludeStart: config.GetPreviewExcludeStart(),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type ScenePreviewChunkOptions struct {
|
|||||||
Duration float64
|
Duration float64
|
||||||
Width int
|
Width int
|
||||||
OutputPath string
|
OutputPath string
|
||||||
|
Audio bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePreviewChunkOptions, preset string, fallback bool) error {
|
func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePreviewChunkOptions, preset string, fallback bool) error {
|
||||||
@@ -23,6 +24,17 @@ func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePre
|
|||||||
"-v", "error",
|
"-v", "error",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
argsAudio := []string{
|
||||||
|
"-c:a", "aac",
|
||||||
|
"-b:a", "128k",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !options.Audio {
|
||||||
|
argsAudio = []string{
|
||||||
|
"-an",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Non-fallback: enable xerror.
|
// Non-fallback: enable xerror.
|
||||||
// "-xerror" causes ffmpeg to fail on warnings, often the preview is fine but could be broken.
|
// "-xerror" causes ffmpeg to fail on warnings, often the preview is fine but could be broken.
|
||||||
if !fallback {
|
if !fallback {
|
||||||
@@ -70,13 +82,12 @@ func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePre
|
|||||||
"-crf", "21",
|
"-crf", "21",
|
||||||
"-threads", "4",
|
"-threads", "4",
|
||||||
"-vf", fmt.Sprintf("scale=%v:-2", options.Width),
|
"-vf", fmt.Sprintf("scale=%v:-2", options.Width),
|
||||||
"-c:a", "aac",
|
|
||||||
"-b:a", "128k",
|
|
||||||
"-strict", "-2",
|
"-strict", "-2",
|
||||||
options.OutputPath,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finalArgs := append(args, args2...)
|
args3 := append(args, args2...)
|
||||||
|
args3 = append(args3, argsAudio...)
|
||||||
|
finalArgs := append(args3, options.OutputPath)
|
||||||
|
|
||||||
_, err := e.run(probeResult, finalArgs)
|
_, err := e.run(probeResult, finalArgs)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -56,14 +56,17 @@ const CalculateMD5 = "calculate_md5"
|
|||||||
// should be used when generating and using generated files for scenes.
|
// should be used when generating and using generated files for scenes.
|
||||||
const VideoFileNamingAlgorithm = "video_file_naming_algorithm"
|
const VideoFileNamingAlgorithm = "video_file_naming_algorithm"
|
||||||
|
|
||||||
const PreviewPreset = "preview_preset"
|
|
||||||
|
|
||||||
const MaxTranscodeSize = "max_transcode_size"
|
const MaxTranscodeSize = "max_transcode_size"
|
||||||
const MaxStreamingTranscodeSize = "max_streaming_transcode_size"
|
const MaxStreamingTranscodeSize = "max_streaming_transcode_size"
|
||||||
|
|
||||||
const ParallelTasks = "parallel_tasks"
|
const ParallelTasks = "parallel_tasks"
|
||||||
const parallelTasksDefault = 1
|
const parallelTasksDefault = 1
|
||||||
|
|
||||||
|
const PreviewPreset = "preview_preset"
|
||||||
|
|
||||||
|
const PreviewAudio = "preview_audio"
|
||||||
|
const previewAudioDefault = true
|
||||||
|
|
||||||
const PreviewSegmentDuration = "preview_segment_duration"
|
const PreviewSegmentDuration = "preview_segment_duration"
|
||||||
const previewSegmentDurationDefault = 0.75
|
const previewSegmentDurationDefault = 0.75
|
||||||
|
|
||||||
@@ -403,6 +406,11 @@ func (i *Instance) GetParallelTasksWithAutoDetection() int {
|
|||||||
return parallelTasks
|
return parallelTasks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) GetPreviewAudio() bool {
|
||||||
|
viper.SetDefault(PreviewAudio, previewAudioDefault)
|
||||||
|
return viper.GetBool(PreviewAudio)
|
||||||
|
}
|
||||||
|
|
||||||
// GetPreviewSegments returns the amount of segments in a scene preview file.
|
// GetPreviewSegments returns the amount of segments in a scene preview file.
|
||||||
func (i *Instance) GetPreviewSegments() int {
|
func (i *Instance) GetPreviewSegments() int {
|
||||||
return viper.GetInt(PreviewSegments)
|
return viper.GetInt(PreviewSegments)
|
||||||
@@ -560,7 +568,6 @@ func (i *Instance) GetMenuItems() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instance) GetSoundOnPreview() bool {
|
func (i *Instance) GetSoundOnPreview() bool {
|
||||||
viper.SetDefault(SoundOnPreview, false)
|
|
||||||
return viper.GetBool(SoundOnPreview)
|
return viper.GetBool(SoundOnPreview)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,6 +747,7 @@ func (i *Instance) setDefaultValues() error {
|
|||||||
viper.SetDefault(PreviewSegments, previewSegmentsDefault)
|
viper.SetDefault(PreviewSegments, previewSegmentsDefault)
|
||||||
viper.SetDefault(PreviewExcludeStart, previewExcludeStartDefault)
|
viper.SetDefault(PreviewExcludeStart, previewExcludeStartDefault)
|
||||||
viper.SetDefault(PreviewExcludeEnd, previewExcludeEndDefault)
|
viper.SetDefault(PreviewExcludeEnd, previewExcludeEndDefault)
|
||||||
|
viper.SetDefault(SoundOnPreview, false)
|
||||||
|
|
||||||
viper.SetDefault(Database, i.GetDefaultDatabaseFilePath())
|
viper.SetDefault(Database, i.GetDefaultDatabaseFilePath())
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ type GeneratorInfo struct {
|
|||||||
ExcludeEnd string
|
ExcludeEnd string
|
||||||
|
|
||||||
VideoFile ffmpeg.VideoFile
|
VideoFile ffmpeg.VideoFile
|
||||||
|
|
||||||
|
Audio bool // used for preview generation
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGeneratorInfo(videoFile ffmpeg.VideoFile) (*GeneratorInfo, error) {
|
func newGeneratorInfo(videoFile ffmpeg.VideoFile) (*GeneratorInfo, error) {
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder, fallback bool)
|
|||||||
logger.Warnf("[generator] Segment duration (%f) too short.Using 0.75 instead.", g.Info.ChunkDuration)
|
logger.Warnf("[generator] Segment duration (%f) too short.Using 0.75 instead.", g.Info.ChunkDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
includeAudio := g.Info.Audio
|
||||||
|
|
||||||
for i := 0; i < g.Info.ChunkCount; i++ {
|
for i := 0; i < g.Info.ChunkCount; i++ {
|
||||||
time := offset + (float64(i) * stepSize)
|
time := offset + (float64(i) * stepSize)
|
||||||
num := fmt.Sprintf("%.3d", i)
|
num := fmt.Sprintf("%.3d", i)
|
||||||
@@ -124,6 +126,7 @@ func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder, fallback bool)
|
|||||||
Duration: durationSegment,
|
Duration: durationSegment,
|
||||||
Width: 640,
|
Width: 640,
|
||||||
OutputPath: chunkOutputPath,
|
OutputPath: chunkOutputPath,
|
||||||
|
Audio: includeAudio,
|
||||||
}
|
}
|
||||||
if err := encoder.ScenePreviewVideoChunk(g.Info.VideoFile, options, g.PreviewPreset, fallback); err != nil {
|
if err := encoder.ScenePreviewVideoChunk(g.Info.VideoFile, options, g.PreviewPreset, fallback); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
@@ -50,6 +51,7 @@ func (t *GeneratePreviewTask) Start(wg *sizedwaitgroup.SizedWaitGroup) {
|
|||||||
generator.Info.ChunkDuration = *t.Options.PreviewSegmentDuration
|
generator.Info.ChunkDuration = *t.Options.PreviewSegmentDuration
|
||||||
generator.Info.ExcludeStart = *t.Options.PreviewExcludeStart
|
generator.Info.ExcludeStart = *t.Options.PreviewExcludeStart
|
||||||
generator.Info.ExcludeEnd = *t.Options.PreviewExcludeEnd
|
generator.Info.ExcludeEnd = *t.Options.PreviewExcludeEnd
|
||||||
|
generator.Info.Audio = config.GetInstance().GetPreviewAudio()
|
||||||
|
|
||||||
if err := generator.Generate(); err != nil {
|
if err := generator.Generate(); err != nil {
|
||||||
logger.Errorf("error generating preview: %s", err.Error())
|
logger.Errorf("error generating preview: %s", err.Error())
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* Added [DLNA server](/settings?tab=dlna). ([#1364](https://github.com/stashapp/stash/pull/1364))
|
* Added [DLNA server](/settings?tab=dlna). ([#1364](https://github.com/stashapp/stash/pull/1364))
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Add option to disable audio for generated previews. ([#1454](https://github.com/stashapp/stash/pull/1454))
|
||||||
* Prompt when leaving scene edit page with unsaved changes. ([#1429](https://github.com/stashapp/stash/pull/1429))
|
* Prompt when leaving scene edit page with unsaved changes. ([#1429](https://github.com/stashapp/stash/pull/1429))
|
||||||
* Make multi-set mode buttons more obvious in multi-edit dialog. ([#1435](https://github.com/stashapp/stash/pull/1435))
|
* Make multi-set mode buttons more obvious in multi-edit dialog. ([#1435](https://github.com/stashapp/stash/pull/1435))
|
||||||
* Filter modifiers and sort by options are now sorted alphabetically. ([#1406](https://github.com/stashapp/stash/pull/1406))
|
* Filter modifiers and sort by options are now sorted alphabetically. ([#1406](https://github.com/stashapp/stash/pull/1406))
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
GQL.HashAlgorithm | undefined
|
GQL.HashAlgorithm | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
const [parallelTasks, setParallelTasks] = useState<number>(0);
|
const [parallelTasks, setParallelTasks] = useState<number>(0);
|
||||||
|
const [previewAudio, setPreviewAudio] = useState<boolean>(true);
|
||||||
const [previewSegments, setPreviewSegments] = useState<number>(0);
|
const [previewSegments, setPreviewSegments] = useState<number>(0);
|
||||||
const [previewSegmentDuration, setPreviewSegmentDuration] = useState<number>(
|
const [previewSegmentDuration, setPreviewSegmentDuration] = useState<number>(
|
||||||
0
|
0
|
||||||
@@ -149,6 +150,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
videoFileNamingAlgorithm:
|
videoFileNamingAlgorithm:
|
||||||
(videoFileNamingAlgorithm as GQL.HashAlgorithm) ?? undefined,
|
(videoFileNamingAlgorithm as GQL.HashAlgorithm) ?? undefined,
|
||||||
parallelTasks,
|
parallelTasks,
|
||||||
|
previewAudio,
|
||||||
previewSegments,
|
previewSegments,
|
||||||
previewSegmentDuration,
|
previewSegmentDuration,
|
||||||
previewExcludeStart,
|
previewExcludeStart,
|
||||||
@@ -194,6 +196,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
setVideoFileNamingAlgorithm(conf.general.videoFileNamingAlgorithm);
|
setVideoFileNamingAlgorithm(conf.general.videoFileNamingAlgorithm);
|
||||||
setCalculateMD5(conf.general.calculateMD5);
|
setCalculateMD5(conf.general.calculateMD5);
|
||||||
setParallelTasks(conf.general.parallelTasks);
|
setParallelTasks(conf.general.parallelTasks);
|
||||||
|
setPreviewAudio(conf.general.previewAudio);
|
||||||
setPreviewSegments(conf.general.previewSegments);
|
setPreviewSegments(conf.general.previewSegments);
|
||||||
setPreviewSegmentDuration(conf.general.previewSegmentDuration);
|
setPreviewSegmentDuration(conf.general.previewSegmentDuration);
|
||||||
setPreviewExcludeStart(conf.general.previewExcludeStart);
|
setPreviewExcludeStart(conf.general.previewExcludeStart);
|
||||||
@@ -656,6 +659,19 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
not recommended.
|
not recommended.
|
||||||
</Form.Text>
|
</Form.Text>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Check
|
||||||
|
id="preview-include-audio"
|
||||||
|
checked={previewAudio}
|
||||||
|
label="Include audio"
|
||||||
|
onChange={() => setPreviewAudio(!previewAudio)}
|
||||||
|
/>
|
||||||
|
<Form.Text className="text-muted">
|
||||||
|
Includes audio stream when generating previews.
|
||||||
|
</Form.Text>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
<Form.Group id="preview-segments">
|
<Form.Group id="preview-segments">
|
||||||
<h6>Number of segments in preview</h6>
|
<h6>Number of segments in preview</h6>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
|
|||||||
Reference in New Issue
Block a user