Compare commits

..

No commits in common. 'main' and '1.3.5' have entirely different histories.
main ... 1.3.5

  1. 3
      .gitignore
  2. 2
      Makefile
  3. 66
      OBM.go
  4. 9
      README.md
  5. 16
      flags.go
  6. 3
      go.mod
  7. 0
      logger/logger.go
  8. 11
      manager/beatmap.go
  9. 10
      manager/parse.go
  10. 2
      manager/remove.go
  11. 4
      manager/replace.go
  12. 4
      manager/retrieve.go
  13. 0
      manager/search.go
  14. 15
      settings/settings.go
  15. 0
      settings/structure.go
  16. 3
      src/go.mod
  17. 0
      util/background.go
  18. 2
      util/checks.go
  19. 0
      util/copy.go
  20. 51
      worker.go

3
.gitignore vendored

@ -1,3 +0,0 @@
settings.json
OBM
logs

2
Makefile

@ -1,2 +0,0 @@
all:
cd src && go build && mv OBM ..

66
src/OBM.go → OBM.go

@ -7,68 +7,12 @@ import (
"sync"
"time"
"unbewohnte/OBM/logger"
"unbewohnte/OBM/manager"
"unbewohnte/OBM/settings"
"unbewohnte/OBM/util"
"github.com/Unbewohnte/OBM/logger"
"github.com/Unbewohnte/OBM/manager"
"github.com/Unbewohnte/OBM/settings"
"github.com/Unbewohnte/OBM/util"
)
const 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")
)
// a basic implementation of a concurrent worker
func worker(jobs <-chan job, results chan result, WG *sync.WaitGroup) {
defer WG.Done()
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.remove == true {
s, f := job.beatmap.RemoveBackgrounds()
successful += s
failed += f
}
results <- result{
beatmapName: job.beatmap.Name,
numberOfDiffs: uint(len(job.beatmap.Diffs)),
successful: successful,
failed: failed,
}
}
}
// the `starter` that `glues` workers and jobs together
func workerPool(jobs chan job, results chan result, numOfWorkers int, WG *sync.WaitGroup) {
// check if there are less jobs than workers
if numOfWorkers > len(jobs) {
numOfWorkers = len(jobs)
}
// replacing backgrounds for each beatmap concurrently
for i := 0; i < numOfWorkers; i++ {
WG.Add(1)
go worker(jobs, results, WG)
}
}
type result struct {
beatmapName string
numberOfDiffs uint
@ -99,7 +43,7 @@ func init() {
// parse for `-beatmap` argument
flag.Parse()
// if `-showOrder` is checked - show the message and exit
// if `-showOrder` is checked - show the message
if *showOrder == true {
fmt.Print(orderMessage)
os.Exit(0)

9
README.md

@ -1,7 +1,6 @@
# OBM (Osu!-Background-Manager)
## This utility will help you with replacement, retrievement and removement of Osu!\`s beatmaps\` backgrounds
## NOTE: does NOT work with Osu!Lazer (Lazer has a different handling for beatmaps)
## This utility will help you with replacement and retrievement of Osu!\`s beatmaps\` backgrounds
**Use at your own risk !**
There is no way to return removed original backgrounds unless you delete all beatmaps and reimport newly downloaded versions of them again.
@ -24,7 +23,7 @@ There is no way to return removed original backgrounds unless you delete all bea
---
## Usage
To run - `./OBM` in terminal (on Unix) || `OBM.exe` in command line (on Windows) (a simple double-click on exe will certainly work as well)
To run - `./OBM` in terminal (on Unix) || `OBM` in command line (on Windows) (a simple double-click on exe will certainly work as well)
### First run
1. The program will generate a settings.json file if it is not already in the directory when you run it
@ -36,8 +35,8 @@ To run - `./OBM` in terminal (on Unix) || `OBM.exe` in command line (on Windows)
1. Start the utility again. If it has found the settings file - it will perform the magic according to provided rules
### Flags
Right now there are 2 arguments that you can specify before running the program - "beatmap" and "showOrder".
### Flags (starting from version 1.3.4)
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.

16
flags.go

@ -0,0 +1,16 @@
package main
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")
)

3
go.mod

@ -0,0 +1,3 @@
module github.com/Unbewohnte/OBM
go 1.16

0
src/logger/logger.go → logger/logger.go

11
src/manager/beatmap.go → manager/beatmap.go

@ -1,11 +1,12 @@
package manager
import (
"errors"
"fmt"
"os"
"path/filepath"
"unbewohnte/OBM/util"
"github.com/Unbewohnte/OBM/util"
)
// the main beatmap struct, contains necessary data for functions
@ -21,10 +22,10 @@ func getSongsDir(baseOsuDir string) (string, error) {
stat, err := os.Stat(songsDir)
if err != nil {
return "", fmt.Errorf("could not process the given path : %s", err)
return "", errors.New(fmt.Sprintf("Could not process the given path : %s", err))
}
if !stat.IsDir() {
return "", fmt.Errorf("given Osu! directory is not a directory")
return "", errors.New("Given Osu! directory is not a directory !")
}
return songsDir, nil
@ -34,7 +35,7 @@ func getSongsDir(baseOsuDir string) (string, error) {
func getDiffs(path string) ([]string, error) {
files, err := os.ReadDir(path)
if err != nil {
return nil, fmt.Errorf("could not read a directory : %s", err)
return nil, errors.New(fmt.Sprintf("Could not read a directory : %s", err))
}
var diffs []string
@ -64,7 +65,7 @@ func GetBeatmaps(baseOsuDir string) ([]Beatmap, error) {
}
contents, err := os.ReadDir(songsDir)
if err != nil {
return nil, fmt.Errorf("could not read a directory : %s", err)
return nil, errors.New(fmt.Sprintf("Could not read a directory : %s", err))
}
var beatmaps []Beatmap

10
src/manager/parse.go → manager/parse.go

@ -1,18 +1,18 @@
package manager
import (
"fmt"
"errors"
"os"
"path/filepath"
"strings"
"unbewohnte/OBM/util"
"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 function asks for a certain difficulty (.osu filename) to be sure
// 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))
@ -24,13 +24,13 @@ func (BEATMAP *Beatmap) GetBackgroundName(diff string) (string, error) {
// get index of "[Events]" (this is where BG filename is stored)
eventsIndex := strings.Index(beatmapContents, "[Events]")
if eventsIndex == -1 {
return "", fmt.Errorf("could not retrieve index of \"[Events]\"")
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 "", fmt.Errorf("could not retrieve index of \"[TimingPoints]\"")
return "", errors.New("Could not retrieve index of \"[TimingPoints]\"")
}
contentBetween := strings.Split(beatmapContents[eventsIndex:timingPointsIndex], ",")

2
src/manager/remove.go → manager/remove.go

@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
"unbewohnte/OBM/logger"
"github.com/Unbewohnte/OBM/logger"
)
// parses each difficulty for background info, removes found backgrounds

4
src/manager/replace.go → manager/replace.go

@ -5,8 +5,8 @@ import (
"os"
"path/filepath"
"unbewohnte/OBM/logger"
"unbewohnte/OBM/util"
"github.com/Unbewohnte/OBM/logger"
"github.com/Unbewohnte/OBM/util"
)
// parses each beatmap`s .osu file for background info;

4
src/manager/retrieve.go → manager/retrieve.go

@ -5,8 +5,8 @@ import (
"os"
"path/filepath"
"unbewohnte/OBM/logger"
"unbewohnte/OBM/util"
"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

0
src/manager/search.go → manager/search.go

15
src/settings/settings.go → settings/settings.go

@ -2,11 +2,12 @@ package settings
import (
"encoding/json"
"errors"
"fmt"
"os"
"unbewohnte/OBM/logger"
"unbewohnte/OBM/util"
"github.com/Unbewohnte/OBM/logger"
"github.com/Unbewohnte/OBM/util"
)
const (
@ -17,7 +18,7 @@ const (
func DoesExist() (bool, error) {
files, err := os.ReadDir(".")
if err != nil {
return false, fmt.Errorf("wasn`t able to read current directory %s", err)
return false, errors.New(fmt.Sprintf("Unable to read current directory %s", err))
}
for _, file := range files {
@ -41,9 +42,8 @@ func Create() error {
file, err := os.Create(settingsFilename)
if err != nil {
return fmt.Errorf("could not create settings file : %s", err)
return errors.New(fmt.Sprintf("Unable to create settings file : %s", err))
}
defer file.Close()
// marshaling default settings
settingsJson, err := json.MarshalIndent(Settings{
@ -67,10 +67,11 @@ func Create() error {
Workers: 100,
}, "", " ")
if err != nil {
return fmt.Errorf("could not marshal settings into file : %s", err)
return errors.New(fmt.Sprintf("Could not marshal settings into file : %s", err))
}
file.Write(settingsJson)
file.Close()
return nil
}
@ -89,7 +90,7 @@ func Get() Settings {
}
// if all features are disabled
if !settings.BackgroundReplacement.Enabled && !settings.BackgroundRetrievement.Enabled && !settings.BackgroundRemovement.Enabled {
if !settings.BackgroundReplacement.Enabled && !settings.BackgroundRetrievement.Enabled {
logger.LogInfo("No features enabled. Exiting...")
os.Exit(0)
}

0
src/settings/structure.go → settings/structure.go

3
src/go.mod

@ -1,3 +0,0 @@
module unbewohnte/OBM
go 1.16

0
src/util/background.go → util/background.go

2
src/util/checks.go → util/checks.go

@ -5,7 +5,6 @@ import (
"strings"
)
// checks if given path is a directory
func IsDir(path string) bool {
info, err := os.Stat(path)
if err != nil {
@ -46,7 +45,6 @@ func IsImage(filename string) bool {
return false
}
// checks if given directory/file does exist
func DoesExist(path string) bool {
_, err := os.Stat(path)
if err != nil {

0
src/util/copy.go → util/copy.go

51
worker.go

@ -0,0 +1,51 @@
package main
import (
"sync"
)
// a basic implementation of a concurrent worker
func worker(jobs <-chan job, results chan result, WG *sync.WaitGroup) {
defer WG.Done()
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.remove == true {
s, f := job.beatmap.RemoveBackgrounds()
successful += s
failed += f
}
results <- result{
beatmapName: job.beatmap.Name,
numberOfDiffs: uint(len(job.beatmap.Diffs)),
successful: successful,
failed: failed,
}
}
}
func workerPool(jobs chan job, results chan result, numOfWorkers int, WG *sync.WaitGroup) {
// check if there are less jobs than workers
if numOfWorkers > len(jobs) {
numOfWorkers = len(jobs)
}
// replacing backgrounds for each beatmap concurrently
for i := 0; i < numOfWorkers; i++ {
WG.Add(1)
go worker(jobs, results, WG)
}
}
Loading…
Cancel
Save