func constructSongInfo(songDetails mpd.Attrs, m *MpdBackend) *musebot.SongInfo { length, _ := strconv.ParseInt(songDetails["Time"], 10, 0) musicUrl := songDetails["file"] if songDetails["file"][0:7] != "file://" { musicUrl = "file://" + m.musicDir + musicUrl } //stickermap, _ := m.client.StickerGet("song", songDetails["file"], "coverarturl") stickermap_b, _ := m.client.StickerGet("song", songDetails["file"], "songinfo") si := musebot.SongInfo{} //log.Println(si, stickermap_b["songinfo"]) err := json.Unmarshal([]byte(stickermap_b["songinfo"]), &si) if err == nil { var wasOk bool sdp, wasOk := si.ProviderName.(string) if wasOk { si.Provider, wasOk = musebot.CurrentProviders[sdp] } } else { si.ProviderName = "<<LOCAL>>" si.Title = songDetails["Title"] si.Album = songDetails["Album"] si.Artist = songDetails["Artist"] } si.MusicUrl = musicUrl si.Length = int(length) si.Id = songDetails["Id"] return &si }
func (m *MpdBackend) Add(s musebot.SongInfo) error { m.ifNotPlayingEmptyQueue() // here goes path := s.MusicUrl // this should be a local filesystem path by now... if len(path) == 0 || path[0] != '/' { err := errors.New("MpdBackend: path invalid for song with path " + path) log.Println("Error adding song to queue: non-absolute path!", err) return err } if !strings.HasPrefix(path, m.musicDir) { hash := sha256.New() hash.Write([]byte(s.MusicUrl)) // we need the suffix endBit := strings.LastIndex(path, ".") newPath := m.musicDir + "/zz_" + hex.EncodeToString(hash.Sum([]byte{})) + path[endBit:] os.Symlink(path, newPath) s.MusicUrl = newPath } s.MusicUrl = s.MusicUrl[len(m.musicDir):] log.Println(s) log.Println(s.MusicUrl) // defer this defer m.forcePlayback() s.ProviderName = s.Provider.PackageName() s.Provider = nil jsonS, err := json.Marshal(s) m.client.Update(s.MusicUrl) m.client.StickerSet("song", s.MusicUrl, "coverarturl", s.CoverArtUrl) if err == nil { m.client.StickerSet("song", s.MusicUrl, "songinfo", string(jsonS)) } return m.client.Add(s.MusicUrl) }
func (p *GroovesharkProvider) FetchSong(song *musebot.SongInfo, comms chan musebot.ProviderMessage) { // this should be run inside a goroutine :P // here goes if song.ProviderName != p.PackageName() { // what. the. hell. comms <- musebot.ProviderMessage{"error", errors.New("Song was not from this provider!")} } downloadLocation := p.cacheDir + "/" + (song.ProviderId) + ".mp3" if c, _ := doesFileExist(downloadLocation); !c { // GO GO GO params := map[string]interface{}{} params["mobile"] = false params["prefetch"] = false params["songID"] = song.ProviderId params["country"] = p.info.headers["country"] comms <- musebot.ProviderMessage{"stages", 1} comms <- musebot.ProviderMessage{"current_stage", 1} comms <- musebot.ProviderMessage{"current_stage_description", "Downloading file..."} res, err := p.apiCall("getStreamKeyFromSongIDEx", params) if err != nil { comms <- musebot.ProviderMessage{"error", err} return } // now we need two pieces of iformation: // the streamKey, and the ip _, didFail := ((res.(map[string]interface{}))["result"]).([]interface{}) if didFail { comms <- musebot.ProviderMessage{"error", errors.New("That song appears to no longer exist!")} return } resMap := ((res.(map[string]interface{}))["result"]).(map[string]interface{}) resStreamKey := (resMap["streamKey"]).(string) resIp := (resMap["ip"]).(string) finalUrl := "http://" + resIp + "/stream.php?streamKey=" + resStreamKey // phew! downloadFileAndReportProgress(finalUrl, downloadLocation, comms) } else { comms <- musebot.ProviderMessage{"stages", 0} // awesome } song.MusicUrl = downloadLocation // that's actually us done! :) comms <- musebot.ProviderMessage{"done", nil} }
func (p *GroovesharkProvider) groovesharkSongToMuseBotSong(r map[string]interface{}, song *musebot.SongInfo) { coverArtFn := r["CoverArtFilename"].(string) if coverArtFn == "" { coverArtFn = "http://images.grooveshark.com/static/albums/500_default.png" } else { coverArtFn = "http://images.grooveshark.com/static/albums/500_" + coverArtFn } Title, ok := r["SongName"].(string) if !ok { Title = r["Name"].(string) } song.Title = Title song.Artist = r["ArtistName"].(string) song.Album = r["AlbumName"].(string) song.CoverArtUrl = coverArtFn song.Provider = p song.ProviderName = p.PackageName() song.ProviderId = r["SongID"].(string) }
func runHttpServer(cfg *musebot.JsonCfg) { if len(cfg.SessionStoreAuthKey) != 32 && len(cfg.SessionStoreAuthKey) != 64 { b64 := base64.StdEncoding log.Fatalln("SessionStoreAuthKey must be 32 or 64 bytes long, not", len(cfg.SessionStoreAuthKey), "bytes! Here's a suggested value:", b64.EncodeToString(securecookie.GenerateRandomKey(64))) } sessionStore = sessions.NewCookieStore(cfg.SessionStoreAuthKey) // let's try to get the default provider defaultProvider, wasFound := musebot.CurrentProviders[config.DefaultProvider] if !wasFound { for _, p := range musebot.CurrentProviders { defaultProvider = p break } } // job ID generator go func(generatorPipe chan int) { i := 0 for { generatorPipe <- i i = i + 1 } }(jobIdGenerator) var eProviderNotFound = errors.New("Provider not found") // GO GO WEB HANDLER http.HandleFunc("/api/current_song/", func(w http.ResponseWriter, r *http.Request) { if !enforceLoggedIn(getSession(r), w) { return } currentSong, isPlaying, err := musebot.CurrentBackend.CurrentSong() if err != nil { writeApiResponse(w, wrapApiError(err)) } else { if isPlaying { writeApiResponse(w, musebot.CurrentSongApiResponse{Playing: isPlaying, CurrentSong: ¤tSong}) } else { writeApiResponse(w, musebot.CurrentSongApiResponse{Playing: isPlaying, CurrentSong: nil}) } } }) http.HandleFunc("/api/playback_queue/", func(w http.ResponseWriter, r *http.Request) { if !enforceLoggedIn(getSession(r), w) { return } playbackQueue, err := musebot.CurrentBackend.PlaybackQueue() if err != nil { writeApiResponse(w, wrapApiError(err)) } else { writeApiResponse(w, musebot.PlaybackQueueApiResponse{playbackQueue}) } }) http.HandleFunc("/api/search_and_queue_first/", func(w http.ResponseWriter, r *http.Request) { if !enforceLoggedIn(getSession(r), w) { return } // get the query! queryStrMap := r.URL.Query() queryArray, ok := queryStrMap["q"] if !ok || len(queryArray) < 1 || len(queryArray[0]) == 0 { writeApiResponse(w, wrapApiError(errors.New("You must pass a 'q' argument specifying the query!"))) return } query := queryArray[0] // cool searchRes, err := musebot.CurrentProviders["provider.GroovesharkProvider"].Search(query) if err != nil { writeApiResponse(w, wrapApiError(err)) return } if len(searchRes) == 0 { writeApiResponse(w, wrapApiError(errors.New("There were no results for that query."))) return } fetchChan := make(chan musebot.ProviderMessage, 1000) log.Println(searchRes[0].Title) go searchRes[0].Provider.FetchSong(&searchRes[0], fetchChan) waitToEnd := make(chan bool) go (func(fetchChan chan musebot.ProviderMessage, song *musebot.SongInfo, done chan bool) { for { msg := <-fetchChan log.Println(msg) if msg.Type == "done" { musebot.CurrentBackend.Add(*song) writeApiResponse(w, musebot.QueuedApiResponse{*song}) done <- true return } else if msg.Type == "error" { writeApiResponse(w, wrapApiError(msg.Content.(error))) done <- true return } } })(fetchChan, &searchRes[0], waitToEnd) <-waitToEnd }) http.HandleFunc("/api/available_providers/", func(w http.ResponseWriter, r *http.Request) { if !enforceLoggedIn(getSession(r), w) { return } outputBlah := make(map[string]string) for k, v := range musebot.CurrentProviders { outputBlah[k] = v.Name() } writeApiResponse(w, musebot.AvailableProvidersApiResponse{outputBlah}) }) http.HandleFunc("/api/search/", func(w http.ResponseWriter, r *http.Request) { if !enforceLoggedIn(getSession(r), w) { return } // get the query! queryStrMap := r.URL.Query() qArray, ok := queryStrMap["q"] if !ok || len(qArray) < 1 || len(qArray[0]) == 0 { writeApiResponse(w, wrapApiError(errors.New("You must pass a 'q' argument specifying the query!"))) return } q := qArray[0] // now the provider providerArray, ok := queryStrMap["provider"] var provider provider.Provider if !ok || len(providerArray) < 1 || len(providerArray[0]) == 0 { provider = defaultProvider } else { providerName := providerArray[0] provider, ok = musebot.CurrentProviders[providerName] if !ok { writeApiResponse(w, wrapApiError(eProviderNotFound)) return } } // now we have a provider, we can search! searchResults, err := provider.Search(q) if err != nil { writeApiResponse(w, wrapApiError(err)) return } writeApiResponse(w, musebot.SearchResultsApiResponse{searchResults}) }) http.HandleFunc("/api/logout/", func(w http.ResponseWriter, r *http.Request) { sess := getSession(r) result := true if !isLoggedIn(sess) { result = false } // cool sess.Values = map[interface{}]interface{}{} sess.Save(r, w) writeApiResponse(w, musebot.LoggedOutApiResponse{result}) return }) http.HandleFunc("/api/quit/", func(w http.ResponseWriter, r *http.Request) { if !enforceLoggedIn(getSession(r), w) { return } log.Println() log.Println("Exiting on request!") os.Exit(0) }) http.HandleFunc("/api/add_to_queue/", func(w http.ResponseWriter, r *http.Request) { sess := getSession(r) if !enforceLoggedIn(sess, w) { return } providerName := r.FormValue("provider") providerId := r.FormValue("provider_id") si := musebot.SongInfo{} si.ProviderName = providerName si.ProviderId = providerId provider, exists := musebot.CurrentProviders[providerName] if !exists { writeApiResponse(w, wrapApiError(errors.New("That provider doesn't exist."))) } si.Provider = provider log.Println("UPDATING SONG INFO") si.Provider.UpdateSongInfo(&si) log.Println(si) provMessage := make(chan musebot.ProviderMessage) quit := make(chan bool) go si.Provider.FetchSong(&si, provMessage) log.Println(si, provMessage) log.Println(sess.Values["username"]) go func(quit chan bool, provMessage chan musebot.ProviderMessage, w http.ResponseWriter, s *musebot.SongInfo, user string) { hasQuit := false jobId := <-jobIdGenerator var m musebot.ProviderMessage for { m = <-provMessage if m.Type == "error" { if !hasQuit { writeApiResponse(w, wrapApiError(m.Content.(error))) quit <- true return // done } } else if m.Type == "stages" { if !hasQuit { // tell them that we're AWESOME if m.Content == 0 { log.Println("ORDERING BACKEND TO ADD", s) musebot.CurrentBackend.Add(*s) writeApiResponse(w, musebot.QueuedApiResponse{*s}) quit <- true return // done } else { writeApiResponse(w, musebot.JobQueuedApiResponse{strconv.Itoa(jobId)}) quit <- true hasQuit = true } } } else if m.Type == "done" && hasQuit { musebot.CurrentBackend.Add(*s) } if hasQuit { outputData := musebot.JobWebSocketApiResponse{JobId: strconv.Itoa(jobId), Data: m} jsonData, _ := json.Marshal(outputData) userOutputData := UserMessage{user: user, message: "JOB_DATA " + string(jsonData)} h.broadcastUser <- userOutputData } } }(quit, provMessage, w, &si, sess.Values["username"].(string)) <-quit }) http.HandleFunc("/api/login/", func(w http.ResponseWriter, r *http.Request) { if r.TLS == nil { writeApiResponse(w, wrapApiError(errors.New("This method requires TLS! :<"))) return } sess := getSession(r) username := r.FormValue("username") password := r.FormValue("password") a := musebot.CurrentAuthenticator result, user, err := a.CheckLogin(username, password) if err != nil { writeApiResponse(w, wrapApiError(err)) return } if result { sess.Values["logged-in"] = true sess.Values["username"] = user.Username sess.Values["administrator"] = user.Administrator sess.Save(r, w) writeApiResponse(w, musebot.LoggedInApiResponse{username}) } else { writeApiResponse(w, wrapApiError(errors.New("The username or password was incorrect."))) } }) http.HandleFunc("/api/masquerade/", func(w http.ResponseWriter, r *http.Request) { if r.TLS == nil { writeApiResponse(w, wrapApiError(errors.New("This method requires TLS! :<"))) return } sess := getSession(r) if !enforceLoggedIn(sess, w) { return } if !isSessionBool(sess, "administrator") { writeApiResponse(w, wrapApiError(errors.New("You're not an administrator!"))) return } queryStrMap := r.URL.Query() qArray, ok := queryStrMap["username"] if !ok || len(qArray) < 1 || len(qArray[0]) == 0 { writeApiResponse(w, wrapApiError(errors.New("You must pass a 'username' argument specifying the user to masquerade as!"))) return } q := qArray[0] sess.Values["logged-in"] = true sess.Values["username"] = q sess.Values["administrator"] = true sess.Save(r, w) w.Write([]byte("YOU ARE NOW LOGGED IN AS ")) w.Write([]byte(q)) }) registerWsHandler() if len(cfg.ListenAddr) != 0 { httpServer := &http.Server{Addr: cfg.ListenAddr} go func() { log.Fatalln(httpServer.ListenAndServe()) }() log.Println(" - HTTP Server is listening on", cfg.ListenAddr) } if len(cfg.SslListenAddr) == 0 { log.Fatalln(" x The HTTPS server *must* run. Login will only take place over HTTPS.") } httpsServer := &http.Server{Addr: cfg.SslListenAddr} go func() { log.Fatalln(httpsServer.ListenAndServeTLS("ssl.pub.pem", "ssl.priv.pem")) }() log.Println(" - HTTPS Server is listening on", cfg.SslListenAddr) }