-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
132 lines (101 loc) · 2.74 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/djherbis/times"
)
const workerMax = 4
func main() {
if len(os.Args) != 3 {
fmt.Println("Usage: sdcopy <src_path> <dst_path>")
os.Exit(1)
}
sourcePath := os.Args[1]
destinationPath := os.Args[2]
var wg sync.WaitGroup
sem := make(chan struct{}, workerMax)
err := filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
sem <- struct{}{}
wg.Add(1)
go func(path string, info os.FileInfo) {
defer func() { wg.Done(); <-sem }()
err := copyFile(path, destinationPath, info)
if err != nil {
fmt.Printf("Error copying file %s: %v\n", path, err)
}
}(path, info)
return nil
})
wg.Wait()
if err != nil {
fmt.Printf("Error scanning source path: %v\n", err)
os.Exit(1)
}
fmt.Println("Media files successfully copied.")
}
type placeholders struct {
year string
month string
day string
}
func resolveDestinationPath(destinationPath string, d time.Time) string {
pl := placeholders{
year: d.Format("2006"),
month: d.Format("01"),
day: d.Format("02"),
}
resolved := destinationPath
resolved = strings.ReplaceAll(resolved, "{year}", pl.year)
resolved = strings.ReplaceAll(resolved, "{month}", pl.month)
resolved = strings.ReplaceAll(resolved, "{day}", pl.day)
re := regexp.MustCompile(`\{[^}]*\}`)
resolved = re.ReplaceAllString(resolved, "")
return resolved
}
func copyFile(sourcePath, destinationPath string, info os.FileInfo) error {
ts := times.Get(info)
mtime := info.ModTime()
atime := ts.AccessTime()
destDir := resolveDestinationPath(destinationPath, mtime)
if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create directory %s: %v", destDir, err)
}
destPath := filepath.Join(destDir, info.Name())
fmt.Printf("Copying file: %s -> %s\n", sourcePath, destPath)
if fileExists(destPath) {
fmt.Printf("File already exists. skipping: %s\n", destPath)
}
sourceFile, err := os.Open(sourcePath)
if err != nil {
return fmt.Errorf("failed to open source file %s: %v", sourcePath, err)
}
defer sourceFile.Close()
destFile, err := os.Create(destPath)
if err != nil {
return fmt.Errorf("failed to create destination file %s: %v", destPath, err)
}
defer destFile.Close()
if _, err := io.Copy(destFile, sourceFile); err != nil {
return fmt.Errorf("failed to copy file from %s to %s: %v", sourcePath, destPath, err)
}
if err := os.Chtimes(destPath, atime, mtime); err != nil {
return fmt.Errorf("failed to preserve timestamps for %s: %v", destPath, err)
}
return nil
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}