Skip to content

Commit a0d4ac5

Browse files
committed
Add endpoint to upload music artist song
1 parent 2e16d03 commit a0d4ac5

File tree

5 files changed

+163
-4
lines changed

5 files changed

+163
-4
lines changed

cache/cache_server.go

+4
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,7 @@ func RemoveCacheServerMusicArtistBanner(id int) error {
7070
func RemoveCacheServerMusicArtistAlbumCover(id int) error {
7171
return RemoveCacheServerFile("music-artist-album-covers", id)
7272
}
73+
74+
func RemoveCacheServerMusicArtistSong(id int) error {
75+
return RemoveCacheServerFile("music-artist-songs", id)
76+
}

cmd/api/server.go

+3
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ func initializeRoutes(engine *gin.Engine) {
288288
engine.DELETE("/v2/artists/album/:id", middleware.RequireAuth, handlers.CreateHandler(handlers.DeleteMusicArtistAlbum))
289289
engine.POST("/v2/artists/album/:id/cover", middleware.RequireAuth, handlers.CreateHandler(handlers.UploadMusicArtistAlbumCover))
290290

291+
// Songs
292+
engine.POST("/v2/artists/album/:id/song", middleware.RequireAuth, handlers.CreateHandler(handlers.UploadMusicArtistSong))
293+
291294
engine.NoRoute(func(c *gin.Context) {
292295
c.JSON(http.StatusNotFound, gin.H{"error": "Not found"})
293296
return

db/music_artists_albums.go

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package db
22

33
type MusicArtistAlbum struct {
4-
Id int `gorm:"column:id; PRIMARY_KEY" json:"id"`
5-
ArtistId int `gorm:"column:artist_id" json:"artist_id"`
6-
Name string `gorm:"column:name" json:"name"`
7-
SortOrder int `gorm:"column:sort_order" json:"sort_order"`
4+
Id int `gorm:"column:id; PRIMARY_KEY" json:"id"`
5+
ArtistId int `gorm:"column:artist_id" json:"artist_id"`
6+
Name string `gorm:"column:name" json:"name"`
7+
SortOrder int `gorm:"column:sort_order" json:"sort_order"`
8+
Songs []*MusicArtistSong `gorm:"-:all" json:"songs,omitempty"`
89
}
910

1011
func (*MusicArtistAlbum) TableName() string {
@@ -28,6 +29,16 @@ func GetMusicArtistAlbums(artistId int) ([]*MusicArtistAlbum, error) {
2829
return nil, result.Error
2930
}
3031

32+
for _, album := range albums {
33+
var err error
34+
35+
album.Songs, err = GetMusicArtistSongsInAlbum(album.Id)
36+
37+
if err != nil {
38+
return nil, err
39+
}
40+
}
41+
3142
return albums, nil
3243
}
3344

@@ -43,6 +54,14 @@ func GetMusicArtistAlbumById(id int) (*MusicArtistAlbum, error) {
4354
return nil, result.Error
4455
}
4556

57+
var err error
58+
59+
album.Songs, err = GetMusicArtistSongsInAlbum(album.Id)
60+
61+
if err != nil {
62+
return nil, err
63+
}
64+
4665
return &album, nil
4766
}
4867

db/music_artists_songs.go

+20
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,23 @@ type MusicArtistSong struct {
1212
func (*MusicArtistSong) TableName() string {
1313
return "music_artists_songs"
1414
}
15+
16+
func (song *MusicArtistSong) ID() int {
17+
return song.Id
18+
}
19+
20+
// GetMusicArtistSongsInAlbum Returns the songs in a music artist's album
21+
func GetMusicArtistSongsInAlbum(albumId int) ([]*MusicArtistSong, error) {
22+
songs := make([]*MusicArtistSong, 0)
23+
24+
result := SQL.
25+
Where("album_id = ?", albumId).
26+
Order("sort_order ASC").
27+
Find(&songs)
28+
29+
if result.Error != nil {
30+
return nil, result.Error
31+
}
32+
33+
return songs, nil
34+
}

handlers/music_artists_songs.go

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package handlers
2+
3+
import (
4+
"fmt"
5+
"github.com/Quaver/api2/azure"
6+
"github.com/Quaver/api2/cache"
7+
"github.com/Quaver/api2/db"
8+
"github.com/gabriel-vasile/mimetype"
9+
"github.com/gin-gonic/gin"
10+
"gorm.io/gorm"
11+
"net/http"
12+
"strconv"
13+
)
14+
15+
// UploadMusicArtistSong Uploads a new song for a music artist's album
16+
// Endpoint: POST /v2/artists/album/:id/song
17+
func UploadMusicArtistSong(c *gin.Context) *APIError {
18+
user := getAuthedUser(c)
19+
20+
if user == nil {
21+
return nil
22+
}
23+
24+
if !canUserAccessAdminRoute(c) {
25+
return nil
26+
}
27+
28+
id, err := strconv.Atoi(c.Param("id"))
29+
30+
if err != nil {
31+
return APIErrorBadRequest("Invalid id")
32+
}
33+
34+
body := struct {
35+
Name string `form:"name" json:"name" binding:"required"`
36+
BPM int `form:"bpm" json:"bpm" binding:"required"`
37+
Length int `form:"length" json:"length" binding:"required"`
38+
}{}
39+
40+
if err := c.ShouldBind(&body); err != nil {
41+
return APIErrorBadRequest("Invalid request body")
42+
}
43+
44+
album, err := db.GetMusicArtistAlbumById(id)
45+
46+
if err != nil && err != gorm.ErrRecordNotFound {
47+
return APIErrorServerError("Error retrieving album from db", err)
48+
}
49+
50+
if album == nil {
51+
return APIErrorNotFound("Album")
52+
}
53+
54+
fileBytes, apiErr := validateUploadedAudio(c)
55+
56+
if apiErr != nil {
57+
return apiErr
58+
}
59+
60+
song := db.MusicArtistSong{
61+
AlbumId: id,
62+
Name: body.Name,
63+
Length: body.Length,
64+
BPM: body.BPM,
65+
SortOrder: len(album.Songs),
66+
}
67+
68+
if err := db.SQL.Create(&song).Error; err != nil {
69+
return APIErrorServerError("Error inserting song into db", err)
70+
}
71+
72+
_ = cache.RemoveCacheServerMusicArtistSong(song.Id)
73+
74+
if err := azure.Client.UploadFile("music-artist-songs", fmt.Sprintf("%v.mp3", song.Id), fileBytes); err != nil {
75+
return APIErrorServerError("Error uploading song to azure", err)
76+
}
77+
78+
c.JSON(http.StatusOK, gin.H{
79+
"message": "Your song has been successfully uploaded.",
80+
"song": song,
81+
})
82+
83+
return nil
84+
}
85+
86+
// Validates an uploaded audio file and returns the file bytes
87+
func validateUploadedAudio(c *gin.Context) ([]byte, *APIError) {
88+
fileHeader, _ := c.FormFile("audio")
89+
90+
if fileHeader == nil {
91+
return nil, APIErrorBadRequest("You must provide a valid `audio` file.")
92+
}
93+
94+
file, err := fileHeader.Open()
95+
96+
if err != nil {
97+
return nil, APIErrorServerError("Error opening file", err)
98+
}
99+
100+
defer file.Close()
101+
102+
var fileBytes = make([]byte, fileHeader.Size)
103+
104+
if _, err = file.Read(fileBytes); err != nil {
105+
return nil, APIErrorServerError("Error reading file", err)
106+
}
107+
108+
if mime := mimetype.Detect(fileBytes); mime == nil || mime.String() != "audio/mpeg" {
109+
return nil, APIErrorBadRequest("The file you provided was not a valid .mp3 file.")
110+
}
111+
112+
return fileBytes, nil
113+
}

0 commit comments

Comments
 (0)