package main import ( "io" "strconv" "strings" "github.com/gin-gonic/gin" ) func (a *App) search(c *gin.Context) { keyword := strings.TrimSpace(c.Query("keyword")) if keyword == "" { c.JSON(400, gin.H{"error": "keyword required"}) return } source := c.DefaultQuery("source", "kw") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) if page < 1 { page = 1 } limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) if limit < 1 { limit = 20 } resp, err := a.am.Search(keyword, source, page, limit) if err != nil { c.JSON(502, gin.H{"error": err.Error()}) return } defer resp.Body.Close() c.Status(resp.StatusCode) _, _ = io.Copy(c.Writer, resp.Body) } func (a *App) listTasks(c *gin.Context) { var ts []Task user := c.MustGet("user").(*User) query := a.db.Where("user_id = ?", user.ID) if user.Role == RoleAdmin && c.Query("all") == "1" { query = a.db } if err := query.Order("id desc").Limit(100).Find(&ts).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"data": ts}) } func (a *App) addTask(c *gin.Context) { var in SongRequest if c.BindJSON(&in) != nil || in.RID == "" || in.Title == "" { c.JSON(400, gin.H{"error": "source/rid/title required"}) return } if in.Source == "" { in.Source = "kw" } if in.Quality == "" { in.Quality = a.cfg.DefaultQuality } user := c.MustGet("user").(*User) var existing Task err := a.db.Where("user_id = ? AND source = ? AND r_id = ? AND quality = ?", user.ID, in.Source, in.RID, in.Quality).Order("id desc").First(&existing).Error if err == nil { if existing.Status != "error" { c.JSON(200, existing) return } r, reserveErr := a.reserveCredit(user.ID, existing.ID, 1) if reserveErr != nil { c.JSON(402, gin.H{"error": reserveErr.Error()}) return } a.db.Model(&existing).Updates(map[string]any{"title": in.Title, "artist": in.Artist, "album": in.Album, "pic": in.Pic, "status": "pending", "error": "", "credit_reservation_id": r.ID}) existing.CreditReservationID = r.ID a.queue <- existing.ID c.JSON(202, existing) return } t := Task{UserID: user.ID, Source: in.Source, RID: in.RID, Title: in.Title, Artist: in.Artist, Album: in.Album, Pic: in.Pic, Quality: in.Quality, Status: "pending"} if err := a.db.Create(&t).Error; err != nil { if a.db.Where("user_id = ? AND source = ? AND r_id = ? AND quality = ?", user.ID, in.Source, in.RID, in.Quality).Order("id desc").First(&existing).Error == nil { c.JSON(200, existing) return } c.JSON(500, gin.H{"error": err.Error()}) return } r, reserveErr := a.reserveCredit(user.ID, t.ID, 1) if reserveErr != nil { _ = a.db.Delete(&t).Error c.JSON(402, gin.H{"error": reserveErr.Error()}) return } t.CreditReservationID = r.ID if err := a.db.Model(&t).Update("credit_reservation_id", r.ID).Error; err != nil { _ = a.refundCredit(&t) c.JSON(500, gin.H{"error": err.Error()}) return } a.queue <- t.ID c.JSON(202, t) } func (a *App) creditInfo(c *gin.Context) { user := c.MustGet("user").(*User) account, err := a.creditBalance(user.ID) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"balance": account.Balance, "reserved": account.Reserved, "available": account.Balance - account.Reserved}) } func (a *App) retryTask(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.Status(400) return } var t Task user := c.MustGet("user").(*User) if a.db.Where("id = ? AND user_id = ?", id, user.ID).First(&t).Error != nil { c.Status(404) return } a.db.Model(&t).Updates(map[string]any{"status": "pending", "error": "", "progress": 0, "downloaded_bytes": 0}) t.Status, t.Error, t.Progress, t.DownloadedBytes = "pending", "", 0, 0 a.queue <- t.ID c.JSON(202, t) } func (a *App) deleteTask(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.Status(400) return } user := c.MustGet("user").(*User) if err := a.db.Where("id = ? AND user_id = ?", id, user.ID).Delete(&Task{}).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.Status(204) }