mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 20:28:15 +03:00
feat(api): add server-side caching for requests that could benefit (#3463)
* feat(api): add server-side caching for requests that could benefit for them * fix(tests): do not cache responses while in tests * fix: remove commented out leftover code * chore(deps): update dependency html-webpack-plugin to v5.5.4 * Bundle embedded web app * fix: remove caching for web app assets under test * chore(tests): re-enable temporarily disabled test * chore(deps): update dependency typescript to v5.3.3 * Bundle embedded web app * chore(deps): update dependency npm to v10.2.5 * Bundle embedded web app --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Owncast <owncast@owncast.online>
This commit is contained in:
parent
b6efe49086
commit
2217f0614a
21 changed files with 316 additions and 119 deletions
|
@ -30,4 +30,8 @@ var (
|
||||||
|
|
||||||
// PublicFilesPath is the optional directory for hosting public files.
|
// PublicFilesPath is the optional directory for hosting public files.
|
||||||
PublicFilesPath = filepath.Join(DataDirectory, "public")
|
PublicFilesPath = filepath.Join(DataDirectory, "public")
|
||||||
|
|
||||||
|
// DisableResponseCaching will disable caching of API and resource
|
||||||
|
// responses. Disable this feature to turn off the optimizations.
|
||||||
|
DisableResponseCaching = false
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/core"
|
"github.com/owncast/owncast/core"
|
||||||
|
@ -13,8 +14,19 @@ import (
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
"github.com/owncast/owncast/router/middleware"
|
"github.com/owncast/owncast/router/middleware"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
cache "github.com/victorspringer/http-cache"
|
||||||
|
"github.com/victorspringer/http-cache/adapter/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FileServerHandler struct {
|
||||||
|
HLSPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fsh *FileServerHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(rw, r, fsh.HLSPath)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleHLSRequest will manage all requests to HLS content.
|
// HandleHLSRequest will manage all requests to HLS content.
|
||||||
func HandleHLSRequest(w http.ResponseWriter, r *http.Request) {
|
func HandleHLSRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
// Sanity check to limit requests to HLS file types.
|
// Sanity check to limit requests to HLS file types.
|
||||||
|
@ -23,6 +35,26 @@ func HandleHLSRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
responseCache, err := memory.NewAdapter(
|
||||||
|
memory.AdapterWithAlgorithm(memory.LRU),
|
||||||
|
memory.AdapterWithCapacity(20),
|
||||||
|
memory.AdapterWithStorageCapacity(209_715_200),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to create web cache", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since HLS segments cannot be changed once they're rendered, we can cache
|
||||||
|
// individual segments for a long time.
|
||||||
|
longTermHLSSegmentCache, err := cache.NewClient(
|
||||||
|
cache.ClientWithAdapter(responseCache),
|
||||||
|
cache.ClientWithTTL(30*time.Second),
|
||||||
|
cache.ClientWithExpiresHeader(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to create web cache client", err)
|
||||||
|
}
|
||||||
|
|
||||||
requestedPath := r.URL.Path
|
requestedPath := r.URL.Path
|
||||||
relativePath := strings.Replace(requestedPath, "/hls/", "", 1)
|
relativePath := strings.Replace(requestedPath, "/hls/", "", 1)
|
||||||
fullPath := filepath.Join(config.HLSStoragePath, relativePath)
|
fullPath := filepath.Join(config.HLSStoragePath, relativePath)
|
||||||
|
@ -48,6 +80,10 @@ func HandleHLSRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
} else {
|
} else {
|
||||||
cacheTime := utils.GetCacheDurationSecondsForPath(relativePath)
|
cacheTime := utils.GetCacheDurationSecondsForPath(relativePath)
|
||||||
w.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(cacheTime))
|
w.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(cacheTime))
|
||||||
|
|
||||||
|
fileServer := &FileServerHandler{HLSPath: fullPath}
|
||||||
|
longTermHLSSegmentCache.Middleware(fileServer).ServeHTTP(w, r)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware.EnableCors(w)
|
middleware.EnableCors(w)
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -69,6 +69,7 @@ require (
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
|
github.com/victorspringer/http-cache v0.0.0-20231006141456-6446fe59efba // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -138,6 +138,8 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f
|
||||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g=
|
github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g=
|
||||||
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
||||||
|
github.com/victorspringer/http-cache v0.0.0-20231006141456-6446fe59efba h1:+oqDKQIOdkkvro1psUKtI4oH9WBeKkGY2S8h9/lo288=
|
||||||
|
github.com/victorspringer/http-cache v0.0.0-20231006141456-6446fe59efba/go.mod h1:D1AD6nlXv7HkIfTVd8ZWK1KQEiXYNy/LbLkx8H9tIQw=
|
||||||
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
||||||
|
|
27
main.go
27
main.go
|
@ -17,17 +17,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dbFile = flag.String("database", "", "Path to the database file.")
|
dbFile = flag.String("database", "", "Path to the database file.")
|
||||||
logDirectory = flag.String("logdir", "", "Directory where logs will be written to")
|
logDirectory = flag.String("logdir", "", "Directory where logs will be written to")
|
||||||
backupDirectory = flag.String("backupdir", "", "Directory where backups will be written to")
|
backupDirectory = flag.String("backupdir", "", "Directory where backups will be written to")
|
||||||
enableDebugOptions = flag.Bool("enableDebugFeatures", false, "Enable additional debugging options.")
|
enableDebugOptions = flag.Bool("enableDebugFeatures", false, "Enable additional debugging options.")
|
||||||
enableVerboseLogging = flag.Bool("enableVerboseLogging", false, "Enable additional logging.")
|
enableVerboseLogging = flag.Bool("enableVerboseLogging", false, "Enable additional logging.")
|
||||||
restoreDatabaseFile = flag.String("restoreDatabase", "", "Restore an Owncast database backup")
|
restoreDatabaseFile = flag.String("restoreDatabase", "", "Restore an Owncast database backup")
|
||||||
newAdminPassword = flag.String("adminpassword", "", "Set your admin password")
|
newAdminPassword = flag.String("adminpassword", "", "Set your admin password")
|
||||||
newStreamKey = flag.String("streamkey", "", "Set a temporary stream key for this session")
|
newStreamKey = flag.String("streamkey", "", "Set a temporary stream key for this session")
|
||||||
webServerPortOverride = flag.String("webserverport", "", "Force the web server to listen on a specific port")
|
webServerPortOverride = flag.String("webserverport", "", "Force the web server to listen on a specific port")
|
||||||
webServerIPOverride = flag.String("webserverip", "", "Force web server to listen on this IP address")
|
webServerIPOverride = flag.String("webserverip", "", "Force web server to listen on this IP address")
|
||||||
rtmpPortOverride = flag.Int("rtmpport", 0, "Set listen port for the RTMP server")
|
rtmpPortOverride = flag.Int("rtmpport", 0, "Set listen port for the RTMP server")
|
||||||
|
disableResponseCaching = flag.Bool("disableResponseCaching", false, "Do not optimize performance by caching of web responses")
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint:cyclop
|
// nolint:cyclop
|
||||||
|
@ -42,6 +43,10 @@ func main() {
|
||||||
config.BackupDirectory = *backupDirectory
|
config.BackupDirectory = *backupDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *disableResponseCaching {
|
||||||
|
config.DisableResponseCaching = *disableResponseCaching
|
||||||
|
}
|
||||||
|
|
||||||
// Create the data directory if needed
|
// Create the data directory if needed
|
||||||
if !utils.DoesFileExists("data") {
|
if !utils.DoesFileExists("data") {
|
||||||
if err := os.Mkdir("./data", 0o700); err != nil {
|
if err := os.Mkdir("./data", 0o700); err != nil {
|
||||||
|
|
109
router/router.go
109
router/router.go
|
@ -24,52 +24,131 @@ import (
|
||||||
"github.com/owncast/owncast/router/middleware"
|
"github.com/owncast/owncast/router/middleware"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
"github.com/owncast/owncast/yp"
|
"github.com/owncast/owncast/yp"
|
||||||
|
|
||||||
|
cache "github.com/victorspringer/http-cache"
|
||||||
|
"github.com/victorspringer/http-cache/adapter/memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start starts the router for the http, ws, and rtmp.
|
// Start starts the router for the http, ws, and rtmp.
|
||||||
func Start() error {
|
func Start() error {
|
||||||
|
// Setup a web response cache
|
||||||
|
enableCache := !config.DisableResponseCaching
|
||||||
|
|
||||||
|
responseCache, err := memory.NewAdapter(
|
||||||
|
memory.AdapterWithAlgorithm(memory.LRU),
|
||||||
|
memory.AdapterWithCapacity(50),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to create web cache", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
superShortCacheClient, err := cache.NewClient(
|
||||||
|
cache.ClientWithAdapter(responseCache),
|
||||||
|
cache.ClientWithTTL(3*time.Second),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to create web cache client", err)
|
||||||
|
}
|
||||||
|
reasonableDurationCacheClient, err := cache.NewClient(
|
||||||
|
cache.ClientWithAdapter(responseCache),
|
||||||
|
cache.ClientWithTTL(8*time.Second),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to create web cache client", err)
|
||||||
|
}
|
||||||
|
longerDurationCacheClient, err := cache.NewClient(
|
||||||
|
cache.ClientWithAdapter(responseCache),
|
||||||
|
cache.ClientWithTTL(3*time.Minute),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to create web cache client", err)
|
||||||
|
}
|
||||||
// The primary web app.
|
// The primary web app.
|
||||||
http.HandleFunc("/", controllers.IndexHandler)
|
if enableCache {
|
||||||
|
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
longerDurationCacheClient.Middleware(http.HandlerFunc(controllers.IndexHandler)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
http.HandleFunc("/", controllers.IndexHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// The admin web app.
|
// The admin web app.
|
||||||
http.HandleFunc("/admin/", middleware.RequireAdminAuth(controllers.IndexHandler))
|
http.HandleFunc("/admin/", middleware.RequireAdminAuth(controllers.IndexHandler))
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
http.HandleFunc("/thumbnail.jpg", controllers.GetThumbnail)
|
http.HandleFunc("/thumbnail.jpg", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
http.HandleFunc("/preview.gif", controllers.GetPreview)
|
superShortCacheClient.Middleware(http.HandlerFunc(controllers.GetThumbnail)).ServeHTTP(rw, r)
|
||||||
http.HandleFunc("/logo", controllers.GetLogo)
|
})
|
||||||
|
|
||||||
|
http.HandleFunc("/preview.gif", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
superShortCacheClient.Middleware(http.HandlerFunc(controllers.GetPreview)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
http.HandleFunc("/logo", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
longerDurationCacheClient.Middleware(http.HandlerFunc(controllers.GetLogo)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// Custom Javascript
|
// Custom Javascript
|
||||||
http.HandleFunc("/customjavascript", controllers.ServeCustomJavascript)
|
http.HandleFunc("/customjavascript", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
longerDurationCacheClient.Middleware(http.HandlerFunc(controllers.ServeCustomJavascript)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// Return a single emoji image.
|
// Return a single emoji image.
|
||||||
http.HandleFunc(config.EmojiDir, controllers.GetCustomEmojiImage)
|
http.HandleFunc(config.EmojiDir, func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
longerDurationCacheClient.Middleware(http.HandlerFunc(controllers.GetCustomEmojiImage)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// return the logo
|
// return the logo
|
||||||
|
|
||||||
// return a logo that's compatible with external social networks
|
// return a logo that's compatible with external social networks
|
||||||
http.HandleFunc("/logo/external", controllers.GetCompatibleLogo)
|
http.HandleFunc("/logo/external", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
longerDurationCacheClient.Middleware(http.HandlerFunc(controllers.GetCompatibleLogo)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// robots.txt
|
// robots.txt
|
||||||
http.HandleFunc("/robots.txt", controllers.GetRobotsDotTxt)
|
http.HandleFunc("/robots.txt", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
longerDurationCacheClient.Middleware(http.HandlerFunc(controllers.GetRobotsDotTxt)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// status of the system
|
// status of the system
|
||||||
http.HandleFunc("/api/status", controllers.GetStatus)
|
if enableCache {
|
||||||
|
http.HandleFunc("/api/status", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
superShortCacheClient.Middleware(http.HandlerFunc(controllers.GetStatus)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
http.HandleFunc("/api/status", controllers.GetStatus)
|
||||||
|
}
|
||||||
|
|
||||||
// custom emoji supported in the chat
|
// custom emoji supported in the chat
|
||||||
http.HandleFunc("/api/emoji", controllers.GetCustomEmojiList)
|
http.HandleFunc("/api/emoji", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
reasonableDurationCacheClient.Middleware(http.HandlerFunc(controllers.GetCustomEmojiList)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// chat rest api
|
// chat rest api
|
||||||
http.HandleFunc("/api/chat", middleware.RequireUserAccessToken(controllers.GetChatMessages))
|
if enableCache {
|
||||||
|
http.HandleFunc("/api/chat", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
superShortCacheClient.Middleware(middleware.RequireUserAccessToken(controllers.GetChatMessages))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
http.HandleFunc("/api/chat", middleware.RequireUserAccessToken(controllers.GetChatMessages))
|
||||||
|
}
|
||||||
|
|
||||||
// web config api
|
// web config api
|
||||||
http.HandleFunc("/api/config", controllers.GetWebConfig)
|
if enableCache {
|
||||||
|
http.HandleFunc("/api/config", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
superShortCacheClient.Middleware(http.HandlerFunc(controllers.GetWebConfig)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
http.HandleFunc("/api/config", controllers.GetWebConfig)
|
||||||
|
}
|
||||||
|
|
||||||
// return the YP protocol data
|
// return the YP protocol data
|
||||||
http.HandleFunc("/api/yp", yp.GetYPResponse)
|
http.HandleFunc("/api/yp", yp.GetYPResponse)
|
||||||
|
|
||||||
// list of all social platforms
|
// list of all social platforms
|
||||||
http.HandleFunc("/api/socialplatforms", controllers.GetAllSocialPlatforms)
|
http.HandleFunc("/api/socialplatforms", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
reasonableDurationCacheClient.Middleware(http.HandlerFunc(controllers.GetAllSocialPlatforms)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// return the list of video variants available
|
// return the list of video variants available
|
||||||
http.HandleFunc("/api/video/variants", controllers.GetVideoStreamOutputVariants)
|
http.HandleFunc("/api/video/variants", controllers.GetVideoStreamOutputVariants)
|
||||||
|
@ -84,7 +163,9 @@ func Start() error {
|
||||||
http.HandleFunc("/api/remotefollow", controllers.RemoteFollow)
|
http.HandleFunc("/api/remotefollow", controllers.RemoteFollow)
|
||||||
|
|
||||||
// return followers
|
// return followers
|
||||||
http.HandleFunc("/api/followers", middleware.HandlePagination(controllers.GetFollowers))
|
http.HandleFunc("/api/followers", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
reasonableDurationCacheClient.Middleware(middleware.HandlePagination(controllers.GetFollowers)).ServeHTTP(rw, r)
|
||||||
|
})
|
||||||
|
|
||||||
// save client video playback metrics
|
// save client video playback metrics
|
||||||
http.HandleFunc("/api/metrics/playback", controllers.ReportPlaybackMetrics)
|
http.HandleFunc("/api/metrics/playback", controllers.ReportPlaybackMetrics)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
self.__SSG_MANIFEST=new Set,self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB();
|
1
static/web/_next/static/chunks/4281-f024035bb909af4e.js
vendored
Normal file
1
static/web/_next/static/chunks/4281-f024035bb909af4e.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/web/_next/static/chunks/5056-b14d7a3d2aee94c3.js
vendored
Normal file
1
static/web/_next/static/chunks/5056-b14d7a3d2aee94c3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
static/web/_next/static/chunks/5283-23ec4c27947dae66.js
vendored
Normal file
11
static/web/_next/static/chunks/5283-23ec4c27947dae66.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/web/_next/static/chunks/5584.08b6806ea3012a43.js
vendored
Normal file
1
static/web/_next/static/chunks/5584.08b6806ea3012a43.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[5584],{15584:function(e,t,n){n.r(t);var r,a,s,o,i,l=n(67294);function _extends(){return(_extends=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.default=function(e){return l.createElement("svg",_extends({xmlns:"http://www.w3.org/2000/svg",width:500,height:500,viewBox:"0 0 132.292 132.292"},e),r||(r=l.createElement("linearGradient",{id:"like_svg__a",x1:432.851,x2:464.644,y1:49.977,y2:49.977,gradientUnits:"userSpaceOnUse"},l.createElement("stop",{offset:0,stopColor:"#2087e2"}),l.createElement("stop",{offset:1,stopColor:"#b63fff"}))),a||(a=l.createElement("path",{fill:"url(#like_svg__a)",d:"M438.672 34.08h20.151a5.82 5.82 45 0 1 5.82 5.821v20.151a5.82 5.82 135 0 1-5.82 5.821h-20.15a5.82 5.82 45 0 1-5.822-5.82V39.9a5.82 5.82 135 0 1 5.821-5.82z",transform:"matrix(4.1611 0 0 4.1611 -1801.14 -141.813)"})),s||(s=l.createElement("path",{fill:"#853dd0",d:"M106.243 25.198 110 33.435l5.378 24.12-20.557 29.696-28.676 20.66-35.66-24.468 49.536 48.849h28.048a24.221 24.221 0 0 0 24.222-24.222V44.165z",opacity:.75})),o||(o=l.createElement("path",{fill:"#8392ee",d:"M51.275 39.14s-36.386-7.356-17.999 25.83c13.869 25.032 29.59 23.091 29.59 23.091S47.512 65.822 51.275 39.14"})),i||(i=l.createElement("path",{fill:"none",stroke:"#fff",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:7.865,d:"M674.216 70.254c-21.056-22.863-45.943 2.215-45.943 2.215s-24.888-25.078-45.943-2.216c-21.056 22.863 16.89 64.133 45.943 78.023 29.053-13.89 66.998-55.16 45.943-78.022",transform:"matrix(.95455 0 0 .95455 -533.57 -33.626)"})))}}}]);
|
19
static/web/_next/static/chunks/6017-bb9b0312a26c35dd.js
vendored
Normal file
19
static/web/_next/static/chunks/6017-bb9b0312a26c35dd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/web/_next/static/chunks/7268.461abbd8430d83c0.js
vendored
Normal file
1
static/web/_next/static/chunks/7268.461abbd8430d83c0.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7268],{97268:function(e,t,r){r.r(t);var a,n,s,o,l=r(67294);function _extends(){return(_extends=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e}).apply(this,arguments)}t.default=function(e){return l.createElement("svg",_extends({xmlns:"http://www.w3.org/2000/svg",width:500,height:500,viewBox:"0 0 132.292 132.292"},e),a||(a=l.createElement("linearGradient",{id:"repost_svg__a",x1:432.851,x2:464.644,y1:49.977,y2:49.977,gradientUnits:"userSpaceOnUse"},l.createElement("stop",{offset:0,stopColor:"#2087e2"}),l.createElement("stop",{offset:1,stopColor:"#b63fff"}))),n||(n=l.createElement("path",{fill:"url(#repost_svg__a)",d:"M438.672 34.08h20.151a5.82 5.82 45 0 1 5.82 5.821v20.151a5.82 5.82 135 0 1-5.82 5.821h-20.15a5.82 5.82 45 0 1-5.822-5.82V39.9a5.82 5.82 135 0 1 5.821-5.82z",transform:"matrix(4.16112 0 0 4.1611 -1801.146 -141.813)"})),s||(s=l.createElement("path",{fill:"#7f40cf",d:"m103.028 50.073-.794 41.033-10.18 12.882-49.412 3.477 26.027 24.827h39.4c13.378 0 24.223-10.845 24.222-24.222V68.265l-9.86-12.31z",opacity:.75})),o||(o=l.createElement("g",{fill:"none",stroke:"#fff",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:7.865},l.createElement("path",{d:"M741.453 94.965h-41.418a14.744 14.744 0 0 0-14.777 14.777v41.418m14.777 14.777h41.418c8.187 0 14.777-6.59 14.777-14.777v-41.418",transform:"translate(-696.642 -71.915)scale(1.05833)"}),l.createElement("path",{d:"m670.076 143.371 15.182 13.79 15.5-13.6",transform:"translate(-696.642 -71.915)scale(1.05833)"}),l.createElement("path",{d:"m670.076 143.371 15.182 13.79 15.5-13.6",transform:"rotate(180 414.466 105.278)scale(1.05833)"}))))}}}]);
|
1
static/web/_next/static/chunks/7521-9644c656878abfe6.js
vendored
Normal file
1
static/web/_next/static/chunks/7521-9644c656878abfe6.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/web/_next/static/chunks/9069.663a9f4ff16078e0.js
vendored
Normal file
1
static/web/_next/static/chunks/9069.663a9f4ff16078e0.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9069],{49069:function(e,t,r){r.r(t);var a,n,o,l,s=r(67294);function _extends(){return(_extends=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e}).apply(this,arguments)}t.default=function(e){return s.createElement("svg",_extends({xmlns:"http://www.w3.org/2000/svg",width:500,height:500,viewBox:"0 0 132.292 132.292"},e),a||(a=s.createElement("linearGradient",{id:"follow_svg__a",x1:432.851,x2:464.644,y1:49.977,y2:49.977,gradientUnits:"userSpaceOnUse"},s.createElement("stop",{offset:0,stopColor:"#2087e2"}),s.createElement("stop",{offset:1,stopColor:"#b63fff"}))),n||(n=s.createElement("path",{fill:"url(#follow_svg__a)",d:"M438.672 34.08h20.151a5.82 5.82 45 0 1 5.82 5.821v20.151a5.82 5.82 135 0 1-5.82 5.821h-20.15a5.82 5.82 45 0 1-5.822-5.82V39.9a5.82 5.82 135 0 1 5.821-5.82z",transform:"matrix(4.16112 0 0 4.1611 -1801.146 -141.813)"})),o||(o=s.createElement("path",{fill:"#8842da",d:"m99.29 73.002-1.238 22.769-22.423.995 25.259 35.526h7.183c13.377-.001 24.22-10.845 24.22-24.222V89.683z",opacity:.85})),l||(l=s.createElement("g",{stroke:"#fff",strokeLinecap:"round",strokeLinejoin:"round"},s.createElement("circle",{cx:876.218,cy:118.03,r:21.554,fill:"none",strokeWidth:8.788,transform:"matrix(.90817 0 0 .9124 -737.017 -65.428)"}),s.createElement("path",{fill:"none",strokeWidth:6.641,d:"M845.107 163.996c0-16.543 13.41-29.953 29.953-29.953a29.953 29.953 0 0 1 19.632 7.331",transform:"matrix(1.14743 0 0 1.26483 -944.188 -103.004)"}),s.createElement("g",{fill:"#fff",strokeWidth:7.559},s.createElement("path",{d:"m881.641 159.874 34.92.28",transform:"translate(-853.609 -74.031)scale(1.05833)"}),s.createElement("path",{d:"m881.641 159.874 34.92.28",transform:"rotate(90 561.76 -294.47)scale(1.05833)"})))))}}}]);
|
6
static/web/_next/static/chunks/ee8b1517-4bb4890d4ff38215.js
vendored
Normal file
6
static/web/_next/static/chunks/ee8b1517-4bb4890d4ff38215.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/web/_next/static/chunks/pages/admin/upgrade-ba6e73bb555cba17.js
vendored
Normal file
1
static/web/_next/static/chunks/pages/admin/upgrade-ba6e73bb555cba17.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9262],{70918:function(e,t,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/upgrade",function(){return a(7494)}])},7494:function(e,t,a){"use strict";a.r(t);var n=a(85893),s=a(67294),l=a(59899),r=a(2307),d=a(53740),u=a(92863),i=a(15578);let{Title:o}=d.default,AssetTable=e=>{let t=Object.values(e);return(0,n.jsx)(r.Z,{dataSource:t,columns:[{title:"Name",dataIndex:"name",key:"name",render:(e,t)=>(0,n.jsx)("a",{href:t.browser_download_url,children:e})},{title:"Size",dataIndex:"size",key:"size",render:e=>"".concat((e/1024/1024).toFixed(2)," MB")}],rowKey:e=>e.id,size:"large",pagination:!1})},Logs=()=>{let[e,t]=(0,s.useState)({html_url:"",name:"",created_at:null,body:"",assets:[]}),getRelease=async()=>{try{let e=await (0,u.Kt)();t(e)}catch(e){console.log("==== error",e)}};return((0,s.useEffect)(()=>{getRelease()},[]),e)?(0,n.jsxs)("div",{className:"upgrade-page",children:[(0,n.jsx)(o,{level:2,children:(0,n.jsx)("a",{href:e.html_url,children:e.name})}),(0,n.jsx)(o,{level:5,children:new Date(e.created_at).toDateString()}),(0,n.jsx)(l.U,{children:e.body}),(0,n.jsx)("h3",{children:"Downloads"}),(0,n.jsx)(AssetTable,{...e.assets})]}):null};Logs.getLayout=function(e){return(0,n.jsx)(i.l,{page:e})},t.default=Logs}},function(e){e.O(0,[5596,1130,4104,9403,1024,3942,971,6697,1664,1749,1700,2122,7752,5891,2891,4749,6627,8966,7521,5578,9774,2888,179],function(){return e(e.s=70918)}),_N_E=e.O()}]);
|
1
static/web/_next/static/chunks/webpack-17c574c3c0f8700e.js
vendored
Normal file
1
static/web/_next/static/chunks/webpack-17c574c3c0f8700e.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -9,9 +9,9 @@ request = request('http://127.0.0.1:8080');
|
||||||
var ajv = new Ajv();
|
var ajv = new Ajv();
|
||||||
var nodeInfoSchema = jsonfile.readFileSync('schema/nodeinfo_2.0.json');
|
var nodeInfoSchema = jsonfile.readFileSync('schema/nodeinfo_2.0.json');
|
||||||
|
|
||||||
const serverName = 'owncast.server.test'
|
const serverName = 'owncast.server.test';
|
||||||
const serverURL = 'http://' + serverName
|
const serverURL = 'http://' + serverName;
|
||||||
const fediUsername = 'streamer'
|
const fediUsername = 'streamer';
|
||||||
|
|
||||||
test('disable federation', async (done) => {
|
test('disable federation', async (done) => {
|
||||||
const res = await sendAdminRequest('config/federation/enable', false);
|
const res = await sendAdminRequest('config/federation/enable', false);
|
||||||
|
@ -59,10 +59,7 @@ test('verify responses of /federation/ when federation is disabled', async (done
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set required parameters and enable federation', async (done) => {
|
test('set required parameters and enable federation', async (done) => {
|
||||||
const res1 = await sendAdminRequest(
|
const res1 = await sendAdminRequest('config/serverurl', serverURL);
|
||||||
'config/serverurl',
|
|
||||||
serverURL
|
|
||||||
);
|
|
||||||
const res2 = await sendAdminRequest(
|
const res2 = await sendAdminRequest(
|
||||||
'config/federation/username',
|
'config/federation/username',
|
||||||
fediUsername
|
fediUsername
|
||||||
|
@ -73,28 +70,47 @@ test('set required parameters and enable federation', async (done) => {
|
||||||
|
|
||||||
test('verify responses of /.well-known/webfinger when federation is enabled', async (done) => {
|
test('verify responses of /.well-known/webfinger when federation is enabled', async (done) => {
|
||||||
const resNoResource = request.get('/.well-known/webfinger').expect(400);
|
const resNoResource = request.get('/.well-known/webfinger').expect(400);
|
||||||
const resBadResource = request.get(
|
const resBadResource = request
|
||||||
'/.well-known/webfinger?resource=' + fediUsername + '@' + serverName
|
.get('/.well-known/webfinger?resource=' + fediUsername + '@' + serverName)
|
||||||
).expect(400);
|
.expect(400);
|
||||||
const resBadResource2 = request.get(
|
const resBadResource2 = request
|
||||||
'/.well-known/webfinger?resource=notacct:' + fediUsername + '@' + serverName
|
.get(
|
||||||
).expect(400);
|
'/.well-known/webfinger?resource=notacct:' +
|
||||||
const resBadServer = request.get(
|
fediUsername +
|
||||||
'/.well-known/webfinger?resource=acct:' + fediUsername + '@not' + serverName
|
'@' +
|
||||||
).expect(404);
|
serverName
|
||||||
const resBadUser = request.get(
|
)
|
||||||
'/.well-known/webfinger?resource=acct:not' + fediUsername + '@' + serverName
|
.expect(400);
|
||||||
).expect(404);
|
const resBadServer = request
|
||||||
const resNoAccept = request.get(
|
.get(
|
||||||
'/.well-known/webfinger?resource=acct:' + fediUsername + '@' + serverName
|
'/.well-known/webfinger?resource=acct:' +
|
||||||
).expect(200)
|
fediUsername +
|
||||||
|
'@not' +
|
||||||
|
serverName
|
||||||
|
)
|
||||||
|
.expect(404);
|
||||||
|
const resBadUser = request
|
||||||
|
.get(
|
||||||
|
'/.well-known/webfinger?resource=acct:not' +
|
||||||
|
fediUsername +
|
||||||
|
'@' +
|
||||||
|
serverName
|
||||||
|
)
|
||||||
|
.expect(404);
|
||||||
|
const resNoAccept = request
|
||||||
|
.get(
|
||||||
|
'/.well-known/webfinger?resource=acct:' + fediUsername + '@' + serverName
|
||||||
|
)
|
||||||
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
parseJson(res.text);
|
parseJson(res.text);
|
||||||
});
|
});
|
||||||
const resWithAccept = request.get(
|
const resWithAccept = request
|
||||||
'/.well-known/webfinger?resource=acct:' + fediUsername + '@' + serverName
|
.get(
|
||||||
).expect(200)
|
'/.well-known/webfinger?resource=acct:' + fediUsername + '@' + serverName
|
||||||
|
)
|
||||||
|
.expect(200)
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -104,14 +120,16 @@ test('verify responses of /.well-known/webfinger when federation is enabled', as
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify responses of /.well-known/host-meta when federation is enabled', async (done) => {
|
test('verify responses of /.well-known/host-meta when federation is enabled', async (done) => {
|
||||||
const res = request.get('/.well-known/host-meta')
|
const res = request
|
||||||
|
.get('/.well-known/host-meta')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /xml/);
|
.expect('Content-Type', /xml/);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify responses of /.well-known/nodeinfo when federation is enabled', async (done) => {
|
test('verify responses of /.well-known/nodeinfo when federation is enabled', async (done) => {
|
||||||
const res = request.get('/.well-known/nodeinfo')
|
const res = request
|
||||||
|
.get('/.well-known/nodeinfo')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -121,7 +139,8 @@ test('verify responses of /.well-known/nodeinfo when federation is enabled', asy
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify responses of /.well-known/x-nodeinfo2 when federation is enabled', async (done) => {
|
test('verify responses of /.well-known/x-nodeinfo2 when federation is enabled', async (done) => {
|
||||||
const res = request.get('/.well-known/x-nodeinfo2')
|
const res = request
|
||||||
|
.get('/.well-known/x-nodeinfo2')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -143,7 +162,8 @@ test('verify responses of /nodeinfo/2.0 when federation is enabled', async (done
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify responses of /api/v1/instance when federation is enabled', async (done) => {
|
test('verify responses of /api/v1/instance when federation is enabled', async (done) => {
|
||||||
const res = request.get('/api/v1/instance')
|
const res = request
|
||||||
|
.get('/api/v1/instance')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -153,15 +173,17 @@ test('verify responses of /api/v1/instance when federation is enabled', async (d
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify responses of /federation/user/ when federation is enabled', async (done) => {
|
test('verify responses of /federation/user/ when federation is enabled', async (done) => {
|
||||||
const resNoAccept = request.get('/federation/user/')
|
const resNoAccept = request.get('/federation/user/').expect(307);
|
||||||
.expect(307);
|
const resWithAccept = request
|
||||||
const resWithAccept = request.get('/federation/user/')
|
.get('/federation/user/')
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.expect(404);
|
.expect(404);
|
||||||
const resWithAcceptWrongUsername = request.get('/federation/user/not' + fediUsername)
|
const resWithAcceptWrongUsername = request
|
||||||
|
.get('/federation/user/not' + fediUsername)
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.expect(404);
|
.expect(404);
|
||||||
const resWithAcceptUsername = request.get('/federation/user/' + fediUsername)
|
const resWithAcceptUsername = request
|
||||||
|
.get('/federation/user/' + fediUsername)
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -172,9 +194,9 @@ test('verify responses of /federation/user/ when federation is enabled', async (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('verify responses of /federation/ when federation is enabled', async (done) => {
|
test('verify responses of /federation/ when federation is enabled', async (done) => {
|
||||||
const resNoAccept = request.get('/federation/')
|
const resNoAccept = request.get('/federation/').expect(307);
|
||||||
.expect(307);
|
const resWithAccept = request
|
||||||
const resWithAccept = request.get('/federation/')
|
.get('/federation/')
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.expect(404);
|
.expect(404);
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -3,91 +3,91 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
function install_ffmpeg() {
|
function install_ffmpeg() {
|
||||||
# install a specific version of ffmpeg
|
# install a specific version of ffmpeg
|
||||||
|
|
||||||
FFMPEG_VER="4.4.1"
|
FFMPEG_VER="4.4.1"
|
||||||
FFMPEG_PATH="$(pwd)/ffmpeg-$FFMPEG_VER"
|
FFMPEG_PATH="$(pwd)/ffmpeg-$FFMPEG_VER"
|
||||||
PATH=$FFMPEG_PATH:$PATH
|
PATH=$FFMPEG_PATH:$PATH
|
||||||
|
|
||||||
if ! [[ -d "$FFMPEG_PATH" ]]; then
|
if ! [[ -d "$FFMPEG_PATH" ]]; then
|
||||||
mkdir "$FFMPEG_PATH"
|
mkdir "$FFMPEG_PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pushd "$FFMPEG_PATH" >/dev/null
|
pushd "$FFMPEG_PATH" >/dev/null
|
||||||
|
|
||||||
if [[ -x "$FFMPEG_PATH/ffmpeg" ]]; then
|
if [[ -x "$FFMPEG_PATH/ffmpeg" ]]; then
|
||||||
|
|
||||||
ffmpeg_version=$("$FFMPEG_PATH/ffmpeg" -version | awk -F 'ffmpeg version' '{print $2}' | awk 'NR==1{print $1}')
|
|
||||||
|
|
||||||
if [[ "$ffmpeg_version" == "$FFMPEG_VER-static" ]]; then
|
ffmpeg_version=$("$FFMPEG_PATH/ffmpeg" -version | awk -F 'ffmpeg version' '{print $2}' | awk 'NR==1{print $1}')
|
||||||
popd >/dev/null
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
mv "$FFMPEG_PATH/ffmpeg" "$FFMPEG_PATH/ffmpeg.bk" || rm -f "$FFMPEG_PATH/ffmpeg"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f ffmpeg.zip
|
if [[ "$ffmpeg_version" == "$FFMPEG_VER-static" ]]; then
|
||||||
curl -sL --fail https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v${FFMPEG_VER}/ffmpeg-${FFMPEG_VER}-linux-64.zip --output ffmpeg.zip >/dev/null
|
popd >/dev/null
|
||||||
unzip -o ffmpeg.zip >/dev/null && rm -f ffmpeg.zip
|
return 0
|
||||||
chmod +x ffmpeg
|
else
|
||||||
PATH=$FFMPEG_PATH:$PATH
|
mv "$FFMPEG_PATH/ffmpeg" "$FFMPEG_PATH/ffmpeg.bk" || rm -f "$FFMPEG_PATH/ffmpeg"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
popd >/dev/null
|
rm -f ffmpeg.zip
|
||||||
|
curl -sL --fail https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v${FFMPEG_VER}/ffmpeg-${FFMPEG_VER}-linux-64.zip --output ffmpeg.zip >/dev/null
|
||||||
|
unzip -o ffmpeg.zip >/dev/null && rm -f ffmpeg.zip
|
||||||
|
chmod +x ffmpeg
|
||||||
|
PATH=$FFMPEG_PATH:$PATH
|
||||||
|
|
||||||
|
popd >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
function start_owncast() {
|
function start_owncast() {
|
||||||
# Build and run owncast from source
|
# Build and run owncast from source
|
||||||
echo "Building owncast..."
|
echo "Building owncast..."
|
||||||
pushd "$(git rev-parse --show-toplevel)" >/dev/null
|
pushd "$(git rev-parse --show-toplevel)" >/dev/null
|
||||||
go build -o owncast main.go
|
go build -o owncast main.go
|
||||||
|
|
||||||
echo "Running owncast..."
|
echo "Running owncast..."
|
||||||
./owncast -database "$TEMP_DB" &
|
./owncast -disableResponseCaching -database "$TEMP_DB" &
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function start_stream() {
|
function start_stream() {
|
||||||
# Start streaming the test file over RTMP to the local owncast instance.
|
# Start streaming the test file over RTMP to the local owncast instance.
|
||||||
../../ocTestStream.sh &
|
../../ocTestStream.sh &
|
||||||
STREAM_PID=$!
|
STREAM_PID=$!
|
||||||
|
|
||||||
echo "Waiting for stream to start..."
|
echo "Waiting for stream to start..."
|
||||||
sleep 12
|
sleep 12
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_storage_config() {
|
function update_storage_config() {
|
||||||
echo "Configuring external storage to use ${S3_BUCKET}..."
|
echo "Configuring external storage to use ${S3_BUCKET}..."
|
||||||
|
|
||||||
# Hard-coded to admin:abc123 for auth
|
# Hard-coded to admin:abc123 for auth
|
||||||
curl --fail 'http://localhost:8080/api/admin/config/s3' \
|
curl --fail 'http://localhost:8080/api/admin/config/s3' \
|
||||||
-H 'Authorization: Basic YWRtaW46YWJjMTIz' \
|
-H 'Authorization: Basic YWRtaW46YWJjMTIz' \
|
||||||
--data-raw "{\"value\":{\"accessKey\":\"${S3_ACCESS_KEY}\",\"acl\":\"\",\"bucket\":\"${S3_BUCKET}\",\"enabled\":true,\"endpoint\":\"${S3_ENDPOINT}\",\"region\":\"${S3_REGION}\",\"secret\":\"${S3_SECRET}\",\"servingEndpoint\":\"\"}}"
|
--data-raw "{\"value\":{\"accessKey\":\"${S3_ACCESS_KEY}\",\"acl\":\"\",\"bucket\":\"${S3_BUCKET}\",\"enabled\":true,\"endpoint\":\"${S3_ENDPOINT}\",\"region\":\"${S3_REGION}\",\"secret\":\"${S3_SECRET}\",\"servingEndpoint\":\"\"}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function kill_with_kids() {
|
function kill_with_kids() {
|
||||||
# kill a process and all its children (by pid)! return no error.
|
# kill a process and all its children (by pid)! return no error.
|
||||||
|
|
||||||
if [[ -n $1 ]]; then
|
if [[ -n $1 ]]; then
|
||||||
mapfile -t CHILDREN_PID_LIST < <(ps --ppid "$1" -o pid= &>/dev/null || true)
|
mapfile -t CHILDREN_PID_LIST < <(ps --ppid "$1" -o pid= &>/dev/null || true)
|
||||||
for child_pid in "${CHILDREN_PID_LIST[@]}"; do
|
for child_pid in "${CHILDREN_PID_LIST[@]}"; do
|
||||||
kill "$child_pid" &>/dev/null || true
|
kill "$child_pid" &>/dev/null || true
|
||||||
wait "$child_pid" &>/dev/null || true
|
wait "$child_pid" &>/dev/null || true
|
||||||
done
|
done
|
||||||
kill "$1" &>/dev/null || true
|
kill "$1" &>/dev/null || true
|
||||||
wait "$1" &>/dev/null || true
|
wait "$1" &>/dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function finish() {
|
function finish() {
|
||||||
echo "Cleaning up..."
|
echo "Cleaning up..."
|
||||||
kill_with_kids "$STREAM_PID"
|
kill_with_kids "$STREAM_PID"
|
||||||
kill "$SERVER_PID" &>/dev/null || true
|
kill "$SERVER_PID" &>/dev/null || true
|
||||||
wait "$SERVER_PID" &>/dev/null || true
|
wait "$SERVER_PID" &>/dev/null || true
|
||||||
rm -fr "$TEMP_DB"
|
rm -fr "$TEMP_DB"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue