diff --git a/OBM.go b/OBM.go index 5afef9a..9a83838 100644 --- a/OBM.go +++ b/OBM.go @@ -24,6 +24,7 @@ type job struct { beatmap manager.Beatmap replacementImagePath string retrievementPath string + remove bool } func init() { @@ -41,6 +42,12 @@ func init() { // parse for `-beatmap` argument flag.Parse() + + // if `-showOrder` is checked - show the message + if *showOrder == true { + fmt.Print(orderMessage) + os.Exit(0) + } return } @@ -68,7 +75,7 @@ func main() { } logger.LogInfo(fmt.Sprintf("Found %d beatmaps", len(beatmaps))) - // If `cmdlnBeatmap` is specified - do the magic only for found beatmaps + // If `-beatmap` flag is specified - do the magic only on found beatmaps if *cmdlnBeatmap != "" { logger.LogInfo(fmt.Sprintf("Trying to locate \"%s\"...", *cmdlnBeatmap)) found, n := manager.Search(beatmaps, *cmdlnBeatmap) @@ -89,6 +96,7 @@ func main() { beatmap: beatmap, replacementImagePath: SETTINGS.BackgroundReplacement.ReplacementImagePath, retrievementPath: SETTINGS.BackgroundRetrievement.RetrievementPath, + remove: SETTINGS.BackgroundRemovement.Enabled, } } close(jobs) @@ -110,7 +118,7 @@ func main() { } total := successful + failed - logger.LogInfo(fmt.Sprintf("DONE in %v. %d operations successful (%d%%/100%%); %d failed (%d%%/100%%)", - time.Since(startingTime), successful, successful/total*100, failed, failed/total*100)) + logger.LogInfo(fmt.Sprintf("DONE in %v. %d operations successful (%.2f%%/100%%); %d failed (%.2f%%/100%%)", + time.Since(startingTime), successful, float32(successful)/float32(total)*100, failed, float32(failed)/float32(total)*100)) } diff --git a/README.md b/README.md index 38fd6dd..cb954a9 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,12 @@ To run - `./OBM` in terminal (on Unix) || `OBM` in command line (on Windows) (a ### Flags (starting from version 1.3.4) -Right now there is one argument that you can specify before running the program - "beatmap". -This flag will tell the program to do its work **ONLY** on beatmaps with specified name; others will be ignored. -The names of beatmaps in Osu! consist an id, author of the soundtrack and the name of the soundtrack, so you can -specify any name in the flag that will contain one of those parts. +Right now there is 2 arguments that you can specify before running the program - "beatmap" and "showOrder". +"-beatmap" flag takes a string; it will tell the program to do its work **ONLY** on beatmaps with specified name; others will be ignored. +The names of beatmaps in Osu! consist an id, artist and the name of the soundtrack, so you can +specify any name in the flag that will contain one of those parts. + +"-showOrder" flag takes a boolean; if set to **true** - it will print an order in which the workers perform operations over each beatmap. Right now it`s just a helper flag. #### Examples 1. `./OBM -beatmap=""` - the same as just `./OBM`. It will affect **all** of your beatmaps @@ -50,6 +52,8 @@ specify any name in the flag that will contain one of those parts. The search is case-insensitive, so for example `./OBM -beatmap="Road of Resistance"` and `./OBM -beatmap="ROAD of rEsIsTaNcE"` will get you the same results +6. `./OBM -showOrder=true` - will print the order and exit +7. `./OBM -showOrder=true -beatmap="something here"` - will print the order and exit, just like in the previous one --- ## License diff --git a/flags.go b/flags.go index 014af26..c564b02 100644 --- a/flags.go +++ b/flags.go @@ -4,8 +4,13 @@ import ( "flag" ) +var ( + orderMessage string = "Order: Replace -> Retrieve -> Remove\n" +) + var ( cmdlnBeatmap = flag.String("beatmap", "", `Specifies a certain beatmap. If set to non "" - the program will search for given name and perform the magic provided in settings if successful`) + showOrder = flag.Bool("showOrder", false, "Prints an order in which functions are performed on a beatmap") ) diff --git a/manager/beatmap.go b/manager/beatmap.go index f76526a..bb92e9a 100644 --- a/manager/beatmap.go +++ b/manager/beatmap.go @@ -5,9 +5,7 @@ import ( "fmt" "os" "path/filepath" - "strings" - "github.com/Unbewohnte/OBM/logger" "github.com/Unbewohnte/OBM/util" ) @@ -89,117 +87,3 @@ func GetBeatmaps(baseOsuDir string) ([]Beatmap, error) { return beatmaps, nil } - -// parses given .osu file and returns the filename of its background -// NOTE: Osu! beatmap (as whole) can have multiple backgrounds for each .osu file -// the perfect example : https://osu.ppy.sh/beatmapsets/43701#osu/137122 -// this is why this functions asks for a certain difficulty (.osu filename) to be sure -// to return the correct background name -func (BEATMAP *Beatmap) GetBackgroundName(mapName string) (string, error) { - beatmapBytes, err := os.ReadFile(filepath.Join(BEATMAP.Path, mapName)) - if err != nil { - return "", err - } - beatmapContents := string(beatmapBytes) - - // get index of "[Events]" (this is where BG filename is stored) - eventsIndex := strings.Index(beatmapContents, "[Events]") - if eventsIndex == -1 { - return "", errors.New("Could not retrieve index of \"[Events]\"") - } - // get index of [TimingPoints] (this tag is right after the previous "[Events]" tag, - // so we can grab the whole "[Events]" tag contents) - timingPointsIndex := strings.Index(beatmapContents, "[TimingPoints]") - if timingPointsIndex == -1 { - return "", errors.New("Could not retrieve index of \"[TimingPoints]\"") - } - contentBetween := strings.Split(beatmapContents[eventsIndex:timingPointsIndex], ",") - - for _, chunk := range contentBetween { - if util.IsImage(chunk) { - return strings.Split(chunk, "\"")[1], nil - } - } - return "", nil -} - -// parses each beatmap`s .osu file for background info; -// removes original background and replaces it with copied version of given image -func (BEATMAP *Beatmap) ReplaceBackgrounds(replacementImgPath string) (successful, failed uint) { - // looping through each .osu file of a beatmap - for _, diff := range BEATMAP.Diffs { - background, err := BEATMAP.GetBackgroundName(diff) - if err != nil || background == "" { - logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error gettings background filename: %s", diff, err)) - failed++ - continue - } - // remove old bg - err = os.Remove(filepath.Join(BEATMAP.Path, background)) - if err != nil { - logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Could not remove the old bg: %s", diff, err)) - failed++ - continue - } - - // copy given picture, thus replacing background - err = util.CopyFile(replacementImgPath, filepath.Join(BEATMAP.Path, background)) - if err != nil { - logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Could not copy: %s", diff, err)) - failed++ - continue - } - successful++ - } - return successful, failed -} - -// retrieves backgrounds from given beatmap folder (same as in `ReplaceBackgrounds`) and copies them to the retrievement path -func (BEATMAP *Beatmap) RetrieveBackgrounds(retrievementPath string) (successful, failed uint) { - // looping through each .osu file of a beatmap - for _, diff := range BEATMAP.Diffs { - background, err := BEATMAP.GetBackgroundName(diff) - if err != nil || background == "" { - logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error gettings background filename: %s", diff, err)) - failed++ - continue - } - - // creating directory that represents current beatmap - dstPath := filepath.Join(retrievementPath, BEATMAP.Name) - - err = os.MkdirAll(dstPath, os.ModePerm) - if err != nil { - logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error creating a directory: %s", diff, err)) - failed++ - continue - } - - // copy background to the `retrievementPath` - err = util.CopyFile(filepath.Join(BEATMAP.Path, background), filepath.Join(dstPath, background)) - if err != nil { - logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Could not copy: %s", diff, err)) - failed++ - continue - } - successful++ - } - return successful, failed -} - -// Search tries to locate instances of beatmaps with the provided name (or part of the name); -// returns a slice of found beatmaps and a number of searched beatmaps -func Search(beatmaps []Beatmap, name string) ([]Beatmap, uint64) { - var instances []Beatmap - var searched uint64 = 0 - - // to make a search case-insensitive - name = strings.ToLower(name) - for _, beatmap := range beatmaps { - if strings.Contains(strings.ToLower(beatmap.Name), name) { - instances = append(instances, beatmap) - } - searched++ - } - return instances, searched -} diff --git a/manager/parse.go b/manager/parse.go new file mode 100644 index 0000000..cf95453 --- /dev/null +++ b/manager/parse.go @@ -0,0 +1,43 @@ +package manager + +import ( + "errors" + "os" + "path/filepath" + "strings" + + "github.com/Unbewohnte/OBM/util" +) + +// parses given .osu file and returns the filename of its background +// NOTE: Osu! beatmap (as whole) can have multiple backgrounds for each .osu file +// the perfect example : https://osu.ppy.sh/beatmapsets/43701#osu/137122 +// this is why this functions asks for a certain difficulty (.osu filename) to be sure +// to return the correct background name +func (BEATMAP *Beatmap) GetBackgroundName(diff string) (string, error) { + beatmapBytes, err := os.ReadFile(filepath.Join(BEATMAP.Path, diff)) + if err != nil { + return "", err + } + beatmapContents := string(beatmapBytes) + + // get index of "[Events]" (this is where BG filename is stored) + eventsIndex := strings.Index(beatmapContents, "[Events]") + if eventsIndex == -1 { + return "", errors.New("Could not retrieve index of \"[Events]\"") + } + // get index of [TimingPoints] (this tag is right after the previous "[Events]" tag, + // so we can grab the whole "[Events]" tag contents) + timingPointsIndex := strings.Index(beatmapContents, "[TimingPoints]") + if timingPointsIndex == -1 { + return "", errors.New("Could not retrieve index of \"[TimingPoints]\"") + } + contentBetween := strings.Split(beatmapContents[eventsIndex:timingPointsIndex], ",") + + for _, chunk := range contentBetween { + if util.IsImage(chunk) { + return strings.Split(chunk, "\"")[1], nil + } + } + return "", nil +} diff --git a/manager/remove.go b/manager/remove.go new file mode 100644 index 0000000..f110b11 --- /dev/null +++ b/manager/remove.go @@ -0,0 +1,31 @@ +package manager + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/Unbewohnte/OBM/logger" +) + +// parses each difficulty for background info, removes found backgrounds +func (BEATMAP *Beatmap) RemoveBackgrounds() (successful, failed uint) { + // looping through each .osu file of a beatmap + for _, diff := range BEATMAP.Diffs { + background, err := BEATMAP.GetBackgroundName(diff) + if err != nil || background == "" { + logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error getting background filename: %s", diff, err)) + failed++ + continue + } + // remove background + err = os.Remove(filepath.Join(BEATMAP.Path, background)) + if err != nil { + // background file does not exist (success ???) + successful++ + continue + } + successful++ + } + return successful, failed +} diff --git a/manager/replace.go b/manager/replace.go new file mode 100644 index 0000000..52e2d66 --- /dev/null +++ b/manager/replace.go @@ -0,0 +1,36 @@ +package manager + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/Unbewohnte/OBM/logger" + "github.com/Unbewohnte/OBM/util" +) + +// parses each beatmap`s .osu file for background info; +// removes original background and replaces it with copied version of given image +func (BEATMAP *Beatmap) ReplaceBackgrounds(replacementImgPath string) (successful, failed uint) { + // looping through each .osu file of a beatmap + for _, diff := range BEATMAP.Diffs { + background, err := BEATMAP.GetBackgroundName(diff) + if err != nil || background == "" { + logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error getting background filename: %s", diff, err)) + failed++ + continue + } + // remove old bg (if there is no background file - no need to worry) + os.Remove(filepath.Join(BEATMAP.Path, background)) + + // copy given picture, thus replacing background + err = util.CopyFile(replacementImgPath, filepath.Join(BEATMAP.Path, background)) + if err != nil { + logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Could not copy: %s", diff, err)) + failed++ + continue + } + successful++ + } + return successful, failed +} diff --git a/manager/retrieve.go b/manager/retrieve.go new file mode 100644 index 0000000..1979948 --- /dev/null +++ b/manager/retrieve.go @@ -0,0 +1,52 @@ +package manager + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/Unbewohnte/OBM/logger" + "github.com/Unbewohnte/OBM/util" +) + +// retrieves backgrounds from given beatmap folder (same as in `ReplaceBackgrounds`) and copies them to the retrievement path +func (BEATMAP *Beatmap) RetrieveBackgrounds(retrievementPath string) (successful, failed uint) { + // looping through each .osu file of a beatmap + for _, diff := range BEATMAP.Diffs { + background, err := BEATMAP.GetBackgroundName(diff) + if err != nil || background == "" { + logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error getting background filename: %s", diff, err)) + failed++ + continue + } + + // check if the background does exist + exists := util.DoesExist(filepath.Join(BEATMAP.Path, background)) + if !exists { + // if not - we cannot copy it, so moving to the next diff + logger.LogWarning(fmt.Sprintf("BEATMAP: %s: Background does not exist", diff)) + failed++ + continue + } + + // creating directory that represents current beatmap + dstPath := filepath.Join(retrievementPath, BEATMAP.Name) + + err = os.MkdirAll(dstPath, os.ModePerm) + if err != nil { + logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Error creating a directory: %s", diff, err)) + failed++ + continue + } + + // copy background to the `retrievementPath` + err = util.CopyFile(filepath.Join(BEATMAP.Path, background), filepath.Join(dstPath, background)) + if err != nil { + logger.LogError(false, fmt.Sprintf("BEATMAP: %s: Could not copy: %s", diff, err)) + failed++ + continue + } + successful++ + } + return successful, failed +} diff --git a/manager/search.go b/manager/search.go new file mode 100644 index 0000000..33bd9e0 --- /dev/null +++ b/manager/search.go @@ -0,0 +1,20 @@ +package manager + +import "strings" + +// Search tries to locate instances of beatmaps with the provided name (or part of the name); +// returns a slice of found beatmaps and a number of searched beatmaps +func Search(beatmaps []Beatmap, name string) ([]Beatmap, uint64) { + var instances []Beatmap + var searched uint64 = 0 + + // to make the search case-insensitive + name = strings.ToLower(name) + for _, beatmap := range beatmaps { + if strings.Contains(strings.ToLower(beatmap.Name), name) { + instances = append(instances, beatmap) + } + searched++ + } + return instances, searched +} diff --git a/settings/structure.go b/settings/structure.go index 8e98a48..ebcd5d4 100644 --- a/settings/structure.go +++ b/settings/structure.go @@ -14,6 +14,10 @@ type backgroundRetrievement struct { RetrievementPath string `json:"retrievementPath"` } +type backgroundRemovement struct { + Enabled bool `json:"enabled"` +} + type backgroundCreatement struct { Enabled bool `json:"enabled"` Width uint `json:"width"` @@ -25,6 +29,7 @@ type Settings struct { OsuDir string `json:"pathToOsu"` BackgroundReplacement backgroundReplacement `json:"backgroundReplacement"` BackgroundRetrievement backgroundRetrievement `json:"backgroundRetrievement"` + BackgroundRemovement backgroundRemovement `json:"backgroundRemovement"` CreateBlackBGImage backgroundCreatement `json:"blackBackgroundCreatement"` Workers int `json:"workers"` } diff --git a/util/background.go b/util/background.go index a7f745a..67d4e5b 100644 --- a/util/background.go +++ b/util/background.go @@ -9,7 +9,7 @@ import ( "os" ) -// creates a complete black image file +// creates a complete black image file in current working directory func CreateBlackBG(width, height uint) error { bgfile, err := os.Create("blackBG.png") if err != nil { diff --git a/util/checks.go b/util/checks.go index 001615e..70a6313 100644 --- a/util/checks.go +++ b/util/checks.go @@ -44,3 +44,11 @@ func IsImage(filename string) bool { } return false } + +func DoesExist(path string) bool { + _, err := os.Stat(path) + if err != nil { + return false + } + return true +} diff --git a/util/copy.go b/util/copy.go index 79f4a15..311ce96 100644 --- a/util/copy.go +++ b/util/copy.go @@ -9,12 +9,14 @@ import ( // opens given files, copies one into another func CopyFile(srcPath, dstPath string) error { + // open for reading (source) srcFile, err := os.Open(srcPath) if err != nil { return errors.New(fmt.Sprintf("Could not open src file : %s", err)) } defer srcFile.Close() + // open for writing (destination) (create file, if it does not exist already) dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE, os.ModePerm) if err != nil { return errors.New(fmt.Sprintf("Could not open dst file : %s", err)) diff --git a/worker.go b/worker.go index e63a65f..50d70d4 100644 --- a/worker.go +++ b/worker.go @@ -10,16 +10,23 @@ func worker(jobs <-chan job, results chan result, WG *sync.WaitGroup) { for job := range jobs { var successful, failed uint = 0, 0 + // the order is: Replace->Retrieve->Remove (if all 3 options are enabled) + if job.replacementImagePath != "" { + s, f := job.beatmap.ReplaceBackgrounds(job.replacementImagePath) + successful += s + failed += f + } if job.retrievementPath != "" { s, f := job.beatmap.RetrieveBackgrounds(job.retrievementPath) successful += s failed += f } - if job.replacementImagePath != "" { - s, f := job.beatmap.ReplaceBackgrounds(job.replacementImagePath) + if job.remove == true { + s, f := job.beatmap.RemoveBackgrounds() successful += s failed += f } + results <- result{ beatmapName: job.beatmap.Name, numberOfDiffs: uint(len(job.beatmap.Diffs)),