From 2791b6de769241d53785a3e7b4ede750c106ea94 Mon Sep 17 00:00:00 2001 From: juli0n21 Date: Thu, 6 Feb 2025 00:23:12 +0100 Subject: [PATCH] add collection sugar --- go-backend/database.go | 85 ++++++++++++++++++++++++++++++++---- go-backend/docs/docs.go | 69 +++-------------------------- go-backend/docs/swagger.json | 69 +++-------------------------- go-backend/docs/swagger.yaml | 46 ++----------------- go-backend/go.mod | 2 +- go-backend/go.sum | 4 +- go-backend/handlers.go | 76 +++++++++++--------------------- go-backend/models.go | 3 +- 8 files changed, 124 insertions(+), 230 deletions(-) diff --git a/go-backend/database.go b/go-backend/database.go index 8628da7..2420674 100644 --- a/go-backend/database.go +++ b/go-backend/database.go @@ -419,13 +419,78 @@ func getCollection(db *sql.DB, limit, offset, index int) (Collection, error) { return c, nil } -func getCollections(db *sql.DB, q string, limit, offset int) ([]Collection, error) { - //not correct - rows, err := db.Query("SELECT Name, FROM Collections WHERE name = ? LIMIT ? OFFSET ?", q, limit, offset) +func getCollectionByName(db *sql.DB, limit, offset int, name string) (Collection, error) { + rows, err := db.Query(` + 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 { - 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) { @@ -472,14 +537,18 @@ func scanSong(row *sql.Row) (Song, error) { } 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 { fmt.Println(err) return "404.png" } - if len(bm.Events) > 1 && len(bm.Events[0].EventParams) > 1 { - return fmt.Sprintf("%s/%s", folder, strings.Trim(bm.Events[0].EventParams[0], "\"")) + if bm.Version > 3 { + 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" diff --git a/go-backend/docs/docs.go b/go-backend/docs/docs.go index b248f6a..64bfec7 100644 --- a/go-backend/docs/docs.go +++ b/go-backend/docs/docs.go @@ -77,8 +77,13 @@ const docTemplate = `{ "type": "integer", "description": "Index", "name": "index", - "in": "query", - "required": true + "in": "query" + }, + { + "type": "string", + "description": "Index", + "name": "name", + "in": "query" } ], "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}": { "get": { "description": "Retrieves an image file from the server based on the provided encoded filepath", diff --git a/go-backend/docs/swagger.json b/go-backend/docs/swagger.json index c817a9b..134c5ee 100644 --- a/go-backend/docs/swagger.json +++ b/go-backend/docs/swagger.json @@ -71,8 +71,13 @@ "type": "integer", "description": "Index", "name": "index", - "in": "query", - "required": true + "in": "query" + }, + { + "type": "string", + "description": "Index", + "name": "name", + "in": "query" } ], "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}": { "get": { "description": "Retrieves an image file from the server based on the provided encoded filepath", diff --git a/go-backend/docs/swagger.yaml b/go-backend/docs/swagger.yaml index 604a528..9e2b806 100644 --- a/go-backend/docs/swagger.yaml +++ b/go-backend/docs/swagger.yaml @@ -105,8 +105,11 @@ paths: - description: Index in: query name: index - required: true type: integer + - description: Index + in: query + name: name + type: string produces: - application/json responses: @@ -127,47 +130,6 @@ paths: summary: Get a collection of songs by index tags: - 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}: get: consumes: diff --git a/go-backend/go.mod b/go-backend/go.mod index 98612ab..62193c0 100644 --- a/go-backend/go.mod +++ b/go-backend/go.mod @@ -4,7 +4,7 @@ go 1.23.5 require ( 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/swag v1.16.4 modernc.org/sqlite v1.34.5 diff --git a/go-backend/go.sum b/go-backend/go.sum index 995fafa..9e4b7e3 100644 --- a/go-backend/go.sum +++ b/go-backend/go.sum @@ -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/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/juli0n21/go-osu-parser v0.0.6 h1:r5BTNrEDUHsF0ZFCvx0vfRcjU2IRvT3va4O1r3dm7og= -github.com/juli0n21/go-osu-parser v0.0.6/go.mod h1:oLLWnZReOMW4i5aNva/zvXsFqzdQigrbjyxOSs0cx+0= +github.com/juli0n21/go-osu-parser v0.0.8 h1:aQtuhAniGvpUw446arhq/3aUOK9YvZEkL7aYUGlViAo= +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/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= diff --git a/go-backend/handlers.go b/go-backend/handlers.go index 0501a6f..d1411fb 100644 --- a/go-backend/handlers.go +++ b/go-backend/handlers.go @@ -38,7 +38,6 @@ func (s *Server) registerRoutes() { http.HandleFunc("/api/v1/songs/favorites", s.favorites) 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/active", s.activeSearch) @@ -46,7 +45,7 @@ func (s *Server) registerRoutes() { http.HandleFunc("/api/v1/audio/{filepath}", s.songFile) http.HandleFunc("/api/v1/image/{filepath}", s.imageFile) - http.Handle("/swagger-ui", httpSwagger.WrapHandler) + http.Handle("/swagger/", httpSwagger.WrapHandler) } func run(s *Server) { @@ -100,7 +99,7 @@ func (s *Server) song(w http.ResponseWriter, r *http.Request) { song, err := getSong(s.Db, hash) if err != nil { fmt.Println(err) - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusNotFound) + http.Error(w, "beatmap not found by hash", http.StatusNotFound) return } @@ -170,58 +169,39 @@ func (s *Server) favorites(w http.ResponseWriter, r *http.Request) { // @Tags songs // @Accept 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 // @Failure 400 {string} string "Invalid parameter" // @Failure 500 {string} string "Internal server error" // @Router /collection [get] 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")) if err != nil { http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) return - } - - limit, offset := pagination(r) - - recent, err := getCollection(s.Db, limit, offset, index) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(w, recent, 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) + } else { + c, err := getCollection(s.Db, limit, offset, index) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(w, c, http.StatusOK) 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 @@ -238,22 +218,16 @@ func (s *Server) collections(w http.ResponseWriter, r *http.Request) { // @Router /search/collections [get] func (s *Server) collectionSearch(w http.ResponseWriter, r *http.Request) { q := r.URL.Query().Get("query") - if q == "" { - http.Error(w, ErrRequiredParameterNotPresent.Error(), http.StatusBadRequest) - return - } - - //TODO limit, offset := pagination(r) - recent, err := getCollections(s.Db, q, limit, offset) + preview, err := getCollections(s.Db, q, limit, offset) if err != nil { fmt.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - writeJSON(w, recent, http.StatusOK) + writeJSON(w, preview, http.StatusOK) } // @Summary Searches active records based on a query diff --git a/go-backend/models.go b/go-backend/models.go index 825799c..02d2ad3 100644 --- a/go-backend/models.go +++ b/go-backend/models.go @@ -18,9 +18,8 @@ type Song struct { // CollectionPreview represents a preview of a song collection // @Description CollectionPreview contains summary data of a song collection type CollectionPreview struct { - Name string `json:"name" example:"Favorite Songs"` + Name string `json:"name" example:"Collection Name"` Image string `json:"image" example:"cover.jpg"` - Index int `json:"index" example:"1"` Items int `json:"items" example:"10"` }