mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
* add phasher A simple `phasher` program that accepts a video file as a command line argument and calculates and prints its PHASH. The goal of this separate executable is to have a simple way to calculate phashes that doesn't depend on a full stash instance so that third-party systems and tools can independently generate PHASHes which can be used for interacting with stash and stash-box APIs and data. Currently `phasher` is built in the default make target along with `stash` by simply running `make`. Cross-platform targets have not been considered. Concurrency is intentionally not implemented because it is simpler to use [GNU Parallel](https://www.gnu.org/software/parallel/). For example: ``` parallel phasher {} ::: *.mp4 ``` * standard dir structure for phasher and separate make target The make target still needs to be integrated into the rest of the Makefile so it can be built as part of normal releases. * phasher: basic usage output and quiet option * phasher: allow and process multiple command line arguments * phasher: camelCase identifiers * phasher: initialize ffmpeg and ffprobe only once
83 lines
1.8 KiB
Go
83 lines
1.8 KiB
Go
// TODO: document in README.md
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
flag "github.com/spf13/pflag"
|
|
"github.com/stashapp/stash/pkg/ffmpeg"
|
|
"github.com/stashapp/stash/pkg/file"
|
|
"github.com/stashapp/stash/pkg/hash/videophash"
|
|
)
|
|
|
|
func customUsage() {
|
|
fmt.Fprintf(os.Stderr, "Usage:\n")
|
|
fmt.Fprintf(os.Stderr, "%s [OPTIONS] VIDEOFILE...\n\nOptions:\n", os.Args[0])
|
|
flag.PrintDefaults()
|
|
}
|
|
|
|
func printPhash(ff *ffmpeg.FFMpeg, ffp ffmpeg.FFProbe, inputfile string, quiet *bool) error {
|
|
ffvideoFile, err := ffp.NewVideoFile(inputfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// All we need for videophash.Generate() is
|
|
// videoFile.Path (from BaseFile)
|
|
// videoFile.Duration
|
|
// The rest of the struct isn't needed.
|
|
vf := &file.VideoFile{
|
|
BaseFile: &file.BaseFile{Path: inputfile},
|
|
Duration: ffvideoFile.FileDuration,
|
|
}
|
|
|
|
phash, err := videophash.Generate(ff, vf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if *quiet {
|
|
fmt.Printf("%x\n", *phash)
|
|
} else {
|
|
fmt.Printf("%x %v\n", *phash, vf.Path)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
flag.Usage = customUsage
|
|
quiet := flag.BoolP("quiet", "q", false, "print only the phash")
|
|
help := flag.BoolP("help", "h", false, "print this help output")
|
|
flag.Parse()
|
|
|
|
if *help {
|
|
flag.Usage()
|
|
os.Exit(2)
|
|
}
|
|
|
|
args := flag.Args()
|
|
|
|
if len(args) < 1 {
|
|
fmt.Fprintf(os.Stderr, "Missing VIDEOFILE argument.\n")
|
|
flag.Usage()
|
|
os.Exit(2)
|
|
}
|
|
|
|
if len(args) > 1 {
|
|
fmt.Fprintln(os.Stderr, "Files will be processed sequentially! Consier using GNU Parallel.")
|
|
fmt.Fprintf(os.Stderr, "Example: parallel %v ::: *.mp4\n", os.Args[0])
|
|
}
|
|
|
|
ffmpegPath, ffprobePath := ffmpeg.GetPaths(nil)
|
|
encoder := ffmpeg.NewEncoder(ffmpegPath)
|
|
encoder.InitHWSupport(context.TODO())
|
|
ffprobe := ffmpeg.FFProbe(ffprobePath)
|
|
|
|
for _, item := range args {
|
|
if err := printPhash(encoder, ffprobe, item, quiet); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
}
|
|
}
|
|
}
|