init
This commit is contained in:
commit
1f009c4e05
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.exe
|
||||
*.idea
|
||||
phototransfer
|
448
main.go
Normal file
448
main.go
Normal file
@ -0,0 +1,448 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
proc = modkernel32.NewProc("GetFileTime")
|
||||
allExts []string
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Stop bool
|
||||
ImgSizeThreshold int64
|
||||
VideoSizeThreshold int64
|
||||
CopyUseLink bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
context := &Context{
|
||||
Stop: false,
|
||||
}
|
||||
var rootPath string
|
||||
var targetPath string
|
||||
var exts string
|
||||
var videoExts string
|
||||
var copyExts string
|
||||
var threadCount int
|
||||
var videoThreadCount int
|
||||
flag.StringVar(&rootPath, "root", "", "source root dir")
|
||||
flag.StringVar(&targetPath, "target", "", "target dir")
|
||||
flag.StringVar(&exts, "ext", "jpg|bmp|gif", "image ext")
|
||||
flag.StringVar(&videoExts, "video-ext", "", "video file ext")
|
||||
flag.StringVar(©Exts, "copy-ext", "mkv|mp4|mov|m4v|mpeg", "copy file ext")
|
||||
flag.Int64Var(&context.ImgSizeThreshold, "img-size", 1024, "image size larger than will be processed (KiB)")
|
||||
flag.Int64Var(&context.VideoSizeThreshold, "video-size", 2048000, "video size larger than will be processed (KiB)")
|
||||
flag.IntVar(&threadCount, "thread", 10, "thread count")
|
||||
flag.IntVar(&videoThreadCount, "video-thread", 1, "video thread count")
|
||||
flag.BoolVar(&context.CopyUseLink, "link", true, "copy use link instead")
|
||||
flag.Parse()
|
||||
|
||||
_, errSt := os.Stat(rootPath)
|
||||
if errSt != nil {
|
||||
fmt.Println("root path not exist")
|
||||
return
|
||||
}
|
||||
|
||||
if targetPath == "" {
|
||||
fmt.Println("target path not set")
|
||||
return
|
||||
}
|
||||
|
||||
if rootPath[len(rootPath)-1:] == "\\" && rootPath[len(rootPath)-1:] == "/" {
|
||||
rootPath = rootPath[:len(rootPath)-1]
|
||||
}
|
||||
if targetPath[len(targetPath)-1:] == "\\" && targetPath[len(targetPath)-1:] == "/" {
|
||||
targetPath = targetPath[:len(targetPath)-1]
|
||||
}
|
||||
|
||||
_, errTarget := os.Stat(targetPath)
|
||||
if errTarget != nil {
|
||||
os.MkdirAll(targetPath, os.ModePerm)
|
||||
}
|
||||
|
||||
ext := strings.Split(strings.ToLower(exts), "|")
|
||||
videoExt := strings.Split(strings.ToLower(videoExts), "|")
|
||||
copyExt := strings.Split(strings.ToLower(copyExts), "|")
|
||||
|
||||
allExts = append(allExts, ext...)
|
||||
allExts = append(allExts, videoExt...)
|
||||
allExts = append(allExts, copyExt...)
|
||||
|
||||
var paths []string
|
||||
var videoPaths []string
|
||||
var copyPaths []string
|
||||
var dirs []string
|
||||
err := filepath.Walk(rootPath, func(path string, f os.FileInfo, err error) error {
|
||||
if f == nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
dirs = append(dirs, path)
|
||||
return nil
|
||||
}
|
||||
if len(path) > 3 {
|
||||
e := path[len(path)-3:]
|
||||
if checkIsInList(strings.ToLower(e), copyExt) {
|
||||
copyPaths = append(copyPaths, path)
|
||||
return nil
|
||||
}
|
||||
if checkIsInList(strings.ToLower(e), ext) {
|
||||
paths = append(paths, path)
|
||||
return nil
|
||||
}
|
||||
if checkIsInList(strings.ToLower(e), videoExt) {
|
||||
videoPaths = append(videoPaths, path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("filepath.Walk() returned %v\n\n", err)
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
processDir(dir, rootPath, targetPath)
|
||||
}
|
||||
//copy
|
||||
if len(copyExt) > 0 {
|
||||
runWorker(copyPaths, threadCount, rootPath, targetPath, "copy", context)
|
||||
}
|
||||
//photo
|
||||
if len(ext) > 0 {
|
||||
runWorker(paths, threadCount, rootPath, targetPath, "image", context)
|
||||
}
|
||||
//video
|
||||
if len(videoExt) > 0 {
|
||||
runWorker(videoPaths, videoThreadCount, rootPath, targetPath, "video", context)
|
||||
}
|
||||
}
|
||||
|
||||
func runWorker(paths []string, threadCount int, rootPath string, targetPath string, processType string, context *Context) {
|
||||
cmd := make(chan string, len(paths))
|
||||
resultCh := make(chan string, len(paths))
|
||||
for i := 0; i < threadCount; i++ {
|
||||
go worker(cmd, resultCh, rootPath, targetPath, processType, context)
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
cmd <- path
|
||||
}
|
||||
total := len(paths)
|
||||
for i := range paths {
|
||||
s := <-resultCh
|
||||
fmt.Println(processType + ": " + strconv.Itoa(i+1) + "/" + strconv.Itoa(total) + " | " + s)
|
||||
}
|
||||
}
|
||||
|
||||
func worker(path <-chan string, result chan<- string, rootPath string, targetPath string, processType string, context *Context) {
|
||||
for {
|
||||
if context.Stop {
|
||||
return
|
||||
}
|
||||
if str, ok := <-path; ok {
|
||||
if str != "" {
|
||||
switch processType {
|
||||
case "image":
|
||||
stat, err := os.Stat(str)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if stat.Size() > context.ImgSizeThreshold*1024 {
|
||||
process(str, rootPath, targetPath)
|
||||
} else {
|
||||
if context.CopyUseLink {
|
||||
processLink(str, rootPath, targetPath)
|
||||
} else {
|
||||
processCopy(str, rootPath, targetPath)
|
||||
}
|
||||
}
|
||||
case "video":
|
||||
stat, err := os.Stat(str)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if stat.Size() > context.VideoSizeThreshold*1024 {
|
||||
processVideo(str, rootPath, targetPath)
|
||||
} else {
|
||||
if context.CopyUseLink {
|
||||
processLink(str, rootPath, targetPath)
|
||||
} else {
|
||||
processCopy(str, rootPath, targetPath)
|
||||
}
|
||||
}
|
||||
case "copy":
|
||||
if context.CopyUseLink {
|
||||
processLink(str, rootPath, targetPath)
|
||||
} else {
|
||||
processCopy(str, rootPath, targetPath)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
result <- str
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processDir(path string, rootPath string, targetPath string) {
|
||||
rawDir := path[len(rootPath):]
|
||||
|
||||
_, errStat := os.Stat(targetPath + rawDir)
|
||||
|
||||
var ctime, atime, mtime time.Time
|
||||
var errGetFileTime error
|
||||
flag := false
|
||||
filepath.Walk(path, func(p string, info fs.FileInfo, err error) error {
|
||||
if flag {
|
||||
return nil
|
||||
}
|
||||
if len(p) > 3 {
|
||||
e := p[len(p)-3:]
|
||||
if checkIsInList(strings.ToLower(e), allExts) {
|
||||
ctime, atime, mtime, errGetFileTime = GetFileTime(p)
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if errGetFileTime != nil || !flag {
|
||||
ctime, atime, mtime, _ = GetFileTime(path)
|
||||
}
|
||||
if os.IsNotExist(errStat) {
|
||||
os.MkdirAll(targetPath+rawDir, os.ModePerm)
|
||||
SetFileTime(targetPath+rawDir, ctime, atime, mtime)
|
||||
fmt.Println("mkdir: " + targetPath + rawDir)
|
||||
} else {
|
||||
SetFileTime(targetPath+rawDir, ctime, atime, mtime)
|
||||
fmt.Println("Exist: " + targetPath + rawDir)
|
||||
}
|
||||
}
|
||||
|
||||
func process(path string, rootPath string, targetPath string) {
|
||||
rawPath := path[len(rootPath):]
|
||||
|
||||
_, err := os.Stat(targetPath + rawPath)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
command := exec.Command("magick.exe", "convert", "-resize", "1920x1920>", "-quality", "90", path, targetPath+rawPath)
|
||||
stdout, _ := command.StdoutPipe()
|
||||
|
||||
defer stdout.Close()
|
||||
|
||||
err2 := command.Start()
|
||||
if err2 != nil {
|
||||
fmt.Println("command Start Error:" + err2.Error())
|
||||
}
|
||||
ioutil.ReadAll(stdout)
|
||||
|
||||
ctime, atime, mtime, errGetFileTime := GetFileTime(rootPath + rawPath)
|
||||
if errGetFileTime != nil {
|
||||
fmt.Println("GetFileTime Error:" + errGetFileTime.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err3 := SetFileTime(targetPath+rawPath, ctime, atime, mtime)
|
||||
if err3 != nil {
|
||||
fmt.Println("SetFileTime Error:" + err3.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func processCopy(path string, rootPath string, targetPath string) {
|
||||
rawPath := path[len(rootPath):]
|
||||
|
||||
_, err := os.Stat(targetPath + rawPath)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, errCopy := copy(path, targetPath+rawPath)
|
||||
if errCopy != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctime, atime, mtime, errGetFileTime := GetFileTime(path)
|
||||
if errGetFileTime != nil {
|
||||
fmt.Println("GetFileTime Error:" + errGetFileTime.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err3 := SetFileTime(targetPath+rawPath, ctime, atime, mtime)
|
||||
if err3 != nil {
|
||||
fmt.Println("SetFileTime Error:" + err3.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func processLink(path string, rootPath string, targetPath string) {
|
||||
rawPath := path[len(rootPath):]
|
||||
|
||||
_, err := os.Stat(targetPath + rawPath)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
errLink := os.Link(path, targetPath+rawPath)
|
||||
if errLink == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, errCopy := copy(path, targetPath+rawPath)
|
||||
if errCopy != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctime, atime, mtime, errGetFileTime := GetFileTime(path)
|
||||
if errGetFileTime != nil {
|
||||
fmt.Println("GetFileTime Error:" + errGetFileTime.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err3 := SetFileTime(targetPath+rawPath, ctime, atime, mtime)
|
||||
if err3 != nil {
|
||||
fmt.Println("SetFileTime Error:" + err3.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func processVideo(path string, rootPath string, targetPath string) {
|
||||
rawPath := path[len(rootPath):]
|
||||
|
||||
_, err := os.Stat(targetPath + rawPath)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
index := strings.LastIndex(rawPath, ".")
|
||||
litePath := rawPath[:index]
|
||||
|
||||
out := targetPath + litePath + ".mp4"
|
||||
|
||||
_, errOut := os.Stat(out)
|
||||
if errOut == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, errTmp := os.Stat(out + ".tmp")
|
||||
if errTmp == nil {
|
||||
os.Remove(out + ".tmp")
|
||||
}
|
||||
|
||||
command := exec.Command("ffmpeg.exe", "-i", path, "-pix_fmt", "yuv420p", "-acodec", "copy", "-vcodec", "libx264", "-crf", "21", "-f", "mp4", out+".tmp")
|
||||
stdout, _ := command.StdoutPipe()
|
||||
|
||||
defer stdout.Close()
|
||||
|
||||
err2 := command.Start()
|
||||
if err2 != nil {
|
||||
fmt.Println("command Start Error:" + err2.Error())
|
||||
}
|
||||
ioutil.ReadAll(stdout)
|
||||
|
||||
ctime, atime, mtime, errGetFileTime := GetFileTime(rootPath + rawPath)
|
||||
if errGetFileTime != nil {
|
||||
fmt.Println("GetFileTime Error:" + errGetFileTime.Error())
|
||||
return
|
||||
}
|
||||
|
||||
os.Rename(out+".tmp", out)
|
||||
err3 := SetFileTime(out, ctime, atime, mtime)
|
||||
if err3 != nil {
|
||||
fmt.Println("SetFileTime Error:" + err3.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func checkIsInList(str string, list []string) bool {
|
||||
for _, s := range list {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SetFileTime(path string, ctime, atime, mtime time.Time) (err error) {
|
||||
path, err = syscall.FullPath(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pathPtr, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
handle, err := syscall.CreateFile(pathPtr, syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer syscall.Close(handle)
|
||||
a := syscall.NsecToFiletime(syscall.TimespecToNsec(syscall.NsecToTimespec(atime.UnixNano())))
|
||||
c := syscall.NsecToFiletime(syscall.TimespecToNsec(syscall.NsecToTimespec(ctime.UnixNano())))
|
||||
m := syscall.NsecToFiletime(syscall.TimespecToNsec(syscall.NsecToTimespec(mtime.UnixNano())))
|
||||
return syscall.SetFileTime(handle, &c, &a, &m)
|
||||
}
|
||||
|
||||
func GetFileTime(path string) (ctime time.Time, atime time.Time, mtime time.Time, err error) {
|
||||
path, err = syscall.FullPath(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pathPtr, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
handle, err := syscall.CreateFile(pathPtr, syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var a syscall.Filetime
|
||||
var c syscall.Filetime
|
||||
var m syscall.Filetime
|
||||
|
||||
syscall.Syscall6(proc.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(&c)), uintptr(unsafe.Pointer(&a)), uintptr(unsafe.Pointer(&m)), 0, 0)
|
||||
return time.Unix(c.Nanoseconds()/1e9, 0), time.Unix(c.Nanoseconds()/1e9, 0), time.Unix(m.Nanoseconds()/1e9, 0), err
|
||||
}
|
||||
|
||||
func copy(src, dst string) (int64, error) {
|
||||
sourceFileStat, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !sourceFileStat.Mode().IsRegular() {
|
||||
return 0, fmt.Errorf("%s is not a regular file", src)
|
||||
}
|
||||
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer destination.Close()
|
||||
nBytes, err := io.Copy(destination, source)
|
||||
return nBytes, err
|
||||
}
|
Loading…
Reference in New Issue
Block a user