diff --git a/client/episodes.html b/client/episodes.html index ca2a45f..ec18c67 100644 --- a/client/episodes.html +++ b/client/episodes.html @@ -46,6 +46,12 @@ display: block; } } + .button-enqueue{ + display: none; + } + body.playerExists .button-enqueue{ + display: inline-block; + } @@ -123,6 +129,12 @@ title="Play Episode" > + {{else}} {{if not $setting.AutoDownload}} + diff --git a/client/index.html b/client/index.html index f4ec3b5..a944b41 100644 --- a/client/index.html +++ b/client/index.html @@ -175,6 +175,15 @@ > + + @@ -227,13 +236,31 @@ } }, created(){ - + const self=this; + this.socket= getWebsocketConnection(function(event){ + const message= getWebsocketMessage("Register","Home") + self.socket.send(message); + },function(x){ + const msg= JSON.parse(x.data) + if(msg.messageType=="NoPlayer"){ + self.playerExists=false; + } + if(msg.messageType=="PlayerExists"){ + self.playerExists=true; + } + }); }, methods:{ removePodcast(id) { const index= this.podcasts.findIndex(x=>x.ID===id); this.podcasts.splice(index,1); }, + enquePodcast(id){ + if(!this.playerExists){ + return + } + this.socket.send(getWebsocketMessage("Enqueue",`{"podcastId":"${id}"}`)) + }, sortPodcasts(order){ let compareFunction; switch(order){ @@ -353,6 +380,8 @@ }, }, data: { + socket:null, + playerExists:false, isMobile:false, layoutOptions:["list","grid"], layout:"grid", @@ -527,5 +556,9 @@ return false; } + diff --git a/client/player.html b/client/player.html index c33605d..d9926ed 100644 --- a/client/player.html +++ b/client/player.html @@ -656,6 +656,7 @@ div#large-visualization{ {{template "navbar" .}}
{{$setting := .setting}} +
@@ -721,8 +722,8 @@ div#large-visualization{
- {{range $index,$item:=.podcastItems}} -
+ +
@@ -730,21 +731,21 @@ div#large-visualization{
- {{ formatDuration .Duration}} + ${ formatDuration(item.Duration)}
- {{end}} +
- +
@@ -752,88 +753,139 @@ div#large-visualization{ {{template "scripts"}} + + diff --git a/client/scripts.html b/client/scripts.html index 51ece27..6c604c6 100644 --- a/client/scripts.html +++ b/client/scripts.html @@ -5,62 +5,103 @@ {{end}} diff --git a/controllers/pages.go b/controllers/pages.go index 831cbb2..6b62b16 100644 --- a/controllers/pages.go +++ b/controllers/pages.go @@ -94,6 +94,23 @@ func PodcastPage(c *gin.Context) { } +func getItemsToPlay(itemId, podcastId string) []db.PodcastItem { + var items []db.PodcastItem + if itemId != "" { + toAdd := service.GetPodcastItemById(itemId) + items = append(items, *toAdd) + + } else if podcastId != "" { + pod := service.GetPodcastById(podcastId) + for _, item := range pod.PodcastItems { + if item.DownloadStatus == db.Downloaded { + items = append(items, item) + } + } + } + return items +} + func PlayerPage(c *gin.Context) { itemId, hasItemId := c.GetQuery("itemId") diff --git a/controllers/websockets.go b/controllers/websockets.go new file mode 100644 index 0000000..57918f1 --- /dev/null +++ b/controllers/websockets.go @@ -0,0 +1,148 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/gorilla/websocket" +) + +type EnqueuePayload struct { + ItemId string `json:"itemId"` + PodcastId string `json:"podcastId"` +} + +var wsupgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +var activePlayers = make(map[*websocket.Conn]string) +var allConnections = make(map[*websocket.Conn]string) + +var broadcast = make(chan Message) // broadcast channel + +type Message struct { + Identifier string `json:"identifier"` + MessageType string `json:"messageType"` + Payload string `json:"payload"` + Connection *websocket.Conn `json:"-"` +} + +func Wshandler(w http.ResponseWriter, r *http.Request) { + conn, err := wsupgrader.Upgrade(w, r, nil) + if err != nil { + fmt.Println("Failed to set websocket upgrade: %+v", err) + return + } + defer conn.Close() + for { + var mess Message + err := conn.ReadJSON(&mess) + if err != nil { + // fmt.Println("Socket Error") + //fmt.Println(err.Error()) + isPlayer := activePlayers[conn] != "" + if isPlayer { + delete(activePlayers, conn) + broadcast <- Message{ + MessageType: "PlayerRemoved", + Identifier: mess.Identifier, + } + } + delete(allConnections, conn) + break + } + mess.Connection = conn + allConnections[conn] = mess.Identifier + broadcast <- mess + // conn.WriteJSON(mess) + } +} + +func HandleWebsocketMessages() { + for { + // Grab the next message from the broadcast channel + msg := <-broadcast + //fmt.Println(msg) + + switch msg.MessageType { + case "RegisterPlayer": + activePlayers[msg.Connection] = msg.Identifier + for connection, _ := range allConnections { + connection.WriteJSON(Message{ + Identifier: msg.Identifier, + MessageType: "PlayerExists", + }) + } + fmt.Println("Player Registered") + case "PlayerRemoved": + for connection, _ := range allConnections { + connection.WriteJSON(Message{ + Identifier: msg.Identifier, + MessageType: "NoPlayer", + }) + } + fmt.Println("Player Registered") + case "Enqueue": + var payload EnqueuePayload + fmt.Println(msg.Payload) + err := json.Unmarshal([]byte(msg.Payload), &payload) + if err == nil { + items := getItemsToPlay(payload.ItemId, payload.PodcastId) + var player *websocket.Conn + for connection, id := range activePlayers { + + if msg.Identifier == id { + player = connection + break + } + } + if player != nil { + payloadStr, err := json.Marshal(items) + if err == nil { + player.WriteJSON(Message{ + Identifier: msg.Identifier, + MessageType: "Enqueue", + Payload: string(payloadStr), + }) + } + } + } else { + fmt.Println(err.Error()) + } + case "Register": + var player *websocket.Conn + for connection, id := range activePlayers { + + if msg.Identifier == id { + player = connection + break + } + } + + if player == nil { + fmt.Println("Player Not Exists") + msg.Connection.WriteJSON(Message{ + Identifier: msg.Identifier, + MessageType: "NoPlayer", + }) + } else { + msg.Connection.WriteJSON(Message{ + Identifier: msg.Identifier, + MessageType: "PlayerExists", + }) + } + } + // Send it out to every client that is currently connected + // for client := range clients { + // err := client.WriteJSON(msg) + // if err != nil { + // log.Printf("error: %v", err) + // client.Close() + // delete(clients, client) + // } + // } + } +} diff --git a/go.mod b/go.mod index 9f3c09d..2f66d58 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.6.3 github.com/gobeam/stringy v0.0.0-20200717095810-8a3637503f62 + github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 github.com/jasonlvhit/gocron v0.0.1 github.com/joho/godotenv v1.3.0 diff --git a/go.sum b/go.sum index e366161..d68e266 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 h1:ETqBvCd8SQaNCb0TwQ5A+IlkecGuwjW1EUTxK9if+UE= github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= diff --git a/main.go b/main.go index 38f12ab..9f184e1 100644 --- a/main.go +++ b/main.go @@ -133,6 +133,11 @@ func main() { router.GET("/opml", controllers.GetOmpl) router.GET("/player", controllers.PlayerPage) + r.GET("/ws", func(c *gin.Context) { + controllers.Wshandler(c.Writer, c.Request) + }) + go controllers.HandleWebsocketMessages() + go assetEnv() go intiCron() @@ -144,6 +149,7 @@ func setupSettings() gin.HandlerFunc { setting := db.GetOrCreateSetting() c.Set("setting", setting) + c.Next() } }