From 2d200e5f0d61af283127c82986f541bc149587a8 Mon Sep 17 00:00:00 2001 From: velvettear Date: Tue, 28 May 2024 16:16:08 +0200 Subject: [PATCH] implement multithreading and handle timeouts --- .gitignore | 2 +- .vscode/launch.json | 4 ++- internal/config/config.go | 9 ++++++ main.go | 61 ++++++++++++++++++++++++++++++++------- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 04ace28..d970015 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -tagcleaner \ No newline at end of file +id3tool \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 65fe4ee..2887162 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,9 @@ "program": "${workspaceFolder}/main.go", "args": [ "--debug", - "/home/velvettear/downloads/test" + "--timeout", + "5", + "/tmp/share/music" ] } ] diff --git a/internal/config/config.go b/internal/config/config.go index 046f683..abfbccd 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,6 +2,7 @@ package config import ( "os" + "strconv" "strings" "git.velvettear.de/velvettear/loggo" @@ -9,6 +10,7 @@ import ( var Directory string var FramesToKeep []string +var Timeout int // check program arguments and set variables accordingly func CheckArguments() { @@ -24,6 +26,10 @@ func CheckArguments() { fallthrough case "--frames": framesToKeep = strings.Split(os.Args[index+1], ",") + case "-t": + fallthrough + case "--timeout": + Timeout, _ = strconv.Atoi(os.Args[index+1]) case "-h": fallthrough case "--help": @@ -38,6 +44,9 @@ func CheckArguments() { FramesToKeep = append(FramesToKeep, strings.ToUpper(strings.TrimSpace(frame))) } } + if Timeout <= 0 { + Timeout = 30 + } } // print the help diff --git a/main.go b/main.go index 399cf51..60d5cc6 100644 --- a/main.go +++ b/main.go @@ -5,8 +5,10 @@ import ( "io/fs" "os" "path/filepath" + "runtime" "strconv" "strings" + "sync" "time" "git.velvettear.de/velvettear/id3tool/internal/config" @@ -14,6 +16,12 @@ import ( "github.com/bogem/id3v2/v2" ) +var taskLimiter = make(chan struct{}, runtime.NumCPU()) +var taskCommunication = make(chan task) +var waitGroup = sync.WaitGroup{} +var counter int +var taskState = make(map[int]task) + func main() { loggo.SetLogFormat("[$LOGLEVEL$] > $MESSAGE$ ($EXTRAS$) [$TIMEDIFF$]") @@ -31,33 +39,60 @@ func main() { } filepath.WalkDir(config.Directory, filterValidFiles) + waitGroup.Wait() } func filterValidFiles(path string, dir fs.DirEntry, err error) error { if err != nil { return err } - tmp := filepath.Clean(path) - loggo.Debug(tmp) + extension := filepath.Ext(path) if dir.IsDir() || (extension != ".flac" && extension != ".mp3") { return nil } - cleanFrames(path) + + counter++ + waitGroup.Add(1) + taskLimiter <- struct{}{} + tmp := task{counter, path, false} + go func(tmp task) { + go func(tmp task) { + time.Sleep(time.Duration(config.Timeout) * time.Second) + tmp.timeout = true + taskCommunication <- tmp + <-taskLimiter + }(tmp) + cleanFrames(tmp.id, tmp.file) + tmp.timeout = false + taskCommunication <- tmp + <-taskLimiter + }(tmp) + + result := <-taskCommunication + check := taskState[result.id] + if check.id == 0 { + taskState[result.id] = result + if result.timeout { + loggo.Warning("(" + strconv.Itoa(result.id) + ") cleaning of file's '" + result.file + "' id3 tags timed out") + } + } + + waitGroup.Done() return nil } -func cleanFrames(file string) { +func cleanFrames(id int, file string) { if len(file) == 0 { return } timestamp := time.Now() - loggo.Info("cleaning file's '" + file + "' id3 tags...") + loggo.Info("(" + strconv.Itoa(id) + ") cleaning file's '" + file + "' id3 tags...") tag, err := id3v2.Open(file, id3v2.Options{Parse: true}) if err != nil { - loggo.Error("encountered an error opening the file '"+file+"' for reading the id3 tags", err.Error()) + loggo.Error("("+strconv.Itoa(id)+") encountered an error opening the file '"+file+"' for reading the id3 tags", err.Error()) return } defer tag.Close() @@ -73,16 +108,16 @@ func cleanFrames(file string) { } if deleted > 0 { - loggo.Debug("saving modified id3 tags (" + strconv.Itoa(deleted) + " deleted) to file '" + file + "'...") + loggo.Debug("(" + strconv.Itoa(id) + ") saving modified id3 tags (" + strconv.Itoa(deleted) + " deleted) to file '" + file + "'...") err := tag.Save() if err != nil { - loggo.Error("encountered an error saving the modified id3 tags to file '"+file+"'", err.Error()) + loggo.Error("("+strconv.Itoa(id)+") encountered an error saving the modified id3 tags to file '"+file+"'", err.Error()) return } - loggo.DebugTimed("deleted "+strconv.Itoa(deleted)+" tags", timestamp.UnixMilli()) + loggo.DebugTimed("("+strconv.Itoa(id)+") deleted "+strconv.Itoa(deleted)+" tag(s) from file '"+file+"'", timestamp.UnixMilli()) } - loggo.DebugTimed("finished cleaning file", timestamp.UnixMilli()) + loggo.DebugTimed("("+strconv.Itoa(id)+") finished cleaning file's '"+file+"' id3 tags", timestamp.UnixMilli()) } func frameFilter(frame string) bool { @@ -93,3 +128,9 @@ func frameFilter(frame string) bool { } return true } + +type task struct { + id int + file string + timeout bool +}