package main import ( "bytes" "encoding/json" "flag" "fmt" "git.gammaspectra.live/S.O.N.G/Kirika/audio" "io/ioutil" "log" "net/http" "strconv" "strings" "sync" ) func getQueueEntryFromBody(body []byte) *QueueTrackEntry { entry := &QueueTrackEntry{} err := json.Unmarshal(body, &entry.original) if err != nil { return nil } err = json.Unmarshal(body, &entry.Metadata) if err != nil { return nil } var val interface{} var strVal string var ok bool if val, ok = entry.original["path"]; ok { if strVal, ok = val.(string); ok { entry.Path = strVal } } if len(entry.Path) > 0 { return entry } return nil } type resultResponse struct { Success bool `json:"success"` Reason error `json:"reason"` } type queueResultResponse struct { resultResponse QueueId audio.QueueIdentifier `json:"queue_id"` } func main() { configPath := flag.String("config", "config.toml", "Config path") flag.Parse() config, err := GetConfig(*configPath) if err != nil { log.Panic(err) } var wg sync.WaitGroup queue := NewQueue(config) var nr *QueueTrackEntry getRandomTrack := func() *QueueTrackEntry { response, err := http.DefaultClient.Get(config.Queue.RandomSongApi) if err != nil { return nil } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return nil } return getQueueEntryFromBody(body) } getFallbackTrack := func() *QueueTrackEntry { m := make(map[string]interface{}) m["path"] = config.Queue.FallbackPath return &QueueTrackEntry{ Path: config.Queue.FallbackPath, original: m, } } sendNowRandom := func(nr *QueueTrackEntry) { if config.Queue.NowRandom != "" { jsonData, _ := json.Marshal(nr.original) response, err := http.DefaultClient.Post(config.Queue.NowRandom, "application/json; charset=utf-8", bytes.NewReader(jsonData)) if err != nil { log.Print(err) } if response != nil { defer response.Body.Close() } } } wg.Add(1) go func() { defer wg.Done() defer close(queue.QueueEmpty) //TODO: close properly for { if e := getRandomTrack(); e != nil { queue.QueueEmpty <- e nr = e sendNowRandom(nr) } else if e = getFallbackTrack(); e != nil { queue.QueueEmpty <- e nr = e sendNowRandom(nr) } } }() wg.Add(1) go func() { defer wg.Done() for np := range queue.NowPlaying { jsonData, _ := json.Marshal(np.original) response, err := http.DefaultClient.Post(config.Queue.NowPlaying, "application/json; charset=utf-8", bytes.NewReader(jsonData)) if err != nil { log.Print(err) } if response != nil { response.Body.Close() } } }() wg.Add(1) go func() { defer wg.Done() server := http.Server{ Addr: fmt.Sprintf("%s:%d", config.Api.Host, config.Api.Port), Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Server", "MeteorLight/api") writer.Header().Set("Content-Type", "application/json; charset=utf-8") pathSegments := strings.Split(request.URL.Path, "/") if len(pathSegments) > 1 { switch pathSegments[1] { case "listeners": jsonData, _ := json.Marshal(queue.GetListeners()) writer.Write(jsonData) return case "np": if e := queue.GetNowPlaying(); e != nil { jsonData, _ := json.Marshal(e.original) writer.Write(jsonData) } else { writer.Write([]byte{'{', '}'}) } return case "random": if nr != nil { jsonData, _ := json.Marshal(nr.original) writer.Write(jsonData) } else { writer.Write([]byte{'{', '}'}) } return case "queue": if len(pathSegments) == 2 { if request.Method != "GET" { return } var blobs = make([]map[string]interface{}, 0, 1) for _, e := range queue.GetQueue() { blobs = append(blobs, e.original) } jsonData, _ := json.Marshal(blobs) writer.Write(jsonData) return } else { switch pathSegments[2] { case "head": if request.Method == "POST" { result := queueResultResponse{} if body, err := ioutil.ReadAll(request.Body); err == nil { if e := getQueueEntryFromBody(body); e != nil { if err = queue.AddTrack(e, false); err == nil { result.Success = true result.QueueId = e.QueueIdentifier } } } jsonData, _ := json.Marshal(result) writer.Write(jsonData) return } else if request.Method == "DELETE" { result := resultResponse{} if head := queue.GetHead(); head != nil { result.Success = queue.Remove(head.QueueIdentifier) } jsonData, _ := json.Marshal(result) writer.Write(jsonData) return } case "tail": if request.Method == "POST" { result := queueResultResponse{} if body, err := ioutil.ReadAll(request.Body); err == nil { if e := getQueueEntryFromBody(body); e != nil { if err = queue.AddTrack(e, true); err == nil { result.Success = true result.QueueId = e.QueueIdentifier } } } jsonData, _ := json.Marshal(result) writer.Write(jsonData) return } else if request.Method == "DELETE" { result := resultResponse{} if head := queue.GetTail(); head != nil { result.Success = queue.Remove(head.QueueIdentifier) } jsonData, _ := json.Marshal(result) writer.Write(jsonData) return } case "clear": if request.Method != "POST" { return } result := resultResponse{} for _, e := range queue.GetQueue() { queue.Remove(e.QueueIdentifier) } result.Success = true jsonData, _ := json.Marshal(result) writer.Write(jsonData) return default: if request.Method != "POST" { return } if i, err := strconv.ParseInt(pathSegments[2], 10, 0); err == nil { result := resultResponse{} result.Success = queue.Remove(audio.QueueIdentifier(i)) jsonData, _ := json.Marshal(result) writer.Write(jsonData) return } } } case "skip": if request.Method != "POST" { return } result := resultResponse{} result.Success = queue.SkipNowPlaying() jsonData, _ := json.Marshal(result) writer.Write(jsonData) return } } writer.WriteHeader(http.StatusNotFound) writer.Write([]byte{'{', '}'}) }), } if err := server.ListenAndServe(); err != nil { log.Panic(err) } }() wg.Add(1) go func() { defer wg.Done() server := http.Server{ Addr: fmt.Sprintf(":%d", config.Radio.Port), Handler: http.HandlerFunc(queue.HandleRadioRequest), } if err := server.ListenAndServe(); err != nil { log.Panic(err) } }() if e := getRandomTrack(); e != nil { queue.AddTrack(e, false) } else if e = getFallbackTrack(); e != nil { queue.AddTrack(e, false) } wg.Wait() queue.Wait() }