From ca9d5de1922248574c612ea688b9aa54b28e56da Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Mon, 11 Oct 2021 15:04:16 -0700 Subject: [PATCH] Embed static resources (#1466) * Replace pkger with go:embed for bundling the admin. Closes #844 * Remove references to pkged.go * Point tests to use an updated version of Go * Add comment to new exported function * Cleanup * Add a dummy pkged.go to alert people to stop using it. * Add simple browser test to make sure the admin is available and renders * Don't panic * Embed bot/scraper metadata template. Add browser test to validate the rendering of this template. * Use embedded offline.ts segment * Remove placeholder thumbnail as its unnecessary * Remove copying the static directory into the release * Cleanup --- Dockerfile | 1 - build/release/build.sh | 1 - controllers/index.go | 9 +++- core/core.go | 23 +++++---- core/streamState.go | 14 ++++- core/transcoder/transcoder.go | 4 +- static/logo.png | Bin 47288 -> 0 bytes static/{metadata.html => metadata.html.tmpl} | 0 static/static.go | 24 ++++++++- .../browser/bot-share-search-scrapers.test.js | 48 ++++++++++++++++++ 10 files changed, 105 insertions(+), 19 deletions(-) delete mode 100644 static/logo.png rename static/{metadata.html => metadata.html.tmpl} (100%) create mode 100644 test/automated/browser/bot-share-search-scrapers.test.js diff --git a/Dockerfile b/Dockerfile index 7966ada37..4fa0fb85b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,5 @@ RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates && updat WORKDIR /app COPY --from=build /build/owncast /app/owncast COPY --from=build /build/webroot /app/webroot -COPY --from=build /build/static /app/static RUN mkdir /app/data CMD ["/app/owncast"] diff --git a/build/release/build.sh b/build/release/build.sh index abec14586..a5ecc5883 100755 --- a/build/release/build.sh +++ b/build/release/build.sh @@ -67,7 +67,6 @@ build() { # Copy the production pruned+minified css to the build's directory. cp "${TMPDIR}tailwind.min.css" ./dist/${NAME}/webroot/js/web_modules/tailwindcss/dist/tailwind.min.css - cp -R static/ dist/${NAME}/static cp README.md dist/${NAME} pushd dist/${NAME} >> /dev/null diff --git a/controllers/index.go b/controllers/index.go index 7e2f84be3..3f37f1083 100644 --- a/controllers/index.go +++ b/controllers/index.go @@ -8,7 +8,6 @@ import ( "path" "path/filepath" "strings" - "text/template" log "github.com/sirupsen/logrus" @@ -17,6 +16,7 @@ import ( "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" "github.com/owncast/owncast/router/middleware" + "github.com/owncast/owncast/static" "github.com/owncast/owncast/utils" ) @@ -74,7 +74,12 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { // Return a basic HTML page with server-rendered metadata from the config file // to give to Opengraph clients and web scrapers (bots, web crawlers, etc). func handleScraperMetadataPage(w http.ResponseWriter, r *http.Request) { - tmpl := template.Must(template.ParseFiles(path.Join("static", "metadata.html"))) + tmpl, err := static.GetBotMetadataTemplate() + if err != nil { + log.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + return + } scheme := "http" diff --git a/core/core.go b/core/core.go index 763da6367..08ef25591 100644 --- a/core/core.go +++ b/core/core.go @@ -1,6 +1,7 @@ package core import ( + "io" "os" "path" "path/filepath" @@ -14,6 +15,7 @@ import ( "github.com/owncast/owncast/core/transcoder" "github.com/owncast/owncast/core/user" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/static" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/yp" ) @@ -79,13 +81,6 @@ func Start() error { } func createInitialOfflineState() error { - // Provide default files - if !utils.DoesFileExists(filepath.Join(config.WebRoot, "thumbnail.jpg")) { - if err := utils.Copy("static/logo.png", filepath.Join(config.WebRoot, "thumbnail.jpg")); err != nil { - return err - } - } - transitionToOfflineVideoStreamContent() return nil @@ -97,12 +92,18 @@ func createInitialOfflineState() error { func transitionToOfflineVideoStreamContent() { log.Traceln("Firing transcoder with offline stream state") - offlineFilename := "offline.ts" - offlineFilePath := "static/" + offlineFilename + r, w := io.Pipe() + _transcoder := transcoder.NewTranscoder() - _transcoder.SetInput(offlineFilePath) + _transcoder.SetInput("pipe:0") + _transcoder.SetStdin(r) _transcoder.SetIdentifier("offline") - _transcoder.Start() + go _transcoder.Start() + + d := static.GetOfflineSegment() + if _, err := w.Write(d); err != nil { + log.Errorln(err) + } // Copy the logo to be the thumbnail logo := data.GetLogoPath() diff --git a/core/streamState.go b/core/streamState.go index 9e48f3f4c..35a88b54b 100644 --- a/core/streamState.go +++ b/core/streamState.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "io" + "io/ioutil" "os" "path/filepath" "time" @@ -17,6 +18,7 @@ import ( "github.com/owncast/owncast/core/transcoder" "github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/static" "github.com/owncast/owncast/utils" "github.com/grafov/m3u8" @@ -84,8 +86,18 @@ func SetStreamAsDisconnected() { _stats.LastConnectTime = nil _broadcaster = nil + offlineFileData := static.GetOfflineSegment() offlineFilename := "offline.ts" - offlineFilePath := "static/" + offlineFilename + offlineTmpFile, err := ioutil.TempFile(os.TempDir(), offlineFilename) + if err != nil { + log.Errorln("unable to create temp file for offline video segment") + } + + if _, err = offlineTmpFile.Write(offlineFileData); err != nil { + log.Errorln("unable to write offline segment to disk", err) + } + + offlineFilePath := offlineTmpFile.Name() transcoder.StopThumbnailGenerator() rtmp.Disconnect() diff --git a/core/transcoder/transcoder.go b/core/transcoder/transcoder.go index a47bd217d..229bddd9c 100644 --- a/core/transcoder/transcoder.go +++ b/core/transcoder/transcoder.go @@ -386,8 +386,8 @@ func (t *Transcoder) SetInput(input string) { } // SetStdin sets the Stdin of the ffmpeg command. -func (t *Transcoder) SetStdin(rtmp *io.PipeReader) { - t.stdin = rtmp +func (t *Transcoder) SetStdin(pipe *io.PipeReader) { + t.stdin = pipe } // SetOutputPath sets the root directory that should include playlists and video segments. diff --git a/static/logo.png b/static/logo.png deleted file mode 100644 index 4dd93a711cea51dee8bcc85266ae038d80176e43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47288 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 diff --git a/static/metadata.html b/static/metadata.html.tmpl similarity index 100% rename from static/metadata.html rename to static/metadata.html.tmpl diff --git a/static/static.go b/static/static.go index e64ca5f14..a530d3e82 100644 --- a/static/static.go +++ b/static/static.go @@ -1,6 +1,9 @@ package static -import "embed" +import ( + "embed" + "html/template" +) //go:embed admin/* //go:embed admin/_next/static @@ -12,3 +15,22 @@ var adminFiles embed.FS func GetAdmin() embed.FS { return adminFiles } + +//go:embed metadata.html.tmpl +var botMetadataTemplate embed.FS + +// GetBotMetadataTemplate will return the bot/scraper metadata template. +func GetBotMetadataTemplate() (*template.Template, error) { + name := "metadata.html.tmpl" + t, err := template.ParseFS(botMetadataTemplate, name) + tmpl := template.Must(t, err) + return tmpl, err +} + +//go:embed offline.ts +var offlineVideoSegment []byte + +// GetOfflineSegment will return the offline video segment data. +func GetOfflineSegment() []byte { + return offlineVideoSegment +} diff --git a/test/automated/browser/bot-share-search-scrapers.test.js b/test/automated/browser/bot-share-search-scrapers.test.js new file mode 100644 index 000000000..35e6112b8 --- /dev/null +++ b/test/automated/browser/bot-share-search-scrapers.test.js @@ -0,0 +1,48 @@ +const listenForErrors = require('./lib/errors.js').listenForErrors; + +describe('Video embed page', () => { + + async function getMetaTagContent(property) { + const selector = `meta[property="${property}"]`; + + const tag = await page.evaluate((selector) => { + return document.head.querySelector(selector).getAttribute("content"); + }, selector); + return tag; + } + + beforeAll(async () => { + await page.setViewport({ width: 1080, height: 720 }); + listenForErrors(browser, page); + page.setUserAgent( + "Mastodon" + ); + await page.goto('http://localhost:5309'); + }); + + afterAll(async () => { + await page.waitForTimeout(3000); + await page.screenshot({ path: 'screenshots/screenshot_bots_share_search_scrapers.png', fullPage: true }); + }); + + it('should have rendered the simple bot accessible html page', async () => { + await page.waitForSelector('h1'); + await page.waitForSelector('h3'); + + const ogVideo = await getMetaTagContent('og:video'); + expect(ogVideo).toBe('http://localhost:5309/hls/stream.m3u8'); + + const ogVideoType = await getMetaTagContent('og:video:type'); + expect(ogVideoType).toBe('application/x-mpegURL'); + + // When stream is live the thumbnail is provided as the image. + const ogImage = await getMetaTagContent('og:image'); + expect(ogImage).toBe('http://localhost:5309/thumbnail.jpg'); + + const twitterUrl = await getMetaTagContent('twitter:url'); + expect(twitterUrl).toBe('http://localhost:5309/'); + + const twitterImage = await getMetaTagContent('twitter:image'); + expect(twitterImage).toBe('http://localhost:5309/logo/external'); + }); +});