Moved around more code. Implemented more sqlc. Added support to generate swagger.
Added support for profiling. Removed the pkg module altogether. Everything except old sync is now using code generated by sqlc.
This commit is contained in:
103
internal/database/game.go
Normal file
103
internal/database/game.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"music-server/internal/db"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type gameData struct {
|
||||
Id int
|
||||
GameName string
|
||||
Added time.Time
|
||||
Deleted time.Time
|
||||
LastChanged time.Time
|
||||
Path string
|
||||
TimesPlayed int
|
||||
LastPlayed time.Time
|
||||
NumberOfSongs int32
|
||||
}
|
||||
|
||||
func GetGameName(gameId int) string {
|
||||
var gameName = ""
|
||||
err := db.Dbpool.QueryRow(db.Ctx,
|
||||
"SELECT game_name FROM game WHERE id = $1", gameId).Scan(&gameName)
|
||||
if err != nil {
|
||||
if compareError.Error() != err.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return gameName
|
||||
}
|
||||
|
||||
func SetGameDeletionDate() {
|
||||
_, err := db.Dbpool.Exec(db.Ctx,
|
||||
"UPDATE game SET deleted=$1 WHERE deleted IS NULL", time.Now())
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateGameName(id int, name string, path string) {
|
||||
_, err := db.Dbpool.Exec(db.Ctx,
|
||||
"UPDATE game SET game_name=$1, path=$2, last_changed=$3 WHERE id=$4",
|
||||
name, path, time.Now(), id)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveDeletionDate(id int) {
|
||||
_, err := db.Dbpool.Exec(db.Ctx,
|
||||
"UPDATE game SET deleted=null WHERE id=$1", id)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetIdByGameName(name string) int {
|
||||
var gameId = -1
|
||||
err := db.Dbpool.QueryRow(db.Ctx,
|
||||
"SELECT id FROM game WHERE game_name = $1", name).Scan(&gameId)
|
||||
if err != nil {
|
||||
if compareError.Error() != err.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return gameId
|
||||
}
|
||||
|
||||
func InsertGame(name string, path string) int {
|
||||
gameId := -1
|
||||
err := db.Dbpool.QueryRow(db.Ctx,
|
||||
"INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
|
||||
name, path, time.Now()).Scan(&gameId)
|
||||
if err != nil {
|
||||
if compareError.Error() != err.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
db.ResetGameIdSeq()
|
||||
err2 := db.Dbpool.QueryRow(db.Ctx,
|
||||
"INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
|
||||
name, path, time.Now()).Scan(&gameId)
|
||||
if err2 != nil {
|
||||
if compareError.Error() != err2.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return gameId
|
||||
}
|
||||
|
||||
func InsertGameWithExistingId(id int, name string, path string) {
|
||||
_, err := db.Dbpool.Exec(db.Ctx,
|
||||
"INSERT INTO game(id, game_name, path, added) VALUES ($1, $2, $3, $4)",
|
||||
id, name, path, time.Now())
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
206
internal/database/oldSync.go
Normal file
206
internal/database/oldSync.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
func SyncGames() {
|
||||
start := time.Now()
|
||||
dir := os.Getenv("MUSIC_PATH")
|
||||
fmt.Printf("dir: %s\n", dir)
|
||||
foldersToSkip := []string{".sync", "dist", "old", "characters"}
|
||||
fmt.Println(foldersToSkip)
|
||||
SetGameDeletionDate()
|
||||
checkBrokenSongs()
|
||||
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
syncGame(file, foldersToSkip, dir)
|
||||
}
|
||||
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"))
|
||||
}
|
||||
|
||||
func SyncGamesQuick() {
|
||||
start := time.Now()
|
||||
dir := os.Getenv("MUSIC_PATH")
|
||||
fmt.Printf("dir: %s\n", dir)
|
||||
foldersToSkip := []string{".sync", "dist", "old", "characters"}
|
||||
fmt.Println(foldersToSkip)
|
||||
SetGameDeletionDate()
|
||||
checkBrokenSongs()
|
||||
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
syncGame(file, foldersToSkip, dir)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
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"))
|
||||
}
|
||||
|
||||
func syncGame(file os.DirEntry, foldersToSkip []string, dir string) {
|
||||
if file.IsDir() && !contains(foldersToSkip, file.Name()) {
|
||||
fmt.Println(file.Name())
|
||||
path := dir + file.Name() + "/"
|
||||
fmt.Println(path)
|
||||
|
||||
entries, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
id := -1
|
||||
for _, entry := range entries {
|
||||
fileInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
id = getIdFromFile(fileInfo)
|
||||
if id != -1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if id == -1 {
|
||||
addNewGame(file.Name(), path)
|
||||
} else {
|
||||
checkIfChanged(id, file.Name(), path)
|
||||
checkSongs(path, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getIdFromFile(file os.FileInfo) int {
|
||||
name := file.Name()
|
||||
if !file.IsDir() && strings.HasSuffix(name, ".id") {
|
||||
name = strings.Replace(name, ".id", "", 1)
|
||||
name = strings.Replace(name, ".", "", 1)
|
||||
i, _ := strconv.Atoi(name)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func checkIfChanged(id int, name string, path string) {
|
||||
fmt.Printf("Id from file: %v\n", id)
|
||||
nameFromDb := GetGameName(id)
|
||||
fmt.Printf("Name from file: %v\n", name)
|
||||
fmt.Printf("Name from DB: %v\n", nameFromDb)
|
||||
if nameFromDb == "" {
|
||||
fmt.Println("Not in db")
|
||||
InsertGameWithExistingId(id, name, path)
|
||||
fmt.Println("Added to db")
|
||||
} else if name != nameFromDb {
|
||||
fmt.Println("Diff name")
|
||||
UpdateGameName(id, name, path)
|
||||
checkBrokenSongs()
|
||||
}
|
||||
RemoveDeletionDate(id)
|
||||
}
|
||||
|
||||
func addNewGame(name string, path string) {
|
||||
newId := GetIdByGameName(name)
|
||||
if newId != -1 {
|
||||
checkBrokenSongs()
|
||||
RemoveDeletionDate(newId)
|
||||
} else {
|
||||
newId = InsertGame(name, path)
|
||||
}
|
||||
|
||||
fmt.Printf("newId = %v", newId)
|
||||
fileName := path + "/." + strconv.Itoa(newId) + ".id"
|
||||
fmt.Printf("fileName = %v", fileName)
|
||||
|
||||
err := os.WriteFile(fileName, nil, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
checkSongs(path, newId)
|
||||
}
|
||||
|
||||
func checkSongs(gameDir string, gameId int) {
|
||||
files, err := os.ReadDir(gameDir)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
entry, err := file.Info()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if isSong(entry) {
|
||||
path := gameDir + entry.Name()
|
||||
fileName := entry.Name()
|
||||
songName, _ := strings.CutSuffix(fileName, ".mp3")
|
||||
if CheckSong(path) {
|
||||
UpdateSong(songName, fileName, path)
|
||||
} else {
|
||||
AddSong(SongData{GameId: gameId, SongName: songName, Path: path, FileName: fileName})
|
||||
}
|
||||
} else if isCoverImage(entry) {
|
||||
//TODO: Later add cover art image here in db
|
||||
}
|
||||
}
|
||||
//TODO: Add number of songs here
|
||||
}
|
||||
|
||||
func checkBrokenSongs() {
|
||||
allSongs := FetchAllSongs()
|
||||
var brokenSongs []SongData
|
||||
for _, song := range allSongs {
|
||||
//Check if file exists and open
|
||||
openFile, err := os.Open(song.Path)
|
||||
if err != nil {
|
||||
//File not found
|
||||
brokenSongs = append(brokenSongs, song)
|
||||
fmt.Printf("song broken: %v", song.Path)
|
||||
} else {
|
||||
err = openFile.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
RemoveBrokenSongs(brokenSongs)
|
||||
}
|
||||
|
||||
func isSong(entry fs.FileInfo) bool {
|
||||
return !entry.IsDir() && strings.HasSuffix(entry.Name(), ".mp3")
|
||||
}
|
||||
|
||||
func isCoverImage(entry fs.FileInfo) bool {
|
||||
return !entry.IsDir() && strings.Contains(entry.Name(), "cover") &&
|
||||
(strings.HasSuffix(entry.Name(), ".jpg") || strings.HasSuffix(entry.Name(), ".png"))
|
||||
}
|
||||
|
||||
func contains(s []string, searchTerm string) bool {
|
||||
i := sort.SearchStrings(s, searchTerm)
|
||||
return i < len(s) && s[i] == searchTerm
|
||||
}
|
||||
92
internal/database/song.go
Normal file
92
internal/database/song.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"music-server/internal/db"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SongData struct {
|
||||
GameId int
|
||||
SongName string
|
||||
Path string
|
||||
TimesPlayed int
|
||||
FileName string
|
||||
}
|
||||
|
||||
var compareError = errors.New("no rows in result set")
|
||||
|
||||
func AddSong(song SongData) {
|
||||
_, err := db.Dbpool.Exec(db.Ctx,
|
||||
"INSERT INTO song(game_id, song_name, path, file_name) VALUES ($1, $2, $3, $4)",
|
||||
song.GameId, song.SongName, song.Path, song.FileName)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckSong(songPath string) bool {
|
||||
var path string
|
||||
err := db.Dbpool.QueryRow(db.Ctx,
|
||||
"SELECT path FROM song WHERE path = $1", songPath).Scan(&path)
|
||||
if err != nil {
|
||||
if compareError.Error() != err.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
return path != ""
|
||||
}
|
||||
|
||||
func UpdateSong(songName string, fileName string, path string) {
|
||||
_, err := db.Dbpool.Exec(db.Ctx,
|
||||
"UPDATE song SET song_name=$1, file_name=$2 WHERE path = $3",
|
||||
songName, fileName, path)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func FetchAllSongs() []SongData {
|
||||
rows, err := db.Dbpool.Query(db.Ctx,
|
||||
"SELECT song_name, path FROM song")
|
||||
if err != nil {
|
||||
if compareError.Error() != err.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var songDataList []SongData
|
||||
for rows.Next() {
|
||||
var songName string
|
||||
var path string
|
||||
|
||||
err := rows.Scan(&songName, &path)
|
||||
if err != nil {
|
||||
if compareError.Error() != err.Error() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
songDataList = append(songDataList, SongData{
|
||||
SongName: songName,
|
||||
Path: path,
|
||||
})
|
||||
}
|
||||
return songDataList
|
||||
}
|
||||
|
||||
func RemoveBrokenSongs(songs []SongData) {
|
||||
joined := ""
|
||||
for _, song := range songs {
|
||||
joined += "'" + song.Path + "',"
|
||||
}
|
||||
joined = strings.TrimSuffix(joined, ",")
|
||||
|
||||
_, err := db.Dbpool.Exec(db.Ctx, "DELETE FROM song where path in ($1)", joined)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user