initial commit

This commit is contained in:
Daniel Sommer 2023-08-11 13:34:54 +02:00
commit 646732fa5f
11 changed files with 293 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__debug_bin
worklog

18
.vscode/launch.json vendored Normal file
View 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
View file

@ -0,0 +1,5 @@
{
"go.delveConfig": {
"debugAdapter": "dlv-dap",
}
}

20
LICENSE.md Normal file
View 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
View file

@ -0,0 +1,3 @@
# dedupe
simple command line tool to find and move/delete duplicate audio files

55
files/finder.go Normal file
View 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
View file

@ -0,0 +1,3 @@
module velvettear/dedupe
go 1.20

99
log/log.go Normal file
View 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
View 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
View 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
View 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)
}