mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
CLI: Improve "photoprism video" subcommands
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
4b8c41b96d
commit
898f6bc69b
9 changed files with 61 additions and 19 deletions
|
|
@ -56,7 +56,6 @@ var PhotoPrism = []*cli.Command{
|
|||
StatusCommand,
|
||||
IndexCommand,
|
||||
FindCommand,
|
||||
VideoCommands,
|
||||
ImportCommand,
|
||||
CopyCommand,
|
||||
DownloadCommand,
|
||||
|
|
@ -69,6 +68,7 @@ var PhotoPrism = []*cli.Command{
|
|||
MomentsCommand,
|
||||
ConvertCommand,
|
||||
ThumbsCommand,
|
||||
VideosCommands,
|
||||
MigrateCommand,
|
||||
MigrationsCommands,
|
||||
BackupCommand,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ var VideoInfoCommand = &cli.Command{
|
|||
func videoInfoAction(ctx *cli.Context) error {
|
||||
return CallWithDependencies(ctx, func(conf *config.Config) error {
|
||||
filter := videoNormalizeFilter(ctx.Args().Slice())
|
||||
results, err := videoSearchResults(filter, ctx.Uint(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
results, err := videoSearchResults(filter, ctx.Int(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func videoListAction(ctx *cli.Context) error {
|
|||
filter := videoNormalizeFilter(ctx.Args().Slice())
|
||||
includeSidecar := ctx.Bool(videoIncludeSidecarFlag.Name)
|
||||
|
||||
results, err := videoSearchResults(filter, ctx.Uint(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), includeSidecar)
|
||||
results, err := videoSearchResults(filter, ctx.Int(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), includeSidecar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func videoRemuxAction(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
filter := videoNormalizeFilter(ctx.Args().Slice())
|
||||
results, err := videoSearchResults(filter, ctx.Uint(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
results, err := videoSearchResults(filter, ctx.Int(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ import (
|
|||
)
|
||||
|
||||
// videoSearchResults runs a video-only search and applies offset/count after sidecar filtering.
|
||||
func videoSearchResults(query string, count uint, offset int, includeSidecar bool) ([]search.Photo, error) {
|
||||
func videoSearchResults(query string, count int, offset int, includeSidecar bool) ([]search.Photo, error) {
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
if count <= 0 {
|
||||
return []search.Photo{}, nil
|
||||
}
|
||||
|
||||
|
|
@ -25,20 +25,20 @@ func videoSearchResults(query string, count uint, offset int, includeSidecar boo
|
|||
}
|
||||
|
||||
if includeSidecar {
|
||||
frm.Count = int(count)
|
||||
frm.Count = count
|
||||
frm.Offset = offset
|
||||
results, _, err := search.Photos(frm)
|
||||
return results, err
|
||||
}
|
||||
|
||||
target := int(count) + offset
|
||||
target := count + offset
|
||||
if target < 0 {
|
||||
target = 0
|
||||
}
|
||||
|
||||
collected := make([]search.Photo, 0, target)
|
||||
searchOffset := 0
|
||||
batchSize := int(count)
|
||||
batchSize := count
|
||||
if batchSize < 200 {
|
||||
batchSize = 200
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ func videoSearchResults(query string, count uint, offset int, includeSidecar boo
|
|||
return []search.Photo{}, nil
|
||||
}
|
||||
|
||||
end := offset + int(count)
|
||||
end := offset + count
|
||||
if end > len(collected) {
|
||||
end = len(collected)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func videoTranscodeAction(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
filter := videoNormalizeFilter(ctx.Args().Slice())
|
||||
results, err := videoSearchResults(filter, ctx.Uint(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
results, err := videoSearchResults(filter, ctx.Int(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func videoTrimAction(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
filter := videoNormalizeFilter(filterArgs)
|
||||
results, err := videoSearchResults(filter, ctx.Uint(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
results, err := videoSearchResults(filter, ctx.Int(videoCountFlag.Name), ctx.Int(OffsetFlag.Name), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -216,7 +216,15 @@ func videoTrimFile(conf *config.Config, convert *photoprism.Convert, plan videoT
|
|||
}
|
||||
|
||||
destDir := filepath.Dir(plan.DestPath)
|
||||
tempPath, err := videoTempPath(destDir, ".trim-*.tmp")
|
||||
ext := filepath.Ext(plan.DestPath)
|
||||
if ext == "" {
|
||||
ext = filepath.Ext(plan.SrcPath)
|
||||
}
|
||||
if ext == "" {
|
||||
ext = ".tmp"
|
||||
}
|
||||
|
||||
tempPath, err := videoTempPath(destDir, ".trim-*"+ext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -224,6 +232,8 @@ func videoTrimFile(conf *config.Config, convert *photoprism.Convert, plan videoT
|
|||
cmd := videoTrimCmd(conf.FFmpegBin(), plan.SrcPath, tempPath, start, remaining)
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", conf.CmdCachePath()))
|
||||
|
||||
log.Debugf("ffmpeg: %s", clean.Log(cmd.String()))
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
|
|
@ -308,9 +318,24 @@ func videoTrimCmd(ffmpegBin, srcName, destName string, start, duration time.Dura
|
|||
"-ignore_unknown",
|
||||
"-codec", "copy",
|
||||
"-avoid_negative_ts", "make_zero",
|
||||
destName,
|
||||
)
|
||||
|
||||
if videoTrimFastStart(destName) {
|
||||
args = append(args, "-movflags", "+faststart")
|
||||
}
|
||||
|
||||
args = append(args, destName)
|
||||
|
||||
// #nosec G204 -- arguments are built from validated inputs and config.
|
||||
return exec.Command(ffmpegBin, args...)
|
||||
}
|
||||
|
||||
// videoTrimFastStart reports whether the trim output should enable faststart for MP4/MOV containers.
|
||||
func videoTrimFastStart(destName string) bool {
|
||||
switch strings.ToLower(filepath.Ext(destName)) {
|
||||
case fs.ExtMp4, fs.ExtMov, fs.ExtQT, ".m4v":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
internal/commands/video_trim_test.go
Normal file
17
internal/commands/video_trim_test.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestVideoTrimFastStart verifies which extensions get the faststart flag.
|
||||
func TestVideoTrimFastStart(t *testing.T) {
|
||||
assert.True(t, videoTrimFastStart("clip.mp4"))
|
||||
assert.True(t, videoTrimFastStart("clip.MOV"))
|
||||
assert.True(t, videoTrimFastStart("clip.m4v"))
|
||||
assert.True(t, videoTrimFastStart("clip.qt"))
|
||||
assert.False(t, videoTrimFastStart("clip.mkv"))
|
||||
assert.False(t, videoTrimFastStart(""))
|
||||
}
|
||||
|
|
@ -2,10 +2,10 @@ package commands
|
|||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
// VideoCommands configures the CLI subcommands for working with indexed videos.
|
||||
var VideoCommands = &cli.Command{
|
||||
Name: "video",
|
||||
Usage: "Video subcommands",
|
||||
// VideosCommands configures the CLI subcommands for working with indexed videos.
|
||||
var VideosCommands = &cli.Command{
|
||||
Name: "videos",
|
||||
Usage: "Video troubleshooting and editing subcommands",
|
||||
Subcommands: []*cli.Command{
|
||||
VideoListCommand,
|
||||
VideoTrimCommand,
|
||||
|
|
@ -16,7 +16,7 @@ var VideoCommands = &cli.Command{
|
|||
}
|
||||
|
||||
// videoCountFlag limits the number of results returned by video commands.
|
||||
var videoCountFlag = &cli.UintFlag{
|
||||
var videoCountFlag = &cli.IntFlag{
|
||||
Name: "count",
|
||||
Aliases: []string{"n"},
|
||||
Usage: "maximum `NUMBER` of results",
|
||||
Loading…
Add table
Add a link
Reference in a new issue