Yonah 5fe911dd01
songs: represent autoplay as integer
This is represented in the same way as CID fields, so use the same data type.

Signed-off-by: Yonah <contrib@gensokyo.uk>
2025-09-19 20:50:02 +09:00

188 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"))
}
}
}