diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..9c55e5d --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,114 @@ +name: build release + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: '1.13.0' + + - name: Build + run: make -j $(nproc) compile VERSION=${{ github.ref }} + + - name: create release + id: create_release + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + + - name: darwin-386 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-darwin-386 + asset_name: ${{ github.event.repository.name }}-darwin-386 + asset_content_type: application/octet-stream + + - name: darwin-amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-darwin-amd64 + asset_name: ${{ github.event.repository.name }}-darwin-amd64 + asset_content_type: application/octet-stream + + - name: freebsd-386 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-freebsd-386 + asset_name: ${{ github.event.repository.name }}-freebsd-386 + asset_content_type: application/octet-stream + + - name: freebsd-amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-freebsd-amd64 + asset_name: ${{ github.event.repository.name }}-freebsd-amd64 + asset_content_type: application/octet-stream + + - name: linux-386 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-linux-386 + asset_name: ${{ github.event.repository.name }}-linux-386 + asset_content_type: application/octet-stream + + - name: linux-amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-linux-amd64 + asset_name: ${{ github.event.repository.name }}-linux-amd64 + asset_content_type: application/octet-stream + + - name: windows-386 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-windows-386.exe + asset_name: ${{ github.event.repository.name }}-windows-386.exe + asset_content_type: application/octet-stream + + - name: windows-amd64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin/${{ github.event.repository.name }}-windows-amd64.exe + asset_name: ${{ github.event.repository.name }}-windows-amd64.exe + asset_content_type: application/octet-stream + diff --git a/Makefile b/Makefile index 42b42c0..4a6a79a 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,19 @@ -NAME=range-gen -VERSION=0.1.0 -GOCMD=go -LDFLAGS:="$(LDFLAGS) -X 'main.Version=$(VERSION)'" -GOBUILD=$(GOCMD) build -ldflags=$(LDFLAGS) -SRCMAIN=. -BINDIR=bin -BIN=$(BINDIR)/$(NAME) -PREFIX=/usr +NAME = range-gen +VERSION = dev_$(shell date +%F_%T) +GOCMD = go +LDFLAGS := "$(LDFLAGS) -X 'main.Version=$(VERSION)'" +GOBUILD = $(GOCMD) build -ldflags=$(LDFLAGS) +SRCMAIN = . +BINDIR = bin +BIN = $(BINDIR)/$(NAME) +PREFIX = /usr + +.PHONY: all prep build check-build freebsd-386 darwin-386 linux-386 windows-386 freebsd-amd64 darwin-amd64 linux-amd64 windows-amd64 compile install check-install uninstall clean all: build prep: clean + go mod init; go mod tidy mkdir $(BINDIR) build: prep diff --git a/args.go b/args.go index 887f859..43a592c 100644 --- a/args.go +++ b/args.go @@ -3,7 +3,10 @@ package main import ( "github.com/juju/gnuflag" + "range-gen/output" + "errors" + "os" ) var ( @@ -16,12 +19,28 @@ var ( ArgJobs int = 0 ArgDefaultNoiseLevel int = 0 + + ArgVersion bool = false + ArgHelp bool = false ) -func Use(vals ...interface{}) { - for _, val := range vals { - _ = val - } +func printHelp() { + output.Println("Сreates a list of scene ranges based on a set of frames from the video") + output.Println("") + output.Println("Usage: range-gen [option]... ") + output.Println("input_dir Directory with png images") + output.Println("output_file Range list file") + output.Println("threshold Image similarity threshold (0-1024)") + output.Println("") + output.Println("Options:") + output.Println(" -j, --jobs N Allow N jobs at once") + output.Println(" -n, --noise Default noise level for each range") + output.Println(" -h, --help Show this page") + output.Println(" -v, --version Show version") +} + +func printVersion() { + output.Println("multini ", Version) } func init() { @@ -29,11 +48,24 @@ func init() { gnuflag.IntVar(&ArgJobs, "j", 0, "") gnuflag.IntVar(&ArgDefaultNoiseLevel, "noise", -1, "") gnuflag.IntVar(&ArgDefaultNoiseLevel, "n", -1, "") + gnuflag.BoolVar(&ArgVersion, "version", false, "") + gnuflag.BoolVar(&ArgVersion, "v", false, "") + gnuflag.BoolVar(&ArgHelp, "help", false, "") + gnuflag.BoolVar(&ArgHelp, "h", false, "") } func parseArgs() error { gnuflag.Parse(false) + switch { + case ArgHelp: + printHelp() + os.Exit(EXIT_SUCCESS) + case ArgVersion: + printVersion() + os.Exit(EXIT_SUCCESS) + } + for i := 0; i < 3 && i < gnuflag.NArg(); i++ { switch i { case 0: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c02a976 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module range-gen + +go 1.13 + +require ( + github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect + github.com/dsoprea/go-perceptualhash v0.0.0-20200804060033-5ef3a3d0c14b + github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b934681 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg= +github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= +github.com/dsoprea/go-perceptualhash v0.0.0-20200804060033-5ef3a3d0c14b h1:MvVsQ3fIkIpkUwphIPzjsuJGNjYQaPMAn7nXtet6Jcc= +github.com/dsoprea/go-perceptualhash v0.0.0-20200804060033-5ef3a3d0c14b/go.mod h1:mOZww7GHNRuvbz9vRejK7aGUgZU6Z+dleJ6PrFCDieE= +github.com/go-errors/errors v1.0.2 h1:xMxH9j2fNg/L4hLn/4y3M0IUsn0M6Wbu/Uh9QlOfBh4= +github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index 0fd1b80..cdd1b26 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,14 @@ import ( "image" "io/ioutil" "os" + "os/signal" + "path/filepath" + "runtime" "sort" "strconv" "strings" + "sync" + "syscall" _ "image/png" @@ -25,12 +30,14 @@ const ( ) var ( + Version string = "dev" + hashes map[string]string names []string ) func main() { - output.SetEndOfLineNative() + closeHandler() // Ctrl+C if err := parseArgs(); err != nil { output.Errorln(err) @@ -49,19 +56,73 @@ func main() { os.Exit(EXIT_DIR_READ_ERR) } + if ArgJobs == 0 { + ArgJobs = runtime.NumCPU() + } + runtime.GOMAXPROCS(ArgJobs) + + hashes, names := calcHashes(files) + + ranges := calcRanges(hashes, names, Threshold) + + writeRanges(ranges) + + wg := new(sync.WaitGroup) + + //wg.Add(1) + + wg.Wait() + + os.Exit(EXIT_SUCCESS) +} + +func closeHandler() { + interrupt := make(chan os.Signal, 2) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + go func() { + <-interrupt + output.Println("Closed by SIGINT") + os.Exit(EXIT_SUCCESS) + }() +} + +func calcHashes(files []os.FileInfo) (map[string]string, []string) { + var hashes map[string]string + var keys []string + var err error + hashes = make(map[string]string) + for _, file := range files { if !file.IsDir() && strings.HasSuffix(file.Name(), ".png") { - names = append(names, file.Name()) - hashes[file.Name()], err = calchash(ArgInput + file.Name()) + keys = append(keys, file.Name()) + hashes[file.Name()], err = calcHash(filepath.Join(ArgInput, file.Name())) if err != nil { output.Errorln(err) } } } - sort.Strings(names) + sort.Strings(keys) + return hashes, keys +} +func calcHash(filepath string) (string, error) { + file, err := os.Open(filepath) + if err != nil { + return "", err + } + defer file.Close() + + image, _, err := image.Decode(file) + if err != nil { + return "", err + } + + return blockhash.NewBlockhash(image, 16).Hexdigest(), nil +} + +func calcRanges(hashes map[string]string, names []string, Threshold int) string { var ranges strings.Builder var prevHash string = "" var dist int = 0 @@ -86,12 +147,16 @@ func main() { } ranges.WriteString(startName + "\t" + names[len(names)-1] + rangeNoise + output.EOL()) + return ranges.String() +} + +func writeRanges(ranges string) { mode := os.FileMode(int(0644)) targetFile, err := os.OpenFile(ArgOutput, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) defer targetFile.Close() if err == nil { datawriter := bufio.NewWriter(targetFile) - _, err = datawriter.WriteString(ranges.String()) + _, err = datawriter.WriteString(ranges) if err == nil { err = datawriter.Flush() } @@ -101,23 +166,6 @@ func main() { output.Errorln(err) os.Exit(EXIT_FILE_WRITE_ERR) } - - os.Exit(EXIT_SUCCESS) -} - -func calchash(filepath string) (string, error) { - file, err := os.Open(filepath) - if err != nil { - return "", err - } - defer file.Close() - - image, _, err := image.Decode(file) - if err != nil { - return "", err - } - - return blockhash.NewBlockhash(image, 16).Hexdigest(), nil } func hammingDistance(prev, cur string) int { diff --git a/output/output.go b/output/output.go index 7b16205..2d3aefc 100644 --- a/output/output.go +++ b/output/output.go @@ -15,6 +15,10 @@ var ( stderr *log.Logger = log.New(os.Stderr, "", 0) ) +func init() { + SetEndOfLineNative() +} + func SetQuiet(enabled bool) { if enabled { stdout = devNull