initial commit
This commit is contained in:
commit
646732fa5f
11 changed files with 293 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
__debug_bin
|
||||||
|
worklog
|
18
.vscode/launch.json
vendored
Normal file
18
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"version": "0.0.1",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "dedupe",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/main.go",
|
||||||
|
"args": [
|
||||||
|
"/tmp/nfs/music/lossless",
|
||||||
|
"/tmp/nfs/music/mp3",
|
||||||
|
"-v",
|
||||||
|
"--delete"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"go.delveConfig": {
|
||||||
|
"debugAdapter": "dlv-dap",
|
||||||
|
}
|
||||||
|
}
|
20
LICENSE.md
Normal file
20
LICENSE.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# MIT License
|
||||||
|
**Copyright (c) 2023 Daniel Sommer \<daniel.sommer@velvettear.de\>**
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.**
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# dedupe
|
||||||
|
|
||||||
|
simple command line tool to find and move/delete duplicate audio files
|
55
files/finder.go
Normal file
55
files/finder.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
"velvettear/dedupe/log"
|
||||||
|
"velvettear/dedupe/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sourceFiles []string
|
||||||
|
var comparisonFiles []string
|
||||||
|
|
||||||
|
// exported function(s)
|
||||||
|
func Scan() {
|
||||||
|
timestamp := time.Now()
|
||||||
|
log.Info("scanning source directory...", settings.SourceDirectory)
|
||||||
|
filepath.WalkDir(settings.SourceDirectory, fillSourceFiles)
|
||||||
|
log.InfoTimed("found "+strconv.Itoa(len(sourceFiles))+" source files", timestamp.UnixMilli())
|
||||||
|
|
||||||
|
timestamp = time.Now()
|
||||||
|
log.Info("scanning comparison directory...", settings.SourceDirectory)
|
||||||
|
filepath.WalkDir(settings.ComparisonDirectory, fillComparisonFiles)
|
||||||
|
log.InfoTimed("found "+strconv.Itoa(len(comparisonFiles))+" comparison files", timestamp.UnixMilli())
|
||||||
|
|
||||||
|
for _, sourceFile := range sourceFiles {
|
||||||
|
log.Debug("checking file", sourceFile)
|
||||||
|
sourceFileName := filepath.Base(sourceFile)
|
||||||
|
log.Debug("derp", sourceFileName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unexported function(s)
|
||||||
|
func fillSourceFiles(path string, dir fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dir.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sourceFiles = append(sourceFiles, path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillComparisonFiles(path string, dir fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dir.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
comparisonFiles = append(comparisonFiles, path)
|
||||||
|
return nil
|
||||||
|
}
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module velvettear/dedupe
|
||||||
|
|
||||||
|
go 1.20
|
99
log/log.go
Normal file
99
log/log.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LEVEL_DEBUG = 0
|
||||||
|
const LEVEL_INFO = 1
|
||||||
|
const LEVEL_WARNING = 2
|
||||||
|
const LEVEL_ERROR = 3
|
||||||
|
const LEVEL_FATAL = 4
|
||||||
|
|
||||||
|
var logLevel = 1
|
||||||
|
|
||||||
|
// exported functions
|
||||||
|
func SetLogLevel(level int) {
|
||||||
|
logLevel = level
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(message string, extras ...string) {
|
||||||
|
DebugTimed(message, -1, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DebugTimed(message string, timestamp int64, extras ...string) {
|
||||||
|
trace(LEVEL_DEBUG, timestamp, message, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(message string, extras ...string) {
|
||||||
|
InfoTimed(message, -1, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InfoTimed(message string, timestamp int64, extras ...string) {
|
||||||
|
trace(LEVEL_INFO, timestamp, message, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warning(message string, extras ...string) {
|
||||||
|
WarningTimed(message, -1, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WarningTimed(message string, timestamp int64, extras ...string) {
|
||||||
|
trace(LEVEL_WARNING, timestamp, message, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(message string, extras ...string) {
|
||||||
|
ErrorTimed(message, -1, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorTimed(message string, timestamp int64, extras ...string) {
|
||||||
|
trace(LEVEL_ERROR, -1, message, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatal(message string, extras ...string) {
|
||||||
|
FatalTimed(message, -1, extras...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FatalTimed(message string, timestamp int64, extras ...string) {
|
||||||
|
trace(LEVEL_FATAL, timestamp, message, extras...)
|
||||||
|
trace(LEVEL_FATAL, -1, "exiting...")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unexported functions
|
||||||
|
func trace(level int, timestamp int64, message string, extras ...string) {
|
||||||
|
if len(message) == 0 || level < logLevel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
suffix := strings.Join(extras, " | ")
|
||||||
|
if len(suffix) > 0 {
|
||||||
|
message += " (" + suffix + ")"
|
||||||
|
}
|
||||||
|
if timestamp >= 0 {
|
||||||
|
|
||||||
|
message += " [" + strconv.Itoa(int(time.Now().UnixMilli()-timestamp)) + "ms" + "]"
|
||||||
|
}
|
||||||
|
fmt.Println(buildLogMessage(getPrefixForLogLevel(level), message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrefixForLogLevel(level int) string {
|
||||||
|
switch level {
|
||||||
|
case LEVEL_FATAL:
|
||||||
|
return "fatal"
|
||||||
|
case LEVEL_ERROR:
|
||||||
|
return "error"
|
||||||
|
case LEVEL_WARNING:
|
||||||
|
return "warning"
|
||||||
|
case LEVEL_INFO:
|
||||||
|
return "info"
|
||||||
|
default:
|
||||||
|
return "debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLogMessage(prefix string, message string) string {
|
||||||
|
return "[" + prefix + "] > " + message
|
||||||
|
}
|
11
main.go
Normal file
11
main.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"velvettear/dedupe/files"
|
||||||
|
"velvettear/dedupe/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
settings.Initialize()
|
||||||
|
files.Scan()
|
||||||
|
}
|
43
settings/arguments.go
Normal file
43
settings/arguments.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"velvettear/dedupe/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// exported function(s)
|
||||||
|
func Initialize() {
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
log.Fatal("error: missing arguments")
|
||||||
|
}
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
arg = strings.ToLower(arg)
|
||||||
|
switch arg {
|
||||||
|
case "-d":
|
||||||
|
fallthrough
|
||||||
|
case "--delete":
|
||||||
|
setDelete(true)
|
||||||
|
case "-v":
|
||||||
|
fallthrough
|
||||||
|
case "--verbose":
|
||||||
|
setVerbose(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSourceDirectory(os.Args[1])
|
||||||
|
setComparisonDirectory(os.Args[2])
|
||||||
|
stats, error := os.Stat(SourceDirectory)
|
||||||
|
if os.IsNotExist(error) {
|
||||||
|
log.Fatal("given source directory does not exist", SourceDirectory)
|
||||||
|
}
|
||||||
|
if !stats.IsDir() {
|
||||||
|
log.Fatal("given source directory is not a directory", SourceDirectory)
|
||||||
|
}
|
||||||
|
stats, error = os.Stat(ComparisonDirectory)
|
||||||
|
if os.IsNotExist(error) {
|
||||||
|
log.Fatal("given comparison directory does not exist", ComparisonDirectory)
|
||||||
|
}
|
||||||
|
if !stats.IsDir() {
|
||||||
|
log.Fatal("given comparison directory is not a directory", ComparisonDirectory)
|
||||||
|
}
|
||||||
|
}
|
34
settings/variables.go
Normal file
34
settings/variables.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"velvettear/dedupe/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// exported variable(s)
|
||||||
|
var Verbose bool
|
||||||
|
var Delete bool
|
||||||
|
var SourceDirectory string
|
||||||
|
var ComparisonDirectory string
|
||||||
|
|
||||||
|
// unexported function(s)
|
||||||
|
func setVerbose(verbose bool) {
|
||||||
|
Verbose = verbose
|
||||||
|
log.SetLogLevel(0)
|
||||||
|
log.Debug("set verbose flag", strconv.FormatBool(Verbose))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDelete(delete bool) {
|
||||||
|
Delete = delete
|
||||||
|
log.Debug("set delete flag", strconv.FormatBool(Delete))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSourceDirectory(directory string) {
|
||||||
|
SourceDirectory = directory
|
||||||
|
log.Debug("set source directory", SourceDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setComparisonDirectory(directory string) {
|
||||||
|
ComparisonDirectory = directory
|
||||||
|
log.Debug("set source directory", ComparisonDirectory)
|
||||||
|
}
|
Loading…
Reference in a new issue