#1 - Created request to check newest version of the app
All checks were successful
Build / build (push) Successful in 2m35s

#2 - Added request to download the newest version of the app
#3 - Added request to check progress during sync
#4 - Now blocking all request while sync is in progress
#5 - Implemented ants for thread pooling
#6 - Changed the sync request to now only start the sync
This commit is contained in:
2025-08-23 11:36:03 +02:00
parent 0d1c69d95e
commit 806e88adeb
26 changed files with 673 additions and 675 deletions

View File

@@ -7,8 +7,7 @@ import (
)
func GetCharacters() []string {
musicPath := os.Getenv("MUSIC_PATH")
charactersPath := musicPath + "characters/"
charactersPath := os.Getenv("CHARACTERS_PATH")
files, err := os.ReadDir(charactersPath)
if err != nil {
log.Fatal(err)
@@ -32,4 +31,3 @@ func GetCharacter(character string) string {
func isImage(entry os.DirEntry) bool {
return !entry.IsDir() && (strings.HasSuffix(entry.Name(), ".jpg") || strings.HasSuffix(entry.Name(), ".png"))
}

View File

@@ -0,0 +1,106 @@
package backend
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
)
type giteaResponse struct {
Id int `json:"id"`
Name string `json:"name"`
Assets []assetResponse `json:"assets"`
}
type assetResponse struct {
Id int `json:"id"`
Name string `json:"name"`
DownloadUrl string `json:"browser_download_url"`
}
func CheckLatest() string {
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
//Create a variable of the same type as our model
var cResp giteaResponse
//Decode the data
if err := json.NewDecoder(resp.Body).Decode(&cResp); err != nil {
fmt.Println(err)
log.Fatal("ooopsss! an error occurred, please try again")
}
log.Printf("Id: %v, Name: %v", cResp.Id, cResp.Name)
return cResp.Name
}
func ListAssetsOfLatest() []string {
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
//Create a variable of the same type as our model
var cResp giteaResponse
//Decode the data
if err := json.NewDecoder(resp.Body).Decode(&cResp); err != nil {
fmt.Println(err)
log.Fatal("ooopsss! an error occurred, please try again")
}
log.Printf("Id: %v, Name: %v", cResp.Id, cResp.Name)
var assets []string
for _, asset := range cResp.Assets {
log.Printf("Id: %v, Name: %v, Asset: %v", cResp.Id, cResp.Name, asset.Name)
assets = append(assets, asset.Name)
}
return assets
}
func DownloadLatestWindows() string {
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
//Create a variable of the same type as our model
var cResp giteaResponse
//Decode the data
if err := json.NewDecoder(resp.Body).Decode(&cResp); err != nil {
fmt.Println(err)
log.Fatal("ooopsss! an error occurred, please try again")
}
log.Printf("Id: %v, Name: %v", cResp.Id, cResp.Name)
for _, asset := range cResp.Assets {
log.Printf("Id: %v, Name: %v, Asset: %v", cResp.Id, cResp.Name, asset.Name)
if strings.HasSuffix(asset.Name, ".exe") {
return asset.DownloadUrl
}
}
return ""
}
func DownloadLatestLinux() string {
resp, err := http.Get("https://gitea.sanplex.tech/api/v1/repos/sansan/MusicPlayer/releases/latest")
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
//Create a variable of the same type as our model
var cResp giteaResponse
//Decode the data
if err := json.NewDecoder(resp.Body).Decode(&cResp); err != nil {
fmt.Println(err)
log.Fatal("ooopsss! an error occurred, please try again")
}
log.Printf("Id: %v, Name: %v", cResp.Id, cResp.Name)
for _, asset := range cResp.Assets {
log.Printf("Id: %v, Name: %v, Asset: %v", cResp.Id, cResp.Name, asset.Name)
if strings.HasSuffix(asset.Name, ".x86_64") {
return asset.DownloadUrl
}
}
return ""
}

View File

@@ -15,9 +15,26 @@ type VersionData struct {
}
func GetVersionHistory() VersionData {
data := VersionData{Version: "3.2",
Changelog: "Upgraded Go version and the version of all dependencies. Fixed som more bugs.",
data := VersionData{Version: "4.5.0",
Changelog: "#1 - Created request to check newest version of the app\n" +
"#2 - Added request to download the newest version of the app\n" +
"#3 - Added request to check progress during sync\n" +
"#4 - Now blocking all request while sync is in progress\n" +
"#5 - Implemented ants for thread pooling\n" +
"#6 - Changed the sync request to now only start the sync",
History: []VersionData{
{
Version: "4.0.0",
Changelog: "Changed framework from gin to Echo\n" +
"Reorganized the code\n" +
"Implemented sqlc\n" +
"Added support to send character images from the server\n" +
"Added function to create a new database of no one exists",
},
{
Version: "3.2",
Changelog: "Upgraded Go version and the version of all dependencies. Fixed som more bugs.",
},
{
Version: "3.1",
Changelog: "Fixed some bugs with songs not found made the application crash. Now checking if song exists and if not, remove song from DB and find another one. Frontend is now decoupled from the backend.",

View File

@@ -21,13 +21,10 @@ type SongInfo struct {
var currentSong = -1
// var games []models.GameData
var gamesNew []repository.Game
// var songQue []models.SongData
var songQueNew []repository.Song
// var lastFetched models.SongData
var lastFetchedNew repository.Song
var repo *repository.Queries
@@ -60,7 +57,6 @@ func Reset() {
currentSong = -1
initRepo()
gamesNew, _ = repo.FindAllGames(db.Ctx)
//games = db.FindAllGames()
}
func AddLatestToQue() {
@@ -80,8 +76,6 @@ func AddLatestPlayed() {
initRepo()
repo.AddGamePlayed(db.Ctx, currentSongData.GameID)
repo.AddSongPlayed(db.Ctx, repository.AddSongPlayedParams{GameID: currentSongData.GameID, SongName: currentSongData.SongName})
//db.AddGamePlayed(currentSongData.GameId)
//db.AddSongPlayed(currentSongData.GameId, currentSongData.SongName)
}
func SetPlayed(songNumber int) {
@@ -92,14 +86,9 @@ func SetPlayed(songNumber int) {
initRepo()
repo.AddGamePlayed(db.Ctx, songData.GameID)
repo.AddSongPlayed(db.Ctx, repository.AddSongPlayedParams{GameID: songData.GameID, SongName: songData.SongName})
//db.AddGamePlayed(songData.GameId)
//db.AddSongPlayed(songData.GameId, songData.SongName)
}
func GetRandomSong() string {
/*if len(games) == 0 {
games = db.FindAllGames()
}*/
getAllGames()
if len(gamesNew) == 0 {
return ""
@@ -111,12 +100,8 @@ func GetRandomSong() string {
}
func GetRandomSongLowChance() string {
/*if len(games) == 0 {
games = db.FindAllGames()
}*/
getAllGames()
//var listOfGames []models.GameData
var listOfGames []repository.Game
var averagePlayed = getAveragePlayed()
@@ -139,15 +124,10 @@ func GetRandomSongLowChance() string {
}
func GetRandomSongClassic() string {
/*if games == nil || len(games) == 0 {
games = db.FindAllGames()
}*/
getAllGames()
var listOfAllSongs []repository.Song
for _, game := range gamesNew {
//listOfAllSongs = append(listOfAllSongs, db.FindSongsFromGame(game.Id)...)
songList, _ := repo.FindSongsFromGame(db.Ctx, game.ID)
listOfAllSongs = append(listOfAllSongs, songList...)
}
@@ -156,13 +136,10 @@ func GetRandomSongClassic() string {
var song repository.Song
for !songFound {
song = listOfAllSongs[rand.Intn(len(listOfAllSongs))]
//gameData, err := db.GetGameById(song.GameId)
gameData, err := repo.GetGameById(db.Ctx, song.GameID)
if err != nil {
//db.RemoveBrokenSong(song)
repo.RemoveBrokenSong(db.Ctx, song.Path)
//log.Println("Song not found, song '" + song.SongName + "' deleted from game '" + gameData.GameName + "' FileName: " + song.FileName)
log.Printf("Song not found, song '%s' deleted from game '%s' FileName: %v\n", song.SongName, gameData.GameName, song.FileName)
continue
}
@@ -171,9 +148,7 @@ func GetRandomSongClassic() string {
openFile, err := os.Open(song.Path)
if err != nil || (song.FileName != nil && gameData.Path+*song.FileName != song.Path) {
//File not found
//db.RemoveBrokenSong(song)
repo.RemoveBrokenSong(db.Ctx, song.Path)
//log.Println("Song not found, song '" + song.SongName + "' deleted from game '" + gameData.GameName + "' FileName: " + song.FileName)
log.Printf("Song not found, song '%s' deleted from game '%s' FileName: %v\n", song.SongName, gameData.GameName, song.FileName)
} else {
songFound = true
@@ -234,9 +209,6 @@ func GetSong(song string) string {
}
func GetAllGames() []string {
/*if games == nil || len(games) == 0 {
games = db.FindAllGames()
}*/
getAllGames()
var jsonArray []string
@@ -247,9 +219,6 @@ func GetAllGames() []string {
}
func GetAllGamesRandom() []string {
/*if games == nil || len(games) == 0 {
games = db.FindAllGames()
}*/
getAllGames()
var jsonArray []string
@@ -293,10 +262,7 @@ func getSongFromList(games []repository.Game) repository.Song {
var song repository.Song
for !songFound {
game := getRandomGame(games)
//log.Println("game = ", game)
//songs := db.FindSongsFromGame(game.Id)
songs, _ := repo.FindSongsFromGame(db.Ctx, game.ID)
//log.Println("songs = ", songs)
if len(songs) == 0 {
continue
}
@@ -305,14 +271,9 @@ func getSongFromList(games []repository.Game) repository.Song {
//Check if file exists and open
openFile, err := os.Open(song.Path)
//log.Println("game.Path+song.FileName: ", game.Path+song.FileName)
//log.Println("song.Path: ", song.Path)
//log.Println("game.Path+song.FileName != song.Path: ", game.Path+song.FileName != song.Path)
if err != nil || (song.FileName != nil && game.Path+*song.FileName != song.Path) || (song.FileName != nil && strings.HasSuffix(*song.FileName, ".wav")) {
//File not found
//db.RemoveBrokenSong(song)
repo.RemoveBrokenSong(db.Ctx, song.Path)
//log.Println("Song not found, song '" + song.SongName + "' deleted from game '" + game.GameName + "' FileName: " + song.FileName)
log.Printf("Song not found, song '%s' deleted from game '%s' FileName: %v\n", song.SongName, game.GameName, song.FileName)
} else {
songFound = true

View File

@@ -5,6 +5,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/panjf2000/ants/v2"
"io"
"io/fs"
"log"
@@ -21,6 +22,11 @@ import (
"github.com/spf13/afero"
)
var Syncing = false
var foldersSynced float32
var numberOfFoldersToSync float32
var totalTime time.Duration
var allGames []repository.Game
var gamesBeforeSync []repository.Game
var gamesAfterSync []repository.Game
@@ -61,67 +67,18 @@ func (gs GameStatus) String() string {
return statusName[gs]
}
var syncWg sync.WaitGroup
func ResetDB() {
//db.ClearSongs(-1)
repo.ClearSongs(db.Ctx)
//db.ClearGames()
repo.ClearGames(db.Ctx)
}
func SyncGamesNewFull() Response {
return syncGamesNew(true)
func SyncProgress() string {
//log.Printf("Progress: %v%%\n", int((foldersSynced/numberOfFoldersToSync)*10))
log.Printf("Progress: %v/%v %v%%\n", int(foldersSynced), int(numberOfFoldersToSync), int((foldersSynced/numberOfFoldersToSync)*100))
return fmt.Sprintf("Progress: %v%%", int((foldersSynced/numberOfFoldersToSync)*100))
}
func SyncGamesNewOnlyChanges() Response {
return syncGamesNew(false)
}
func syncGamesNew(full bool) Response {
musicPath := os.Getenv("MUSIC_PATH")
fmt.Printf("dir: %s\n", musicPath)
initRepo()
start := time.Now()
foldersToSkip := []string{".sync", "dist", "old", "characters"}
fmt.Println(foldersToSkip)
var err error
gamesAdded = nil
gamesReAdded = nil
gamesChangedTitle = nil
gamesChangedContent = nil
gamesRemoved = nil
catchedErrors = nil
brokenSongs = nil
gamesBeforeSync, err = repo.FindAllGames(db.Ctx)
handleError("FindAllGames Before", err, "")
fmt.Printf("Games Before: %d\n", len(gamesBeforeSync))
allGames, err = repo.GetAllGamesIncludingDeleted(db.Ctx)
handleError("GetAllGamesIncludingDeleted", err, "")
err = repo.SetGameDeletionDate(db.Ctx)
handleError("SetGameDeletionDate", err, "")
directories, err := os.ReadDir(musicPath)
if err != nil {
log.Fatal(err)
}
syncWg.Add(len(directories))
for _, dir := range directories {
go func() {
defer syncWg.Done()
syncGameNew(dir, foldersToSkip, musicPath, full)
}()
}
syncWg.Wait()
checkBrokenSongsNew()
gamesAfterSync, err = repo.FindAllGames(db.Ctx)
handleError("FindAllGames After", err, "")
func SyncResult() Response {
fmt.Printf("\nGames Before: %d\n", len(gamesBeforeSync))
fmt.Printf("Games After: %d\n", len(gamesAfterSync))
@@ -148,16 +105,16 @@ func syncGamesNew(full bool) Response {
fmt.Printf("\n\n")
var gamesRemovedTemp []string
for _, beforeGame := range gamesBeforeSync {
var found bool = false
var found = false
for _, afterGame := range gamesAfterSync {
if beforeGame.GameName == afterGame.GameName {
found = true
//fmt.Printf("Game: %s, Found: %v break\n", beforeGame.GameName, found)
fmt.Printf("Game: %s, Found: %v break\n", beforeGame.GameName, found)
break
}
}
if !found {
//fmt.Printf("Game: %s, Found: %v\n", beforeGame.GameName, found)
fmt.Printf("Game: %s, Found: %v\n", beforeGame.GameName, found)
gamesRemovedTemp = append(gamesRemovedTemp, beforeGame.GameName)
}
}
@@ -186,8 +143,6 @@ func syncGamesNew(full bool) Response {
fmt.Printf("%s\n", error)
}
finished := time.Now()
totalTime := finished.Sub(start)
out := time.Time{}.Add(totalTime)
fmt.Printf("\nTotal time: %v\n", totalTime)
fmt.Printf("Total time: %v\n", out.Format("15:04:05.00000"))
@@ -202,16 +157,94 @@ func syncGamesNew(full bool) Response {
}
}
func SyncGamesNewFull() {
syncGamesNew(true)
Reset()
}
func SyncGamesNewOnlyChanges() {
syncGamesNew(false)
Reset()
}
func syncGamesNew(full bool) {
Syncing = true
musicPath := os.Getenv("MUSIC_PATH")
fmt.Printf("dir: %s\n", musicPath)
if !strings.HasSuffix(musicPath, "/") {
musicPath += "/"
}
var syncWg sync.WaitGroup
initRepo()
start := time.Now()
foldersToSkip := []string{".sync", "dist", "old", "characters"}
fmt.Println(foldersToSkip)
var err error
gamesAdded = nil
gamesReAdded = nil
gamesChangedTitle = nil
gamesChangedContent = nil
gamesRemoved = nil
catchedErrors = nil
brokenSongs = nil
gamesBeforeSync, err = repo.FindAllGames(db.Ctx)
handleError("FindAllGames Before", err, "")
fmt.Printf("Games Before: %d\n", len(gamesBeforeSync))
allGames, err = repo.GetAllGamesIncludingDeleted(db.Ctx)
handleError("GetAllGamesIncludingDeleted", err, "")
err = repo.SetGameDeletionDate(db.Ctx)
handleError("SetGameDeletionDate", err, "")
directories, err := os.ReadDir(musicPath)
if err != nil {
log.Fatal(err)
}
pool, _ := ants.NewPool(50, ants.WithPreAlloc(true))
defer pool.Release()
numberOfFoldersToSync = float32(len(directories))
syncWg.Add(int(numberOfFoldersToSync))
for _, dir := range directories {
pool.Submit(func() {
defer syncWg.Done()
syncGameNew(dir, foldersToSkip, musicPath, full)
})
}
syncWg.Wait()
checkBrokenSongsNew()
gamesAfterSync, err = repo.FindAllGames(db.Ctx)
handleError("FindAllGames After", err, "")
finished := time.Now()
totalTime = finished.Sub(start)
out := time.Time{}.Add(totalTime)
fmt.Printf("\nTotal time: %v\n", totalTime)
fmt.Printf("Total time: %v\n", out.Format("15:04:05.00000"))
Syncing = false
}
func checkBrokenSongsNew() {
allSongs, err := repo.FetchAllSongs(db.Ctx)
handleError("FetchAllSongs", err, "")
var brokenWg sync.WaitGroup
poolBroken, _ := ants.NewPool(50, ants.WithPreAlloc(true))
defer poolBroken.Release()
brokenWg.Add(len(allSongs))
for _, song := range allSongs {
go func() {
poolBroken.Submit(func() {
defer brokenWg.Done()
checkBrokenSongNew(song)
}()
})
}
brokenWg.Wait()
err = repo.RemoveBrokenSongs(db.Ctx, brokenSongs)
@@ -343,6 +376,8 @@ func syncGameNew(file os.DirEntry, foldersToSkip []string, baseDir string, full
err = repo.RemoveDeletionDate(db.Ctx, id)
handleError("RemoveDeletionDate", err, "")
}
foldersSynced++
log.Printf("Progress: %v/%v %v%%\n", int(foldersSynced), int(numberOfFoldersToSync), int((foldersSynced/numberOfFoldersToSync)*100))
}
func insertGameNew(name string, path string, hash string) int32 {
@@ -365,19 +400,26 @@ func insertGameNew(name string, path string, hash string) int32 {
func newCheckSongs(entries []os.DirEntry, gameDir string, id int32) int32 {
//hasher := md5.New()
var numberOfSongs int32
numberOfFiles := len(entries)
var songWg sync.WaitGroup
songWg.Add(len(entries))
poolSong, _ := ants.NewPool(numberOfFiles, ants.WithPreAlloc(true))
defer poolSong.Release()
songWg.Add(numberOfFiles)
for _, entry := range entries {
go func() {
poolSong.Submit(func() {
defer songWg.Done()
newCheckSong(entry, gameDir, id)
}()
if newCheckSong(entry, gameDir, id) {
numberOfSongs++
}
})
}
songWg.Wait()
return numberOfSongs
}
func newCheckSong(entry os.DirEntry, gameDir string, id int32) {
func newCheckSong(entry os.DirEntry, gameDir string, id int32) bool {
fileInfo, err := entry.Info()
if err != nil {
log.Println(err)
@@ -396,7 +438,7 @@ func newCheckSong(entry os.DirEntry, gameDir string, id int32) {
handleError("GetSongWithHash", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
if err == nil {
if song.SongName == songName && song.Path == path {
return
return false
}
}
fmt.Printf("Song Changed\n")
@@ -432,9 +474,11 @@ func newCheckSong(entry os.DirEntry, gameDir string, id int32) {
}
}
return true
} else if isCoverImage(fileInfo) {
//TODO: Later add cover art image here in db
}
return false
}
func handleError(funcName string, err error, msg string) {