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
This commit is contained in:
Gabe Kangas 2021-10-11 15:04:16 -07:00 committed by GitHub
parent f0bd7d2528
commit ca9d5de192
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 105 additions and 19 deletions

View file

@ -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"]

View file

@ -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

View file

@ -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"

View file

@ -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()

View file

@ -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()

View file

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View file

@ -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
}

View file

@ -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');
});
});