Using a web template

This commit is contained in:
MichaelOultram 2015-07-30 13:48:53 +01:00
parent ef0d9691b8
commit dbc58bd95b
6 changed files with 185 additions and 129 deletions

View file

@ -171,13 +171,7 @@ func add(user *gumble.User, url string) error {
dj.SendPrivateMessage(user, NO_ARGUMENT_MSG)
return errors.New("NO_ARGUMENT")
} else {
title, err := findServiceAndAdd(user, url)
if err == nil {
dj.client.Self.Channel.Send(fmt.Sprintf(SONG_ADDED_HTML, user.Name, title), false)
} else {
dj.SendPrivateMessage(user, err.Error())
}
return err
return findServiceAndAdd(user, url)
}
}

View file

@ -2,17 +2,37 @@
<html>
<head>
<meta charset="ISO-8859-1">
<title>%s - mumbledj</title>
<title>{{user}} - mumbledj</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">
function addURL(url) {
function api(type) {
return "{{.siteURL}}" + type + "?token={{.token}}";
}
function addURL() {
var url = $("#textbox");
$.ajax(api("add") + "&value=" + url.value);
url.value = "";
}
function volume() {
var volume = $("#textbox");
$.ajax(api("volume") + "&value=" + volume.value);
volume.value = "";
}
function skip(val) {
$.ajax(api("skip") + "&value=" + val);
}
</script>
</head>
<body>
<h1>Add Song Form</h1>
<input type="text"/>
<input type="button" value="Add Song" />
<input id="textbox" type="text"/>
<input id="add" type="button" value="Add Song" onclick="addURL()"/>
<input id="volume" type="button" value="Set Volume" onclick="volume()"/>
<input id="skipSong" type="button" value="Skip Current Song" onclick="skip('song')"/>
<input id="skipPlaylist" type="button" value="Skip Current Playlist" onclick="skip('playlist')"/>
</body>
</html>

13
main.go
View file

@ -152,6 +152,17 @@ func isNil(a interface{}) bool {
return a == nil || reflect.ValueOf(a).IsNil()
}
func RegexpFromURL(url string, patterns []string) *regexp.Regexp {
for _, pattern := range patterns {
if re, err := regexp.Compile(pattern); err == nil {
if re.MatchString(url) {
return re
}
}
}
return nil
}
// dj variable declaration. This is done outside of main() to allow global use.
var dj = mumbledj{
keepAlive: make(chan bool),
@ -230,7 +241,7 @@ func main() {
os.Exit(1)
}
Webserver()
Webserver(9563)
<-dj.keepAlive
}

View file

@ -53,7 +53,7 @@ type Playlist interface {
var services = []Service{YouTube{}}
func findServiceAndAdd(user *gumble.User, url string) (string, error) {
func findServiceAndAdd(user *gumble.User, url string) error {
var urlService Service
// Checks all services to see if any can take the URL
@ -69,7 +69,9 @@ func findServiceAndAdd(user *gumble.User, url string) (string, error) {
oldLength := dj.queue.Len()
var title string
var err error
if title, err = urlService.NewRequest(user, url); err == nil {
dj.client.Self.Channel.Send(fmt.Sprintf(SONG_ADDED_HTML, user.Name, title), false)
// Starts playing the new song if nothing else is playing
if oldLength == 0 && dj.queue.Len() != 0 && !dj.audioStream.IsPlaying() {
@ -81,7 +83,9 @@ func findServiceAndAdd(user *gumble.User, url string) (string, error) {
return "", errors.New("FAILED_TO_DOWNLOAD")
}
}
} else {
dj.SendPrivateMessage(user, err.Error())
}
return title, err
return err
}
}

View file

@ -35,64 +35,13 @@ var youtubeVideoPatterns = []string{
`https?:\/\/www.youtube.com\/v\/([\w-]+)(\?t=\d*m?\d*s?)?`,
}
// ---------------
// YOUTUBE SERVICE
// ---------------
// ------
// TYPES
// ------
// YouTube implements the Service interface
type YouTube struct{}
// Name of the service
func (y YouTube) ServiceName() string {
return "Youtube"
}
// Checks to see if service will accept URL
func (y YouTube) URLRegex(url string) bool {
return RegexpFromURL(url, append(youtubeVideoPatterns, []string{youtubePlaylistPattern}...)) != nil
}
func RegexpFromURL(url string, patterns []string) *regexp.Regexp {
for _, pattern := range patterns {
if re, err := regexp.Compile(pattern); err == nil {
if re.MatchString(url) {
return re
}
}
}
return nil
}
// Creates the requested song/playlist and adds to the queue
func (y YouTube) NewRequest(user *gumble.User, url string) (string, error) {
var shortURL, startOffset = "", ""
if re, err := regexp.Compile(youtubePlaylistPattern); err == nil {
if re.MatchString(url) {
if dj.HasPermission(user.Name, dj.conf.Permissions.AdminAddPlaylists) {
shortURL = re.FindStringSubmatch(url)[1]
playlist, err := NewYouTubePlaylist(user.Name, shortURL)
return playlist.Title(), err
} else {
return "", errors.New("NO_PLAYLIST_PERMISSION")
}
} else {
re = RegexpFromURL(url, youtubeVideoPatterns)
matches := re.FindAllStringSubmatch(url, -1)
shortURL = matches[0][1]
if len(matches[0]) == 3 {
startOffset = matches[0][2]
}
song, err := NewYouTubeSong(user.Name, shortURL, startOffset, nil)
return song.Title(), err
}
} else {
return "", err
}
}
// ------------
// YOUTUBE SONG
// ------------
// YouTubeSong holds the metadata for a song extracted from a YouTube video.
type YouTubeSong struct {
submitter string
@ -107,14 +56,61 @@ type YouTubeSong struct {
dontSkip bool
}
// NewYouTubeSong gathers the metadata for a song extracted from a YouTube video, and returns
// YouTubePlaylist holds the metadata for a YouTube playlist.
type YouTubePlaylist struct {
id string
title string
}
// ---------------
// YOUTUBE SERVICE
// ---------------
// Name of the service
func (yt YouTube) ServiceName() string {
return "Youtube"
}
// Checks to see if service will accept URL
func (yt YouTube) URLRegex(url string) bool {
return RegexpFromURL(url, append(youtubeVideoPatterns, []string{youtubePlaylistPattern}...)) != nil
}
// Creates the requested song/playlist and adds to the queue
func (yt YouTube) NewRequest(user *gumble.User, url string) (string, error) {
var shortURL, startOffset = "", ""
if re, err := regexp.Compile(youtubePlaylistPattern); err == nil {
if re.MatchString(url) {
if dj.HasPermission(user.Name, dj.conf.Permissions.AdminAddPlaylists) {
shortURL = re.FindStringSubmatch(url)[1]
playlist, err := yt.NewPlaylist(user.Name, shortURL)
return playlist.Title(), err
} else {
return "", errors.New("NO_PLAYLIST_PERMISSION")
}
} else {
re = RegexpFromURL(url, youtubeVideoPatterns)
matches := re.FindAllStringSubmatch(url, -1)
shortURL = matches[0][1]
if len(matches[0]) == 3 {
startOffset = matches[0][2]
}
song, err := yt.NewSong(user.Name, shortURL, startOffset, nil)
return song.Title(), err
}
} else {
return "", err
}
}
// NewSong gathers the metadata for a song extracted from a YouTube video, and returns
// the song.
func NewYouTubeSong(user, id, offset string, playlist *YouTubePlaylist) (*YouTubeSong, error) {
func (yt YouTube) NewSong(user, id, offset string, playlist *YouTubePlaylist) (*YouTubeSong, error) {
var apiResponse *jsonq.JsonQuery
var err error
url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=%s&key=%s",
id, os.Getenv("YOUTUBE_API_KEY"))
if apiResponse, err = PerformGetRequest(url); err != nil {
if apiResponse, err = yt.PerformGetRequest(url); err != nil {
return nil, errors.New(INVALID_API_KEY)
}
@ -203,6 +199,46 @@ func NewYouTubeSong(user, id, offset string, playlist *YouTubePlaylist) (*YouTub
return nil, errors.New(VIDEO_TOO_LONG_MSG)
}
// NewPlaylist gathers the metadata for a YouTube playlist and returns it.
func (yt YouTube) NewPlaylist(user, id string) (*YouTubePlaylist, error) {
var apiResponse *jsonq.JsonQuery
var err error
// Retrieve title of playlist
url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=%s&key=%s",
id, os.Getenv("YOUTUBE_API_KEY"))
if apiResponse, err = yt.PerformGetRequest(url); err != nil {
return nil, err
}
title, _ := apiResponse.String("items", "0", "snippet", "title")
playlist := &YouTubePlaylist{
id: id,
title: title,
}
// Retrieve items in playlist
url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=%s&key=%s",
id, os.Getenv("YOUTUBE_API_KEY"))
if apiResponse, err = yt.PerformGetRequest(url); err != nil {
return nil, err
}
numVideos, _ := apiResponse.Int("pageInfo", "totalResults")
if numVideos > 50 {
numVideos = 50
}
for i := 0; i < numVideos; i++ {
index := strconv.Itoa(i)
videoID, _ := apiResponse.String("items", index, "snippet", "resourceId", "videoId")
yt.NewSong(user, videoID, "", playlist)
}
return playlist, nil
}
// ------------
// YOUTUBE SONG
// ------------
// Download downloads the song via youtube-dl if it does not already exist on disk.
// All downloaded songs are stored in ~/.mumbledj/songs and should be automatically cleaned.
func (s *YouTubeSong) Download() error {
@ -380,48 +416,6 @@ func (s *YouTubeSong) SetDontSkip(value bool) {
// YOUTUBE PLAYLIST
// ----------------
// YouTubePlaylist holds the metadata for a YouTube playlist.
type YouTubePlaylist struct {
id string
title string
}
// NewYouTubePlaylist gathers the metadata for a YouTube playlist and returns it.
func NewYouTubePlaylist(user, id string) (*YouTubePlaylist, error) {
var apiResponse *jsonq.JsonQuery
var err error
// Retrieve title of playlist
url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=%s&key=%s",
id, os.Getenv("YOUTUBE_API_KEY"))
if apiResponse, err = PerformGetRequest(url); err != nil {
return nil, err
}
title, _ := apiResponse.String("items", "0", "snippet", "title")
playlist := &YouTubePlaylist{
id: id,
title: title,
}
// Retrieve items in playlist
url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=%s&key=%s",
id, os.Getenv("YOUTUBE_API_KEY"))
if apiResponse, err = PerformGetRequest(url); err != nil {
return nil, err
}
numVideos, _ := apiResponse.Int("pageInfo", "totalResults")
if numVideos > 50 {
numVideos = 50
}
for i := 0; i < numVideos; i++ {
index := strconv.Itoa(i)
videoID, _ := apiResponse.String("items", index, "snippet", "resourceId", "videoId")
NewYouTubeSong(user, videoID, "", playlist)
}
return playlist, nil
}
// AddSkip adds a skip to the playlist's skippers slice.
func (p *YouTubePlaylist) AddSkip(username string) error {
for _, user := range dj.playlistSkips[p.ID()] {
@ -475,7 +469,7 @@ func (p *YouTubePlaylist) Title() string {
// -----------
// PerformGetRequest does all the grunt work for a YouTube HTTPS GET request.
func PerformGetRequest(url string) (*jsonq.JsonQuery, error) {
func (yt YouTube) PerformGetRequest(url string) (*jsonq.JsonQuery, error) {
jsonString := ""
if response, err := http.Get(url); err == nil {

61
web.go
View file

@ -12,37 +12,70 @@ import (
"github.com/layeh/gumble/gumble"
)
var client_token = make(map[*gumble.User]string)
var token_client = make(map[string]*gumble.User)
var external_ip = ""
func Webserver() {
http.HandleFunc("/", homepage)
http.HandleFunc("/add", addSong)
http.ListenAndServe(":9563", nil)
rand.Seed(time.Now().UnixNano())
type WebServer struct {
port int
client_token map[*gumble.User]string
token_client map[string]*gumble.User
}
func homepage(w http.ResponseWriter, r *http.Request) {
type Page struct {
siteUrl string
token string
}
var external_ip = ""
func Webserver(port int) *WebServer {
var webserver = WebServer{port, make(map[*gumble.User]string), make(map[string]*gumble.User)}
http.HandleFunc("/", webServer.homepage)
http.HandleFunc("/add", webserver.add)
http.HandleFunc("/volume", webserver.volume)
http.HandleFunc("/skip", webserver.skip)
http.ListenAndServe(":"+port, nil)
rand.Seed(time.Now().UnixNano())
return &webserver
}
func (w WebServer) homepage(w http.ResponseWriter, r *http.Request) {
var uname = token_client[r.URL.Path[1:]]
if uname == nil {
fmt.Fprintf(w, "Invalid Token")
} else {
fmt.Fprintf(w, "Hang in there %s, I haven't made the website yet!", uname.Name)
t, _ := template.ParseFiles("index.html")
t.Execute(w, Page{"http://" + getIp() + ":" + w.port + "/", r.URL.Path[1:]})
}
}
func addSong(w http.ResponseWriter, r *http.Request) {
func (w WebServer) add(w http.ResponseWriter, r *http.Request) {
var uname = token_client[r.FormValue("token")]
if uname == nil {
fmt.Fprintf(w, "Invalid Token")
} else {
var url = html.UnescapeString(r.FormValue("url"))
add(uname, html.UnescapeString(r.FormValue("value")))
}
}
func (w WebServer) volume(w http.ResponseWriter, r *http.Request) {
var uname = token_client[r.FormValue("token")]
if uname == nil {
fmt.Fprintf(w, "Invalid Token")
} else {
var url = html.UnescapeString(r.FormValue("value"))
add(uname, url)
}
}
func GetWebAddress(user *gumble.User) {
func (w WebServer) skip(w http.ResponseWriter, r *http.Request) {
var uname = token_client[r.FormValue("token")]
if uname == nil {
fmt.Fprintf(w, "Invalid Token")
} else {
var url = html.UnescapeString(r.FormValue("value"))
add(uname, url)
}
}
func (w WebServer) GetWebAddress(user *gumble.User) {
if client_token[user] != "" {
token_client[client_token[user]] = nil
}