archive
This commit is contained in:
378
MovieNight/handlers.go
Executable file
378
MovieNight/handlers.go
Executable file
@@ -0,0 +1,378 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/zorchenhimer/MovieNight/common"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/nareix/joy4/av/avutil"
|
||||
"github.com/nareix/joy4/av/pubsub"
|
||||
"github.com/nareix/joy4/format/flv"
|
||||
"github.com/nareix/joy4/format/rtmp"
|
||||
)
|
||||
|
||||
var (
|
||||
// Read/Write mutex for rtmp stream
|
||||
l = NewSuperLock()
|
||||
|
||||
// Map of active streams
|
||||
channels = map[string]*Channel{}
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
que *pubsub.Queue
|
||||
}
|
||||
|
||||
type writeFlusher struct {
|
||||
httpflusher http.Flusher
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (self writeFlusher) Flush() error {
|
||||
self.httpflusher.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serving static files
|
||||
func wsStaticFiles(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/favicon.ico":
|
||||
http.ServeFile(w, r, "./favicon.png")
|
||||
return
|
||||
case "/justvideo":
|
||||
http.ServeFile(w, r, "./static/justvideo.html")
|
||||
return
|
||||
}
|
||||
|
||||
goodPath := r.URL.Path[8:len(r.URL.Path)]
|
||||
common.LogDebugf("[static] serving %q from folder ./static/\n", goodPath)
|
||||
|
||||
http.ServeFile(w, r, "./static/"+goodPath)
|
||||
}
|
||||
|
||||
func wsWasmFile(w http.ResponseWriter, r *http.Request) {
|
||||
if settings.NoCache {
|
||||
w.Header().Set("Cache-Control", "no-cache, must-revalidate")
|
||||
}
|
||||
common.LogDebugln("[static] serving wasm file")
|
||||
http.ServeFile(w, r, "./static/main.wasm")
|
||||
}
|
||||
|
||||
func wsImages(w http.ResponseWriter, r *http.Request) {
|
||||
base := filepath.Base(r.URL.Path)
|
||||
common.LogDebugln("[img] ", base)
|
||||
http.ServeFile(w, r, "./static/img/"+base)
|
||||
}
|
||||
|
||||
func wsEmotes(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, path.Join("./static/", r.URL.Path))
|
||||
}
|
||||
|
||||
// Handling the websocket
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool { return true }, //not checking origin
|
||||
}
|
||||
|
||||
//this is also the handler for joining to the chat
|
||||
func wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("ws handler")
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
common.LogErrorln("Error upgrading to websocket:", err)
|
||||
return
|
||||
}
|
||||
|
||||
common.LogDebugln("Connection has been upgraded to websocket")
|
||||
|
||||
go func() {
|
||||
// Handle incomming messages
|
||||
for {
|
||||
var data common.ClientData
|
||||
err := conn.ReadJSON(&data)
|
||||
if err != nil { //if error then assuming that the connection is closed
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
// returns if it's OK to proceed
|
||||
func checkRoomAccess(w http.ResponseWriter, r *http.Request) bool {
|
||||
session, err := sstore.Get(r, "moviesession")
|
||||
if err != nil {
|
||||
// Don't return as server error here, just make a new session.
|
||||
common.LogErrorf("Unable to get session for client %s: %v\n", r.RemoteAddr, err)
|
||||
}
|
||||
|
||||
if settings.RoomAccess == AccessPin {
|
||||
pin := session.Values["pin"]
|
||||
// No pin found in session
|
||||
if pin == nil || len(pin.(string)) == 0 {
|
||||
if r.Method == "POST" {
|
||||
// Check for correct pin
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
common.LogErrorf("Error parsing form")
|
||||
http.Error(w, "Unable to get session data", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
postPin := strings.TrimSpace(r.Form.Get("txtInput"))
|
||||
common.LogDebugf("Received pin: %s\n", postPin)
|
||||
if postPin == settings.RoomAccessPin {
|
||||
// Pin is correct. Save it to session and return true.
|
||||
session.Values["pin"] = settings.RoomAccessPin
|
||||
session.Save(r, w)
|
||||
return true
|
||||
}
|
||||
// Pin is incorrect.
|
||||
handlePinTemplate(w, r, "Incorrect PIN")
|
||||
return false
|
||||
}
|
||||
// nope. display pin entry and return
|
||||
handlePinTemplate(w, r, "")
|
||||
return false
|
||||
}
|
||||
|
||||
// Pin found in session, but it has changed since last time.
|
||||
if pin.(string) != settings.RoomAccessPin {
|
||||
// Clear out the old pin.
|
||||
session.Values["pin"] = nil
|
||||
session.Save(r, w)
|
||||
|
||||
// Prompt for new one.
|
||||
handlePinTemplate(w, r, "Pin has changed. Enter new PIN.")
|
||||
return false
|
||||
}
|
||||
|
||||
// Correct pin found in session
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO: this.
|
||||
if settings.RoomAccess == AccessRequest {
|
||||
http.Error(w, "Requesting access not implemented yet", http.StatusNotImplemented)
|
||||
return false
|
||||
}
|
||||
|
||||
// Room is open.
|
||||
return true
|
||||
}
|
||||
|
||||
func handlePinTemplate(w http.ResponseWriter, r *http.Request, errorMessage string) {
|
||||
log.Println("handle pin temp")
|
||||
type Data struct {
|
||||
Title string
|
||||
SubmitText string
|
||||
Notice string
|
||||
}
|
||||
|
||||
if errorMessage == "" {
|
||||
errorMessage = "Please enter the PIN"
|
||||
}
|
||||
|
||||
data := Data{
|
||||
Title: "Enter Pin",
|
||||
SubmitText: "Submit Pin",
|
||||
Notice: errorMessage,
|
||||
}
|
||||
|
||||
err := common.ExecuteServerTemplate(w, "pin", data)
|
||||
if err != nil {
|
||||
common.LogErrorf("Error executing file, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleHelpTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
func handleEmoteTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("handle emote temp")
|
||||
type Data struct {
|
||||
Title string
|
||||
Emotes map[string]string
|
||||
}
|
||||
|
||||
data := Data{
|
||||
Title: "Available Emotes",
|
||||
Emotes: common.Emotes,
|
||||
}
|
||||
|
||||
err := common.ExecuteServerTemplate(w, "emotes", data)
|
||||
if err != nil {
|
||||
common.LogErrorf("Error executing file, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func handlePin(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("handle pin")
|
||||
session, err := sstore.Get(r, "moviesession")
|
||||
if err != nil {
|
||||
common.LogDebugf("Unable to get session: %v\n", err)
|
||||
}
|
||||
|
||||
val := session.Values["pin"]
|
||||
if val == nil {
|
||||
session.Values["pin"] = "1234"
|
||||
err := session.Save(r, w)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "unable to save session: %v", err)
|
||||
}
|
||||
fmt.Fprint(w, "Pin was not set")
|
||||
common.LogDebugln("pin was not set")
|
||||
} else {
|
||||
fmt.Fprintf(w, "pin set: %v", val)
|
||||
common.LogDebugf("pin is set: %v\n", val)
|
||||
}
|
||||
}
|
||||
|
||||
func handleIndexTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("handle ind temp")
|
||||
if settings.RoomAccess != AccessOpen {
|
||||
if !checkRoomAccess(w, r) {
|
||||
common.LogDebugln("Denied access")
|
||||
return
|
||||
}
|
||||
common.LogDebugln("Granted access")
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
Video, Chat bool
|
||||
MessageHistoryCount int
|
||||
Title string
|
||||
}
|
||||
|
||||
data := Data{
|
||||
Video: true,
|
||||
Chat: true,
|
||||
MessageHistoryCount: settings.MaxMessageCount,
|
||||
Title: "Movie Night!",
|
||||
}
|
||||
|
||||
path := strings.Split(strings.TrimLeft(r.URL.Path, "/"), "/")
|
||||
if path[0] == "video" {
|
||||
data.Chat = false
|
||||
data.Title += " - video"
|
||||
}
|
||||
|
||||
// Force browser to replace cache since file was not changed
|
||||
if settings.NoCache {
|
||||
w.Header().Set("Cache-Control", "no-cache, must-revalidate")
|
||||
}
|
||||
|
||||
err := common.ExecuteServerTemplate(w, "main", data)
|
||||
if err != nil {
|
||||
common.LogErrorf("Error executing file, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func handlePublish(conn *rtmp.Conn) {
|
||||
log.Println("handle publish")
|
||||
streams, _ := conn.Streams()
|
||||
|
||||
l.Lock()
|
||||
common.LogDebugln("request string->", conn.URL.RequestURI())
|
||||
urlParts := strings.Split(strings.Trim(conn.URL.RequestURI(), "/"), "/")
|
||||
common.LogDebugln("urlParts->", urlParts)
|
||||
|
||||
if len(urlParts) > 2 {
|
||||
common.LogErrorln("Extra garbage after stream key")
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
if len(urlParts) != 2 {
|
||||
common.LogErrorln("Missing stream key")
|
||||
return
|
||||
}
|
||||
|
||||
if urlParts[1] != settings.GetStreamKey() {
|
||||
common.LogErrorln("Stream key is incorrect. Denying stream.")
|
||||
return //If key not match, deny stream
|
||||
}
|
||||
*/
|
||||
|
||||
streamPath := urlParts[0]
|
||||
ch := channels[streamPath]
|
||||
if ch == nil {
|
||||
ch = &Channel{}
|
||||
ch.que = pubsub.NewQueue()
|
||||
ch.que.WriteHeader(streams)
|
||||
channels[streamPath] = ch
|
||||
} else {
|
||||
ch = nil
|
||||
}
|
||||
l.Unlock()
|
||||
if ch == nil {
|
||||
common.LogErrorln("Unable to start stream, channel is nil.")
|
||||
return
|
||||
}
|
||||
|
||||
stats.startStream()
|
||||
|
||||
common.LogInfoln("Stream started")
|
||||
avutil.CopyPackets(ch.que, conn)
|
||||
common.LogInfoln("Stream finished")
|
||||
|
||||
stats.endStream()
|
||||
|
||||
l.Lock()
|
||||
delete(channels, streamPath)
|
||||
l.Unlock()
|
||||
ch.que.Close()
|
||||
}
|
||||
|
||||
func handlePlay(conn *rtmp.Conn) {
|
||||
log.Println("handle play")
|
||||
l.RLock()
|
||||
ch := channels[conn.URL.Path]
|
||||
l.RUnlock()
|
||||
|
||||
if ch != nil {
|
||||
cursor := ch.que.Latest()
|
||||
avutil.CopyFile(conn, cursor)
|
||||
}
|
||||
}
|
||||
|
||||
func handleDefault(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("handle def")
|
||||
l.RLock()
|
||||
ch := channels[strings.Trim(r.URL.Path, "/")]
|
||||
l.RUnlock()
|
||||
|
||||
if ch != nil {
|
||||
l.StartStream()
|
||||
defer l.StopStream()
|
||||
|
||||
w.Header().Set("Content-Type", "video/x-flv")
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.WriteHeader(200)
|
||||
flusher := w.(http.Flusher)
|
||||
flusher.Flush()
|
||||
|
||||
muxer := flv.NewMuxerWriteFlusher(writeFlusher{httpflusher: flusher, Writer: w})
|
||||
cursor := ch.que.Latest()
|
||||
|
||||
avutil.CopyFile(muxer, cursor)
|
||||
} else {
|
||||
if r.URL.Path != "/" {
|
||||
// not really an error for the server, but for the client.
|
||||
common.LogInfoln("[http 404] ", r.URL.Path)
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
handleIndexTemplate(w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user