init: initial commit
This commit is contained in:
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/lavafroth/okiiparty
|
||||
|
||||
go 1.24.5
|
||||
|
||||
require github.com/gorilla/websocket v1.5.3
|
||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
131
index.html
Normal file
131
index.html
Normal file
@@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Okiiparty</title>
|
||||
|
||||
<style>
|
||||
* {padding:0; margin: 0}
|
||||
body {background: black}
|
||||
|
||||
video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: content;
|
||||
}
|
||||
|
||||
.banner {
|
||||
padding: 1rem;
|
||||
margin: 1rem;
|
||||
outline: .1rem solid #fefefe;
|
||||
border-radius: 1rem;
|
||||
backdrop-filter: blur(10px) brightness(60%);
|
||||
box-sizing: border-box;
|
||||
color: #eee;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 4;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
abbr {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="banner">
|
||||
<abbr title="Okiiparty - Made with ❤️ for pookie">The stream is muted because autoplay requires audio to be switched off. Please unmute the stream and I will disappear.</abbr>
|
||||
</div>
|
||||
|
||||
<video muted controls>
|
||||
<source src="/stream" type="video/mp4">
|
||||
</video>
|
||||
|
||||
<script>
|
||||
const loc = window.location;
|
||||
let proto = loc.protocol == "https:" ? "wss:" : "ws:";
|
||||
let host = loc.host;
|
||||
let wsUri = `${proto}//${host}/broker`;
|
||||
const socket = new WebSocket(wsUri);
|
||||
const video = document.querySelector("video");
|
||||
const banner = document.querySelector(".banner");
|
||||
let requestedWait = false
|
||||
|
||||
function broadcastSeek() {
|
||||
broadcast({
|
||||
action: "seek",
|
||||
time: video.currentTime
|
||||
})
|
||||
}
|
||||
|
||||
function broadcastPlay() {
|
||||
broadcast({action: "play"})
|
||||
}
|
||||
|
||||
function broadcastPause() {
|
||||
console.log("you paused")
|
||||
broadcast({action: "pause"})
|
||||
}
|
||||
|
||||
function messageHandler(event) {
|
||||
let message = JSON.parse(event.data)
|
||||
if (message.action == "play") {
|
||||
video.removeEventListener("play", broadcastPlay)
|
||||
console.log("someone played")
|
||||
video.play()
|
||||
video.addEventListener("play", () => { video.addEventListener("play", broadcastPlay) })
|
||||
}
|
||||
if (message.action == "pause") {
|
||||
video.removeEventListener("pause", broadcastPause)
|
||||
console.log("someone paused")
|
||||
video.pause()
|
||||
video.addEventListener("pause", () => { video.addEventListener("pause", broadcastPause) })
|
||||
}
|
||||
if (message.action == "wait") {
|
||||
if (!video.paused) video.pause()
|
||||
video.controls = false
|
||||
}
|
||||
if (message.action == "canplay") {
|
||||
video.controls = true
|
||||
}
|
||||
|
||||
if (message.action == "seek") {
|
||||
video.removeEventListener("seeked", broadcastSeek)
|
||||
video.currentTime = message.time
|
||||
// this one time, let the event not propagate
|
||||
video.addEventListener("seeked", () => { video.addEventListener("seeked", broadcastSeek) })
|
||||
}
|
||||
}
|
||||
|
||||
socket.addEventListener("message", messageHandler)
|
||||
function broadcast(message) {
|
||||
socket.send(JSON.stringify(message))
|
||||
}
|
||||
|
||||
video.addEventListener("volumechange", () => {
|
||||
banner.style.display = "none";
|
||||
})
|
||||
video.addEventListener("play", broadcastPlay)
|
||||
video.addEventListener("pause", broadcastPause)
|
||||
video.addEventListener("waiting", () => {
|
||||
requestedWait = true
|
||||
broadcast({action: "wait"})
|
||||
video.controls = false
|
||||
})
|
||||
|
||||
video.addEventListener("canplay", () => {
|
||||
if (!requestedWait) {
|
||||
return
|
||||
}
|
||||
broadcast({action: "canplay"})
|
||||
video.controls = true
|
||||
requestedWait = false
|
||||
})
|
||||
|
||||
video.addEventListener("seeked", broadcastSeek)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
83
main.go
Normal file
83
main.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 128,
|
||||
WriteBufferSize: 128,
|
||||
}
|
||||
|
||||
var broker Broker
|
||||
|
||||
type Broker struct {
|
||||
sockets map[*websocket.Conn]struct{}
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func newStreamHandler(movieFile string) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, movieFile)
|
||||
}
|
||||
}
|
||||
|
||||
func playPauseBroker(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Printf("failed to fulfill client request for playPauseBroker: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
broker.mutex.Lock()
|
||||
broker.sockets[conn] = struct{}{}
|
||||
log.Printf("broker currently handles %d clients", len(broker.sockets))
|
||||
broker.mutex.Unlock()
|
||||
|
||||
for {
|
||||
messageType, p, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Printf("failed to read message from client %p: %v", conn, err)
|
||||
log.Printf("will attempt to close connection %p", conn)
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Printf("failed to close connection at %p", conn)
|
||||
}
|
||||
delete(broker.sockets, conn)
|
||||
return
|
||||
}
|
||||
|
||||
for client := range broker.sockets {
|
||||
if client == conn {
|
||||
continue // prevent self echo
|
||||
}
|
||||
|
||||
if err := client.WriteMessage(messageType, p); err != nil {
|
||||
log.Printf("while sending: %s to client %p: %v", p, client, err)
|
||||
}
|
||||
log.Printf("successfully sent %s", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
listenPort := flag.Uint("port", 8000, "port to listen on")
|
||||
flag.Parse()
|
||||
streamFile := flag.Arg(0)
|
||||
if streamFile == "" {
|
||||
log.Fatal("stream file is undefined")
|
||||
}
|
||||
broker.sockets = make(map[*websocket.Conn]struct{})
|
||||
http.HandleFunc("/stream", newStreamHandler(streamFile))
|
||||
http.HandleFunc("/broker", playPauseBroker)
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, "index.html")
|
||||
})
|
||||
|
||||
panic(http.ListenAndServe(fmt.Sprintf(":%d", *listenPort), nil))
|
||||
}
|
||||
Reference in New Issue
Block a user