Added a new sync that uses hash

Added a new sync that uses hash and sqlc for the queries. Added db
migration. Started adding a config file.
This commit is contained in:
2024-12-19 12:11:20 +01:00
parent 8fa93d580d
commit 5ab19e16e5
26 changed files with 1527 additions and 45 deletions

View File

@@ -8,7 +8,6 @@ import (
//go:embed swagger
var swagger embed.FS
//go:embed search
var search embed.FS

View File

@@ -0,0 +1,4 @@
DROP TABLE game;
DROP TABLE song;
DROP TABLE song_list;
DROP TABLE vgmq;

View File

@@ -0,0 +1,43 @@
CREATE TABLE game (
id serial4 NOT NULL,
game_name varchar NOT NULL,
added timestamp NOT NULL,
deleted timestamp NULL,
last_changed timestamp NULL,
"path" varchar NOT NULL,
times_played int4 DEFAULT 0 NULL,
last_played timestamp NULL,
number_of_songs int4 NULL,
hash varchar NULL,
CONSTRAINT game_pkey PRIMARY KEY (id)
);
CREATE TABLE song (
game_id int4 NOT NULL,
song_name varchar NOT NULL,
"path" varchar NOT NULL,
times_played int4 DEFAULT 0 NULL,
hash varchar NULL,
file_name varchar NULL,
CONSTRAINT song_pkey PRIMARY KEY (game_id, path)
);
CREATE TABLE vgmq (
song_no int4 NOT NULL,
"path" varchar(50) NULL,
clue varchar(200) NULL,
answered bool DEFAULT false NOT NULL,
answer varchar(50) NULL,
CONSTRAINT vgmq_pk PRIMARY KEY (song_no)
);
CREATE UNIQUE INDEX vgmq_song_no_uindex ON vgmq USING btree (song_no);
CREATE TABLE song_list (
match_date date NOT NULL,
match_id int4 NOT NULL,
song_no int4 NOT NULL,
game_name varchar(50) NULL,
song_name varchar(50) NULL,
CONSTRAINT song_list_pkey PRIMARY KEY (match_date, match_id, song_no)
);
CREATE INDEX song_list_game_name_idx ON song_list USING btree (game_name);

View File

@@ -0,0 +1,3 @@
Alter table game
alter column number_of_songs set null,
alter column hash set null;

View File

@@ -0,0 +1,19 @@
BEGIN;
UPDATE game
SET number_of_songs = 0
WHERE number_of_songs IS NULL;
UPDATE game
SET hash = ''
WHERE hash IS NULL;
UPDATE song
SET hash = ''
WHERE hash IS NULL;
COMMIT;
BEGIN;
Alter table game
alter column number_of_songs set not null,
alter column number_of_songs set default 0,
ALTER COLUMN hash SET NOT NULL;
ALTER TABLE song
ALTER COLUMN hash SET NOT NULL;
COMMIT;

49
db/queries/game.sql Normal file
View File

@@ -0,0 +1,49 @@
-- name: ResetGameIdSeq :one
SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);
-- name: GetGameNameById :one
SELECT game_name FROM game WHERE id = $1;
-- name: GetGameById :one
SELECT *
FROM game
WHERE id = $1
AND deleted IS NULL;
-- name: SetGameDeletionDate :exec
UPDATE game SET deleted=now() WHERE deleted IS NULL;
-- name: ClearGames :exec
DELETE FROM game;
-- name: UpdateGameName :exec
UPDATE game SET game_name=sqlc.arg(name), path=sqlc.arg(path), last_changed=now() WHERE id=sqlc.arg(id);
-- name: UpdateGameHash :exec
UPDATE game SET hash=sqlc.arg(hash), last_changed=now() WHERE id=sqlc.arg(id);
-- name: RemoveDeletionDate :exec
UPDATE game SET deleted=NULL WHERE id=$1;
-- name: GetIdByGameName :one
SELECT id FROM game WHERE game_name = $1;
-- name: InsertGame :one
INSERT INTO game (game_name, path, hash, added) VALUES ($1, $2, $3, now()) returning id;
-- name: InsertGameWithExistingId :exec
INSERT INTO game (id, game_name, path, hash, added) VALUES ($1, $2, $3, $4, now());
-- name: FindAllGames :many
SELECT *
FROM game
WHERE deleted IS NULL
ORDER BY game_name;
-- name: GetAllGamesIncludingDeleted :many
SELECT *
FROM game
ORDER BY game_name;
-- name: AddGamePlayed :exec
UPDATE game SET times_played = times_played + 1, last_played = now() WHERE id = $1;

41
db/queries/song.sql Normal file
View File

@@ -0,0 +1,41 @@
-- name: ClearSongs :exec
DELETE FROM song;
-- name: ClearSongsByGameId :exec
DELETE FROM song WHERE game_id = $1;
-- name: AddSong :exec
INSERT INTO song(game_id, song_name, path, file_name, hash) VALUES ($1, $2, $3, $4, $5);
-- name: CheckSong :one
SELECT COUNT(*) FROM song WHERE path = $1;
-- name: CheckSongWithHash :one
SELECT COUNT(*) FROM song WHERE hash = $1;
-- name: GetSongWithHash :one
SELECT * FROM song WHERE hash = $1;
-- name: UpdateSong :exec
UPDATE song SET song_name=$1, file_name=$2, path=$3 where hash=$4;
-- name: AddHashToSong :exec
UPDATE song SET hash=$1 where path=$2;
-- name: FindSongsFromGame :many
SELECT *
FROM song
WHERE game_id = $1;
-- name: AddSongPlayed :exec
UPDATE song SET times_played = times_played + 1
WHERE game_id = $1 AND song_name = $2;
-- name: FetchAllSongs :many
SELECT * FROM song;
-- name: RemoveBrokenSong :exec
DELETE FROM song WHERE path = $1;
-- name: RemoveBrokenSongs :exec
DELETE FROM song where path = any (sqlc.slice('paths'));

9
db/queries/song_list.sql Normal file
View File

@@ -0,0 +1,9 @@
-- name: InsertSongInList :exec
INSERT INTO song_list (match_date, match_id, song_no, game_name, song_name)
VALUES ($1, $2, $3, $4, $5);
-- name: GetSongList :many
SELECT *
FROM song_list
WHERE match_date = $1
ORDER BY song_no DESC;

0
db/queries/vgmq.sql Normal file
View File

32
db/repository/db.go Normal file
View File

@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package repository
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

246
db/repository/game.sql.go Normal file
View File

@@ -0,0 +1,246 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: game.sql
package repository
import (
"context"
)
const addGamePlayed = `-- name: AddGamePlayed :exec
UPDATE game SET times_played = times_played + 1, last_played = now() WHERE id = $1
`
func (q *Queries) AddGamePlayed(ctx context.Context, id int32) error {
_, err := q.db.Exec(ctx, addGamePlayed, id)
return err
}
const clearGames = `-- name: ClearGames :exec
DELETE FROM game
`
func (q *Queries) ClearGames(ctx context.Context) error {
_, err := q.db.Exec(ctx, clearGames)
return err
}
const findAllGames = `-- name: FindAllGames :many
SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs, hash
FROM game
WHERE deleted IS NULL
ORDER BY game_name
`
func (q *Queries) FindAllGames(ctx context.Context) ([]Game, error) {
rows, err := q.db.Query(ctx, findAllGames)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Game
for rows.Next() {
var i Game
if err := rows.Scan(
&i.ID,
&i.GameName,
&i.Added,
&i.Deleted,
&i.LastChanged,
&i.Path,
&i.TimesPlayed,
&i.LastPlayed,
&i.NumberOfSongs,
&i.Hash,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllGamesIncludingDeleted = `-- name: GetAllGamesIncludingDeleted :many
SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs, hash
FROM game
ORDER BY game_name
`
func (q *Queries) GetAllGamesIncludingDeleted(ctx context.Context) ([]Game, error) {
rows, err := q.db.Query(ctx, getAllGamesIncludingDeleted)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Game
for rows.Next() {
var i Game
if err := rows.Scan(
&i.ID,
&i.GameName,
&i.Added,
&i.Deleted,
&i.LastChanged,
&i.Path,
&i.TimesPlayed,
&i.LastPlayed,
&i.NumberOfSongs,
&i.Hash,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getGameById = `-- name: GetGameById :one
SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs, hash
FROM game
WHERE id = $1
AND deleted IS NULL
`
func (q *Queries) GetGameById(ctx context.Context, id int32) (Game, error) {
row := q.db.QueryRow(ctx, getGameById, id)
var i Game
err := row.Scan(
&i.ID,
&i.GameName,
&i.Added,
&i.Deleted,
&i.LastChanged,
&i.Path,
&i.TimesPlayed,
&i.LastPlayed,
&i.NumberOfSongs,
&i.Hash,
)
return i, err
}
const getGameNameById = `-- name: GetGameNameById :one
SELECT game_name FROM game WHERE id = $1
`
func (q *Queries) GetGameNameById(ctx context.Context, id int32) (string, error) {
row := q.db.QueryRow(ctx, getGameNameById, id)
var game_name string
err := row.Scan(&game_name)
return game_name, err
}
const getIdByGameName = `-- name: GetIdByGameName :one
SELECT id FROM game WHERE game_name = $1
`
func (q *Queries) GetIdByGameName(ctx context.Context, gameName string) (int32, error) {
row := q.db.QueryRow(ctx, getIdByGameName, gameName)
var id int32
err := row.Scan(&id)
return id, err
}
const insertGame = `-- name: InsertGame :one
INSERT INTO game (game_name, path, hash, added) VALUES ($1, $2, $3, now()) returning id
`
type InsertGameParams struct {
GameName string `json:"game_name"`
Path string `json:"path"`
Hash string `json:"hash"`
}
func (q *Queries) InsertGame(ctx context.Context, arg InsertGameParams) (int32, error) {
row := q.db.QueryRow(ctx, insertGame, arg.GameName, arg.Path, arg.Hash)
var id int32
err := row.Scan(&id)
return id, err
}
const insertGameWithExistingId = `-- name: InsertGameWithExistingId :exec
INSERT INTO game (id, game_name, path, hash, added) VALUES ($1, $2, $3, $4, now())
`
type InsertGameWithExistingIdParams struct {
ID int32 `json:"id"`
GameName string `json:"game_name"`
Path string `json:"path"`
Hash string `json:"hash"`
}
func (q *Queries) InsertGameWithExistingId(ctx context.Context, arg InsertGameWithExistingIdParams) error {
_, err := q.db.Exec(ctx, insertGameWithExistingId,
arg.ID,
arg.GameName,
arg.Path,
arg.Hash,
)
return err
}
const removeDeletionDate = `-- name: RemoveDeletionDate :exec
UPDATE game SET deleted=NULL WHERE id=$1
`
func (q *Queries) RemoveDeletionDate(ctx context.Context, id int32) error {
_, err := q.db.Exec(ctx, removeDeletionDate, id)
return err
}
const resetGameIdSeq = `-- name: ResetGameIdSeq :one
SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1)
`
func (q *Queries) ResetGameIdSeq(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, resetGameIdSeq)
var setval int64
err := row.Scan(&setval)
return setval, err
}
const setGameDeletionDate = `-- name: SetGameDeletionDate :exec
UPDATE game SET deleted=now() WHERE deleted IS NULL
`
func (q *Queries) SetGameDeletionDate(ctx context.Context) error {
_, err := q.db.Exec(ctx, setGameDeletionDate)
return err
}
const updateGameHash = `-- name: UpdateGameHash :exec
UPDATE game SET hash=$1, last_changed=now() WHERE id=$2
`
type UpdateGameHashParams struct {
Hash string `json:"hash"`
ID int32 `json:"id"`
}
func (q *Queries) UpdateGameHash(ctx context.Context, arg UpdateGameHashParams) error {
_, err := q.db.Exec(ctx, updateGameHash, arg.Hash, arg.ID)
return err
}
const updateGameName = `-- name: UpdateGameName :exec
UPDATE game SET game_name=$1, path=$2, last_changed=now() WHERE id=$3
`
type UpdateGameNameParams struct {
Name string `json:"name"`
Path string `json:"path"`
ID int32 `json:"id"`
}
func (q *Queries) UpdateGameName(ctx context.Context, arg UpdateGameNameParams) error {
_, err := q.db.Exec(ctx, updateGameName, arg.Name, arg.Path, arg.ID)
return err
}

47
db/repository/models.go Normal file
View File

@@ -0,0 +1,47 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
package repository
import (
"time"
)
type Game struct {
ID int32 `json:"id"`
GameName string `json:"game_name"`
Added time.Time `json:"added"`
Deleted *time.Time `json:"deleted"`
LastChanged *time.Time `json:"last_changed"`
Path string `json:"path"`
TimesPlayed *int32 `json:"times_played"`
LastPlayed *time.Time `json:"last_played"`
NumberOfSongs int32 `json:"number_of_songs"`
Hash string `json:"hash"`
}
type Song struct {
GameID int32 `json:"game_id"`
SongName string `json:"song_name"`
Path string `json:"path"`
TimesPlayed *int32 `json:"times_played"`
Hash string `json:"hash"`
FileName *string `json:"file_name"`
}
type SongList struct {
MatchDate time.Time `json:"match_date"`
MatchID int32 `json:"match_id"`
SongNo int32 `json:"song_no"`
GameName *string `json:"game_name"`
SongName *string `json:"song_name"`
}
type Vgmq struct {
SongNo int32 `json:"song_no"`
Path *string `json:"path"`
Clue *string `json:"clue"`
Answered bool `json:"answered"`
Answer *string `json:"answer"`
}

223
db/repository/song.sql.go Normal file
View File

@@ -0,0 +1,223 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: song.sql
package repository
import (
"context"
)
const addHashToSong = `-- name: AddHashToSong :exec
UPDATE song SET hash=$1 where path=$2
`
type AddHashToSongParams struct {
Hash string `json:"hash"`
Path string `json:"path"`
}
func (q *Queries) AddHashToSong(ctx context.Context, arg AddHashToSongParams) error {
_, err := q.db.Exec(ctx, addHashToSong, arg.Hash, arg.Path)
return err
}
const addSong = `-- name: AddSong :exec
INSERT INTO song(game_id, song_name, path, file_name, hash) VALUES ($1, $2, $3, $4, $5)
`
type AddSongParams struct {
GameID int32 `json:"game_id"`
SongName string `json:"song_name"`
Path string `json:"path"`
FileName *string `json:"file_name"`
Hash string `json:"hash"`
}
func (q *Queries) AddSong(ctx context.Context, arg AddSongParams) error {
_, err := q.db.Exec(ctx, addSong,
arg.GameID,
arg.SongName,
arg.Path,
arg.FileName,
arg.Hash,
)
return err
}
const addSongPlayed = `-- name: AddSongPlayed :exec
UPDATE song SET times_played = times_played + 1
WHERE game_id = $1 AND song_name = $2
`
type AddSongPlayedParams struct {
GameID int32 `json:"game_id"`
SongName string `json:"song_name"`
}
func (q *Queries) AddSongPlayed(ctx context.Context, arg AddSongPlayedParams) error {
_, err := q.db.Exec(ctx, addSongPlayed, arg.GameID, arg.SongName)
return err
}
const checkSong = `-- name: CheckSong :one
SELECT COUNT(*) FROM song WHERE path = $1
`
func (q *Queries) CheckSong(ctx context.Context, path string) (int64, error) {
row := q.db.QueryRow(ctx, checkSong, path)
var count int64
err := row.Scan(&count)
return count, err
}
const checkSongWithHash = `-- name: CheckSongWithHash :one
SELECT COUNT(*) FROM song WHERE hash = $1
`
func (q *Queries) CheckSongWithHash(ctx context.Context, hash string) (int64, error) {
row := q.db.QueryRow(ctx, checkSongWithHash, hash)
var count int64
err := row.Scan(&count)
return count, err
}
const clearSongs = `-- name: ClearSongs :exec
DELETE FROM song
`
func (q *Queries) ClearSongs(ctx context.Context) error {
_, err := q.db.Exec(ctx, clearSongs)
return err
}
const clearSongsByGameId = `-- name: ClearSongsByGameId :exec
DELETE FROM song WHERE game_id = $1
`
func (q *Queries) ClearSongsByGameId(ctx context.Context, gameID int32) error {
_, err := q.db.Exec(ctx, clearSongsByGameId, gameID)
return err
}
const fetchAllSongs = `-- name: FetchAllSongs :many
SELECT game_id, song_name, path, times_played, hash, file_name FROM song
`
func (q *Queries) FetchAllSongs(ctx context.Context) ([]Song, error) {
rows, err := q.db.Query(ctx, fetchAllSongs)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Song
for rows.Next() {
var i Song
if err := rows.Scan(
&i.GameID,
&i.SongName,
&i.Path,
&i.TimesPlayed,
&i.Hash,
&i.FileName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const findSongsFromGame = `-- name: FindSongsFromGame :many
SELECT game_id, song_name, path, times_played, hash, file_name
FROM song
WHERE game_id = $1
`
func (q *Queries) FindSongsFromGame(ctx context.Context, gameID int32) ([]Song, error) {
rows, err := q.db.Query(ctx, findSongsFromGame, gameID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Song
for rows.Next() {
var i Song
if err := rows.Scan(
&i.GameID,
&i.SongName,
&i.Path,
&i.TimesPlayed,
&i.Hash,
&i.FileName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getSongWithHash = `-- name: GetSongWithHash :one
SELECT game_id, song_name, path, times_played, hash, file_name FROM song WHERE hash = $1
`
func (q *Queries) GetSongWithHash(ctx context.Context, hash string) (Song, error) {
row := q.db.QueryRow(ctx, getSongWithHash, hash)
var i Song
err := row.Scan(
&i.GameID,
&i.SongName,
&i.Path,
&i.TimesPlayed,
&i.Hash,
&i.FileName,
)
return i, err
}
const removeBrokenSong = `-- name: RemoveBrokenSong :exec
DELETE FROM song WHERE path = $1
`
func (q *Queries) RemoveBrokenSong(ctx context.Context, path string) error {
_, err := q.db.Exec(ctx, removeBrokenSong, path)
return err
}
const removeBrokenSongs = `-- name: RemoveBrokenSongs :exec
DELETE FROM song where path = any ($1)
`
func (q *Queries) RemoveBrokenSongs(ctx context.Context, paths []string) error {
_, err := q.db.Exec(ctx, removeBrokenSongs, paths)
return err
}
const updateSong = `-- name: UpdateSong :exec
UPDATE song SET song_name=$1, file_name=$2, path=$3 where hash=$4
`
type UpdateSongParams struct {
SongName string `json:"song_name"`
FileName *string `json:"file_name"`
Path string `json:"path"`
Hash string `json:"hash"`
}
func (q *Queries) UpdateSong(ctx context.Context, arg UpdateSongParams) error {
_, err := q.db.Exec(ctx, updateSong,
arg.SongName,
arg.FileName,
arg.Path,
arg.Hash,
)
return err
}

View File

@@ -0,0 +1,68 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: song_list.sql
package repository
import (
"context"
"time"
)
const getSongList = `-- name: GetSongList :many
SELECT match_date, match_id, song_no, game_name, song_name
FROM song_list
WHERE match_date = $1
ORDER BY song_no DESC
`
func (q *Queries) GetSongList(ctx context.Context, matchDate time.Time) ([]SongList, error) {
rows, err := q.db.Query(ctx, getSongList, matchDate)
if err != nil {
return nil, err
}
defer rows.Close()
var items []SongList
for rows.Next() {
var i SongList
if err := rows.Scan(
&i.MatchDate,
&i.MatchID,
&i.SongNo,
&i.GameName,
&i.SongName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertSongInList = `-- name: InsertSongInList :exec
INSERT INTO song_list (match_date, match_id, song_no, game_name, song_name)
VALUES ($1, $2, $3, $4, $5)
`
type InsertSongInListParams struct {
MatchDate time.Time `json:"match_date"`
MatchID int32 `json:"match_id"`
SongNo int32 `json:"song_no"`
GameName *string `json:"game_name"`
SongName *string `json:"song_name"`
}
func (q *Queries) InsertSongInList(ctx context.Context, arg InsertSongInListParams) error {
_, err := q.db.Exec(ctx, insertSongInList,
arg.MatchDate,
arg.MatchID,
arg.SongNo,
arg.GameName,
arg.SongName,
)
return err
}

20
go.mod
View File

@@ -3,10 +3,14 @@ module music-server
go 1.22.2
require (
github.com/MShekow/directory-checksum v1.4.6
github.com/gin-contrib/static v1.1.2
github.com/gin-gonic/gin v1.10.0
github.com/golang-migrate/migrate v3.5.4+incompatible
github.com/jackc/pgtype v1.14.3
github.com/jackc/pgx/v5 v5.5.5
github.com/spf13/afero v1.11.0
gopkg.in/yaml.v3 v3.0.1
)
require (
@@ -14,12 +18,19 @@ require (
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.3.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
@@ -27,18 +38,23 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lib/pq v1.10.2 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

62
go.sum
View File

@@ -1,5 +1,9 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/MShekow/directory-checksum v1.4.6 h1:2fhlCYbpjEN1iH9S0tdmEM0p1wvNT9x5x0rIchGI7nE=
github.com/MShekow/directory-checksum v1.4.6/go.mod h1:bMfFBkaIlNk7O9VgEi8D2X7Q2Jfk3c7d67z3t6cpIi4=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -15,6 +19,16 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -23,8 +37,14 @@ github.com/gin-contrib/static v1.1.2 h1:c3kT4bFkUJn2aoRU3s6XnMjJT8J6nNWJkR0Nglqm
github.com/gin-contrib/static v1.1.2/go.mod h1:Fw90ozjHCmZBWbgrsqrDvO28YbhKEKzKp8GixhR4yLw=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -37,8 +57,12 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
@@ -98,6 +122,7 @@ github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
@@ -126,13 +151,20 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -147,6 +179,8 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -168,8 +202,18 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -201,12 +245,16 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@@ -215,9 +263,12 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -228,6 +279,7 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -264,13 +316,15 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=

View File

@@ -1,9 +1,10 @@
package api
import (
"github.com/gin-gonic/gin"
"music-server/pkg/server"
"net/http"
"github.com/gin-gonic/gin"
)
type Sync struct {
@@ -25,6 +26,12 @@ func (s *Sync) SyncGamesQuick(ctx *gin.Context) {
ctx.JSON(http.StatusOK, "Games are synced")
}
func (s *Sync) SyncGamesNew(ctx *gin.Context) {
response := server.SyncGamesNew()
server.Reset()
ctx.JSON(http.StatusOK, response)
}
func (s *Sync) ResetGames(ctx *gin.Context) {
server.ResetDB()
ctx.JSON(http.StatusOK, "Games and songs are deleted from the database")

View File

@@ -7,6 +7,7 @@ import (
"music-server/pkg/api"
"music-server/pkg/db"
"music-server/pkg/helpers"
"music-server/pkg/server"
"os"
"strconv"
@@ -15,6 +16,12 @@ import (
)
func SetupDb() {
/*err := server.ReadConf("conf.yaml")
if err != nil {
log.Fatal(err)
}*/
// Get the value of an Environment Variable
host := os.Getenv("DB_HOST")
dbPort, dbPortErr := strconv.Atoi(os.Getenv("DB_PORT"))
@@ -35,16 +42,35 @@ func SetupDb() {
dbPort = 5432
}
if username == "" {
username = "sebastian"
username = "postgres"
}
if password == "" {
password = "950100"
password = "postgres"
}
if dbName == "" {
dbName = "music_dev_local"
dbName = "music_test_local"
}
db.Migrate_db(host, dbPort, username, password, dbName)
db.InitDB(host, dbPort, username, password, dbName)
var dir string
if host != "localhost" {
dir = "/sorted/"
} else {
dir = "/Users/sebastian/ResilioSync/Sorterat_test/"
}
server.Conf = &server.Config{
Host: host,
Port: dbPort,
User: username,
Password: password,
Dbname: dbName,
Path: dir,
}
}
func CloseDb() {
@@ -60,6 +86,7 @@ func SetupRestServer(swagger embed.FS, search embed.FS) {
syncGroup := router.Group("/sync")
{
syncGroup.GET("", sync.SyncGames)
syncGroup.GET("/new", sync.SyncGamesNew)
syncGroup.GET("/quick", sync.SyncGamesQuick)
syncGroup.GET("/reset", sync.ResetGames)
}

View File

@@ -2,14 +2,21 @@ package db
import (
"context"
"database/sql"
"fmt"
"log"
"os"
"github.com/golang-migrate/migrate"
"github.com/golang-migrate/migrate/database/postgres"
_ "github.com/golang-migrate/migrate/database/postgres"
_ "github.com/golang-migrate/migrate/source/file"
"github.com/jackc/pgx/v5/pgxpool"
_ "github.com/lib/pq"
)
var dbpool *pgxpool.Pool
var ctx = context.Background()
var Dbpool *pgxpool.Pool
var Ctx = context.Background()
func InitDB(host string, port int, user string, password string, dbname string) {
@@ -20,14 +27,14 @@ func InitDB(host string, port int, user string, password string, dbname string)
fmt.Println(psqlInfo)
var err error
dbpool, err = pgxpool.New(ctx, psqlInfo)
Dbpool, err = pgxpool.New(Ctx, psqlInfo)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
os.Exit(1)
}
var success string
err = dbpool.QueryRow(ctx, "select 'Successfully connected!'").Scan(&success)
err = Dbpool.QueryRow(Ctx, "select 'Successfully connected!'").Scan(&success)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
os.Exit(1)
@@ -37,11 +44,11 @@ func InitDB(host string, port int, user string, password string, dbname string)
}
func CloseDb() {
dbpool.Close()
Dbpool.Close()
}
func Testf() {
rows, dbErr := dbpool.Query(ctx, "select game_name from game")
rows, dbErr := Dbpool.Query(Ctx, "select game_name from game")
if dbErr != nil {
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
os.Exit(1)
@@ -57,8 +64,60 @@ func Testf() {
}
func resetGameIdSeq() {
_, err := dbpool.Query(ctx, "SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);")
_, err := Dbpool.Query(Ctx, "SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);")
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
}
}
func Migrate_db(host string, port int, user string, password string, dbname string) {
migrationInfo := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable",
user, password, host, port, dbname)
fmt.Println("Migration Info: ", migrationInfo)
db, err := sql.Open("postgres", migrationInfo)
if err != nil {
log.Println(err)
}
driver, err := postgres.WithInstance(db, &postgres.Config{})
if err != nil {
log.Println(err)
}
m, err := migrate.NewWithDatabaseInstance(
"file://./db/migrations/",
"postgres", driver)
if err != nil {
log.Println(err)
}
version, _, err := m.Version()
if err != nil {
log.Println("Migration version err: ", err)
}
fmt.Println("Migration version before: ", version)
err = m.Force(1)
//err = m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to run
if err != nil {
log.Println("Force err: ", err)
}
err = m.Migrate(2)
//err = m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to run
if err != nil {
log.Println("Migration err: ", err)
}
versionAfter, _, err := m.Version()
if err != nil {
log.Println("Migration version err: ", err)
}
fmt.Println("Migration version after: ", versionAfter)
fmt.Println("Migration done")
db.Close()
}

View File

@@ -12,7 +12,7 @@ import (
func GetGameName(gameId int) string {
var gameName = ""
err := dbpool.QueryRow(ctx,
err := Dbpool.QueryRow(Ctx,
"SELECT game_name FROM game WHERE id = $1", gameId).Scan(&gameName)
if err != nil {
if compareError.Error() != err.Error() {
@@ -28,7 +28,7 @@ func GetGameById(gameId int) (models.GameData, error) {
var numberOfSongs pgtype.Int4
var gameName, path string
var added, deleted, lastChanged, lastPlayed pgtype.Timestamp
err := dbpool.QueryRow(ctx,
err := Dbpool.QueryRow(Ctx,
"SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs "+
"FROM game WHERE id = $1 AND deleted IS NULL ", gameId).Scan(&id, &gameName, &added, &deleted, &lastChanged, &path, &timesPlayed, &lastPlayed, &numberOfSongs)
if err != nil {
@@ -51,7 +51,7 @@ func GetGameById(gameId int) (models.GameData, error) {
}
func SetGameDeletionDate() {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"UPDATE game SET deleted=$1 WHERE deleted IS NULL", time.Now())
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
@@ -59,14 +59,14 @@ func SetGameDeletionDate() {
}
func ClearGames() {
_, err := dbpool.Exec(ctx, "DELETE FROM game")
_, err := Dbpool.Exec(Ctx, "DELETE FROM game")
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
}
}
func UpdateGameName(id int, name string, path string) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"UPDATE game SET game_name=$1, path=$2, last_changed=$3 WHERE id=$4",
name, path, time.Now(), id)
if err != nil {
@@ -75,7 +75,7 @@ func UpdateGameName(id int, name string, path string) {
}
func RemoveDeletionDate(id int) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"UPDATE game SET deleted=null WHERE id=$1", id)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
@@ -84,7 +84,7 @@ func RemoveDeletionDate(id int) {
func GetIdByGameName(name string) int {
var gameId = -1
err := dbpool.QueryRow(ctx,
err := Dbpool.QueryRow(Ctx,
"SELECT id FROM game WHERE game_name = $1", name).Scan(&gameId)
if err != nil {
if compareError.Error() != err.Error() {
@@ -97,7 +97,7 @@ func GetIdByGameName(name string) int {
func InsertGame(name string, path string) int {
gameId := -1
err := dbpool.QueryRow(ctx,
err := Dbpool.QueryRow(Ctx,
"INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
name, path, time.Now()).Scan(&gameId)
if err != nil {
@@ -105,7 +105,7 @@ func InsertGame(name string, path string) int {
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
}
resetGameIdSeq()
err2 := dbpool.QueryRow(ctx,
err2 := Dbpool.QueryRow(Ctx,
"INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
name, path, time.Now()).Scan(&gameId)
if err2 != nil {
@@ -119,7 +119,7 @@ func InsertGame(name string, path string) int {
}
func InsertGameWithExistingId(id int, name string, path string) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"INSERT INTO game(id, game_name, path, added) VALUES ($1, $2, $3, $4)",
id, name, path, time.Now())
if err != nil {
@@ -128,7 +128,7 @@ func InsertGameWithExistingId(id int, name string, path string) {
}
func FindAllGames() []models.GameData {
rows, err := dbpool.Query(ctx,
rows, err := Dbpool.Query(Ctx,
"SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs "+
"FROM game WHERE deleted IS NULL "+
"ORDER BY game_name")
@@ -165,7 +165,7 @@ func FindAllGames() []models.GameData {
}
func AddGamePlayed(id int) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"UPDATE game SET times_played=times_played+1, last_played=now() WHERE id=$1", id)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)

View File

@@ -12,12 +12,12 @@ var compareError = errors.New("no rows in result set")
func ClearSongs(gameId int) {
if gameId == -1 {
_, err := dbpool.Exec(ctx, "DELETE FROM song")
_, err := Dbpool.Exec(Ctx, "DELETE FROM song")
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
}
} else {
_, err := dbpool.Exec(ctx, "DELETE FROM song where game_id=$1", gameId)
_, err := Dbpool.Exec(Ctx, "DELETE FROM song where game_id=$1", gameId)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
}
@@ -25,7 +25,7 @@ func ClearSongs(gameId int) {
}
func AddSong(song models.SongData) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(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 {
@@ -35,7 +35,7 @@ func AddSong(song models.SongData) {
func CheckSong(songPath string) bool {
var path string
err := dbpool.QueryRow(ctx,
err := Dbpool.QueryRow(Ctx,
"SELECT path FROM song WHERE path = $1", songPath).Scan(&path)
if err != nil {
if compareError.Error() != err.Error() {
@@ -46,7 +46,7 @@ func CheckSong(songPath string) bool {
}
func UpdateSong(songName string, fileName string, path string) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"UPDATE song SET song_name=$1, file_name=$2 WHERE path = $3",
songName, fileName, path)
if err != nil {
@@ -55,7 +55,7 @@ func UpdateSong(songName string, fileName string, path string) {
}
func FindSongsFromGame(id int) []models.SongData {
rows, err := dbpool.Query(ctx,
rows, err := Dbpool.Query(Ctx,
"SELECT song_name, path, file_name, times_played FROM song WHERE game_id = $1", id)
if err != nil {
if compareError.Error() != err.Error() {
@@ -90,7 +90,7 @@ func FindSongsFromGame(id int) []models.SongData {
}
func AddSongPlayed(id int, name string) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
"UPDATE song SET times_played=times_played+1 WHERE game_id=$1 AND song_name=$2", id, name)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
@@ -98,7 +98,7 @@ func AddSongPlayed(id int, name string) {
}
func FetchAllSongs() []models.SongData {
rows, err := dbpool.Query(ctx,
rows, err := Dbpool.Query(Ctx,
"SELECT song_name, path FROM song")
if err != nil {
if compareError.Error() != err.Error() {
@@ -128,7 +128,7 @@ func FetchAllSongs() []models.SongData {
}
func RemoveBrokenSong(song models.SongData) {
_, err := dbpool.Exec(ctx, "DELETE FROM song where path=$1", song.Path)
_, err := Dbpool.Exec(Ctx, "DELETE FROM song where path=$1", song.Path)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
}
@@ -141,7 +141,7 @@ func RemoveBrokenSongs(songs []models.SongData) {
}
joined = strings.TrimSuffix(joined, ",")
_, err := dbpool.Exec(ctx, "DELETE FROM song where path in ($1)", joined)
_, err := Dbpool.Exec(Ctx, "DELETE FROM song where path in ($1)", joined)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
}

View File

@@ -8,7 +8,7 @@ import (
)
func InsertSongInList(song models.SongListData) {
_, err := dbpool.Exec(ctx,
_, err := Dbpool.Exec(Ctx,
`INSERT INTO song_list (match_date, match_id, song_no, game_name, song_name) VALUES ($1, $2, $3, $4, $5)`,
song.MatchDate, song.MatchId, song.SongNo, song.GameName, song.SongName)
if err != nil {
@@ -17,7 +17,7 @@ func InsertSongInList(song models.SongListData) {
}
func GetSongList(matchId int) []models.SongListData {
rows, err := dbpool.Query(ctx,
rows, err := Dbpool.Query(Ctx,
"SELECT match_date, match_id, song_no, game_name, song_name "+
"FROM song_list WHERE match_date = $1"+
"ORDER BY song_no DESC", matchId)

34
pkg/server/config.go Normal file
View File

@@ -0,0 +1,34 @@
package server
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
type Config struct {
Host string
Port int
User string
Password string
Dbname string
Path string
}
var Conf *Config
func ReadConf(filename string) error {
buf, err := os.ReadFile(filename)
if err != nil {
return err
}
c := &Config{}
err = yaml.Unmarshal(buf, c)
if err != nil {
return fmt.Errorf("in file %q: %w", filename, err)
}
Conf = c
return err
}

View File

@@ -48,6 +48,9 @@ func GetRandomSong() string {
if games == nil || len(games) == 0 {
games = db.FindAllGames()
}
if games == nil || len(games) == 0 {
return ""
}
song := getSongFromList(games)
lastFetched = song

View File

@@ -1,9 +1,14 @@
package server
import (
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"io"
"io/fs"
"log"
"music-server/db/repository"
"music-server/pkg/db"
"music-server/pkg/models"
"os"
@@ -11,17 +16,65 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/MShekow/directory-checksum/directory_checksum"
"github.com/spf13/afero"
)
var allGames []repository.Game
var gamesBeforeSync []repository.Game
var gamesAfterSync []repository.Game
var gamesAdded []string
var gamesReAdded []string
var gamesChangedTitle map[string]string
var gamesChangedContent []string
var gamesRemoved []string
var catchedErrors []string
var brokenSongs []string
type Response struct {
GamesAdded []string `json:"games_added"`
GamesReAdded []string `json:"games_re_added"`
GamesChangedTitle map[string]string `json:"games_changed_title"`
GamesChangedContent []string `json:"games_changed_content"`
GamesRemoved []string `json:"games_removed"`
CatchedErrors []string `json:"catched_errors"`
}
type GameStatus int
const (
NotChanged GameStatus = iota
TitleChanged
GameChanged
NewGame
)
var statusName = map[GameStatus]string{
NotChanged: "Not changed",
TitleChanged: "Title changed",
GameChanged: "Game changed",
NewGame: "New game",
}
func (gs GameStatus) String() string {
return statusName[gs]
}
var syncWg sync.WaitGroup
var repo *repository.Queries
var wg sync.WaitGroup
func SyncGames() {
start := time.Now()
host := os.Getenv("DB_HOST")
var dir string
if host != "" {
dir = "/sorted/"
} else {
dir = "/Users/sebastian/Resilio Sync/Sorterat_test/"
dir = "/Users/sebastian/ResilioSync/Sorterat_test/"
}
fmt.Printf("dir: %s\n", dir)
foldersToSkip := []string{".sync", "dist", "old"}
@@ -37,15 +90,21 @@ func SyncGames() {
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()
host := os.Getenv("DB_HOST")
var dir string
if host != "" {
dir = "/sorted/"
} else {
dir = "/Users/sebastian/Resilio Sync/Sorterat_test/"
dir = "/Users/sebastian/ResilioSync/Sorterat_test/"
}
fmt.Printf("dir: %s\n", dir)
foldersToSkip := []string{".sync", "dist", "old"}
@@ -66,6 +125,11 @@ func SyncGamesQuick() {
}()
}
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) {
@@ -133,7 +197,10 @@ func checkIfChanged(id int, name string, path string) {
func addNewGame(name string, path string) {
newId := db.GetIdByGameName(name)
if newId == -1 {
if newId != -1 {
checkBrokenSongs()
db.RemoveDeletionDate(newId)
} else {
newId = db.InsertGame(name, path)
}
@@ -194,6 +261,401 @@ func checkBrokenSongs() {
db.RemoveBrokenSongs(brokenSongs)
}
func SyncGamesNew() Response {
repo = repository.New(db.Dbpool)
start := time.Now()
fmt.Printf("dir: %s\n", Conf.Path)
foldersToSkip := []string{".sync", "dist", "old"}
fmt.Println(foldersToSkip)
var err error
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(Conf.Path)
if err != nil {
log.Fatal(err)
}
syncWg.Add(len(directories))
for _, dir := range directories {
go func() {
defer syncWg.Done()
syncGameNew(dir, foldersToSkip, Conf.Path)
}()
}
syncWg.Wait()
checkBrokenSongsNew()
gamesAfterSync, err = repo.FindAllGames(db.Ctx)
handleError("FindAllGames After", err, "")
fmt.Printf("\nGames Before: %d\n", len(gamesBeforeSync))
fmt.Printf("Games After: %d\n", len(gamesAfterSync))
fmt.Printf("\nGames added: \n")
for _, game := range gamesAdded {
fmt.Printf("%s\n", game)
}
fmt.Printf("\nGames readded: \n")
for _, game := range gamesReAdded {
fmt.Printf("%s\n", game)
}
fmt.Printf("\nGames with changed title: \n")
for key, value := range gamesChangedTitle {
fmt.Printf("The game: %s changed title to: %s\n", key, value)
}
fmt.Printf("\nGames with changed content: \n")
for _, game := range gamesChangedContent {
fmt.Printf("%s\n", game)
}
fmt.Printf("\n\n")
var gamesRemovedTemp []string
for _, beforeGame := range gamesBeforeSync {
var found bool = false
for _, afterGame := range gamesAfterSync {
if beforeGame.GameName == afterGame.GameName {
found = true
fmt.Printf("Game: %s, Found: %v break\n", beforeGame.GameName, found)
break
}
}
if !found {
fmt.Printf("Game: %s, Found: %v\n", beforeGame.GameName, found)
gamesRemovedTemp = append(gamesRemovedTemp, beforeGame.GameName)
}
}
for _, game := range gamesRemovedTemp {
var found bool = false
for key, _ := range gamesChangedTitle {
if game == key {
found = true
fmt.Printf("Game: %s, Found: %v break2\n", game, found)
break
}
}
if !found {
gamesRemoved = append(gamesRemoved, game)
}
}
fmt.Printf("\nGames removed: \n")
for _, game := range gamesRemoved {
fmt.Printf("%s\n", game)
}
fmt.Printf("\nErrors catched: \n")
for _, error := range catchedErrors {
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"))
return Response{
GamesAdded: gamesAdded,
GamesReAdded: gamesReAdded,
GamesChangedTitle: gamesChangedTitle,
GamesChangedContent: gamesChangedContent,
GamesRemoved: gamesRemoved,
CatchedErrors: catchedErrors,
}
}
func checkBrokenSongsNew() {
allSongs, err := repo.FetchAllSongs(db.Ctx)
handleError("FetchAllSongs", err, "")
var brokenWg sync.WaitGroup
brokenWg.Add(len(allSongs))
for _, song := range allSongs {
go func() {
defer brokenWg.Done()
checkBrokenSongNew(song)
}()
}
brokenWg.Wait()
err = repo.RemoveBrokenSongs(db.Ctx, brokenSongs)
handleError("RemoveBrokenSongs", err, "")
}
func checkBrokenSongNew(song repository.Song) {
//Check if file exists and open
openFile, err := os.Open(song.Path)
if err != nil {
//File not found
brokenSongs = append(brokenSongs, song.Path)
fmt.Printf("song broken: %v\n", song.Path)
} else {
err = openFile.Close()
if err != nil {
log.Println(err)
}
}
}
func syncGameNew(file os.DirEntry, foldersToSkip []string, baseDir string) {
if file.IsDir() && !contains(foldersToSkip, file.Name()) {
gameDir := baseDir + file.Name() + "/"
dirHash := getHashForDir(gameDir)
var status GameStatus = NewGame
var oldGame repository.Game
var id int32 = -1
//fmt.Printf("Games before: %d\n", len(gamesBeforeSync))
for _, currentGame := range allGames {
oldGame = currentGame
//fmt.Printf("%s | %s\n", oldGame.GameName, oldGame.Hash)
if oldGame.GameName == file.Name() && oldGame.Hash == dirHash {
status = NotChanged
id = oldGame.ID
//fmt.Printf("Game not changed\n")
break
} else if oldGame.GameName == file.Name() && oldGame.Hash != dirHash {
status = GameChanged
id = oldGame.ID
//fmt.Printf("Game changed\n")
break
} else if oldGame.GameName != file.Name() && oldGame.Hash == dirHash {
status = TitleChanged
id = oldGame.ID
//fmt.Printf("GameName changed\n")
break
}
}
fmt.Printf("\n\nID: %d | GameName: %s | GameHash: %s | Status: %s\n", id, file.Name(), dirHash, status)
entries, err := os.ReadDir(gameDir)
if err != nil {
log.Println(err)
}
switch status {
case NewGame:
if id != -1 {
for _, entry := range entries {
fileInfo, err := entry.Info()
if err != nil {
log.Println(err)
}
id = getIdFromFileNew(fileInfo)
if id != -1 {
break
}
}
err = repo.InsertGameWithExistingId(db.Ctx, repository.InsertGameWithExistingIdParams{ID: id, GameName: file.Name(), Path: gameDir, Hash: dirHash})
handleError("InsertGameWithExistingId", err, "")
if err != nil {
fmt.Printf("id = %v\n", id)
fileName := gameDir + "/." + strconv.Itoa(int(id)) + ".id"
fmt.Printf("fileName = %v\n", fileName)
err := os.Remove(fileName)
if err != nil {
fmt.Printf("%s\n", err)
}
newDirHash := getHashForDir(gameDir)
id = insertGameNew(file.Name(), gameDir, newDirHash)
}
} else {
id = insertGameNew(file.Name(), gameDir, dirHash)
}
gamesAdded = append(gamesAdded, file.Name())
newCheckSongs(entries, gameDir, id)
case GameChanged:
err = repo.UpdateGameHash(db.Ctx, repository.UpdateGameHashParams{Hash: dirHash, ID: id})
handleError("UpdateGameHash", err, "")
gamesChangedContent = append(gamesChangedContent, file.Name())
newCheckSongs(entries, gameDir, id)
case TitleChanged:
//println("TitleChanged")
err = repo.UpdateGameName(db.Ctx, repository.UpdateGameNameParams{Name: file.Name(), Path: gameDir, ID: id})
handleError("UpdateGameName", err, "")
newCheckSongs(entries, gameDir, id)
if gamesChangedTitle == nil {
gamesChangedTitle = make(map[string]string)
}
gamesChangedTitle[oldGame.GameName] = file.Name()
case NotChanged:
//println("NotChanged")
var found bool = false
for _, beforeGame := range gamesBeforeSync {
if dirHash == beforeGame.Hash {
found = true
//fmt.Printf("Game %s | %s | %s | %s | %v\n", beforeGame.GameName, beforeGame.Hash, dirHash, status, beforeGame.Deleted)
}
}
if !found {
newCheckSongs(entries, gameDir, id)
gamesReAdded = append(gamesReAdded, file.Name())
}
}
err = repo.RemoveDeletionDate(db.Ctx, id)
handleError("RemoveDeletionDate", err, "")
}
}
func insertGameNew(name string, path string, hash string) int32 {
var duplicateError = errors.New("ERROR: duplicate key value violates unique")
id, err := repo.InsertGame(db.Ctx, repository.InsertGameParams{GameName: name, Path: path, Hash: hash})
handleError("InsertGame", err, "")
if err != nil {
fmt.Printf("Handle id busy\n")
if strings.HasPrefix(err.Error(), duplicateError.Error()) {
fmt.Printf("Handeling this id\n")
_, err = repo.ResetGameIdSeq(db.Ctx)
handleError("ResetGameIdSeq", err, "")
id = insertGameNew(name, path, hash)
}
}
return id
}
func newCheckSongs(entries []os.DirEntry, gameDir string, id int32) int32 {
//hasher := md5.New()
var numberOfSongs int32
var songWg sync.WaitGroup
songWg.Add(len(entries))
for _, entry := range entries {
go func() {
defer songWg.Done()
newCheckSong(entry, gameDir, id)
}()
}
songWg.Wait()
return numberOfSongs
}
func newCheckSong(entry os.DirEntry, gameDir string, id int32) {
fileInfo, err := entry.Info()
if err != nil {
log.Println(err)
}
if isSong(fileInfo) {
path := gameDir + entry.Name()
songHash := getHashForFile(path)
//numberOfSongs++
fileName := entry.Name()
songName, _ := strings.CutSuffix(fileName, ".mp3")
song, err := repo.GetSongWithHash(db.Ctx, songHash)
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
}
}
fmt.Printf("Song Changed\n")
fmt.Printf("Path: %s | SongHash: %s\n", path, songHash)
count, err := repo.CheckSongWithHash(db.Ctx, songHash)
handleError("CheckSongWithHash", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
if err != nil {
count2, err := repo.CheckSong(db.Ctx, path)
handleError("CheckSong", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
if count2 > 0 {
err = repo.AddHashToSong(db.Ctx, repository.AddHashToSongParams{Hash: songHash, Path: path})
handleError("AddHashToSong", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
count, err = repo.CheckSongWithHash(db.Ctx, songHash)
handleError("CheckSongWithHash 2", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
}
}
//count, _ := repo.CheckSong(ctx, path)
if count > 0 {
err = repo.UpdateSong(db.Ctx, repository.UpdateSongParams{SongName: songName, FileName: &fileName, Path: path, Hash: songHash})
handleError("UpdateSong", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
} else {
count2, err := repo.CheckSong(db.Ctx, path)
handleError("CheckSong", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
if count2 > 0 {
err = repo.AddHashToSong(db.Ctx, repository.AddHashToSongParams{Hash: songHash, Path: path})
handleError("AddHashToSong", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
} else {
err = repo.AddSong(db.Ctx, repository.AddSongParams{GameID: id, SongName: songName, Path: path, FileName: &fileName, Hash: songHash})
handleError("AddSong", err, fmt.Sprintf("GameID: %d | Path: %s | SongName: %s | SongHash: %s\n", id, path, entry.Name(), songHash))
}
}
} else if isCoverImage(fileInfo) {
//TODO: Later add cover art image here in db
}
}
func handleError(funcName string, err error, msg string) {
var compareError = errors.New("no rows in result set")
if err != nil {
if compareError.Error() != err.Error() {
fmt.Printf("%s Error: %s\n", funcName, err)
if msg != "" {
fmt.Printf("%s\n", msg)
catchedErrors = append(catchedErrors, fmt.Sprintf("Func: %s\nError message: %s\nDebug message: %s\n", funcName, err, msg))
} else {
catchedErrors = append(catchedErrors, fmt.Sprintf("Func: %s\nError message: %s\n", funcName, err))
}
}
}
}
func getHashForDir(gameDir string) string {
directory, _ := directory_checksum.ScanDirectory(gameDir, afero.NewOsFs())
hash, _ := directory.ComputeDirectoryChecksums()
//fmt.Printf("Hash: |%s|\n", hash)
return hash
}
func getHashForFile(path string) string {
hasher := md5.New()
readFile, err := os.Open(path)
if err != nil {
panic(err)
}
defer readFile.Close()
hasher.Reset()
_, err = io.Copy(hasher, readFile)
if err != nil {
log.Fatal(err)
}
return hex.EncodeToString(hasher.Sum(nil))
}
func getIdFromFileNew(file os.FileInfo) int32 {
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 int32(i)
}
return -1
}
func isSong(entry fs.FileInfo) bool {
return !entry.IsDir() && strings.HasSuffix(entry.Name(), ".mp3")
}

37
sqlc.yaml Normal file
View File

@@ -0,0 +1,37 @@
version: "2"
sql:
- engine: "postgresql"
queries: "./db/queries"
schema: "./db/migrations"
gen:
go:
emit_json_tags: true
package: "repository"
out: "db/repository"
sql_package: "pgx/v5"
overrides:
- db_type: "pg_catalog.timestamp"
go_type:
import: "time"
type: "Time"
- db_type: "pg_catalog.timestamp"
nullable: true
go_type:
pointer: true
import: "time"
type: "Time"
- db_type: "pg_catalog.varchar"
nullable: true
go_type:
pointer: true
type: "string"
- db_type: "int4"
nullable: true
go_type:
pointer: true
type: "int32"
- db_type: "date"
nullable: false
go_type:
import: "time"
type: "Time"