2024-08-02 21:48:26 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2024-09-03 07:07:45 +00:00
|
|
|
"io/fs"
|
2024-08-02 21:48:26 +00:00
|
|
|
"net/http"
|
2024-09-03 07:07:45 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-09-01 03:43:32 +00:00
|
|
|
"strings"
|
2024-11-01 19:15:19 +00:00
|
|
|
"time"
|
2024-08-02 21:48:26 +00:00
|
|
|
|
2024-11-01 19:15:19 +00:00
|
|
|
"arimelody-web/admin"
|
2024-09-03 07:07:45 +00:00
|
|
|
"arimelody-web/global"
|
|
|
|
db "arimelody-web/music/controller"
|
|
|
|
music "arimelody-web/music/controller"
|
|
|
|
"arimelody-web/music/model"
|
2024-08-02 21:48:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func ServeAllArtists() http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2024-09-01 03:43:32 +00:00
|
|
|
var artists = []*model.Artist{}
|
2024-09-01 23:15:23 +00:00
|
|
|
artists, err := db.GetAllArtists(global.DB)
|
2024-08-02 21:48:26 +00:00
|
|
|
if err != nil {
|
2024-09-01 03:43:32 +00:00
|
|
|
fmt.Printf("FATAL: Failed to serve all artists: %s\n", err)
|
2024-08-02 21:48:26 +00:00
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2024-09-01 03:43:32 +00:00
|
|
|
|
|
|
|
w.Header().Add("Content-Type", "application/json")
|
|
|
|
err = json.NewEncoder(w).Encode(artists)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
}
|
2024-08-02 21:48:26 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-09-03 07:07:45 +00:00
|
|
|
func ServeArtist(artist *model.Artist) http.Handler {
|
2024-08-02 21:48:26 +00:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
type (
|
|
|
|
creditJSON struct {
|
2024-11-01 19:15:19 +00:00
|
|
|
ID string `json:"id"`
|
|
|
|
Title string `json:"title"`
|
|
|
|
ReleaseDate time.Time `json:"releaseDate" db:"release_date"`
|
|
|
|
Artwork string `json:"artwork"`
|
|
|
|
Role string `json:"role"`
|
|
|
|
Primary bool `json:"primary"`
|
2024-08-02 21:48:26 +00:00
|
|
|
}
|
|
|
|
artistJSON struct {
|
2024-09-03 07:07:45 +00:00
|
|
|
*model.Artist
|
2024-08-02 21:48:26 +00:00
|
|
|
Credits map[string]creditJSON `json:"credits"`
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2024-11-01 19:15:19 +00:00
|
|
|
show_hidden_releases := admin.GetSession(r) != nil
|
|
|
|
|
2024-09-01 23:15:23 +00:00
|
|
|
var dbCredits []*model.Credit
|
2024-11-01 19:15:19 +00:00
|
|
|
dbCredits, err := db.GetArtistCredits(global.DB, artist.ID, show_hidden_releases)
|
2024-09-01 03:43:32 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("FATAL: Failed to retrieve artist credits for %s: %s\n", artist.ID, err)
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
2024-08-02 21:48:26 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-01 23:15:23 +00:00
|
|
|
var credits = map[string]creditJSON{}
|
|
|
|
for _, credit := range dbCredits {
|
|
|
|
credits[credit.Release.ID] = creditJSON{
|
2024-11-01 19:15:19 +00:00
|
|
|
ID: credit.Release.ID,
|
|
|
|
Title: credit.Release.Title,
|
|
|
|
ReleaseDate: credit.Release.ReleaseDate,
|
|
|
|
Artwork: credit.Release.Artwork,
|
2024-09-01 23:15:23 +00:00
|
|
|
Role: credit.Role,
|
|
|
|
Primary: credit.Primary,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-02 21:48:26 +00:00
|
|
|
w.Header().Add("Content-Type", "application/json")
|
2024-09-01 03:43:32 +00:00
|
|
|
err = json.NewEncoder(w).Encode(artistJSON{
|
|
|
|
Artist: artist,
|
|
|
|
Credits: credits,
|
|
|
|
})
|
2024-08-02 21:48:26 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func CreateArtist() http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2024-09-03 07:07:45 +00:00
|
|
|
var artist model.Artist
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&artist)
|
2024-08-02 21:48:26 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-03 07:07:45 +00:00
|
|
|
if artist.ID == "" {
|
2024-08-03 14:02:01 +00:00
|
|
|
http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest)
|
2024-08-02 23:27:30 +00:00
|
|
|
return
|
|
|
|
}
|
2024-09-03 07:07:45 +00:00
|
|
|
if artist.Name == "" { artist.Name = artist.ID }
|
2024-08-02 23:27:30 +00:00
|
|
|
|
2024-09-03 07:07:45 +00:00
|
|
|
err = music.CreateArtist(global.DB, &artist)
|
2024-08-02 21:48:26 +00:00
|
|
|
if err != nil {
|
2024-09-01 03:43:32 +00:00
|
|
|
if strings.Contains(err.Error(), "duplicate key") {
|
2024-09-03 07:07:45 +00:00
|
|
|
http.Error(w, fmt.Sprintf("Artist %s already exists\n", artist.ID), http.StatusBadRequest)
|
2024-09-01 03:43:32 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Printf("FATAL: Failed to create artist %s: %s\n", artist.ID, err)
|
2024-08-02 21:48:26 +00:00
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
2024-08-03 22:24:15 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-09-03 07:07:45 +00:00
|
|
|
func UpdateArtist(artist *model.Artist) http.Handler {
|
2024-08-03 22:24:15 +00:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2024-09-03 07:07:45 +00:00
|
|
|
err := json.NewDecoder(r.Body).Decode(&artist)
|
2024-08-03 22:24:15 +00:00
|
|
|
if err != nil {
|
2024-09-01 03:43:32 +00:00
|
|
|
fmt.Printf("FATAL: Failed to update artist: %s\n", err)
|
2024-08-03 22:24:15 +00:00
|
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-03 07:07:45 +00:00
|
|
|
if artist.Avatar == "" {
|
|
|
|
artist.Avatar = "/img/default-avatar.png"
|
|
|
|
} else {
|
|
|
|
if strings.Contains(artist.Avatar, ";base64,") {
|
|
|
|
var artworkDirectory = filepath.Join("uploads", "avatar")
|
|
|
|
filename, err := HandleImageUpload(&artist.Avatar, artworkDirectory, artist.ID)
|
|
|
|
|
|
|
|
// clean up files with this ID and different extensions
|
|
|
|
err = filepath.Walk(artworkDirectory, func(path string, info fs.FileInfo, err error) error {
|
|
|
|
if path == filepath.Join(artworkDirectory, filename) { return nil }
|
|
|
|
|
|
|
|
withoutExt := strings.TrimSuffix(path, filepath.Ext(path))
|
|
|
|
if withoutExt != filepath.Join(artworkDirectory, artist.ID) { return nil }
|
|
|
|
|
|
|
|
return os.Remove(path)
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("WARN: Error while cleaning up avatar files: %s\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
artist.Avatar = fmt.Sprintf("/uploads/avatar/%s", filename)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = music.UpdateArtist(global.DB, artist)
|
2024-08-03 22:24:15 +00:00
|
|
|
if err != nil {
|
2024-09-03 07:07:45 +00:00
|
|
|
if strings.Contains(err.Error(), "no rows") {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
2024-09-01 03:43:32 +00:00
|
|
|
fmt.Printf("FATAL: Failed to update artist %s: %s\n", artist.ID, err)
|
2024-08-03 22:24:15 +00:00
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-09-03 07:07:45 +00:00
|
|
|
func DeleteArtist(artist *model.Artist) http.Handler {
|
2024-08-03 22:24:15 +00:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2024-09-03 07:07:45 +00:00
|
|
|
err := music.DeleteArtist(global.DB, artist.ID)
|
2024-08-03 22:24:15 +00:00
|
|
|
if err != nil {
|
2024-09-03 07:07:45 +00:00
|
|
|
if strings.Contains(err.Error(), "no rows") {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
2024-09-01 03:43:32 +00:00
|
|
|
fmt.Printf("FATAL: Failed to delete artist %s: %s\n", artist.ID, err)
|
2024-08-03 22:24:15 +00:00
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
}
|
2024-08-02 21:48:26 +00:00
|
|
|
})
|
|
|
|
}
|