Example #1
0
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
}
Example #2
0
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)
}
Example #3
0
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}
}
Example #4
0
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)
}
Example #5
0
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: &currentSong})
			} 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)
}