This is represented in the same way as CID fields, so use the same data type. Signed-off-by: Yonah <contrib@gensokyo.uk>
188 lines
8.1 KiB
Go
188 lines
8.1 KiB
Go
package main
|
||
|
||
import (
|
||
"flag"
|
||
"log"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"git.gensokyo.uk/yonah/monstersirenfetch"
|
||
)
|
||
|
||
var (
|
||
flagMetadataPath string
|
||
flagAutoplay int
|
||
)
|
||
|
||
func init() {
|
||
flag.StringVar(&flagMetadataPath, "m", "data/metadata", "Path to enriched metadata")
|
||
flag.IntVar(&flagAutoplay, "autoplay", 48794, "Value to set for autoplay field in /api/songs")
|
||
}
|
||
|
||
func handleAPINew(metadata *monstersirenfetch.Metadata) http.HandlerFunc {
|
||
if metadata == nil {
|
||
log.Fatal("invalid metadata")
|
||
}
|
||
|
||
const (
|
||
// TODO(ophestra): stub these out with local artwork
|
||
fontsetResp = `{"code":0,"msg":"","data":{"Sans-Regular":{"tt":"/static/SourceHanSansCN-Regular.ttf","eot":"/static/SourceHanSansCN-Regular.eot","svg":"/static/SourceHanSansCN-Regular.svg","woff":"/static/SourceHanSansCN-Regular.woff"},"Sans-Bold":{"tt":"/static/SourceHanSansCN-Bold.ttf","eot":"/static/SourceHanSansCN-Bold.eot","svg":"/static/SourceHanSansCN-Bold.svg","woff":"/static/SourceHanSansCN-Bold.woff"}}}`
|
||
recommendsResp = `{"code":0,"msg":"","data":[{"title":"#AUS小屋","coverUrl":"https://web.hycdn.cn/siren/pic/20230228/ba2bf4c42b3852a8b35e9721a8c8bbb7.jpg","cover":{"private":false,"path":"siren/pic/20230228/ba2bf4c42b3852a8b35e9721a8c8bbb7.jpg"},"description":"\"嗨,各位,这个冬天过得怎么样?\n我们一直呆在室内,眼看着春天到来,再不出门,雪就要化了,于是大家都被dan拉着去比赛堆雪人。虽然能动弹动弹是很不错,但“这是为了阻止alty祸害厨房”这句话就很多余了不是吗!\n我和frost堆到一半,决定给雪人戴一个耳朵头箍,frost还把围巾也围上去了,造就了一个可爱的卡特斯。就在我们以为赢定了的时候,回头看见dan正站在一个巨大的雪人旁边手舞足蹈。呃,不得不说气势很惊人,有一瞬间甘拜下风的念头。\n但是看了看被惊到的aya旁边的……小雪人,我觉得我和frost的雪人至少","type":2,"data":"750452"},{"title":"#EMPEROR","coverUrl":"https://web.hycdn.cn/siren/pic/20230122/89969a75098c07cd8e289bad831ea4a2.jpg","cover":{"private":false,"path":"siren/pic/20230122/89969a75098c07cd8e289bad831ea4a2.jpg"},"description":"","type":2,"data":"578833"},{"title":"#AUS小屋","coverUrl":"https://web.hycdn.cn/siren/pic/20221128/ede2a04423632c17b702a656414a8c04.jpg","cover":{"private":false,"path":"siren/pic/20221128/ede2a04423632c17b702a656414a8c04.jpg"},"description":"一起用被炉(有点热!)","type":2,"data":"750452"},{"title":"#AUS小屋","coverUrl":"https://web.hycdn.cn/siren/pic/20220930/f4c7886eabcc1f3f178e7d4a9e5f7a21.jpg","cover":{"private":false,"path":"siren/pic/20220930/f4c7886eabcc1f3f178e7d4a9e5f7a21.jpg"},"description":"","type":2,"data":"336217"},{"title":"#D.D.D.PHOTO","coverUrl":"https://web.hycdn.cn/siren/pic/20221228/93393e28e1d0caff9b61c951eaf6a5f5.jpg","cover":{"private":false,"path":"siren/pic/20221228/93393e28e1d0caff9b61c951eaf6a5f5.jpg"},"description":"","type":2,"data":"241307"},{"title":"live演出","coverUrl":"https://web.hycdn.cn/siren/pic/20230427/b524e7b99982a972609de43998f61b46.jpg","cover":{"private":false,"path":"siren/pic/20230427/b524e7b99982a972609de43998f61b46.jpg"},"description":"\"春天好,各位。AUS与仲春一起归来了。\n今天我们刚结束了新live的最后一场演出。我站在舞台中央,被音浪和节奏包围着,即使晃眼的灯光遮挡了视线,也能感受到大家在台下拼命挥舞双手,这同样让我很激动。\n我唱哑了嗓子,alty弹到指头隐隐作痛,frost不时就会揉揉手腕,一向精力充沛的dan回工作室后也累瘫了,但我们一路笑得很大声,连frost也在旁边勾着嘴角。毕竟对于创作者来说,欣赏者的热爱足以融化春寒和创作的倦怠,对吧?\"","type":2,"data":"605965"}]}`
|
||
)
|
||
|
||
var (
|
||
fontsetRespData = []byte(fontsetResp)
|
||
recommendsRespData = []byte(recommendsResp)
|
||
)
|
||
|
||
var albumsRespData []byte
|
||
{
|
||
resp := monstersirenfetch.AlbumsResponse{Data: make(monstersirenfetch.AlbumsData, len(metadata.Albums))}
|
||
for i := range metadata.Albums {
|
||
if !metadata.Albums[i].Copy(&resp.Data[i], monstersirenfetch.AlbumVariantBase) {
|
||
log.Fatal("cannot copy album metadata for base variant")
|
||
}
|
||
}
|
||
albumsRespData = mustMarshalJSON(resp)
|
||
}
|
||
|
||
// data, detail
|
||
albumCidData := make(map[int][2][]byte, len(metadata.Albums))
|
||
for i := range metadata.Albums {
|
||
var (
|
||
a = &metadata.Albums[i]
|
||
v monstersirenfetch.AlbumResponse
|
||
d [2][]byte
|
||
)
|
||
|
||
if !a.Copy(&v.Data, monstersirenfetch.AlbumVariantData) {
|
||
log.Fatal("cannot copy album metadata for data variant")
|
||
}
|
||
d[0] = mustMarshalJSON(&v)
|
||
|
||
if !a.Copy(&v.Data, monstersirenfetch.AlbumVariantDetail) {
|
||
log.Fatal("cannot copy album metadata for detail variant")
|
||
}
|
||
d[1] = mustMarshalJSON(&v)
|
||
|
||
albumCidData[int(a.CID)] = d
|
||
}
|
||
if len(albumCidData) != len(metadata.Albums) {
|
||
log.Fatalf("album has duplicate cid: %d != %d", len(albumCidData), len(metadata.Albums))
|
||
}
|
||
|
||
var songsRespData []byte
|
||
{
|
||
resp := monstersirenfetch.SongsResponse{Data: monstersirenfetch.SongsData{
|
||
List: make([]monstersirenfetch.Song, len(metadata.Songs)),
|
||
Autoplay: monstersirenfetch.StringInt(flagAutoplay),
|
||
}}
|
||
for i := range metadata.Songs {
|
||
if !metadata.Songs[i].Copy(&resp.Data.List[i], monstersirenfetch.SongVariantBase) {
|
||
log.Fatal("cannot copy song metadata for base variant")
|
||
}
|
||
}
|
||
songsRespData = mustMarshalJSON(resp)
|
||
}
|
||
|
||
songsCidData := make(map[int][]byte, len(metadata.Songs))
|
||
for i := range metadata.Songs {
|
||
var (
|
||
s = metadata.Songs[i]
|
||
v monstersirenfetch.SongResponse
|
||
)
|
||
|
||
if !s.Copy(&v.Data, monstersirenfetch.SongVariantFull) {
|
||
log.Fatal("cannot copy song metadata for full variant")
|
||
}
|
||
songsCidData[int(s.CID)] = mustMarshalJSON(&v)
|
||
}
|
||
if len(songsCidData) != len(metadata.Songs) {
|
||
log.Fatalf("song has duplicate cid: %d != %d", len(songsCidData), len(metadata.Songs))
|
||
}
|
||
|
||
log.Printf("loaded %d albums and %d songs", len(metadata.Albums), len(metadata.Songs))
|
||
return func(writer http.ResponseWriter, request *http.Request) {
|
||
if request.URL == nil {
|
||
log.Printf("got invalid request %p", request)
|
||
return
|
||
}
|
||
|
||
const (
|
||
prefix = "/api/"
|
||
endpointAlbum = prefix + "album/"
|
||
endpointSong = prefix + "song/"
|
||
|
||
endpointAlbumSuffixData = "/data"
|
||
endpointAlbumSuffixDetail = "/detail"
|
||
)
|
||
|
||
switch request.URL.Path {
|
||
case prefix + "fontset":
|
||
writeResp(writer, fontsetRespData)
|
||
|
||
case prefix + "recommends":
|
||
writeResp(writer, recommendsRespData)
|
||
|
||
case prefix + "albums":
|
||
writeResp(writer, albumsRespData)
|
||
|
||
case prefix + "songs":
|
||
writeResp(writer, songsRespData)
|
||
|
||
default:
|
||
if strings.HasPrefix(request.URL.Path, endpointAlbum) {
|
||
v := request.URL.Path[len(endpointAlbum):]
|
||
|
||
detail := strings.HasSuffix(v, endpointAlbumSuffixDetail)
|
||
if detail {
|
||
v = v[:len(v)-len(endpointAlbumSuffixDetail)]
|
||
} else if strings.HasSuffix(v, endpointAlbumSuffixData) {
|
||
v = v[:len(v)-len(endpointAlbumSuffixData)]
|
||
} else {
|
||
writer.WriteHeader(http.StatusNotFound)
|
||
writeResp(writer, []byte("unsupported album endpoint"))
|
||
return
|
||
}
|
||
|
||
if c, err := strconv.Atoi(v); err != nil {
|
||
writer.WriteHeader(http.StatusBadRequest)
|
||
writeResp(writer, []byte("invalid cid"))
|
||
return
|
||
} else if albumData, ok := albumCidData[c]; !ok {
|
||
writer.WriteHeader(http.StatusNotFound)
|
||
writeResp(writer, []byte("not found"))
|
||
return
|
||
} else if detail {
|
||
writeResp(writer, albumData[1])
|
||
return
|
||
} else {
|
||
writeResp(writer, albumData[0])
|
||
return
|
||
}
|
||
}
|
||
|
||
if strings.HasPrefix(request.URL.Path, endpointSong) {
|
||
v := request.URL.Path[len(endpointSong):]
|
||
if c, err := strconv.Atoi(v); err != nil {
|
||
writer.WriteHeader(http.StatusBadRequest)
|
||
writeResp(writer, []byte("invalid cid"))
|
||
return
|
||
} else if songData, ok := songsCidData[c]; !ok {
|
||
writer.WriteHeader(http.StatusNotFound)
|
||
writeResp(writer, []byte("not found"))
|
||
return
|
||
} else {
|
||
writeResp(writer, songData)
|
||
return
|
||
}
|
||
}
|
||
|
||
verboseln("api endpoint", request.URL.Path, "not implemented")
|
||
writer.WriteHeader(http.StatusNotFound)
|
||
writeResp(writer, []byte("null"))
|
||
}
|
||
}
|
||
}
|