home to your local SPACEGIRL 💫 arimelody.space
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

well i guess i can POST releases now!

Signed-off-by: ari melody <ari@arimelody.me>

+376 -162
+1
.gitignore
··· 2 2 .idea/ 3 3 tmp/ 4 4 test/ 5 + data/*
+9 -3
api/v1/admin/admin.go
··· 6 6 "math/rand" 7 7 "net/http" 8 8 "os" 9 - "strings" 9 + // "strings" 10 10 "time" 11 11 12 12 "arimelody.me/arimelody.me/discord" ··· 59 59 60 60 func MustAuthorise(next http.Handler) http.Handler { 61 61 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 62 + // TEMPORARY 63 + ctx := context.WithValue(r.Context(), "role", "admin") 64 + next.ServeHTTP(w, r.WithContext(ctx)) 65 + return 66 + 67 + /* 62 68 auth := r.Header.Get("Authorization") 63 69 if strings.HasPrefix(auth, "Bearer ") { 64 70 auth = auth[7:] ··· 96 102 return 97 103 } 98 104 99 - ctx := context.WithValue(r.Context(), "token", session.Token) 100 - ctx = context.WithValue(ctx, "role", "admin") 105 + ctx := context.WithValue(r.Context(), "role", "admin") 101 106 next.ServeHTTP(w, r.WithContext(ctx)) 107 + */ 102 108 }) 103 109 } 104 110
+79 -19
api/v1/music/artist.go
··· 1 1 package music 2 2 3 3 import ( 4 - "fmt" 4 + "encoding/json" 5 + "net/http" 5 6 6 7 "github.com/jmoiron/sqlx" 7 8 ) 8 9 9 10 type Artist struct { 10 - id string 11 - name string 12 - website string 11 + ID string `json:"id"` 12 + Name string `json:"name"` 13 + Website string `json:"website"` 13 14 } 14 15 15 16 var Artists []Artist ··· 26 27 // GETTERS 27 28 28 29 func (artist Artist) GetID() string { 29 - return artist.id 30 + return artist.ID 30 31 } 31 32 32 33 func (artist Artist) GetName() string { 33 - return artist.name 34 + return artist.Name 34 35 } 35 36 36 37 func (artist Artist) GetWebsite() string { 37 - return artist.website 38 + return artist.Website 38 39 } 39 40 40 41 // SETTERS 41 42 42 43 func (artist Artist) SetID(id string) error { 43 - artist.id = id 44 + artist.ID = id 44 45 return nil 45 46 } 46 47 47 48 func (artist Artist) SetName(name string) error { 48 - artist.name = name 49 + artist.Name = name 49 50 return nil 50 51 } 51 52 52 53 func (artist Artist) SetWebsite(website string) error { 53 - artist.website = website 54 + artist.Website = website 54 55 return nil 55 56 } 56 57 57 58 // DATABASE 58 59 59 60 func (artist Artist) PushToDB(db *sqlx.DB) { 60 - fmt.Printf("Pushing artist [%s] to database...", artist.name) 61 + // fmt.Printf("Pushing artist [%s] to database...", artist.Name) 61 62 62 - db.MustExec("INSERT INTO artists (id, name, website) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET name=$2, website=$3", 63 - artist.id, 64 - artist.name, 65 - artist.website, 63 + db.MustExec( 64 + "INSERT INTO artists (id, name, website) "+ 65 + "VALUES ($1, $2, $3) "+ 66 + "ON CONFLICT (id) "+ 67 + "DO UPDATE SET name=$2, website=$3", 68 + artist.ID, 69 + artist.Name, 70 + artist.Website, 66 71 ) 67 72 68 - fmt.Printf("done!\n") 73 + // fmt.Printf("done!\n") 69 74 } 70 75 71 76 func PullAllArtists(db *sqlx.DB) ([]Artist, error) { ··· 80 85 var artist = Artist{} 81 86 82 87 err = rows.Scan( 83 - &artist.id, 84 - &artist.name, 85 - &artist.website, 88 + &artist.ID, 89 + &artist.Name, 90 + &artist.Website, 86 91 ) 87 92 if err != nil { 88 93 return nil, err ··· 93 98 94 99 return artists, nil 95 100 } 101 + 102 + // HTTP HANDLERS 103 + 104 + func ServeArtist() http.Handler { 105 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 106 + if r.URL.Path == "/" { 107 + http.NotFound(w, r) 108 + return 109 + } 110 + 111 + type ( 112 + creditJSON struct { 113 + Role string `json:"role"` 114 + Primary bool `json:"primary"` 115 + } 116 + artistJSON struct { 117 + Artist 118 + Credits map[string]creditJSON `json:"credits"` 119 + } 120 + ) 121 + var res = artistJSON{} 122 + 123 + res.ID = r.URL.Path[1:] 124 + var artist = GetArtist(res.ID) 125 + if artist == nil { 126 + http.NotFound(w, r) 127 + return 128 + } 129 + res.Name = artist.Name 130 + res.Website = artist.Website 131 + res.Credits = make(map[string]creditJSON) 132 + 133 + for _, release := range Releases { 134 + for _, credit := range release.Credits { 135 + if credit.Artist.ID != res.ID { 136 + continue 137 + } 138 + res.Credits[release.ID] = creditJSON{ 139 + Role: credit.Role, 140 + Primary: credit.Primary, 141 + } 142 + } 143 + } 144 + 145 + jsonBytes, err := json.Marshal(res) 146 + if err != nil { 147 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 148 + return 149 + } 150 + 151 + w.Header().Add("Content-Type", "application/json") 152 + w.WriteHeader(http.StatusOK) 153 + w.Write(jsonBytes) 154 + }) 155 + }
+13 -13
api/v1/music/credit.go
··· 7 7 ) 8 8 9 9 type Credit struct { 10 - artist *Artist 11 - role string 12 - primary bool 10 + Artist *Artist `json:"artist"` 11 + Role string `json:"role"` 12 + Primary bool `json:"primary"` 13 13 } 14 14 15 15 // GETTERS 16 16 17 17 func (credit Credit) GetArtist() Artist { 18 - return *credit.artist 18 + return *credit.Artist 19 19 } 20 20 21 21 func (credit Credit) GetRole() string { 22 - return credit.role 22 + return credit.Role 23 23 } 24 24 25 25 func (credit Credit) IsPrimary() bool { 26 - return credit.primary 26 + return credit.Primary 27 27 } 28 28 29 29 // SETTERS 30 30 31 31 func (credit Credit) SetArtist(artist *Artist) error { 32 32 // TODO: update DB 33 - credit.artist = artist 33 + credit.Artist = artist 34 34 return nil 35 35 } 36 36 37 37 func (credit Credit) SetRole(role string) error { 38 38 // TODO: update DB 39 - credit.role = role 39 + credit.Role = role 40 40 return nil 41 41 } 42 42 43 43 func (credit Credit) SetPrimary(primary bool) error { 44 44 // TODO: update DB 45 - credit.primary = primary 45 + credit.Primary = primary 46 46 return nil 47 47 } 48 48 ··· 61 61 var credit = Credit{} 62 62 err = credit_rows.Scan( 63 63 &artistID, 64 - &credit.role, 65 - &credit.primary, 64 + &credit.Role, 65 + &credit.Primary, 66 66 ) 67 67 if err != nil { 68 68 fmt.Printf("Error while pulling credit for release %s: %s\n", releaseID, err) 69 69 continue 70 70 } 71 71 72 - credit.artist = GetArtist(artistID) 73 - if credit.artist == nil { 72 + credit.Artist = GetArtist(artistID) 73 + if credit.Artist == nil { 74 74 // this should absolutely not happen ever due to foreign key 75 75 // constraints, but it doesn't hurt to be sure! 76 76 fmt.Printf("Error while pulling credit for release %s: Artist %s does not exist\n", releaseID, artistID)
+9 -9
api/v1/music/link.go
··· 9 9 ) 10 10 11 11 type Link struct { 12 - name string 13 - url string 12 + Name string `json:"name"` 13 + URL string `json:"url"` 14 14 } 15 15 16 16 // GETTERS 17 17 18 18 func (link Link) GetName() string { 19 - return link.name 19 + return link.Name 20 20 } 21 21 22 22 func (link Link) GetURL() string { 23 - return link.url 23 + return link.URL 24 24 } 25 25 26 26 // SETTERS 27 27 28 28 func (link Link) SetName(name string) error { 29 29 // TODO: update DB 30 - link.name = name 30 + link.Name = name 31 31 return nil 32 32 } 33 33 34 34 func (link Link) SetURL(url string) error { 35 35 // TODO: update DB 36 - link.url = url 36 + link.URL = url 37 37 return nil 38 38 } 39 39 ··· 41 41 42 42 func (link Link) NormaliseName() string { 43 43 rgx := regexp.MustCompile(`[^a-z0-9]`) 44 - return strings.ToLower(rgx.ReplaceAllString(link.name, "")) 44 + return strings.ToLower(rgx.ReplaceAllString(link.Name, "")) 45 45 } 46 46 47 47 // DATABASE ··· 58 58 var link = Link{} 59 59 60 60 err = link_rows.Scan( 61 - &link.name, 62 - &link.url, 61 + &link.Name, 62 + &link.URL, 63 63 ) 64 64 if err != nil { 65 65 fmt.Printf("Error while pulling link for release %s: %s\n", releaseID, err)
+61
api/v1/music/music.go
··· 3 3 import ( 4 4 "fmt" 5 5 "net/http" 6 + "os" 7 + "path/filepath" 8 + "strings" 6 9 7 10 "arimelody.me/arimelody.me/api/v1/admin" 8 11 "arimelody.me/arimelody.me/global" ··· 36 39 37 40 func ServeGateway() http.Handler { 38 41 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 42 + if r.URL.Path == "/" { 43 + http.Redirect(w, r, "/music", http.StatusPermanentRedirect) 44 + return 45 + } 46 + 39 47 id := r.URL.Path[1:] 40 48 release := GetRelease(id) 41 49 if release == nil { ··· 60 68 } 61 69 }) 62 70 } 71 + 72 + func ServeArtwork() http.Handler { 73 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 74 + if r.URL.Path == "/" { 75 + http.NotFound(w, r) 76 + return 77 + } 78 + 79 + if !strings.HasSuffix(r.URL.Path, ".png") { 80 + http.NotFound(w, r) 81 + return 82 + } 83 + 84 + releaseID := r.URL.Path[1:len(r.URL.Path) - 4] 85 + var release = GetRelease(releaseID) 86 + if release == nil { 87 + http.NotFound(w, r) 88 + return 89 + } 90 + 91 + // only allow authorised users to view unreleased releases 92 + authorised := r.Context().Value("role") != nil && r.Context().Value("role") == "admin" 93 + if !release.IsReleased() && !authorised { 94 + admin.MustAuthorise(ServeArtwork()).ServeHTTP(w, r) 95 + return 96 + } 97 + 98 + fp := filepath.Join("data", "music-artwork", releaseID + ".png") 99 + fmt.Println(fp) 100 + info, err := os.Stat(fp) 101 + if err != nil { 102 + if os.IsNotExist(err) { 103 + http.NotFound(w, r) 104 + return 105 + } 106 + } 107 + length := info.Size() 108 + 109 + file, err := os.Open(fp) 110 + if err != nil { 111 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 112 + return 113 + } 114 + defer file.Close() 115 + 116 + var bytes = make([]byte, length) 117 + file.Read(bytes) 118 + 119 + w.Header().Add("Content-Type", "image/png") 120 + w.WriteHeader(http.StatusOK) 121 + w.Write(bytes) 122 + }) 123 + }
+163 -86
api/v1/music/release.go
··· 1 1 package music 2 2 3 3 import ( 4 + "encoding/json" 4 5 "errors" 5 6 "fmt" 7 + "net/http" 6 8 "strings" 7 9 "time" 8 10 11 + "arimelody.me/arimelody.me/api/v1/admin" 9 12 "github.com/jmoiron/sqlx" 10 13 ) 11 14 ··· 19 22 ) 20 23 21 24 type Release struct { 22 - id string 23 - title string 24 - releaseType ReleaseType 25 - releaseDate time.Time 26 - artwork string 27 - buyname string 28 - buylink string 29 - links []Link 30 - description string 31 - credits []Credit 32 - tracks []Track 25 + ID string `json:"id"` 26 + Title string `json:"title"` 27 + Description string `json:"description"` 28 + ReleaseType ReleaseType `json:"type"` 29 + ReleaseDate time.Time `json:"releaseDate"` 30 + Artwork string `json:"artwork"` 31 + Buyname string `json:"buyname"` 32 + Buylink string `json:"buylink"` 33 + Links []Link `json:"links"` 34 + Credits []Credit `json:"credits"` 35 + Tracks []Track `json:"tracks"` 33 36 } 34 37 35 38 var Releases []Release; ··· 37 40 // GETTERS 38 41 39 42 func (release Release) GetID() string { 40 - return release.id 43 + return release.ID 41 44 } 42 45 43 46 func (release Release) GetTitle() string { 44 - return release.title 47 + return release.Title 48 + } 49 + 50 + func (release Release) GetDescription() string { 51 + return release.Description 45 52 } 46 53 47 54 func (release Release) GetType() ReleaseType { 48 - return release.releaseType 55 + return release.ReleaseType 49 56 } 50 57 51 58 func (release Release) GetReleaseDate() time.Time { 52 - return release.releaseDate 59 + return release.ReleaseDate 53 60 } 54 61 55 62 func (release Release) GetArtwork() string { 56 - if release.artwork == "" { 57 - return "/img/music-artwork/default.png" 63 + if release.Artwork == "" { 64 + return "/img/default-cover-art.png" 58 65 } 59 - return release.artwork 66 + return release.Artwork 60 67 } 61 68 62 69 func (release Release) GetBuyName() string { 63 - return release.buyname 70 + return release.Buyname 64 71 } 65 72 66 73 func (release Release) GetBuyLink() string { 67 - return release.buylink 74 + return release.Buylink 68 75 } 69 76 70 77 func (release Release) GetLinks() []Link { 71 - return release.links 72 - } 73 - 74 - func (release Release) GetDescription() string { 75 - return release.description 78 + return release.Links 76 79 } 77 80 78 81 func (release Release) GetCredits() []Credit { 79 - return release.credits 82 + return release.Credits 80 83 } 81 84 82 85 func (release Release) GetTracks() []Track { 83 - return release.tracks 86 + return release.Tracks 84 87 } 85 88 86 89 // SETTERS 87 90 88 91 func (release Release) SetID(id string) error { 89 92 // TODO: update DB 90 - release.id = id 93 + release.ID = id 91 94 return nil 92 95 } 93 96 94 97 func (release Release) SetTitle(title string) error { 95 98 // TODO: update DB 96 - release.title = title 99 + release.Title = title 100 + return nil 101 + } 102 + 103 + func (release Release) SetDescription(description string) error { 104 + // TODO: update DB 105 + release.Description = description 97 106 return nil 98 107 } 99 108 100 109 func (release Release) SetType(releaseType ReleaseType) error { 101 110 // TODO: update DB 102 - release.releaseType = releaseType 111 + release.ReleaseType = releaseType 103 112 return nil 104 113 } 105 114 106 115 func (release Release) SetReleaseDate(releaseDate time.Time) error { 107 116 // TODO: update DB 108 - release.releaseDate = releaseDate 117 + release.ReleaseDate = releaseDate 109 118 return nil 110 119 } 111 120 112 121 func (release Release) SetArtwork(artwork string) error { 113 122 // TODO: update DB 114 - release.artwork = artwork 123 + release.Artwork = artwork 115 124 return nil 116 125 } 117 126 118 127 func (release Release) SetBuyName(buyname string) error { 119 128 // TODO: update DB 120 - release.buyname = buyname 129 + release.Buyname = buyname 121 130 return nil 122 131 } 123 132 124 133 func (release Release) SetBuyLink(buylink string) error { 125 134 // TODO: update DB 126 - release.buylink = buylink 135 + release.Buylink = buylink 127 136 return nil 128 137 } 129 138 130 139 func (release Release) SetLinks(links []Link) error { 131 140 // TODO: update DB 132 - release.links = links 133 - return nil 134 - } 135 - 136 - func (release Release) SetDescription(description string) error { 137 - // TODO: update DB 138 - release.description = description 141 + release.Links = links 139 142 return nil 140 143 } 141 144 142 145 func (release Release) SetCredits(credits []Credit) error { 143 146 // TODO: update DB 144 - release.credits = credits 147 + release.Credits = credits 145 148 return nil 146 149 } 147 150 148 151 func (release Release) SetTracks(tracks []Track) error { 149 152 // TODO: update DB 150 - release.tracks = tracks 153 + release.Tracks = tracks 151 154 return nil 152 155 } 153 156 ··· 163 166 } 164 167 165 168 func (release Release) PrintReleaseDate() string { 166 - return release.releaseDate.Format("02 January 2006") 169 + return release.ReleaseDate.Format("02 January 2006") 167 170 } 168 171 169 172 func (release Release) GetReleaseYear() int { 170 - return release.releaseDate.Year() 173 + return release.ReleaseDate.Year() 171 174 } 172 175 173 176 func (release Release) IsSingle() bool { 174 - return len(release.tracks) == 1; 177 + return len(release.Tracks) == 1; 175 178 } 176 179 177 180 func (release Release) IsReleased() bool { 178 - return release.releaseDate.Before(time.Now()) 181 + return release.ReleaseDate.Before(time.Now()) 179 182 } 180 183 181 184 func (release Release) GetUniqueArtists(only_primary bool) []Artist { 182 185 var artists = []Artist{} 183 186 184 - for _, credit := range release.credits { 185 - if only_primary && !credit.primary { 187 + for _, credit := range release.Credits { 188 + if only_primary && !credit.Primary { 186 189 continue 187 190 } 188 191 189 192 exists := false 190 193 for _, a := range artists { 191 - if a.id == credit.artist.id { 194 + if a.ID == credit.Artist.ID { 192 195 exists = true 193 196 break 194 197 } ··· 198 201 continue 199 202 } 200 203 201 - artists = append(artists, *credit.artist) 204 + artists = append(artists, *credit.Artist) 202 205 } 203 206 204 207 return artists ··· 234 237 // DATABASE 235 238 236 239 func (release Release) PushToDB(db *sqlx.DB) error { 237 - // fmt.Printf("Pushing release [%s] to database...", release.id) 240 + // fmt.Printf("Pushing release [%s] to database...", release.ID) 238 241 239 242 tx, err := db.Begin() 240 243 if err != nil { 241 244 return errors.New(fmt.Sprintf("Failed to initiate transaction: %s", err)) 242 245 } 243 246 244 - _, err = tx.Exec("INSERT INTO musicreleases (id, title, type, release_date, artwork, buyname, buylink) VALUES ($1, $2, $3, $4, $5, $6, $7) "+ 245 - "ON CONFLICT (id) DO UPDATE SET title=$2, type=$3, release_date=$4, artwork=$5, buyname=$6, buylink=$7", 246 - release.id, release.title, release.releaseType, release.releaseDate.Format("2-Jan-2006"), release.artwork, release.buyname, release.buylink) 247 + _, err = tx.Exec( 248 + "INSERT INTO musicreleases (id, title, description, type, release_date, artwork, buyname, buylink) "+ 249 + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8) "+ 250 + "ON CONFLICT (id) "+ 251 + "DO UPDATE SET title=$2, description=$3, type=$4, release_date=$5, artwork=$6, buyname=$7, buylink=$8", 252 + release.ID, 253 + release.Title, 254 + release.Description, 255 + release.ReleaseType, 256 + release.ReleaseDate.Format("2-Jan-2006"), 257 + release.Artwork, 258 + release.Buyname, 259 + release.Buylink, 260 + ) 247 261 248 - for _, link := range release.links { 262 + for _, link := range release.Links { 249 263 _, err = tx.Exec( 250 264 "INSERT INTO musiclinks (release, name, url) "+ 251 265 "VALUES ($1, $2, $3) "+ 252 266 "ON CONFLICT (release, name) "+ 253 267 "DO UPDATE SET url=$3 ", 254 - release.id, 255 - link.name, 256 - link.url, 268 + release.ID, 269 + link.Name, 270 + link.URL, 257 271 ) 258 272 if err != nil { 259 273 return errors.New(fmt.Sprintf("Failed to add music link to transaction: %s", err)) 260 274 } 261 275 } 262 - for _, credit := range release.credits { 276 + for _, credit := range release.Credits { 263 277 _, err = tx.Exec( 264 278 "INSERT INTO musiccredits (release, artist, role, is_primary) "+ 265 279 "VALUES ($1, $2, $3, $4) "+ 266 280 "ON CONFLICT (release, artist) "+ 267 281 "DO UPDATE SET role=$3, is_primary=$4", 268 - release.id, 269 - credit.artist.id, 270 - credit.role, 271 - credit.primary, 282 + release.ID, 283 + credit.Artist.ID, 284 + credit.Role, 285 + credit.Primary, 272 286 ) 273 287 if err != nil { 274 288 return errors.New(fmt.Sprintf("Failed to add music credit to transaction: %s", err)) 275 289 } 276 290 } 277 - for _, track := range release.tracks { 291 + for _, track := range release.Tracks { 278 292 _, err = tx.Exec( 279 293 "INSERT INTO musictracks (release, number, title, description, lyrics, preview_url) "+ 280 294 "VALUES ($1, $2, $3, $4, $5, $6) "+ 281 295 "ON CONFLICT (release, number) "+ 282 296 "DO UPDATE SET title=$3, description=$4, lyrics=$5, preview_url=$6", 283 - release.id, 284 - track.number, 285 - track.title, 286 - track.description, 287 - track.lyrics, 288 - track.previewURL, 297 + release.ID, 298 + track.Number, 299 + track.Title, 300 + track.Description, 301 + track.Lyrics, 302 + track.PreviewURL, 289 303 ) 290 304 if err != nil { 291 305 return errors.New(fmt.Sprintf("Failed to add music track to transaction: %s", err)) ··· 310 324 return errors.New(fmt.Sprintf("Failed to initiate transaction: %s", err)) 311 325 } 312 326 313 - _, err = tx.Exec("DELETE FROM musicreleases WHERE id=$1", release.id) 327 + _, err = tx.Exec("DELETE FROM musicreleases WHERE id=$1", release.ID) 314 328 315 329 err = tx.Commit() 316 330 if err != nil { ··· 332 346 var release = Release{} 333 347 334 348 err = rows.Scan( 335 - &release.id, 336 - &release.title, 337 - &release.description, 338 - &release.releaseType, 339 - &release.releaseDate, 340 - &release.artwork, 341 - &release.buyname, 342 - &release.buylink, 349 + &release.ID, 350 + &release.Title, 351 + &release.Description, 352 + &release.ReleaseType, 353 + &release.ReleaseDate, 354 + &release.Artwork, 355 + &release.Buyname, 356 + &release.Buylink, 343 357 ) 344 358 if err != nil { 345 359 fmt.Printf("Error while pulling a release: %s\n", err) 346 360 continue 347 361 } 348 362 349 - release.credits, err = PullReleaseCredits(db, release.id) 363 + release.Credits, err = PullReleaseCredits(db, release.ID) 350 364 if err != nil { 351 - fmt.Printf("Failed to pull credits for %s: %v\n", release.id, err) 365 + fmt.Printf("Failed to pull credits for %s: %v\n", release.ID, err) 352 366 } 353 367 354 - release.links, err = PullReleaseLinks(db, release.id) 368 + release.Links, err = PullReleaseLinks(db, release.ID) 355 369 if err != nil { 356 - fmt.Printf("Failed to pull links for %s: %v\n", release.id, err) 370 + fmt.Printf("Failed to pull links for %s: %v\n", release.ID, err) 357 371 } 358 372 359 - release.tracks, err = PullReleaseTracks(db, release.id) 373 + release.Tracks, err = PullReleaseTracks(db, release.ID) 360 374 if err != nil { 361 - return nil, errors.New(fmt.Sprintf("error pulling tracks for %s: %v\n", release.id, err)) 375 + return nil, errors.New(fmt.Sprintf("error pulling tracks for %s: %v\n", release.ID, err)) 362 376 } 363 377 364 378 releases = append(releases, release) ··· 366 380 367 381 return releases, nil 368 382 } 383 + 384 + // HTTP HANDLERS 385 + 386 + func ServeRelease() http.Handler { 387 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 388 + if r.URL.Path == "/" { 389 + http.NotFound(w, r) 390 + return 391 + } 392 + 393 + releaseID := r.URL.Path[1:] 394 + var release = GetRelease(releaseID) 395 + if release == nil { 396 + http.NotFound(w, r) 397 + return 398 + } 399 + 400 + // only allow authorised users to view unreleased releases 401 + authorised := r.Context().Value("role") != nil && r.Context().Value("role") == "admin" 402 + if !release.IsReleased() && !authorised { 403 + admin.MustAuthorise(ServeRelease()).ServeHTTP(w, r) 404 + return 405 + } 406 + 407 + jsonBytes, err := json.Marshal(release) 408 + if err != nil { 409 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 410 + return 411 + } 412 + 413 + w.Header().Add("Content-Type", "application/json") 414 + w.WriteHeader(http.StatusOK) 415 + w.Write(jsonBytes) 416 + }) 417 + } 418 + 419 + func PostRelease() http.Handler { 420 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 421 + if r.Method != http.MethodPost { 422 + http.NotFound(w, r) 423 + return 424 + } 425 + 426 + var release Release 427 + err := json.NewDecoder(r.Body).Decode(&release) 428 + if err != nil { 429 + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) 430 + return 431 + } 432 + 433 + if GetRelease(release.ID) != nil { 434 + http.Error(w, fmt.Sprintf("Release %s already exists", release.ID), http.StatusBadRequest) 435 + return 436 + } 437 + 438 + Releases = append(Releases, release) 439 + 440 + jsonBytes, err := json.Marshal(release) 441 + w.Header().Add("Content-Type", "application/json") 442 + w.WriteHeader(http.StatusCreated) 443 + w.Write(jsonBytes) 444 + }) 445 + }
+20 -20
api/v1/music/track.go
··· 7 7 ) 8 8 9 9 type Track struct { 10 - number int 11 - title string 12 - description string 13 - lyrics string 14 - previewURL string 10 + Number int `json:"number"` 11 + Title string `json:"title"` 12 + Description string `json:"description"` 13 + Lyrics string `json:"lyrics"` 14 + PreviewURL string `json:"previewURL"` 15 15 } 16 16 17 17 // GETTERS 18 18 19 19 func (track Track) GetNumber() int { 20 - return track.number 20 + return track.Number 21 21 } 22 22 23 23 func (track Track) GetTitle() string { 24 - return track.title 24 + return track.Title 25 25 } 26 26 27 27 func (track Track) GetDescription() string { 28 - return track.description 28 + return track.Description 29 29 } 30 30 31 31 func (track Track) GetLyrics() string { 32 - return track.lyrics 32 + return track.Lyrics 33 33 } 34 34 35 35 func (track Track) GetPreviewURL() string { 36 - return track.previewURL 36 + return track.PreviewURL 37 37 } 38 38 39 39 // SETTERS 40 40 41 41 func (track Track) SetNumber(number int) error { 42 42 // TODO: update DB 43 - track.number = number 43 + track.Number = number 44 44 return nil 45 45 } 46 46 47 47 func (track Track) SetTitle(title string) error { 48 48 // TODO: update DB 49 - track.title = title 49 + track.Title = title 50 50 return nil 51 51 } 52 52 53 53 func (track Track) SetDescription(description string) error { 54 54 // TODO: update DB 55 - track.description = description 55 + track.Description = description 56 56 return nil 57 57 } 58 58 59 59 func (track Track) SetLyrics(lyrics string) error { 60 60 // TODO: update DB 61 - track.lyrics = lyrics 61 + track.Lyrics = lyrics 62 62 return nil 63 63 } 64 64 65 65 func (track Track) SetPreviewURL(previewURL string) error { 66 66 // TODO: update DB 67 - track.previewURL = previewURL 67 + track.PreviewURL = previewURL 68 68 return nil 69 69 } 70 70 ··· 82 82 var track = Track{} 83 83 84 84 err = track_rows.Scan( 85 - &track.number, 86 - &track.title, 87 - &track.description, 88 - &track.lyrics, 89 - &track.previewURL, 85 + &track.Number, 86 + &track.Title, 87 + &track.Description, 88 + &track.Lyrics, 89 + &track.PreviewURL, 90 90 ) 91 91 if err != nil { 92 92 fmt.Printf("Error while pulling track for release %s: %s\n", releaseID, err)
+8 -3
main.go
··· 46 46 47 47 mux.Handle("/api/v1/admin/", global.HTTPLog(http.StripPrefix("/api/v1/admin", admin.Handler()))) 48 48 49 + mux.Handle("/api/v1/music/artist/", global.HTTPLog(http.StripPrefix("/api/v1/music/artist", music.ServeArtist()))) 50 + mux.Handle("/api/v1/music/", global.HTTPLog(http.StripPrefix("/api/v1/music", music.ServeRelease()))) 51 + mux.Handle("/api/v1/music", global.HTTPLog(music.PostRelease())) 52 + 53 + mux.Handle("/music-artwork/", global.HTTPLog(http.StripPrefix("/music-artwork", music.ServeArtwork()))) 49 54 mux.Handle("/music/", global.HTTPLog(http.StripPrefix("/music", music.ServeGateway()))) 50 55 mux.Handle("/music", global.HTTPLog(music.ServeCatalog())) 51 56 ··· 54 59 global.ServeTemplate("index.html", nil).ServeHTTP(w, r) 55 60 return 56 61 } 57 - staticHandler().ServeHTTP(w, r) 62 + staticHandler("public").ServeHTTP(w, r) 58 63 }))) 59 64 60 65 return mux 61 66 } 62 67 63 - func staticHandler() http.Handler { 68 + func staticHandler(directory string) http.Handler { 64 69 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 65 - info, err := os.Stat(filepath.Join("public", filepath.Clean(r.URL.Path))) 70 + info, err := os.Stat(filepath.Join(directory, filepath.Clean(r.URL.Path))) 66 71 // does the file exist? 67 72 if err != nil { 68 73 if os.IsNotExist(err) {
public/img/music-artwork/default.png public/img/default-cover-art.png
+6
public/style/music-gateway.css
··· 453 453 background-color: var(--primary); 454 454 } 455 455 456 + #tracks summary::marker, 457 + #tracks summary::-webkit-details-marker { 458 + content: ""; 459 + display: none; 460 + } 461 + 456 462 footer { 457 463 position: fixed; 458 464 left: 0;
+4 -4
schema.sql
··· 61 61 -- Foreign keys 62 62 -- 63 63 64 - ALTER TABLE public.musiccredits ADD CONSTRAINT IF NOT EXISTS musiccredits_artist_fk FOREIGN KEY (artist) REFERENCES public.artists(id) ON DELETE CASCADE; 64 + ALTER TABLE musiccredits ADD CONSTRAINT IF NOT EXISTS musiccredits_artist_fk FOREIGN KEY (artist) REFERENCES artists(id) ON DELETE CASCADE ON UPDATE CASCADE; 65 65 66 - ALTER TABLE public.musiccredits ADD CONSTRAINT IF NOT EXISTS musiccredits_release_fk FOREIGN KEY (release) REFERENCES public.musicreleases(id) ON DELETE CASCADE; 66 + ALTER TABLE musiccredits ADD CONSTRAINT IF NOT EXISTS musiccredits_release_fk FOREIGN KEY (release) REFERENCES musicreleases(id) ON DELETE CASCADE; 67 67 68 - ALTER TABLE public.musiclinks ADD CONSTRAINT IF NOT EXISTS musiclinks_release_fk FOREIGN KEY (release) REFERENCES public.musicreleases(id) ON UPDATE CASCADE ON DELETE CASCADE; 68 + ALTER TABLE musiclinks ADD CONSTRAINT IF NOT EXISTS musiclinks_release_fk FOREIGN KEY (release) REFERENCES musicreleases(id) ON UPDATE CASCADE ON DELETE CASCADE; 69 69 70 - ALTER TABLE public.musictracks ADD CONSTRAINT IF NOT EXISTS musictracks_release_fk FOREIGN KEY (release) REFERENCES public.musicreleases(id) ON DELETE CASCADE; 70 + ALTER TABLE musictracks ADD CONSTRAINT IF NOT EXISTS musictracks_release_fk FOREIGN KEY (release) REFERENCES musicreleases(id) ON DELETE CASCADE;
+3 -5
views/music-gateway.html
··· 109 109 {{else}} 110 110 <div id="tracks"> 111 111 <h2>tracks:</h2> 112 - {{range .GetTracks}} 112 + {{range $i, $track := .GetTracks}} 113 113 <details> 114 - <summary class="album-track-title">{{.GetTitle}}</summary> 115 - {{.GetLyrics}} 114 + <summary class="album-track-title">{{$i}}. {{$track.GetTitle}}</summary> 115 + {{$track.GetLyrics}} 116 116 </details> 117 117 {{end}} 118 118 </div> 119 119 {{end}} 120 120 </div> 121 121 122 - {{if or .GetCredits not .IsSingle}} 123 122 <div id="extras"> 124 123 <ul> 125 124 <li><a href="#overview">overview</a></li> ··· 138 137 {{end}} 139 138 </ul> 140 139 </div> 141 - {{end}} 142 140 <!-- <div id="tracks"> --> 143 141 <!-- <% var file = `/audio/preview/${data.id}.webm` %> --> 144 142 <!-- <% if (data.tracks && typeof(data.tracks) == typeof([])) { %> -->