Merge remote-tracking branch 'origin/develop' into webv2
This commit is contained in:
commit
6b22be8cf3
@ -2,7 +2,7 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
end_of_line = lf
|
||||
|
40
.github/workflows/docker-nightly-earthly.yaml
vendored
Normal file
40
.github/workflows/docker-nightly-earthly.yaml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
# See https://docs.earthly.dev/ci-integration/vendor-specific-guides/gh-actions-integration
|
||||
# for details.
|
||||
|
||||
name: Build nightly docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
jobs:
|
||||
Docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: earthly/actions-setup@v1
|
||||
with:
|
||||
version: 'latest' # or pin to an specific version, e.g. "v0.6.10"
|
||||
|
||||
- name: Earthly version
|
||||
run: earthly --version
|
||||
|
||||
- name: Log into GitHub Container Registry
|
||||
env:
|
||||
GH_CR_PAT: ${{ secrets.GH_CR_PAT }}
|
||||
run: echo "${{ secrets.GH_CR_PAT }}" | docker login https://ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
if: env.GH_CR_PAT != null
|
||||
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: all
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout and build
|
||||
if: env.GH_CR_PAT != null
|
||||
env:
|
||||
GH_CR_PAT: ${{ secrets.GH_CR_PAT }}
|
||||
run: cd build/release && ./docker-nightly-earthly.sh
|
129
Earthfile
Normal file
129
Earthfile
Normal file
@ -0,0 +1,129 @@
|
||||
VERSION --new-platform 0.6
|
||||
|
||||
FROM --platform=linux/amd64 alpine:latest
|
||||
ARG version=develop
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
build-all:
|
||||
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 --platform=darwin/amd64 +build
|
||||
|
||||
package-all:
|
||||
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 --platform=darwin/amd64 +package
|
||||
|
||||
docker-all:
|
||||
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 +docker
|
||||
|
||||
crosscompiler:
|
||||
# This image is missing a few platforms, so we'll add them locally
|
||||
FROM --platform=linux/amd64 bdwyertech/go-crosscompile
|
||||
RUN curl -sfL "https://musl.cc/armv7l-linux-musleabihf-cross.tgz" | tar zxf - -C /usr/ --strip-components=1
|
||||
RUN curl -sfL "https://musl.cc/i686-linux-musl-cross.tgz" | tar zxf - -C /usr/ --strip-components=1
|
||||
RUN curl -sfL "https://musl.cc/x86_64-linux-musl-cross.tgz" | tar zxf - -C /usr/ --strip-components=1
|
||||
|
||||
code:
|
||||
FROM --platform=linux/amd64 +crosscompiler
|
||||
COPY . /build
|
||||
# GIT CLONE --branch=$version git@github.com:owncast/owncast.git /build
|
||||
|
||||
build:
|
||||
ARG EARTHLY_GIT_HASH # provided by Earthly
|
||||
ARG TARGETPLATFORM # provided by Earthly
|
||||
ARG TARGETOS # provided by Earthly
|
||||
ARG TARGETARCH # provided by Earthly
|
||||
ARG GOOS=$TARGETOS
|
||||
ARG GOARCH=$TARGETARCH
|
||||
|
||||
FROM --platform=linux/amd64 +code
|
||||
|
||||
RUN echo $EARTHLY_GIT_HASH
|
||||
RUN echo "Finding CC configuration for $TARGETPLATFORM"
|
||||
IF [ "$TARGETPLATFORM" = "linux/amd64" ]
|
||||
ARG NAME=linux-64bit
|
||||
ARG CC=x86_64-linux-musl-gcc
|
||||
ARG CXX=x86_64-linux-musl-g++
|
||||
ELSE IF [ "$TARGETPLATFORM" = "linux/386" ]
|
||||
ARG NAME=linux-32bit
|
||||
ARG CC=i686-linux-musl-gcc
|
||||
ARG CXX=i686-linux-musl-g++
|
||||
ELSE IF [ "$TARGETPLATFORM" = "linux/arm64" ]
|
||||
ARG NAME=linux-arm64
|
||||
ARG CC=aarch64-linux-musl-gcc
|
||||
ARG CXX=aarch64-linux-musl-g++
|
||||
ELSE IF [ "$TARGETPLATFORM" = "linux/arm/v7" ]
|
||||
ARG NAME=linux-arm7
|
||||
ARG CC=armv7l-linux-musleabihf-gcc
|
||||
ARG CXX=armv7l-linux-musleabihf-g++
|
||||
ARG GOARM=7
|
||||
ELSE IF [ "$TARGETPLATFORM" = "darwin/amd64" ]
|
||||
ARG NAME=macOS-64bit
|
||||
ARG CC=o64-clang
|
||||
ARG CXX=o64-clang++
|
||||
ELSE
|
||||
RUN echo "Failed to find CC configuration for $TARGETPLATFORM"
|
||||
ARG --required CC
|
||||
ARG --required CXX
|
||||
END
|
||||
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GOOS=$GOOS
|
||||
ENV GOARCH=$GOARCH
|
||||
ENV GOARM=$GOARM
|
||||
ENV CC=$CC
|
||||
ENV CXX=$CXX
|
||||
|
||||
WORKDIR /build
|
||||
# MacOSX disallows static executables, so we omit the static flag on this platform
|
||||
RUN go build -a -installsuffix cgo -ldflags "$([ "$GOOS"z != darwinz ] && echo "-linkmode external -extldflags -static ") -s -w -X github.com/owncast/owncast/config.GitCommit=$EARTHLY_GIT_HASH -X github.com/owncast/owncast/config.VersionNumber=$version -X github.com/owncast/owncast/config.BuildPlatform=$NAME" -o owncast main.go
|
||||
COPY +tailwind/prod-tailwind.min.css /build/dist/webroot/js/web_modules/tailwindcss/dist/tailwind.min.css
|
||||
|
||||
SAVE ARTIFACT owncast owncast
|
||||
SAVE ARTIFACT webroot webroot
|
||||
SAVE ARTIFACT README.md README.md
|
||||
|
||||
tailwind:
|
||||
FROM +code
|
||||
WORKDIR /build/build/javascript
|
||||
RUN apk add --update --no-cache npm >> /dev/null
|
||||
ENV NODE_ENV=production
|
||||
RUN cd /build/build/javascript && npm install --quiet --no-progress >> /dev/null && npm install -g cssnano postcss postcss-cli --quiet --no-progress --save-dev >> /dev/null && ./node_modules/.bin/tailwind build > /build/tailwind.min.css
|
||||
RUN npx postcss /build/tailwind.min.css > /build/prod-tailwind.min.css
|
||||
SAVE ARTIFACT /build/prod-tailwind.min.css prod-tailwind.min.css
|
||||
|
||||
package:
|
||||
RUN apk add --update --no-cache zip >> /dev/null
|
||||
|
||||
ARG TARGETPLATFORM # provided by Earthly
|
||||
IF [ "$TARGETPLATFORM" = "linux/amd64" ]
|
||||
ARG NAME=linux-64bit
|
||||
ELSE IF [ "$TARGETPLATFORM" = "linux/386" ]
|
||||
ARG NAME=linux-32bit
|
||||
ELSE IF [ "$TARGETPLATFORM" = "linux/arm64" ]
|
||||
ARG NAME=linux-arm64
|
||||
ELSE IF [ "$TARGETPLATFORM" = "linux/arm/v7" ]
|
||||
ARG NAME=linux-arm7
|
||||
ELSE IF [ "$TARGETPLATFORM" = "darwin/amd64" ]
|
||||
ARG NAME=macOS-64bit
|
||||
ELSE
|
||||
ARG NAME=custom
|
||||
END
|
||||
|
||||
COPY (+build/webroot --platform $TARGETPLATFORM) /build/dist/webroot
|
||||
COPY (+build/owncast --platform $TARGETPLATFORM) /build/dist/owncast
|
||||
COPY (+build/README.md --platform $TARGETPLATFORM) /build/dist/README.md
|
||||
ENV ZIPNAME owncast-$version-$NAME.zip
|
||||
RUN cd /build/dist && zip -r -q -8 /build/dist/owncast.zip .
|
||||
SAVE ARTIFACT /build/dist/owncast.zip owncast.zip AS LOCAL dist/$ZIPNAME
|
||||
|
||||
docker:
|
||||
ARG image=ghcr.io/owncast/owncast
|
||||
ARG tag=develop
|
||||
ARG TARGETPLATFORM
|
||||
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates unzip && update-ca-certificates
|
||||
WORKDIR /app
|
||||
COPY --platform=$TARGETPLATFORM +package/owncast.zip /app
|
||||
RUN unzip -x owncast.zip && mkdir data
|
||||
ENTRYPOINT ["/app/owncast"]
|
||||
EXPOSE 8080 1935
|
||||
SAVE IMAGE --push $image:$tag
|
@ -48,7 +48,9 @@ Owncast is an open source, self-hosted, decentralized, single user live video st
|
||||
|
||||
<div>
|
||||
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/owncast/owncast/total?style=for-the-badge">
|
||||
<a href="https://hub.docker.com/r/gabekangas/owncast">
|
||||
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/gabekangas/owncast?style=for-the-badge">
|
||||
</a>
|
||||
<a href="https://github.com/owncast/owncast/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22">
|
||||
<img alt="GitHub issues by-label" src="https://img.shields.io/github/issues-raw/owncast/owncast/good%20first%20issue?style=for-the-badge">
|
||||
</a>
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -21,7 +22,7 @@ import (
|
||||
|
||||
func handle(request apmodels.InboxRequest) {
|
||||
if verified, err := Verify(request.Request); err != nil {
|
||||
log.Debugln("Error in attempting to verify request", err)
|
||||
log.Errorln("Error in attempting to verify request", err)
|
||||
return
|
||||
} else if !verified {
|
||||
log.Debugln("Request failed verification", err)
|
||||
@ -35,6 +36,7 @@ func handle(request apmodels.InboxRequest) {
|
||||
|
||||
// Verify will Verify the http signature of an inbound request as well as
|
||||
// check it against the list of blocked domains.
|
||||
// nolint: cyclop
|
||||
func Verify(request *http.Request) (bool, error) {
|
||||
verifier, err := httpsig.NewVerifier(request)
|
||||
if err != nil {
|
||||
@ -51,6 +53,10 @@ func Verify(request *http.Request) (bool, error) {
|
||||
}
|
||||
|
||||
signature := request.Header.Get("signature")
|
||||
if signature == "" {
|
||||
return false, errors.New("http signature header not found in request")
|
||||
}
|
||||
|
||||
var algorithmString string
|
||||
signatureComponents := strings.Split(signature, ",")
|
||||
for _, component := range signatureComponents {
|
||||
@ -66,26 +72,31 @@ func Verify(request *http.Request) (bool, error) {
|
||||
return false, errors.New("Unable to determine algorithm to verify request")
|
||||
}
|
||||
|
||||
actor, err := resolvers.GetResolvedActorFromIRI(pubKeyID.String())
|
||||
publicKey, err := resolvers.GetResolvedPublicKeyFromIRI(pubKeyID.String())
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "failed to resolve actor from IRI to fetch key")
|
||||
}
|
||||
|
||||
if actor.ActorIri == nil {
|
||||
return false, errors.New("actor IRI is empty")
|
||||
var publicKeyActorIRI *url.URL
|
||||
if ownerProp := publicKey.GetW3IDSecurityV1Owner(); ownerProp != nil {
|
||||
publicKeyActorIRI = ownerProp.Get()
|
||||
}
|
||||
|
||||
if publicKeyActorIRI == nil {
|
||||
return false, errors.New("public key owner IRI is empty")
|
||||
}
|
||||
|
||||
// Test to see if the actor is in the list of blocked federated domains.
|
||||
if isBlockedDomain(actor.ActorIri.Hostname()) {
|
||||
if isBlockedDomain(publicKeyActorIRI.Hostname()) {
|
||||
return false, errors.New("domain is blocked")
|
||||
}
|
||||
|
||||
// If actor is specifically blocked, then fail validation.
|
||||
if blocked, err := isBlockedActor(actor.ActorIri); err != nil || blocked {
|
||||
if blocked, err := isBlockedActor(publicKeyActorIRI); err != nil || blocked {
|
||||
return false, err
|
||||
}
|
||||
|
||||
key := actor.W3IDSecurityV1PublicKey.Begin().Get().GetW3IDSecurityV1PublicKeyPem().Get()
|
||||
key := publicKey.GetW3IDSecurityV1PublicKeyPem().Get()
|
||||
block, _ := pem.Decode([]byte(key))
|
||||
if block == nil {
|
||||
log.Errorln("failed to parse PEM block containing the public key")
|
||||
@ -98,15 +109,25 @@ func Verify(request *http.Request) (bool, error) {
|
||||
return false, errors.Wrap(err, "failed to parse DER encoded public key")
|
||||
}
|
||||
|
||||
var algorithm httpsig.Algorithm = httpsig.Algorithm(algorithmString)
|
||||
|
||||
// The verifier will verify the Digest in addition to the HTTP signature
|
||||
if err := verifier.Verify(parsedKey, algorithm); err != nil {
|
||||
log.Warnln("verification error for", pubKeyID, err)
|
||||
return false, errors.Wrap(err, "verification error: "+pubKeyID.String())
|
||||
algos := []httpsig.Algorithm{
|
||||
httpsig.Algorithm(algorithmString), // try stated algorithm first then other common algorithms
|
||||
httpsig.RSA_SHA256, // <- used by almost all fedi software
|
||||
httpsig.RSA_SHA512,
|
||||
}
|
||||
|
||||
// The verifier will verify the Digest in addition to the HTTP signature
|
||||
triedAlgos := make(map[httpsig.Algorithm]error)
|
||||
for _, algorithm := range algos {
|
||||
if _, tried := triedAlgos[algorithm]; !tried {
|
||||
err := verifier.Verify(parsedKey, algorithm)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
triedAlgos[algorithm] = err
|
||||
}
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("http signature verification error(s) for: %s: %+v", pubKeyID.String(), triedAlgos)
|
||||
}
|
||||
|
||||
func isBlockedDomain(domain string) bool {
|
||||
|
@ -122,6 +122,72 @@ func GetResolvedActorFromActorProperty(actor vocab.ActivityStreamsActorProperty)
|
||||
return apActor, err
|
||||
}
|
||||
|
||||
// GetResolvedPublicKeyFromIRI will resolve a publicKey IRI string to a vocab.W3IDSecurityV1PublicKey.
|
||||
func GetResolvedPublicKeyFromIRI(publicKeyIRI string) (vocab.W3IDSecurityV1PublicKey, error) {
|
||||
var err error
|
||||
var pubkey vocab.W3IDSecurityV1PublicKey
|
||||
resolved := false
|
||||
|
||||
personCallback := func(c context.Context, person vocab.ActivityStreamsPerson) error {
|
||||
if pkProp := person.GetW3IDSecurityV1PublicKey(); pkProp != nil {
|
||||
for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() {
|
||||
if iter.IsW3IDSecurityV1PublicKey() {
|
||||
pubkey = iter.Get()
|
||||
resolved = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("error deriving publickey from activitystreamsperson")
|
||||
}
|
||||
|
||||
serviceCallback := func(c context.Context, service vocab.ActivityStreamsService) error {
|
||||
if pkProp := service.GetW3IDSecurityV1PublicKey(); pkProp != nil {
|
||||
for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() {
|
||||
if iter.IsW3IDSecurityV1PublicKey() {
|
||||
pubkey = iter.Get()
|
||||
resolved = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("error deriving publickey from activitystreamsservice")
|
||||
}
|
||||
|
||||
applicationCallback := func(c context.Context, app vocab.ActivityStreamsApplication) error {
|
||||
if pkProp := app.GetW3IDSecurityV1PublicKey(); pkProp != nil {
|
||||
for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() {
|
||||
if iter.IsW3IDSecurityV1PublicKey() {
|
||||
pubkey = iter.Get()
|
||||
resolved = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("error deriving publickey from activitystreamsapp")
|
||||
}
|
||||
|
||||
pubkeyCallback := func(c context.Context, pk vocab.W3IDSecurityV1PublicKey) error {
|
||||
pubkey = pk
|
||||
resolved = true
|
||||
return nil
|
||||
}
|
||||
|
||||
if e := ResolveIRI(context.Background(), publicKeyIRI, personCallback, serviceCallback, applicationCallback, pubkeyCallback); e != nil {
|
||||
err = e
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error resolving publickey from iri")
|
||||
}
|
||||
|
||||
if !resolved {
|
||||
err = errors.New("error resolving publickey from iri")
|
||||
}
|
||||
|
||||
return pubkey, err
|
||||
}
|
||||
|
||||
// GetResolvedActorFromIRI will resolve an IRI string to a fully populated actor.
|
||||
func GetResolvedActorFromIRI(personOrServiceIRI string) (apmodels.ActivityPubActor, error) {
|
||||
var err error
|
||||
|
@ -68,7 +68,7 @@ func HandleCallbackCode(code, state string) (*Request, *Response, error) {
|
||||
|
||||
var response Response
|
||||
if err := json.Unmarshal(body, &response); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to parse IndieAuth response")
|
||||
return nil, nil, errors.Wrap(err, "unable to parse IndieAuth response: "+string(body))
|
||||
}
|
||||
|
||||
if response.Error != "" || response.ErrorDescription != "" {
|
||||
|
149
build/javascript/package-lock.json
generated
149
build/javascript/package-lock.json
generated
@ -5,9 +5,9 @@
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.17.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz",
|
||||
"integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==",
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz",
|
||||
"integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
@ -296,7 +296,7 @@
|
||||
"boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
|
||||
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
@ -358,9 +358,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001335",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz",
|
||||
"integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w=="
|
||||
"version": "1.0.30001344",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz",
|
||||
"integrity": "sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g=="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
@ -454,7 +454,7 @@
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.9.1",
|
||||
@ -479,7 +479,7 @@
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"css-declaration-sorter": {
|
||||
"version": "6.2.2",
|
||||
@ -535,26 +535,26 @@
|
||||
}
|
||||
},
|
||||
"cssnano-preset-default": {
|
||||
"version": "5.2.7",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz",
|
||||
"integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==",
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.10.tgz",
|
||||
"integrity": "sha512-H8TJRhTjBKVOPltp9vr9El9I+IfYsOMhmXdK0LwdvwJcxYX9oWkY7ctacWusgPWAgQq1vt/WO8v+uqpfLnM7QA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-declaration-sorter": "^6.2.2",
|
||||
"cssnano-utils": "^3.1.0",
|
||||
"postcss-calc": "^8.2.3",
|
||||
"postcss-colormin": "^5.3.0",
|
||||
"postcss-convert-values": "^5.1.0",
|
||||
"postcss-discard-comments": "^5.1.1",
|
||||
"postcss-convert-values": "^5.1.2",
|
||||
"postcss-discard-comments": "^5.1.2",
|
||||
"postcss-discard-duplicates": "^5.1.0",
|
||||
"postcss-discard-empty": "^5.1.1",
|
||||
"postcss-discard-overridden": "^5.1.0",
|
||||
"postcss-merge-longhand": "^5.1.4",
|
||||
"postcss-merge-rules": "^5.1.1",
|
||||
"postcss-merge-longhand": "^5.1.5",
|
||||
"postcss-merge-rules": "^5.1.2",
|
||||
"postcss-minify-font-values": "^5.1.0",
|
||||
"postcss-minify-gradients": "^5.1.1",
|
||||
"postcss-minify-params": "^5.1.2",
|
||||
"postcss-minify-selectors": "^5.2.0",
|
||||
"postcss-minify-params": "^5.1.3",
|
||||
"postcss-minify-selectors": "^5.2.1",
|
||||
"postcss-normalize-charset": "^5.1.0",
|
||||
"postcss-normalize-display-values": "^5.1.0",
|
||||
"postcss-normalize-positions": "^5.1.0",
|
||||
@ -589,7 +589,7 @@
|
||||
"defined": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
|
||||
"integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
|
||||
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ=="
|
||||
},
|
||||
"dependency-graph": {
|
||||
"version": "0.11.0",
|
||||
@ -598,13 +598,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"detective": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
|
||||
"integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz",
|
||||
"integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==",
|
||||
"requires": {
|
||||
"acorn-node": "^1.6.1",
|
||||
"acorn-node": "^1.8.2",
|
||||
"defined": "^1.0.0",
|
||||
"minimist": "^1.1.1"
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"dir-glob": {
|
||||
@ -653,9 +653,9 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.129",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.129.tgz",
|
||||
"integrity": "sha512-GgtN6bsDtHdtXJtlMYZWGB/uOyjZWjmRDumXTas7dGBaB9zUyCjzHet1DY2KhyHN8R0GLbzZWqm4efeddqqyRQ=="
|
||||
"version": "1.4.141",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.141.tgz",
|
||||
"integrity": "sha512-mfBcbqc0qc6RlxrsIgLG2wCqkiPAjEezHxGTu7p3dHHFOurH4EjS9rFZndX5axC8264rI1Pcbw8uQP39oZckeA=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
@ -676,12 +676,12 @@
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.11",
|
||||
@ -736,7 +736,7 @@
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
@ -746,7 +746,7 @@
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
@ -761,9 +761,9 @@
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"fuzzysort": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.2.1.tgz",
|
||||
"integrity": "sha512-egTSF3U6H6T9tXtAhEm5P5guSSDjd96/NUWrbmoGlIu3ATMdXra13gwQdEFRY6ehsFe8xec7UnQz+k34CGWCIg=="
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-1.9.0.tgz",
|
||||
"integrity": "sha512-MOxCT0qLTwLqmEwc7UtU045RKef7mc8Qz8eR4r2bLNEq9dy/c3ZKMEFp6IEst69otkQdFZ4FfgH2dmZD+ddX1g=="
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
@ -778,14 +778,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
@ -838,7 +838,7 @@
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
|
||||
},
|
||||
"htm": {
|
||||
"version": "3.1.1",
|
||||
@ -872,12 +872,12 @@
|
||||
"individual": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz",
|
||||
"integrity": "sha1-gzsJfa0jKU52EXqY+zjg2a1hu5c="
|
||||
"integrity": "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g=="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
@ -913,7 +913,7 @@
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
@ -970,13 +970,13 @@
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
|
||||
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
|
||||
"dev": true
|
||||
},
|
||||
"m3u8-parser": {
|
||||
@ -1062,9 +1062,9 @@
|
||||
}
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
|
||||
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-emoji": {
|
||||
@ -1076,9 +1076,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz",
|
||||
"integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ=="
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz",
|
||||
"integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q=="
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
@ -1271,18 +1271,19 @@
|
||||
}
|
||||
},
|
||||
"postcss-convert-values": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz",
|
||||
"integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz",
|
||||
"integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.20.3",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"postcss-discard-comments": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz",
|
||||
"integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
|
||||
"integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss-discard-duplicates": {
|
||||
@ -1403,9 +1404,9 @@
|
||||
}
|
||||
},
|
||||
"postcss-merge-longhand": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz",
|
||||
"integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==",
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.5.tgz",
|
||||
"integrity": "sha512-NOG1grw9wIO+60arKa2YYsrbgvP6tp+jqc7+ZD5/MalIw234ooH2C6KlR6FEn4yle7GqZoBxSK1mLBE9KPur6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
@ -1413,9 +1414,9 @@
|
||||
}
|
||||
},
|
||||
"postcss-merge-rules": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz",
|
||||
"integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz",
|
||||
"integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.16.6",
|
||||
@ -1445,9 +1446,9 @@
|
||||
}
|
||||
},
|
||||
"postcss-minify-params": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz",
|
||||
"integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz",
|
||||
"integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.16.6",
|
||||
@ -1456,9 +1457,9 @@
|
||||
}
|
||||
},
|
||||
"postcss-minify-selectors": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz",
|
||||
"integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
|
||||
"integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss-selector-parser": "^6.0.5"
|
||||
@ -1975,9 +1976,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
|
||||
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"boolbase": "^1.0.0"
|
||||
@ -2185,9 +2186,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
|
||||
"integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
|
||||
"version": "17.5.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
|
||||
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
|
14
build/release/docker-nightly-earthly.sh
Executable file
14
build/release/docker-nightly-earthly.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Docker build
|
||||
# Must authenticate first: https://docs.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages#authenticating-to-github-packages
|
||||
DOCKER_IMAGE="owncast-earthly"
|
||||
DATE=$(date +"%Y%m%d")
|
||||
VERSION="${DATE}-nightly"
|
||||
|
||||
echo "Building Docker image ${DOCKER_IMAGE}..."
|
||||
|
||||
# Change to the root directory of the repository
|
||||
cd $(git rev-parse --show-toplevel)
|
||||
|
||||
earthly --ci --push +docker-all --image="ghcr.io/owncast/${DOCKER_IMAGE}" --tag=nightly --version="${VERSION}"
|
@ -6,13 +6,16 @@ import (
|
||||
|
||||
ia "github.com/owncast/owncast/auth/indieauth"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/router/middleware"
|
||||
)
|
||||
|
||||
// HandleAuthEndpoint will handle the IndieAuth auth endpoint.
|
||||
func HandleAuthEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
// Require the GET request for IndieAuth to be behind admin login.
|
||||
handleAuthEndpointGet(w, r)
|
||||
f := middleware.RequireAdminAuth(handleAuthEndpointGet)
|
||||
f(w, r)
|
||||
return
|
||||
} else if r.Method == http.MethodPost {
|
||||
handleAuthEndpointPost(w, r)
|
||||
} else {
|
||||
|
@ -3,9 +3,11 @@ package storageproviders
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/core/playlist"
|
||||
@ -176,6 +178,14 @@ func (s *S3Storage) Save(filePath string, retryCount int) (string, error) {
|
||||
}
|
||||
|
||||
func (s *S3Storage) connectAWS() *session.Session {
|
||||
t := http.DefaultTransport.(*http.Transport).Clone()
|
||||
t.MaxIdleConnsPerHost = 100
|
||||
|
||||
httpClient := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Transport: t,
|
||||
}
|
||||
|
||||
creds := credentials.NewStaticCredentials(s.s3AccessKey, s.s3Secret, "")
|
||||
_, err := creds.Get()
|
||||
if err != nil {
|
||||
@ -184,6 +194,7 @@ func (s *S3Storage) connectAWS() *session.Session {
|
||||
|
||||
sess, err := session.NewSession(
|
||||
&aws.Config{
|
||||
HTTPClient: httpClient,
|
||||
Region: aws.String(s.s3Region),
|
||||
Credentials: creds,
|
||||
Endpoint: aws.String(s.s3Endpoint),
|
||||
|
1957
docs/api/index.html
1957
docs/api/index.html
File diff suppressed because one or more lines are too long
12
go.mod
12
go.mod
@ -4,14 +4,14 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/amalfra/etag v1.0.0
|
||||
github.com/aws/aws-sdk-go v1.43.44
|
||||
github.com/aws/aws-sdk-go v1.44.28
|
||||
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
|
||||
github.com/go-fed/httpsig v1.1.0
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/grafov/m3u8 v0.11.1
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/mattn/go-sqlite3 v1.14.13
|
||||
github.com/microcosm-cc/bluemonday v1.0.18
|
||||
github.com/mssola/user_agent v0.5.3
|
||||
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590
|
||||
@ -20,7 +20,7 @@ require (
|
||||
github.com/schollz/sqlite3dump v1.3.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125
|
||||
github.com/yuin/goldmark v1.4.11
|
||||
github.com/yuin/goldmark v1.4.12
|
||||
golang.org/x/mod v0.5.1
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||
mvdan.cc/xurls v1.1.0
|
||||
@ -35,11 +35,11 @@ require (
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861
|
||||
golang.org/x/sys v0.0.0-20220325203850-36772127a21f // indirect
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
)
|
||||
|
||||
require github.com/prometheus/client_golang v1.12.1
|
||||
require github.com/prometheus/client_golang v1.12.2
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
|
14
go.sum
14
go.sum
@ -48,6 +48,8 @@ github.com/aws/aws-sdk-go v1.43.43 h1:1L06qzQvl4aC3Skfh5rV7xVhGHjIZoHcqy16NoyQ1o
|
||||
github.com/aws/aws-sdk-go v1.43.43/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go v1.43.44 h1:t+97cY4ScE/czlNlK5iikUGi7w1fC0uop1OUalDIRT4=
|
||||
github.com/aws/aws-sdk-go v1.43.44/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go v1.44.28 h1:h/OAqEqY18wq//v6h4GNPMmCkxuzSDrWuGyrvSiRqf4=
|
||||
github.com/aws/aws-sdk-go v1.44.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@ -193,6 +195,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2
|
||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I=
|
||||
github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo=
|
||||
@ -232,6 +236,8 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@ -281,6 +287,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.11 h1:i45YIzqLnUc2tGaTlJCyUxSG8TvgyGqhqOZOUKIjJ6w=
|
||||
github.com/yuin/goldmark v1.4.11/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||
github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
|
||||
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@ -368,6 +376,10 @@ golang.org/x/net v0.0.0-20220420153159-1850ba15e1be h1:yx80W7nvY5ySWpaU8UWaj5o9e
|
||||
golang.org/x/net v0.0.0-20220420153159-1850ba15e1be/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8=
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -428,6 +440,8 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=
|
||||
golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
40
openapi.yaml
40
openapi.yaml
@ -1,8 +1,8 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: Owncast
|
||||
description: Owncast is a self-hosted live video and web chat server for use with existing popular broadcasting software. The following APIs represent the state in the development branch.
|
||||
version: '0.0.11'
|
||||
description: Owncast is a self-hosted live video and web chat server for use with existing popular broadcasting software.
|
||||
version: '0.0.12'
|
||||
contact:
|
||||
name: Gabe Kangas
|
||||
email: gabek@real-ity.com
|
||||
@ -334,7 +334,12 @@ components:
|
||||
example: https://mastodon.cloud/users/gabektest
|
||||
type:
|
||||
type: string
|
||||
enum: [FEDIVERSE_ENGAGEMENT_FOLLOW, FEDIVERSE_ENGAGEMENT_LIKE, FEDIVERSE_ENGAGEMENT_REPOST]
|
||||
enum:
|
||||
[
|
||||
FEDIVERSE_ENGAGEMENT_FOLLOW,
|
||||
FEDIVERSE_ENGAGEMENT_LIKE,
|
||||
FEDIVERSE_ENGAGEMENT_REPOST,
|
||||
]
|
||||
|
||||
securitySchemes:
|
||||
AdminBasicAuth:
|
||||
@ -1439,7 +1444,7 @@ paths:
|
||||
/api/admin/config/federation/enable:
|
||||
post:
|
||||
summary: Enable or disable federated social features.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1451,11 +1456,10 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BooleanValue'
|
||||
|
||||
|
||||
/api/admin/config/federation/private:
|
||||
post:
|
||||
summary: Enable or disable private federation mode.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1467,11 +1471,10 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BooleanValue'
|
||||
|
||||
|
||||
/api/admin/config/federation/showengagement:
|
||||
post:
|
||||
summary: Enable or disable Federation activity showing in chat.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1486,7 +1489,7 @@ paths:
|
||||
/api/admin/config/federation/username:
|
||||
post:
|
||||
summary: Set the username you are seen as on the fediverse.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1501,7 +1504,7 @@ paths:
|
||||
/api/admin/config/federation/livemessage:
|
||||
post:
|
||||
summary: Set the message sent to the fediverse when this instance goes live.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1516,7 +1519,7 @@ paths:
|
||||
/api/admin/config/federation/blockdomains:
|
||||
post:
|
||||
summary: Save a collection of domains that should be ignored on the fediverse.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1535,7 +1538,7 @@ paths:
|
||||
/api/admin/federation/send:
|
||||
post:
|
||||
summary: Manually send a message to the fediverse from this instance.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1552,7 +1555,7 @@ paths:
|
||||
/api/admin/federation/actions:
|
||||
get:
|
||||
summary: Get a list of accepted actions that took place on the Fediverse.
|
||||
tags: ["Admin"]
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
responses:
|
||||
@ -1565,7 +1568,6 @@ paths:
|
||||
items:
|
||||
$ref: '#/components/schemas/FederatedAction'
|
||||
|
||||
|
||||
/api/integrations/streamtitle:
|
||||
post:
|
||||
summary: Set the stream title.
|
||||
@ -2126,3 +2128,13 @@ paths:
|
||||
'200':
|
||||
description: The list of default names have been updated.
|
||||
$ref: '#/components/responses/BasicResponse'
|
||||
|
||||
/api/admin/prometheus:
|
||||
get:
|
||||
tags: ['Admin']
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
summary: Return Prometheus-compatible scraper metrics.
|
||||
responses:
|
||||
'200':
|
||||
description: Prometheus-compatible scraper values.
|
||||
|
@ -22,8 +22,10 @@ func RequireActivityPubOrRedirect(handler http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
|
||||
acceptedContentTypes := []string{"application/json", "application/json+ld", "application/activity+json", `application/ld+json; profile="https://www.w3.org/ns/activitystreams"`}
|
||||
acceptString := r.Header.Get("Accept")
|
||||
accept := strings.Split(acceptString, ",")
|
||||
var accept []string
|
||||
for _, a := range r.Header.Values("Accept") {
|
||||
accept = append(accept, strings.Split(a, ",")...)
|
||||
}
|
||||
|
||||
for _, singleType := range accept {
|
||||
if _, accepted := utils.FindInSlice(acceptedContentTypes, strings.TrimSpace(singleType)); accepted {
|
||||
|
@ -355,7 +355,7 @@ func Start() error {
|
||||
|
||||
// Start auth flow
|
||||
http.HandleFunc("/api/auth/indieauth", middleware.RequireUserAccessToken(indieauth.StartAuthFlow))
|
||||
http.HandleFunc("/api/auth/indieauth/callback", middleware.RequireAdminAuth(indieauth.HandleRedirect))
|
||||
http.HandleFunc("/api/auth/indieauth/callback", indieauth.HandleRedirect)
|
||||
http.HandleFunc("/api/auth/provider/indieauth", indieauth.HandleAuthEndpoint)
|
||||
|
||||
http.HandleFunc("/api/auth/fediverse", middleware.RequireUserAccessToken(fediverseauth.RegisterFediverseOTPRequest))
|
||||
|
2
static/admin/404/index.html
vendored
2
static/admin/404/index.html
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/1556-f79a922e799c7a06.js
vendored
Normal file
1
static/admin/_next/static/chunks/1556-f79a922e799c7a06.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/2589-c48f3b04b9a6c7ce.js
vendored
Normal file
1
static/admin/_next/static/chunks/2589-c48f3b04b9a6c7ce.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/6132-4fc73fe4cc2a426e.js
vendored
Normal file
1
static/admin/_next/static/chunks/6132-4fc73fe4cc2a426e.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/9655-722bcfb83a61ab83.js
vendored
Normal file
1
static/admin/_next/static/chunks/9655-722bcfb83a61ab83.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/pages/_app-4e3c6e515fee028c.js
vendored
Normal file
1
static/admin/_next/static/chunks/pages/_app-4e3c6e515fee028c.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/pages/chat/users-c3f6235e6932151e.js
vendored
Normal file
1
static/admin/_next/static/chunks/pages/chat/users-c3f6235e6932151e.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/admin/_next/static/chunks/pages/stream-health-4a811c8adeb950de.js
vendored
Normal file
1
static/admin/_next/static/chunks/pages/stream-health-4a811c8adeb950de.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
self.__BUILD_MANIFEST=function(s,c,a,e,t,i,n,f,o,d,h,g,u,r,k){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,c,a,e,t,i,n,h,"static/chunks/2494-8114e9c6571377d1.js","static/chunks/pages/index-e0ac83ceaf99b5f0.js"],"/_error":["static/chunks/pages/_error-785557186902809b.js"],"/access-tokens":[s,c,a,"static/chunks/pages/access-tokens-d328b918d1f9b3d4.js"],"/actions":[s,c,"static/chunks/pages/actions-9278698db4cd1a16.js"],"/chat/messages":[g,s,c,a,n,u,"static/chunks/pages/chat/messages-0df125d8b9455827.js"],"/chat/users":[g,s,c,a,e,n,"static/chunks/6489-cea2e8971ed16ad4.js",u,"static/chunks/pages/chat/users-201d39dd28f27416.js"],"/config-chat":["static/chunks/pages/config-chat-bacb12d23264144b.js"],"/config-federation":["static/chunks/1829-f5c4fb462b2f7e98.js","static/chunks/pages/config-federation-ea0f018fb4193b61.js"],"/config-notify":["static/chunks/pages/config-notify-10a8844dc11ca4b2.js"],"/config-public-details":[s,c,f,"static/css/e773f9ad06a56dc3.css","static/chunks/2589-e1721280387f6322.js",r,"static/chunks/pages/config-public-details-94ff52653eb2586e.js"],"/config-server-details":[k,"static/chunks/pages/config-server-details-cd516688accb84d3.js"],"/config-social-items":[s,c,r,"static/chunks/pages/config-social-items-42e2ed4eed8d4dd2.js"],"/config-storage":["static/chunks/5473-623385148d67cba2.js","static/chunks/pages/config-storage-5ff120c715bfdb04.js"],"/config-video":[s,c,k,"static/chunks/1556-d7a4de19826e46f3.js","static/chunks/pages/config-video-32d86e0ba98dc6fe.js"],"/federation/actions":[s,c,a,"static/chunks/pages/federation/actions-7cfffddef3b58d86.js"],"/federation/followers":[s,c,a,e,"static/chunks/pages/federation/followers-d2d105c342c79f98.js"],"/hardware-info":[o,a,e,t,i,d,f,"static/chunks/pages/hardware-info-4723b20a84e4f461.js"],"/help":[e,t,"static/chunks/6132-187b2bf3e1265f44.js","static/chunks/pages/help-deeb1c0f667c7d75.js"],"/logs":[s,c,a,h,"static/chunks/pages/logs-df4b23b85b8ac818.js"],"/stream-health":[o,s,a,e,t,i,d,"static/chunks/pages/stream-health-5edc91e4fa00ba5c.js"],"/upgrade":[s,c,"static/chunks/9655-6347f487aa1205af.js","static/chunks/pages/upgrade-6cb31f6812e79694.js"],"/viewer-info":[o,s,c,a,e,t,i,n,d,f,"static/chunks/pages/viewer-info-03fcbea265510389.js"],"/webhooks":[s,c,"static/chunks/pages/webhooks-651cb241952e0e4a.js"],sortedPages:["/","/_app","/_error","/access-tokens","/actions","/chat/messages","/chat/users","/config-chat","/config-federation","/config-notify","/config-public-details","/config-server-details","/config-social-items","/config-storage","/config-video","/federation/actions","/federation/followers","/hardware-info","/help","/logs","/stream-health","/upgrade","/viewer-info","/webhooks"]}}("static/chunks/1741-d9d648ade4ad86b9.js","static/chunks/6003-f37682e25271f05f.js","static/chunks/8091-5bc21baa6d0d3232.js","static/chunks/8879-af8bf87fdc518c08.js","static/chunks/7751-48959ec0f11e9080.js","static/chunks/4763-7fd93797a527a971.js","static/chunks/5533-096cc7dc6703128f.js","static/chunks/7910-699eb8ed3467dc00.js","static/chunks/36bcf0ca-110fd889741d5f41.js","static/chunks/1080-1a127ea7f5a8eb3d.js","static/chunks/2429-ccb4d7fa1648dd38.js","static/chunks/29107295-4a69275373f23f88.js","static/chunks/1371-f41477e42ee50603.js","static/chunks/1017-0760c7f39ffcc2a7.js","static/chunks/4578-afc9eff4fbf5ecb1.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
||||
self.__BUILD_MANIFEST=function(s,c,a,e,t,i,f,n,o,d,h,g,u,b,r){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,c,a,e,t,i,f,h,"static/chunks/2494-8114e9c6571377d1.js","static/chunks/pages/index-e0ac83ceaf99b5f0.js"],"/_error":["static/chunks/pages/_error-785557186902809b.js"],"/access-tokens":[s,c,a,"static/chunks/pages/access-tokens-d328b918d1f9b3d4.js"],"/actions":[s,c,"static/chunks/pages/actions-9278698db4cd1a16.js"],"/chat/messages":[g,s,c,a,f,u,"static/chunks/pages/chat/messages-0df125d8b9455827.js"],"/chat/users":[g,s,c,a,e,f,"static/chunks/6489-cea2e8971ed16ad4.js",u,"static/chunks/pages/chat/users-c3f6235e6932151e.js"],"/config-chat":["static/chunks/pages/config-chat-bacb12d23264144b.js"],"/config-federation":["static/chunks/1829-f5c4fb462b2f7e98.js","static/chunks/pages/config-federation-ea0f018fb4193b61.js"],"/config-notify":["static/chunks/pages/config-notify-10a8844dc11ca4b2.js"],"/config-public-details":[s,c,n,"static/css/e773f9ad06a56dc3.css","static/chunks/2589-c48f3b04b9a6c7ce.js",b,"static/chunks/pages/config-public-details-94ff52653eb2586e.js"],"/config-server-details":[r,"static/chunks/pages/config-server-details-cd516688accb84d3.js"],"/config-social-items":[s,c,b,"static/chunks/pages/config-social-items-42e2ed4eed8d4dd2.js"],"/config-storage":["static/chunks/5473-623385148d67cba2.js","static/chunks/pages/config-storage-5ff120c715bfdb04.js"],"/config-video":[s,c,r,"static/chunks/1556-f79a922e799c7a06.js","static/chunks/pages/config-video-32d86e0ba98dc6fe.js"],"/federation/actions":[s,c,a,"static/chunks/pages/federation/actions-7cfffddef3b58d86.js"],"/federation/followers":[s,c,a,e,"static/chunks/pages/federation/followers-d2d105c342c79f98.js"],"/hardware-info":[o,a,e,t,i,d,n,"static/chunks/pages/hardware-info-4723b20a84e4f461.js"],"/help":[e,t,"static/chunks/6132-4fc73fe4cc2a426e.js","static/chunks/pages/help-deeb1c0f667c7d75.js"],"/logs":[s,c,a,h,"static/chunks/pages/logs-df4b23b85b8ac818.js"],"/stream-health":[o,s,a,e,t,i,d,"static/chunks/pages/stream-health-4a811c8adeb950de.js"],"/upgrade":[s,c,"static/chunks/9655-722bcfb83a61ab83.js","static/chunks/pages/upgrade-6cb31f6812e79694.js"],"/viewer-info":[o,s,c,a,e,t,i,f,d,n,"static/chunks/pages/viewer-info-03fcbea265510389.js"],"/webhooks":[s,c,"static/chunks/pages/webhooks-651cb241952e0e4a.js"],sortedPages:["/","/_app","/_error","/access-tokens","/actions","/chat/messages","/chat/users","/config-chat","/config-federation","/config-notify","/config-public-details","/config-server-details","/config-social-items","/config-storage","/config-video","/federation/actions","/federation/followers","/hardware-info","/help","/logs","/stream-health","/upgrade","/viewer-info","/webhooks"]}}("static/chunks/1741-d9d648ade4ad86b9.js","static/chunks/6003-f37682e25271f05f.js","static/chunks/8091-5bc21baa6d0d3232.js","static/chunks/8879-af8bf87fdc518c08.js","static/chunks/7751-48959ec0f11e9080.js","static/chunks/4763-7fd93797a527a971.js","static/chunks/5533-096cc7dc6703128f.js","static/chunks/7910-699eb8ed3467dc00.js","static/chunks/36bcf0ca-110fd889741d5f41.js","static/chunks/1080-1a127ea7f5a8eb3d.js","static/chunks/2429-ccb4d7fa1648dd38.js","static/chunks/29107295-4a69275373f23f88.js","static/chunks/1371-f41477e42ee50603.js","static/chunks/1017-0760c7f39ffcc2a7.js","static/chunks/4578-afc9eff4fbf5ecb1.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
2
static/admin/access-tokens/index.html
vendored
2
static/admin/access-tokens/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/actions/index.html
vendored
2
static/admin/actions/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/chat/messages/index.html
vendored
2
static/admin/chat/messages/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/chat/users/index.html
vendored
2
static/admin/chat/users/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/config-chat/index.html
vendored
2
static/admin/config-chat/index.html
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
static/admin/config-social-items/index.html
vendored
2
static/admin/config-social-items/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/config-storage/index.html
vendored
2
static/admin/config-storage/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/config-video/index.html
vendored
2
static/admin/config-video/index.html
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
static/admin/hardware-info/index.html
vendored
4
static/admin/hardware-info/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/help/index.html
vendored
2
static/admin/help/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/index.html
vendored
2
static/admin/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/logs/index.html
vendored
2
static/admin/logs/index.html
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
static/admin/upgrade/index.html
vendored
2
static/admin/upgrade/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/viewer-info/index.html
vendored
2
static/admin/viewer-info/index.html
vendored
File diff suppressed because one or more lines are too long
2
static/admin/webhooks/index.html
vendored
2
static/admin/webhooks/index.html
vendored
File diff suppressed because one or more lines are too long
@ -96,8 +96,9 @@ export default class FediverseFollowModal extends Component {
|
||||
return html`
|
||||
<div class="bg-gray-100 bg-center bg-no-repeat p-4">
|
||||
<p class="text-gray-700 text-md">
|
||||
By following this stream you'll get posts and notifications such as
|
||||
when it goes live.
|
||||
By following this stream on the Fediverse you'll receive updates when
|
||||
it goes live, get posts from the streamer, and be featured as a
|
||||
follower.
|
||||
</p>
|
||||
|
||||
<div
|
||||
|
@ -27,7 +27,7 @@ How to help with this? The Owncast Latency Compensator will:
|
||||
- Completely give up on all compensation if too many buffering events occur.
|
||||
*/
|
||||
|
||||
const REBUFFER_EVENT_LIMIT = 5; // Max number of buffering events before we stop compensating for latency.
|
||||
const REBUFFER_EVENT_LIMIT = 4; // Max number of buffering events before we stop compensating for latency.
|
||||
const MIN_BUFFER_DURATION = 200; // Min duration a buffer event must last to be counted.
|
||||
const MAX_SPEEDUP_RATE = 1.08; // The playback rate when compensating for latency.
|
||||
const MAX_SPEEDUP_RAMP = 0.02; // The max amount we will increase the playback rate at once.
|
||||
@ -47,6 +47,7 @@ const STARTUP_WAIT_TIME = 10 * 1000; // The amount of time after we start up tha
|
||||
class LatencyCompensator {
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
this.playing = false;
|
||||
this.enabled = false;
|
||||
this.running = false;
|
||||
this.inTimeout = false;
|
||||
@ -59,14 +60,25 @@ class LatencyCompensator {
|
||||
this.lastJumpOccurred = null;
|
||||
this.startupTime = new Date();
|
||||
this.clockSkewMs = 0;
|
||||
this.currentLatency = null;
|
||||
|
||||
// Keep track of all the latencies we encountered buffering events
|
||||
// in order to determine a new minimum latency.
|
||||
this.bufferedAtLatency = [];
|
||||
|
||||
this.player.on('playing', this.handlePlaying.bind(this));
|
||||
this.player.on('pause', this.handlePause.bind(this));
|
||||
this.player.on('error', this.handleError.bind(this));
|
||||
this.player.on('waiting', this.handleBuffering.bind(this));
|
||||
this.player.on('stalled', this.handleBuffering.bind(this));
|
||||
this.player.on('ended', this.handleEnded.bind(this));
|
||||
this.player.on('canplaythrough', this.handlePlaying.bind(this));
|
||||
this.player.on('canplay', this.handlePlaying.bind(this));
|
||||
|
||||
this.check = this.check.bind(this);
|
||||
this.start = this.start.bind(this);
|
||||
this.enable = this.enable.bind(this);
|
||||
this.countBufferingEvent = this.countBufferingEvent.bind(this);
|
||||
}
|
||||
|
||||
// To keep our client clock in sync with the server clock to determine
|
||||
@ -97,7 +109,6 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
if (this.inTimeout) {
|
||||
console.log('in timeout...');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -162,13 +173,27 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
// How far away from live edge do we stop the compensator.
|
||||
const minLatencyThreshold = Math.max(
|
||||
const computedMinLatencyThreshold = Math.max(
|
||||
MIN_LATENCY,
|
||||
segment.duration * 1000 * LOWEST_LATENCY_SEGMENT_LENGTH_MULTIPLIER
|
||||
);
|
||||
|
||||
// Create an array of all the buffering events in the past along with
|
||||
// the computed min latency above.
|
||||
const targetLatencies = this.bufferedAtLatency.concat([
|
||||
computedMinLatencyThreshold,
|
||||
]);
|
||||
|
||||
// Determine if we need to reduce the minimum latency we computed
|
||||
// above based on buffering events that have taken place in the past by
|
||||
// creating an array of all the buffering events and the above computed
|
||||
// minimum latency target and averaging all those values.
|
||||
const minLatencyThreshold =
|
||||
targetLatencies.reduce((sum, current) => sum + current, 0) /
|
||||
targetLatencies.length;
|
||||
|
||||
// How far away from live edge do we start the compensator.
|
||||
const maxLatencyThreshold = Math.max(
|
||||
let maxLatencyThreshold = Math.max(
|
||||
minLatencyThreshold * 1.4,
|
||||
Math.min(
|
||||
segment.duration * 1000 * HIGHEST_LATENCY_SEGMENT_LENGTH_MULTIPLIER,
|
||||
@ -176,9 +201,17 @@ class LatencyCompensator {
|
||||
)
|
||||
);
|
||||
|
||||
// If this newly adjusted minimum latency ends up being greater than
|
||||
// the previously computed maximum latency then reset the maximum
|
||||
// value using the minimum + an offset.
|
||||
if (minLatencyThreshold >= maxLatencyThreshold) {
|
||||
maxLatencyThreshold = minLatencyThreshold + 3000;
|
||||
}
|
||||
|
||||
const segmentTime = segment.dateTimeObject.getTime();
|
||||
const now = new Date().getTime() + this.clockSkewMs;
|
||||
const latency = now - segmentTime;
|
||||
this.currentLatency = latency;
|
||||
|
||||
// Since the calculation of latency is based on clock times, it's possible
|
||||
// things can be reported incorrectly. So we use a sanity check here to
|
||||
@ -201,7 +234,7 @@ class LatencyCompensator {
|
||||
) {
|
||||
const jumpAmount = latency / 1000 - segment.duration * 3;
|
||||
const seekPosition = this.player.currentTime() + jumpAmount;
|
||||
console.log(
|
||||
console.info(
|
||||
'latency',
|
||||
latency / 1000,
|
||||
'jumping',
|
||||
@ -251,7 +284,7 @@ class LatencyCompensator {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
console.log(
|
||||
console.info(
|
||||
'latency',
|
||||
latency / 1000,
|
||||
'min',
|
||||
@ -275,6 +308,12 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
shouldJumpToLive() {
|
||||
// If we've been rebuffering some recently then don't make it worse by
|
||||
// jumping more into the future.
|
||||
if (this.bufferingCounter > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const now = new Date().getTime();
|
||||
const delta = now - this.lastJumpOccurred;
|
||||
return delta > MAX_JUMP_FREQUENCY;
|
||||
@ -286,7 +325,7 @@ class LatencyCompensator {
|
||||
|
||||
this.lastJumpOccurred = new Date();
|
||||
|
||||
console.log(
|
||||
console.info(
|
||||
'current time',
|
||||
this.player.currentTime(),
|
||||
'seeking to',
|
||||
@ -340,10 +379,6 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
timeout() {
|
||||
if (this.inTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.jumpingToLiveIgnoreBuffer) {
|
||||
return;
|
||||
}
|
||||
@ -363,6 +398,9 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
handlePlaying() {
|
||||
const wasPreviouslyPlaying = this.playing;
|
||||
this.playing = true;
|
||||
|
||||
clearTimeout(this.bufferingTimer);
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
@ -372,12 +410,22 @@ class LatencyCompensator {
|
||||
return;
|
||||
}
|
||||
|
||||
// Seek to live immediately on starting playback to handle any long-pause
|
||||
// If we were not previously playing (was paused, or this is a cold start)
|
||||
// seek to live immediately on starting playback to handle any long-pause
|
||||
// scenarios or somebody starting far back from the live edge.
|
||||
// If we were playing previously then that means we're probably coming back
|
||||
// from a rebuffering event, meaning we should not be adding more seeking
|
||||
// to the mix, just let it play.
|
||||
if (!wasPreviouslyPlaying) {
|
||||
this.jumpingToLiveIgnoreBuffer = true;
|
||||
this.player.liveTracker.seekToLiveEdge();
|
||||
this.lastJumpOccurred = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
handlePause() {
|
||||
this.playing = false;
|
||||
}
|
||||
|
||||
handleEnded() {
|
||||
if (!this.enabled) {
|
||||
@ -392,19 +440,25 @@ class LatencyCompensator {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('handle error', e);
|
||||
this.timeout();
|
||||
}
|
||||
|
||||
countBufferingEvent() {
|
||||
this.bufferingCounter++;
|
||||
|
||||
if (this.bufferingCounter > REBUFFER_EVENT_LIMIT) {
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('timeout due to buffering');
|
||||
this.timeout();
|
||||
this.bufferedAtLatency.push(this.currentLatency);
|
||||
|
||||
console.log(
|
||||
'latency compensation timeout due to buffering:',
|
||||
this.bufferingCounter,
|
||||
'buffering events of',
|
||||
REBUFFER_EVENT_LIMIT
|
||||
);
|
||||
|
||||
// Allow us to forget about old buffering events if enough time goes by.
|
||||
setTimeout(() => {
|
||||
@ -415,7 +469,7 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
handleBuffering() {
|
||||
if (!this.enabled) {
|
||||
if (!this.enabled || this.inTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -424,6 +478,9 @@ class LatencyCompensator {
|
||||
return;
|
||||
}
|
||||
|
||||
this.timeout();
|
||||
|
||||
clearTimeout(this.bufferingTimer);
|
||||
this.bufferingTimer = setTimeout(() => {
|
||||
this.countBufferingEvent();
|
||||
}, MIN_BUFFER_DURATION);
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "App",
|
||||
"name": "Owncast",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/img\/favicon\/android-icon-36x36.png",
|
||||
@ -37,5 +37,6 @@
|
||||
"type": "image\/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
],
|
||||
"display": "fullscreen"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user