owncast support draft

This commit is contained in:
Luxferre
2024-09-14 23:42:47 +03:00
parent 4b11209e6d
commit 492cd515c6
5 changed files with 26 additions and 18 deletions
+1
View File
@@ -7,6 +7,7 @@
*.dylib *.dylib
out/ out/
auth/twitch-token.txt auth/twitch-token.txt
auth/oc-token.txt
go.work go.work
go.work.sum go.work.sum
.env .env
+4 -1
View File
@@ -8,5 +8,8 @@
"yt_backend_enabled": false, "yt_backend_enabled": false,
"yt_api_root_url": "https://yt.lemnoslife.com/noKey", "yt_api_root_url": "https://yt.lemnoslife.com/noKey",
"yt_api_poll_timeout": 3, "yt_api_poll_timeout": 3,
"yt_channel": "@foxyshadow" "yt_channel": "@foxyshadow",
"oc_backend_enabled": false,
"oc_server": "https://streams.luxferre.top",
"oc_api_poll_timeout": 2
} }
+1 -1
View File
@@ -1,7 +1,7 @@
LDFLAGS = -s -w LDFLAGS = -s -w
LDFLAGS_WIN = -H windowsgui -s -w LDFLAGS_WIN = -H windowsgui -s -w
GOFLAGS = -trimpath GOFLAGS = -trimpath
SOURCES = server.go twitch.go youtube.go SOURCES = server.go twitch.go youtube.go owncast.go
BIN = streamgoose BIN = streamgoose
unix: unix:
+4
View File
@@ -164,6 +164,10 @@ func main() {
go youtube_run(config) /* start the YouTube backend */ go youtube_run(config) /* start the YouTube backend */
} }
if _, ok := config["oc_backend_enabled"]; ok && config["oc_backend_enabled"].(bool) == true {
go owncast_run(config) /* start the Owncast backend */
}
/* start the GUI */ /* start the GUI */
debug := false debug := false
if _, ok := config["debug"]; ok && config["debug"].(bool) == true { if _, ok := config["debug"]; ok && config["debug"].(bool) == true {
+16 -16
View File
@@ -16,16 +16,16 @@ import (
) )
/* global API root URL placeholder */ /* global API root URL placeholder */
var API_ROOT string var YT_API_ROOT string
const ID_CACHE_LIMIT = 1000 const YT_ID_CACHE_LIMIT = 1000
/* global message ID cache */ /* global message ID cache */
var msg_id_cache []string var yt_msg_id_cache []string
/* API request helper function */ /* API request helper function */
func api_get(endpoint string) map[string]interface{} { func yt_api_get(endpoint string) map[string]interface{} {
url := API_ROOT + endpoint /* get full URL */ url := YT_API_ROOT + endpoint /* get full URL */
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
req.Header.Set("Accept", "application/json; charset=utf-8") req.Header.Set("Accept", "application/json; charset=utf-8")
@@ -52,7 +52,7 @@ func api_get(endpoint string) map[string]interface{} {
func yt_process_message(rawmsg map[string]interface{}) { func yt_process_message(rawmsg map[string]interface{}) {
/* if the message came from a real user, this is the one we should process */ /* if the message came from a real user, this is the one we should process */
if rawmsg["kind"] == "youtube#liveChatMessage" && rawmsg["snippet"].(map[string]interface{})["type"] == "textMessageEvent" { if rawmsg["kind"] == "youtube#liveChatMessage" && rawmsg["snippet"].(map[string]interface{})["type"] == "textMessageEvent" {
if !slices.Contains(msg_id_cache, rawmsg["id"].(string)) { /* we have a fresh message here */ if !slices.Contains(yt_msg_id_cache, rawmsg["id"].(string)) { /* we have a fresh message here */
snip := rawmsg["snippet"].(map[string]interface{}) snip := rawmsg["snippet"].(map[string]interface{})
author := rawmsg["authorDetails"].(map[string]interface{}) author := rawmsg["authorDetails"].(map[string]interface{})
@@ -97,10 +97,10 @@ func yt_process_message(rawmsg map[string]interface{}) {
message_transport <- out_message message_transport <- out_message
/* update the ID cache */ /* update the ID cache */
msg_id_cache = append(msg_id_cache, rawmsg["id"].(string)) yt_msg_id_cache = append(yt_msg_id_cache, rawmsg["id"].(string))
cachelen := len(msg_id_cache) cachelen := len(yt_msg_id_cache)
if cachelen > ID_CACHE_LIMIT { /* trim the excess */ if cachelen > YT_ID_CACHE_LIMIT { /* trim the excess */
msg_id_cache = msg_id_cache[cachelen-ID_CACHE_LIMIT:cachelen] yt_msg_id_cache = yt_msg_id_cache[cachelen-YT_ID_CACHE_LIMIT:cachelen]
} }
} }
@@ -112,7 +112,7 @@ func youtube_run(config map[string]interface{}) {
API_POLL_TIMEOUT := 1 /* polling timeout in seconds */ API_POLL_TIMEOUT := 1 /* polling timeout in seconds */
if _, ok := config["yt_api_root_url"]; ok { if _, ok := config["yt_api_root_url"]; ok {
API_ROOT = config["yt_api_root_url"].(string) YT_API_ROOT = config["yt_api_root_url"].(string)
} else { } else {
log.Fatal("API Root URL missing in config!") log.Fatal("API Root URL missing in config!")
} }
@@ -126,7 +126,7 @@ func youtube_run(config map[string]interface{}) {
video_id = config["yt_video_id"].(string) video_id = config["yt_video_id"].(string)
} else { /* no video ID supplied, begin with the usual flow */ } else { /* no video ID supplied, begin with the usual flow */
/* first, get channel ID by YouTube handle */ /* first, get channel ID by YouTube handle */
channel_id_data := api_get("/channels?part=id&forHandle=" + config["yt_channel"].(string)) channel_id_data := yt_api_get("/channels?part=id&forHandle=" + config["yt_channel"].(string))
channel_id := "" channel_id := ""
if _, ok := channel_id_data["items"]; ok && len(channel_id_data["items"].([]interface{})) > 0 { if _, ok := channel_id_data["items"]; ok && len(channel_id_data["items"].([]interface{})) > 0 {
channel_id = channel_id_data["items"].([]interface{})[0].(map[string]interface{})["id"].(string) channel_id = channel_id_data["items"].([]interface{})[0].(map[string]interface{})["id"].(string)
@@ -135,7 +135,7 @@ func youtube_run(config map[string]interface{}) {
} }
/* then, get live broadcast video ID by channel ID */ /* then, get live broadcast video ID by channel ID */
video_id_data := api_get("/search?part=snippet&eventType=live&type=video&channelId=" + channel_id) video_id_data := yt_api_get("/search?part=snippet&eventType=live&type=video&channelId=" + channel_id)
if _, ok := video_id_data["items"]; ok && len(video_id_data["items"].([]interface{})) > 0 { if _, ok := video_id_data["items"]; ok && len(video_id_data["items"].([]interface{})) > 0 {
id_desc := video_id_data["items"].([]interface{})[0].(map[string]interface{})["id"] id_desc := video_id_data["items"].([]interface{})[0].(map[string]interface{})["id"]
video_id = id_desc.(map[string]interface{})["videoId"].(string) video_id = id_desc.(map[string]interface{})["videoId"].(string)
@@ -146,7 +146,7 @@ func youtube_run(config map[string]interface{}) {
/* now, get active live chat ID by broadcast video ID */ /* now, get active live chat ID by broadcast video ID */
chat_id := "" chat_id := ""
chat_id_data := api_get("/videos?part=liveStreamingDetails&id=" + video_id) chat_id_data := yt_api_get("/videos?part=liveStreamingDetails&id=" + video_id)
if _, ok := chat_id_data["items"]; ok && len(chat_id_data["items"].([]interface{})) > 0 { if _, ok := chat_id_data["items"]; ok && len(chat_id_data["items"].([]interface{})) > 0 {
details := chat_id_data["items"].([]interface{})[0].(map[string]interface{})["liveStreamingDetails"] details := chat_id_data["items"].([]interface{})[0].(map[string]interface{})["liveStreamingDetails"]
chat_id = details.(map[string]interface{})["activeLiveChatId"].(string) chat_id = details.(map[string]interface{})["activeLiveChatId"].(string)
@@ -161,9 +161,9 @@ func youtube_run(config map[string]interface{}) {
log.Println("StreamGoose YouTube Go backend started") log.Println("StreamGoose YouTube Go backend started")
var raw_chat_data map[string]interface{} var raw_chat_data map[string]interface{}
msg_id_cache = append(msg_id_cache, "") /* init the cache */ yt_msg_id_cache = append(yt_msg_id_cache, "") /* init the cache */
for { /* main loop */ for { /* main loop */
raw_chat_data = api_get("/liveChat/messages?part=snippet,authorDetails&liveChatId=" + chat_id) raw_chat_data = yt_api_get("/liveChat/messages?part=snippet,authorDetails&liveChatId=" + chat_id)
if _, ok := raw_chat_data["items"]; ok && len(raw_chat_data["items"].([]interface{})) > 0 { if _, ok := raw_chat_data["items"]; ok && len(raw_chat_data["items"].([]interface{})) > 0 {
for _, rawmsg := range raw_chat_data["items"].([]interface{}) { /* iterate over raw message objects */ for _, rawmsg := range raw_chat_data["items"].([]interface{}) { /* iterate over raw message objects */
yt_process_message(rawmsg.(map[string]interface{})) yt_process_message(rawmsg.(map[string]interface{}))