add collection sugar

This commit is contained in:
2025-02-06 00:23:12 +01:00
parent 905b488fd4
commit 2791b6de76
8 changed files with 124 additions and 230 deletions

View File

@@ -419,13 +419,78 @@ func getCollection(db *sql.DB, limit, offset, index int) (Collection, error) {
return c, nil return c, nil
} }
func getCollections(db *sql.DB, q string, limit, offset int) ([]Collection, error) { func getCollectionByName(db *sql.DB, limit, offset int, name string) (Collection, error) {
//not correct rows, err := db.Query(`
rows, err := db.Query("SELECT Name, FROM Collections WHERE name = ? LIMIT ? OFFSET ?", q, limit, offset) SELECT
c.Name, b.BeatmapId, b.MD5Hash, b.Title, b.Artist,
b.Creator, b.Folder, b.File, b.Audio, b.TotalTime
FROM Collection c
Join Beatmap b ON c.MD5Hash = b.MD5Hash
WHERE c.Name = ?
LIMIT ?
OFFSET ?;`, name, limit, offset)
if err != nil { if err != nil {
return []Collection{}, err return Collection{}, err
} }
return scanCollections(rows) defer rows.Close()
var c Collection
for rows.Next() {
s := Song{}
if err := rows.Scan(&c.Name, &s.BeatmapID, &s.MD5Hash, &s.Title, &s.Artist, &s.Creator, &s.Folder, &s.File, &s.Audio, &s.TotalTime); err != nil {
return Collection{}, err
}
s.Image = extractImageFromFile(fileName, s.Folder, s.File)
c.Songs = append(c.Songs, s)
}
row := db.QueryRow(`SELECT COUNT(*) FROM Collection WHERE Name = ?`, c.Name)
var count string
row.Scan(&count)
if i, err := strconv.Atoi(count); err == nil {
c.Items = i
} else {
return Collection{}, err
}
return c, nil
}
func getCollections(db *sql.DB, q string, limit, offset int) ([]CollectionPreview, error) {
rows, err := db.Query(`
SELECT
c.Name, COUNT(b.MD5Hash), b.Folder, b.File
FROM Collection c
Join Beatmap b ON c.MD5Hash = b.MD5Hash
WHERE c.Name LIKE ?
GROUP BY c.NAME
LIMIT ?
OFFSET ?;`, "%"+q+"%", limit, offset)
if err != nil {
return []CollectionPreview{}, err
}
defer rows.Close()
var collections []CollectionPreview
for rows.Next() {
var c CollectionPreview
var folder, file, count string
if err := rows.Scan(&c.Name, &count, &folder, &file); err != nil {
return []CollectionPreview{}, err
}
if i, err := strconv.Atoi(count); err == nil {
c.Items = i
}
c.Image = extractImageFromFile(fileName, folder, file)
collections = append(collections, c)
}
return collections, nil
} }
func getSong(db *sql.DB, hash string) (Song, error) { func getSong(db *sql.DB, hash string) (Song, error) {
@@ -472,14 +537,18 @@ func scanSong(row *sql.Row) (Song, error) {
} }
func extractImageFromFile(osuRoot, folder, file string) string { func extractImageFromFile(osuRoot, folder, file string) string {
bm, err := parser.ParseOsuFile(fmt.Sprintf("%sSongs/%s/%s", osuRoot, folder, file)) bm, err := parser.ParseOsuFile(filepath.Join(osuRoot, "Songs", folder, file))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return "404.png" return "404.png"
} }
if len(bm.Events) > 1 && len(bm.Events[0].EventParams) > 1 { if bm.Version > 3 {
return fmt.Sprintf("%s/%s", folder, strings.Trim(bm.Events[0].EventParams[0], "\"")) if len(bm.Events) > 0 && len(bm.Events[0].EventParams) > 0 {
return fmt.Sprintf(`%s/%s`, folder, strings.Trim(bm.Events[0].EventParams[0], "\""))
}
} else {
fmt.Println(bm.Events)
} }
return "404.png" return "404.png"

View File

@@ -77,8 +77,13 @@ const docTemplate = `{
"type": "integer", "type": "integer",
"description": "Index", "description": "Index",
"name": "index", "name": "index",
"in": "query", "in": "query"
"required": true },
{
"type": "string",
"description": "Index",
"name": "name",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -106,66 +111,6 @@ const docTemplate = `{
} }
} }
}, },
"/collections": {
"get": {
"description": "Retrieves collections from the database with pagination based on the query parameter",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"search"
],
"summary": "Retrieves collections based on a query",
"parameters": [
{
"type": "string",
"description": "Search query",
"name": "query",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Limit the number of results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"default": 0,
"description": "Offset for pagination",
"name": "offset",
"in": "query"
}
],
"responses": {
"200": {
"description": "List of collections",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/main.Collection"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},
"/image/{filepath}": { "/image/{filepath}": {
"get": { "get": {
"description": "Retrieves an image file from the server based on the provided encoded filepath", "description": "Retrieves an image file from the server based on the provided encoded filepath",

View File

@@ -71,8 +71,13 @@
"type": "integer", "type": "integer",
"description": "Index", "description": "Index",
"name": "index", "name": "index",
"in": "query", "in": "query"
"required": true },
{
"type": "string",
"description": "Index",
"name": "name",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -100,66 +105,6 @@
} }
} }
}, },
"/collections": {
"get": {
"description": "Retrieves collections from the database with pagination based on the query parameter",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"search"
],
"summary": "Retrieves collections based on a query",
"parameters": [
{
"type": "string",
"description": "Search query",
"name": "query",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Limit the number of results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"default": 0,
"description": "Offset for pagination",
"name": "offset",
"in": "query"
}
],
"responses": {
"200": {
"description": "List of collections",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/main.Collection"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "string"
}
}
}
}
},
"/image/{filepath}": { "/image/{filepath}": {
"get": { "get": {
"description": "Retrieves an image file from the server based on the provided encoded filepath", "description": "Retrieves an image file from the server based on the provided encoded filepath",

View File

@@ -105,8 +105,11 @@ paths:
- description: Index - description: Index
in: query in: query
name: index name: index
required: true
type: integer type: integer
- description: Index
in: query
name: name
type: string
produces: produces:
- application/json - application/json
responses: responses:
@@ -127,47 +130,6 @@ paths:
summary: Get a collection of songs by index summary: Get a collection of songs by index
tags: tags:
- songs - songs
/collections:
get:
consumes:
- application/json
description: Retrieves collections from the database with pagination based on
the query parameter
parameters:
- description: Search query
in: query
name: query
type: string
- default: 10
description: Limit the number of results
in: query
name: limit
type: integer
- default: 0
description: Offset for pagination
in: query
name: offset
type: integer
produces:
- application/json
responses:
"200":
description: List of collections
schema:
items:
$ref: '#/definitions/main.Collection'
type: array
"400":
description: Bad Request
schema:
type: string
"500":
description: Internal Server Error
schema:
type: string
summary: Retrieves collections based on a query
tags:
- search
/image/{filepath}: /image/{filepath}:
get: get:
consumes: consumes:

View File

@@ -4,7 +4,7 @@ go 1.23.5
require ( require (
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/juli0n21/go-osu-parser v0.0.6 github.com/juli0n21/go-osu-parser v0.0.8
github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.4 github.com/swaggo/swag v1.16.4
modernc.org/sqlite v1.34.5 modernc.org/sqlite v1.34.5

View File

@@ -20,8 +20,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/juli0n21/go-osu-parser v0.0.6 h1:r5BTNrEDUHsF0ZFCvx0vfRcjU2IRvT3va4O1r3dm7og= github.com/juli0n21/go-osu-parser v0.0.8 h1:aQtuhAniGvpUw446arhq/3aUOK9YvZEkL7aYUGlViAo=
github.com/juli0n21/go-osu-parser v0.0.6/go.mod h1:oLLWnZReOMW4i5aNva/zvXsFqzdQigrbjyxOSs0cx+0= github.com/juli0n21/go-osu-parser v0.0.8/go.mod h1:oLLWnZReOMW4i5aNva/zvXsFqzdQigrbjyxOSs0cx+0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=

View File

@@ -38,7 +38,6 @@ func (s *Server) registerRoutes() {
http.HandleFunc("/api/v1/songs/favorites", s.favorites) http.HandleFunc("/api/v1/songs/favorites", s.favorites)
http.HandleFunc("/api/v1/collection", s.collection) http.HandleFunc("/api/v1/collection", s.collection)
http.HandleFunc("/api/v1/collections", s.collections)
http.HandleFunc("/api/v1/search/collections", s.collectionSearch) http.HandleFunc("/api/v1/search/collections", s.collectionSearch)
http.HandleFunc("/api/v1/search/active", s.activeSearch) http.HandleFunc("/api/v1/search/active", s.activeSearch)
@@ -46,7 +45,7 @@ func (s *Server) registerRoutes() {
http.HandleFunc("/api/v1/audio/{filepath}", s.songFile) http.HandleFunc("/api/v1/audio/{filepath}", s.songFile)
http.HandleFunc("/api/v1/image/{filepath}", s.imageFile) http.HandleFunc("/api/v1/image/{filepath}", s.imageFile)
http.Handle("/swagger-ui", httpSwagger.WrapHandler) http.Handle("/swagger/", httpSwagger.WrapHandler)
} }
func run(s *Server) { func run(s *Server) {
@@ -100,7 +99,7 @@ func (s *Server) song(w http.ResponseWriter, r *http.Request) {
song, err := getSong(s.Db, hash) song, err := getSong(s.Db, hash)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusNotFound) http.Error(w, "beatmap not found by hash", http.StatusNotFound)
return return
} }
@@ -170,58 +169,39 @@ func (s *Server) favorites(w http.ResponseWriter, r *http.Request) {
// @Tags songs // @Tags songs
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param index query int true "Index" // @Param index query int false "Index"
// @Param name query string false "Index"
// @Success 200 {array} Song // @Success 200 {array} Song
// @Failure 400 {string} string "Invalid parameter" // @Failure 400 {string} string "Invalid parameter"
// @Failure 500 {string} string "Internal server error" // @Failure 500 {string} string "Internal server error"
// @Router /collection [get] // @Router /collection [get]
func (s *Server) collection(w http.ResponseWriter, r *http.Request) { func (s *Server) collection(w http.ResponseWriter, r *http.Request) {
limit, offset := pagination(r)
name := r.URL.Query().Get("name")
if name != "" {
c, err := getCollectionByName(s.Db, limit, offset, name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
writeJSON(w, c, http.StatusOK)
return
}
index, err := strconv.Atoi(r.URL.Query().Get("index")) index, err := strconv.Atoi(r.URL.Query().Get("index"))
if err != nil { if err != nil {
http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest)
return return
} } else {
c, err := getCollection(s.Db, limit, offset, index)
limit, offset := pagination(r)
recent, err := getCollection(s.Db, limit, offset, index)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
writeJSON(w, recent, http.StatusOK) writeJSON(w, c, http.StatusOK)
}
// @Summary Retrieves collections based on a query
// @Description Retrieves collections from the database with pagination based on the query parameter
// @Tags search
// @Accept json
// @Produce json
// @Param query query string false "Search query"
// @Param limit query int false "Limit the number of results" default(10)
// @Param offset query int false "Offset for pagination" default(0)
// @Success 200 {array} Collection "List of collections"
// @Failure 400 {object} string "Bad Request"
// @Failure 500 {object} string "Internal Server Error"
// @Router /collections [get]
func (s *Server) collections(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("query")
if query == "" {
http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest)
return return
} }
//TODO
limit, offset := pagination(r)
recent, err := getCollections(s.Db, query, limit, offset)
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
writeJSON(w, recent, http.StatusOK)
} }
// @Summary Searches collections based on a query // @Summary Searches collections based on a query
@@ -238,22 +218,16 @@ func (s *Server) collections(w http.ResponseWriter, r *http.Request) {
// @Router /search/collections [get] // @Router /search/collections [get]
func (s *Server) collectionSearch(w http.ResponseWriter, r *http.Request) { func (s *Server) collectionSearch(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("query") q := r.URL.Query().Get("query")
if q == "" {
http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest)
return
}
//TODO
limit, offset := pagination(r) limit, offset := pagination(r)
recent, err := getCollections(s.Db, q, limit, offset) preview, err := getCollections(s.Db, q, limit, offset)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
writeJSON(w, recent, http.StatusOK) writeJSON(w, preview, http.StatusOK)
} }
// @Summary Searches active records based on a query // @Summary Searches active records based on a query

View File

@@ -18,9 +18,8 @@ type Song struct {
// CollectionPreview represents a preview of a song collection // CollectionPreview represents a preview of a song collection
// @Description CollectionPreview contains summary data of a song collection // @Description CollectionPreview contains summary data of a song collection
type CollectionPreview struct { type CollectionPreview struct {
Name string `json:"name" example:"Favorite Songs"` Name string `json:"name" example:"Collection Name"`
Image string `json:"image" example:"cover.jpg"` Image string `json:"image" example:"cover.jpg"`
Index int `json:"index" example:"1"`
Items int `json:"items" example:"10"` Items int `json:"items" example:"10"`
} }