Browse Source

Merge branch 'develop' into gek/segment-tracking-persistence

pull/3395/head
Gabe Kangas 2 years ago
parent
commit
ec2b5103e5
No known key found for this signature in database
GPG Key ID: 4345B2060657F330
  1. 2
      .github/workflows/auto-comment-on-label.yaml
  2. 6
      .github/workflows/javascript-format-build.yml
  3. 2
      Dockerfile
  4. 22
      Earthfile
  5. 88
      controllers/index.go
  6. 1
      core/core.go
  7. 6
      core/offlineState.go
  8. 6
      core/rtmp/rtmp.go
  9. 4
      core/storageproviders/rewriteLocalPlaylist.go
  10. 25
      core/storageproviders/s3Storage.go
  11. 2
      core/transcoder/hlsHandler.go
  12. 10
      core/transcoder/transcoder.go
  13. 2
      db/query.sql
  14. 2
      db/query.sql.go
  15. 1
      go.mod
  16. 2
      go.sum
  17. 9
      replays/playlistGenerator.go
  18. 83
      static/metadata.html.tmpl
  19. 11
      static/static.go
  20. 6
      static/web/404.html
  21. 6
      static/web/404/index.html
  22. 1
      static/web/_next/static/RVMtYusC1XoMhDVY7dZn8/_buildManifest.js
  23. 0
      static/web/_next/static/RVMtYusC1XoMhDVY7dZn8/_ssgManifest.js
  24. 1
      static/web/_next/static/chunks/1130-54a10975faad53e6.js
  25. 1
      static/web/_next/static/chunks/1130-a8f4f1269cf500ba.js
  26. 1
      static/web/_next/static/chunks/1390.54a41f441aeb5900.js
  27. 1
      static/web/_next/static/chunks/1390.74b7d711d1b567d8.js
  28. 2
      static/web/_next/static/chunks/1683.bff1967e93ef8f2d.js
  29. 2
      static/web/_next/static/chunks/1700-20ee1212d9d53d1f.js
  30. 2
      static/web/_next/static/chunks/1749-d0c269de311cb336.js
  31. 1
      static/web/_next/static/chunks/1822-942470a32947d41e.js
  32. 1
      static/web/_next/static/chunks/2466-450f21ebfadef131.js
  33. 1
      static/web/_next/static/chunks/2466-b66877346c385e8f.js
  34. 2
      static/web/_next/static/chunks/3595-eff990a9d59bf5b3.js
  35. 1
      static/web/_next/static/chunks/3662.d534a1ec312638f7.js
  36. 2
      static/web/_next/static/chunks/4060-83fee072e10f441e.js
  37. 2
      static/web/_next/static/chunks/4261-085f9ba452c6494b.js
  38. 2
      static/web/_next/static/chunks/4938-d2954cc1d2f2cba9.js
  39. 1
      static/web/_next/static/chunks/4954-cfdba31aeaa0bc8c.js
  40. 1
      static/web/_next/static/chunks/4ad82c5e-4ee9ba5c4a1b7736.js
  41. 1
      static/web/_next/static/chunks/4ad82c5e-8a2b7078d7edfbd1.js
  42. 1
      static/web/_next/static/chunks/5056-699ea3f8794bf8ba.js
  43. 1
      static/web/_next/static/chunks/5056-c1d8c74ee2885189.js
  44. 2
      static/web/_next/static/chunks/5197-5e1c7d67b2ee932d.js
  45. 11
      static/web/_next/static/chunks/5283-188133a899b70038.js
  46. 11
      static/web/_next/static/chunks/5283-4fd930101bbd38a5.js
  47. 8
      static/web/_next/static/chunks/5888-7e4d7abd251e35dc.js
  48. 2
      static/web/_next/static/chunks/6017-bae7d9fa64638d6a.js
  49. 1
      static/web/_next/static/chunks/6108-0aaf6564c29833d0.js
  50. 1
      static/web/_next/static/chunks/6183-34eaa0ed72f73429.js
  51. 1
      static/web/_next/static/chunks/6430.5409e1c56048d9fb.js
  52. 1
      static/web/_next/static/chunks/6430.aaf983756cf43d2c.js
  53. 1
      static/web/_next/static/chunks/6627-33c5c227d9c91eb1.js
  54. 1
      static/web/_next/static/chunks/6627-cd5f0f07f5fd191a.js
  55. 1
      static/web/_next/static/chunks/6697-674d0ad6b6cf6f94.js
  56. 1
      static/web/_next/static/chunks/6697-daaa18ed4a7fbeb3.js
  57. 2
      static/web/_next/static/chunks/686-e54e0686e8b68d42.js
  58. 1
      static/web/_next/static/chunks/6971.20c757ff41adcc55.js
  59. 1
      static/web/_next/static/chunks/7116.4d08cf7bf648c40e.js
  60. 1
      static/web/_next/static/chunks/7116.e553286894fa42f5.js
  61. 1
      static/web/_next/static/chunks/7423-e0d52e40973c386e.js
  62. 1
      static/web/_next/static/chunks/8187-e64e7075c24e044c.js
  63. 1
      static/web/_next/static/chunks/8465-d4471aff35635f2e.js
  64. 1
      static/web/_next/static/chunks/8465-e89ae12ec840d331.js
  65. 1
      static/web/_next/static/chunks/8966-1e438ee19e03d0e0.js
  66. 1
      static/web/_next/static/chunks/8966-4955acf88b3c6b8a.js
  67. 1
      static/web/_next/static/chunks/9266.024e8c7e247613ac.js
  68. 1
      static/web/_next/static/chunks/9266.994c0009cc4d1f4b.js
  69. 31
      static/web/_next/static/chunks/9403-700f4f2fc88e1060.js
  70. 31
      static/web/_next/static/chunks/9403-d68ad167a199814a.js
  71. 1
      static/web/_next/static/chunks/9456-4101ce4fe4ac44c6.js
  72. 1
      static/web/_next/static/chunks/9552-ef0c91f94d86e6e4.js
  73. 1
      static/web/_next/static/chunks/9614.51ab003492e1b36c.js
  74. 1
      static/web/_next/static/chunks/9614.e05165953361b1cb.js
  75. 6
      static/web/_next/static/chunks/ee8b1517-e173562e6d8a9a7c.js
  76. 6
      static/web/_next/static/chunks/ee8b1517-e269878be425639b.js
  77. 1
      static/web/_next/static/chunks/main-4582c4685dd3183e.js
  78. 1
      static/web/_next/static/chunks/main-a0ab4896564e9aa0.js
  79. 1
      static/web/_next/static/chunks/pages/admin-7d7469230bfb9028.js
  80. 1
      static/web/_next/static/chunks/pages/admin-925770471a2e20ea.js
  81. 2
      static/web/_next/static/chunks/pages/admin/access-tokens-7b49f24b06be9bae.js
  82. 2
      static/web/_next/static/chunks/pages/admin/actions-a8b8661938df51f6.js
  83. 2
      static/web/_next/static/chunks/pages/admin/chat/emojis-9a2dcb2fd5e31f36.js
  84. 1
      static/web/_next/static/chunks/pages/admin/chat/messages-1a47f637b4352ca7.js
  85. 1
      static/web/_next/static/chunks/pages/admin/chat/messages-6238d486b783c717.js
  86. 1
      static/web/_next/static/chunks/pages/admin/chat/users-b948e828df595276.js
  87. 1
      static/web/_next/static/chunks/pages/admin/chat/users-bbb4a3a7c9910952.js
  88. 2
      static/web/_next/static/chunks/pages/admin/config-social-items-193d1b325efc2e41.js
  89. 2
      static/web/_next/static/chunks/pages/admin/config-video-e3d0d9cdb6c2769d.js
  90. 1
      static/web/_next/static/chunks/pages/admin/config/general-25b8e527665dc30b.js
  91. 1
      static/web/_next/static/chunks/pages/admin/config/general-5131173209a8f9ac.js
  92. 2
      static/web/_next/static/chunks/pages/admin/config/server-a30c71383907d9f2.js
  93. 2
      static/web/_next/static/chunks/pages/admin/federation/actions-190df41867823a3d.js
  94. 2
      static/web/_next/static/chunks/pages/admin/federation/followers-1b3001aa48d021e4.js
  95. 1
      static/web/_next/static/chunks/pages/admin/hardware-info-0a6d77731a8a4e12.js
  96. 1
      static/web/_next/static/chunks/pages/admin/hardware-info-ebde85da283d92fe.js
  97. 2
      static/web/_next/static/chunks/pages/admin/help-e51ea2b960e8ce58.js
  98. 2
      static/web/_next/static/chunks/pages/admin/logs-5a8813b8cb2b3bbc.js
  99. 1
      static/web/_next/static/chunks/pages/admin/stream-health-4d4b58b11afc93e4.js
  100. 1
      static/web/_next/static/chunks/pages/admin/stream-health-5620d18e5d123967.js
  101. Some files were not shown because too many files have changed in this diff Show More

2
.github/workflows/auto-comment-on-label.yaml

@ -11,7 +11,7 @@ jobs: @@ -11,7 +11,7 @@ jobs:
issues: write
steps:
- name: Add comment
uses: peter-evans/create-or-update-comment@38217c6b94b54c0dbbe75be237257364e2dd2e62
uses: peter-evans/create-or-update-comment@bb291f63cb3095ddc3b7e5572601141854de31d0
with:
issue-number: ${{ github.event.issue.number }}
body: |

6
.github/workflows/javascript-format-build.yml

@ -155,6 +155,12 @@ jobs: @@ -155,6 +155,12 @@ jobs:
- name: Bundle web app (next.js build)
run: build/web/bundleWeb.sh
- name: Rebase
if: ${{ github.ref == 'refs/heads/develop' }}
run: |
git add static/web
git pull --rebase --autostash
# Only commit built web project files on develop.
- name: Commit changes
if: ${{ github.ref == 'refs/heads/develop' }}

2
Dockerfile

@ -22,7 +22,7 @@ ENV NAME=${NAME} @@ -22,7 +22,7 @@ ENV NAME=${NAME}
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags "-extldflags \"-static\" -s -w -X github.com/owncast/owncast/config.GitCommit=$GIT_COMMIT -X github.com/owncast/owncast/config.VersionNumber=$VERSION -X github.com/owncast/owncast/config.BuildPlatform=$NAME" -o owncast .
# Create the image by copying the result of the build into a new alpine image
FROM alpine:3.18.2
FROM alpine:3.18.3
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates && update-ca-certificates
RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast

22
Earthfile

@ -6,10 +6,10 @@ ARG version=develop @@ -6,10 +6,10 @@ 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
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 --platform=darwin/amd64 --platform=darwin/arm64 +build
package-all:
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 --platform=darwin/amd64 +package
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 --platform=darwin/amd64 --platform=darwin/arm64 +package
docker-all:
BUILD --platform=linux/amd64 --platform=linux/386 --platform=linux/arm64 --platform=linux/arm/v7 +docker
@ -36,7 +36,6 @@ build: @@ -36,7 +36,6 @@ build:
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
@ -59,6 +58,10 @@ build: @@ -59,6 +58,10 @@ build:
ARG NAME=macOS-64bit
ARG CC=o64-clang
ARG CXX=o64-clang++
ELSE IF [ "$TARGETPLATFORM" = "darwin/arm64" ]
ARG NAME=macOS-arm64
ARG CC=o64-clang
ARG CXX=o64-clang++
ELSE
RUN echo "Failed to find CC configuration for $TARGETPLATFORM"
ARG --required CC
@ -76,10 +79,13 @@ build: @@ -76,10 +79,13 @@ 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" -tags sqlite_omit_load_extension -o owncast main.go
# Decrease the size of the shipped binary
RUN upx --best --lzma owncast
# Test the binary
RUN upx -t owncast
# Decrease the size of the shipped binary. But only for non-Apple platforms.
# See https://github.com/upx/upx/issues/612
IF [ "$GOOS" != "darwin" ]
RUN upx --best --lzma owncast
# Test the binary
RUN upx -t owncast
END
SAVE ARTIFACT owncast owncast
@ -97,6 +103,8 @@ package: @@ -97,6 +103,8 @@ package:
ARG NAME=linux-arm7
ELSE IF [ "$TARGETPLATFORM" = "darwin/amd64" ]
ARG NAME=macOS-64bit
ELSE IF [ "$TARGETPLATFORM" = "darwin/arm64" ]
ARG NAME=macOS-arm64
ELSE
ARG NAME=custom
END

88
controllers/index.go

@ -4,13 +4,18 @@ import ( @@ -4,13 +4,18 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"path/filepath"
"strings"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core"
"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"
log "github.com/sirupsen/logrus"
)
// IndexHandler handles the default index route.
@ -24,6 +29,13 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { @@ -24,6 +29,13 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
return
}
// For search engine bots and social scrapers return a special
// server-rendered page.
if utils.IsUserAgentABot(r.UserAgent()) && isIndexRequest {
handleScraperMetadataPage(w, r)
return
}
// Set a cache control max-age header
middleware.SetCachingHeaders(w, r)
@ -93,3 +105,79 @@ func renderIndexHtml(w http.ResponseWriter, nonce string) { @@ -93,3 +105,79 @@ func renderIndexHtml(w http.ResponseWriter, nonce string) {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// MetadataPage represents a server-rendered web page for bots and web scrapers.
type MetadataPage struct {
RequestedURL string
Image string
Thumbnail string
TagsString string
Summary string
Name string
Tags []string
SocialHandles []models.SocialHandle
}
// Return a basic HTML page with server-rendered metadata from the config
// to give to Opengraph clients and web scrapers (bots, web crawlers, etc).
func handleScraperMetadataPage(w http.ResponseWriter, r *http.Request) {
tmpl, err := static.GetBotMetadataTemplate()
if err != nil {
log.Errorln(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
scheme := "http"
if siteURL := data.GetServerURL(); siteURL != "" {
if parsed, err := url.Parse(siteURL); err == nil && parsed.Scheme != "" {
scheme = parsed.Scheme
}
}
fullURL, err := url.Parse(fmt.Sprintf("%s://%s%s", scheme, r.Host, r.URL.Path))
if err != nil {
log.Errorln(err)
}
imageURL, err := url.Parse(fmt.Sprintf("%s://%s%s", scheme, r.Host, "/logo/external"))
if err != nil {
log.Errorln(err)
}
status := core.GetStatus()
// If the thumbnail does not exist or we're offline then just use the logo image
var thumbnailURL string
if status.Online && utils.DoesFileExists(filepath.Join(config.DataDirectory, "tmp", "thumbnail.jpg")) {
thumbnail, err := url.Parse(fmt.Sprintf("%s://%s%s", scheme, r.Host, "/thumbnail.jpg"))
if err != nil {
log.Errorln(err)
thumbnailURL = imageURL.String()
} else {
thumbnailURL = thumbnail.String()
}
} else {
thumbnailURL = imageURL.String()
}
tagsString := strings.Join(data.GetServerMetadataTags(), ",")
metadata := MetadataPage{
Name: data.GetServerName(),
RequestedURL: fullURL.String(),
Image: imageURL.String(),
Summary: data.GetServerSummary(),
Thumbnail: thumbnailURL,
TagsString: tagsString,
Tags: data.GetServerMetadataTags(),
SocialHandles: data.GetSocialHandles(),
}
// Set a cache header
middleware.SetCachingHeaders(w, r)
w.Header().Set("Content-Type", "text/html")
if err := tmpl.Execute(w, metadata); err != nil {
log.Errorln(err)
}
}

1
core/core.go

@ -103,6 +103,7 @@ func transitionToOfflineVideoStreamContent() { @@ -103,6 +103,7 @@ func transitionToOfflineVideoStreamContent() {
streamId := "offline"
_storage.SetStreamId(streamId)
fileWriter.SetStreamID(streamId)
handler.SetStreamId(streamId)
_transcoder := transcoder.NewTranscoder(streamId)

6
core/offlineState.go

@ -50,14 +50,14 @@ func appendOfflineToVariantPlaylist(index int, playlistFilePath string) { @@ -50,14 +50,14 @@ func appendOfflineToVariantPlaylist(index int, playlistFilePath string) {
func makeVariantIndexOffline(streamId string, index int, offlineFilePath string, offlineFilename string) {
playlistFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, streamId, "%d/stream.m3u8"), index)
segmentFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, streamId, "%d/%s"), index, offlineFilename)
// segmentFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, streamId, "%d/%s"), index, offlineFilename)
segmentFileDestinationPath := fmt.Sprintf(filepath.Join("hls", streamId, "%d/%s"), index, offlineFilename)
if err := utils.Copy(offlineFilePath, segmentFilePath); err != nil {
if err := utils.Copy(offlineFilePath, offlineFilePath); err != nil {
log.Warnln(err)
}
if _, err := _storage.Save(segmentFilePath, segmentFileDestinationPath, 0); err != nil {
if _, err := _storage.Save(offlineFilePath, segmentFileDestinationPath, 0); err != nil {
log.Warnln(err)
}

6
core/rtmp/rtmp.go

@ -73,7 +73,7 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { @@ -73,7 +73,7 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
}
if _hasInboundRTMPConnection {
log.Errorln("stream already running; can not overtake an existing stream")
log.Errorln("stream already running; can not overtake an existing stream from", nc.RemoteAddr().String())
_ = nc.Close()
return
}
@ -94,14 +94,14 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { @@ -94,14 +94,14 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
}
if !accessGranted {
log.Errorln("invalid streaming key; rejecting incoming stream")
log.Errorln("invalid streaming key; rejecting incoming stream from", nc.RemoteAddr().String())
_ = nc.Close()
return
}
rtmpOut, rtmpIn := io.Pipe()
_pipe = rtmpIn
log.Infoln("Inbound stream connected.")
log.Infoln("Inbound stream connected from", nc.RemoteAddr().String())
_setStreamAsConnected(rtmpOut)
_hasInboundRTMPConnection = true

4
core/storageproviders/rewriteLocalPlaylist.go

@ -13,7 +13,7 @@ import ( @@ -13,7 +13,7 @@ import (
)
// rewriteRemotePlaylist will take a local playlist and rewrite it to have absolute URLs to remote locations.
func rewriteRemotePlaylist(localFilePath, streamId, remoteServingEndpoint string) error {
func rewriteRemotePlaylist(localFilePath, remoteServingEndpoint, pathPrefix string) error {
f, err := os.Open(localFilePath) // nolint
if err != nil {
log.Fatalln(err)
@ -25,7 +25,7 @@ func rewriteRemotePlaylist(localFilePath, streamId, remoteServingEndpoint string @@ -25,7 +25,7 @@ func rewriteRemotePlaylist(localFilePath, streamId, remoteServingEndpoint string
}
for _, item := range p.Variants {
item.URI = remoteServingEndpoint + filepath.Join("/hls", streamId, item.URI)
item.URI = filepath.Join(remoteServingEndpoint, pathPrefix, item.URI)
}
publicPath := filepath.Join(config.HLSStoragePath, filepath.Base(localFilePath))

25
core/storageproviders/s3Storage.go

@ -37,6 +37,7 @@ type S3Storage struct { @@ -37,6 +37,7 @@ type S3Storage struct {
s3AccessKey string
s3Secret string
s3ACL string
s3PathPrefix string
s3ForcePathStyle bool
// If we try to upload a playlist but it is not yet on disk
@ -78,6 +79,7 @@ func (s *S3Storage) Setup() error { @@ -78,6 +79,7 @@ func (s *S3Storage) Setup() error {
s.s3AccessKey = s3Config.AccessKey
s.s3Secret = s3Config.Secret
s.s3ACL = s3Config.ACL
s.s3PathPrefix = s3Config.PathPrefix
s.s3ForcePathStyle = s3Config.ForcePathStyle
s.sess = s.connectAWS()
@ -146,7 +148,11 @@ func (s *S3Storage) VariantPlaylistWritten(localFilePath string) { @@ -146,7 +148,11 @@ func (s *S3Storage) VariantPlaylistWritten(localFilePath string) {
// MasterPlaylistWritten is called when the master hls playlist is written.
func (s *S3Storage) MasterPlaylistWritten(localFilePath string) {
// Rewrite the playlist to use absolute remote S3 URLs
if err := rewriteRemotePlaylist(localFilePath, s.streamId, s.host); err != nil {
pathPrefix := filepath.Join("hls", s.streamId)
if s.s3PathPrefix != "" {
pathPrefix = filepath.Join(s.s3PathPrefix, pathPrefix)
}
if err := rewriteRemotePlaylist(localFilePath, s.host, pathPrefix); err != nil {
log.Warnln(err)
}
}
@ -159,13 +165,24 @@ func (s *S3Storage) Save(localFilePath, remoteDestinationPath string, retryCount @@ -159,13 +165,24 @@ func (s *S3Storage) Save(localFilePath, remoteDestinationPath string, retryCount
}
defer file.Close()
// Convert the local path to the variant/file path by stripping the local storage location.
normalizedPath := strings.TrimPrefix(localFilePath, config.HLSStoragePath)
// Build the remote path by adding the "hls" path prefix.
remotePath := strings.Join([]string{"hls", normalizedPath}, "")
// If a custom path prefix is set prepend it.
if s.s3PathPrefix != "" {
prefix := strings.TrimPrefix(s.s3PathPrefix, "/")
remotePath = strings.Join([]string{prefix, remotePath}, "/")
}
maxAgeSeconds := utils.GetCacheDurationSecondsForPath(localFilePath)
cacheControlHeader := fmt.Sprintf("max-age=%d", maxAgeSeconds)
uploadInput := &s3manager.UploadInput{
Bucket: aws.String(s.s3Bucket), // Bucket to be used
Key: aws.String(remoteDestinationPath), // Name of the file to be saved
Body: file, // File
Bucket: aws.String(s.s3Bucket), // Bucket to be used
Key: aws.String(remotePath), // Name of the file to be saved
Body: file, // File
CacheControl: &cacheControlHeader,
}

2
core/transcoder/hlsHandler.go

@ -32,7 +32,7 @@ func (h *HLSHandler) SetStreamId(streamId string) { @@ -32,7 +32,7 @@ func (h *HLSHandler) SetStreamId(streamId string) {
func (h *HLSHandler) SegmentWritten(localFilePath string) {
remotePath, _, err := h.Storage.SegmentWritten(localFilePath)
if err != nil {
log.Errorln(err)
log.Debugln(err, localFilePath)
return
}

10
core/transcoder/transcoder.go

@ -28,11 +28,9 @@ type Transcoder struct { @@ -28,11 +28,9 @@ type Transcoder struct {
TranscoderCompleted func(error)
StreamID string
playlistOutputPath string
ffmpegPath string
internalListenerPort string
input string
segmentOutputPath string
variants []HLSVariant
currentStreamOutputSettings []models.StreamOutputVariant
@ -286,9 +284,6 @@ func NewTranscoder(streamID string) *Transcoder { @@ -286,9 +284,6 @@ func NewTranscoder(streamID string) *Transcoder {
transcoder.currentStreamOutputSettings = data.GetStreamOutputVariants()
transcoder.currentLatencyLevel = data.GetStreamLatencyLevel()
transcoder.codec = getCodec(data.GetVideoCodec())
transcoder.segmentOutputPath = config.HLSStoragePath
transcoder.playlistOutputPath = config.HLSStoragePath
transcoder.input = "pipe:0" // stdin
for index, quality := range transcoder.currentStreamOutputSettings {
@ -437,11 +432,6 @@ func (t *Transcoder) SetStdin(pipe *io.PipeReader) { @@ -437,11 +432,6 @@ func (t *Transcoder) SetStdin(pipe *io.PipeReader) {
t.stdin = pipe
}
// SetOutputPath sets the root directory that should include playlists and video segments.
func (t *Transcoder) SetOutputPath(output string) {
t.segmentOutputPath = output
}
// SetStreamID sets a unique identifier for the currently transcoding stream.
func (t *Transcoder) SetStreamID(id string) {
t.StreamID = id

2
db/query.sql

@ -101,7 +101,7 @@ UPDATE users SET authenticated_at = CURRENT_TIMESTAMP WHERE id = $1; @@ -101,7 +101,7 @@ UPDATE users SET authenticated_at = CURRENT_TIMESTAMP WHERE id = $1;
SELECT id, body, hidden_at, timestamp FROM messages WHERE eventType = 'CHAT' AND user_id = $1 ORDER BY TIMESTAMP DESC;
-- name: IsDisplayNameAvailable :one
SELECT count(*) FROM users WHERE display_name = $1 AND authenticated_at is not null AND disabled_at is NULL;
SELECT count(*) FROM users WHERE display_name = $1 AND ( type='API' OR authenticated_at IS NOT NULL ) AND disabled_at IS NULL;
-- name: ChangeDisplayName :exec
UPDATE users SET display_name = $1, previous_names = previous_names || $2, namechanged_at = $3 WHERE id = $4;

2
db/query.sql.go

@ -1144,7 +1144,7 @@ func (q *Queries) InsertStream(ctx context.Context, arg InsertStreamParams) erro @@ -1144,7 +1144,7 @@ func (q *Queries) InsertStream(ctx context.Context, arg InsertStreamParams) erro
}
const isDisplayNameAvailable = `-- name: IsDisplayNameAvailable :one
SELECT count(*) FROM users WHERE display_name = $1 AND authenticated_at is not null AND disabled_at is NULL
SELECT count(*) FROM users WHERE display_name = $1 AND ( type='API' OR authenticated_at IS NOT NULL ) AND disabled_at IS NULL
`
func (q *Queries) IsDisplayNameAvailable(ctx context.Context, displayName string) (int64, error) {

1
go.mod

@ -67,6 +67,7 @@ require ( @@ -67,6 +67,7 @@ require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mssola/user_agent v0.6.0 // indirect
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
)

2
go.sum

@ -85,6 +85,8 @@ github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJ @@ -85,6 +85,8 @@ github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJ
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww=
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=

9
replays/playlistGenerator.go

@ -3,6 +3,7 @@ package replays @@ -3,6 +3,7 @@ package replays
import (
"context"
"fmt"
"strings"
"time"
"github.com/grafov/m3u8"
@ -50,8 +51,14 @@ func (p *PlaylistGenerator) createMediaPlaylistForConfigurationAndSegments(confi @@ -50,8 +51,14 @@ func (p *PlaylistGenerator) createMediaPlaylistForConfigurationAndSegments(confi
// Add the segments to the playlist.
for index, segment := range segments {
// If it's a URL leave it as is, if it's a local path then append a slash.
path := segment.Path
if !strings.HasPrefix(path, "http") {
path = "/" + path
}
mediaSegment := m3u8.MediaSegment{
URI: "/" + segment.Path,
URI: path,
Duration: segmentDuration,
SeqId: uint64(index),
ProgramDateTime: segment.Timestamp,

83
static/metadata.html.tmpl vendored

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{.Name}}</title>
<meta name="description" content="{{.Summary}}">
<meta property="og:title" content="{{.Name}}">
<meta property="og:site_name" content="{{.Name}}">
<meta property="og:url" content="{{.RequestedURL}}">
<meta property="og:description" content="{{.Summary}}">
<meta property="og:type" content="video.other">
<meta property="video:tag" content="{{.TagsString}}">
<meta property="og:image" content="{{.Thumbnail}}">
<meta property="og:image:url" content="{{.Thumbnail}}">
<meta property="og:image:alt" content="{{.Image}}">
<meta property="og:video" content='{{.RequestedURL}}embed/video' />
<meta property="og:video:secure_url" content='{{.RequestedURL}}embed/video' />
<meta property="og:video:height" content="315" />
<meta property="og:video:width" content="560" />
<meta property="og:video:type" content="text/html" />
<meta property="og:video:actor" content="{{.Name}}" />
<meta property="twitter:title" content="{{.Name}}">
<meta property="twitter:url" content="{{.RequestedURL}}">
<meta property="twitter:description" content="{{.Summary}}">
<meta property="twitter:image" content="{{.Image}}">
<meta property="twitter:card" content="player" />
<meta property="twitter:player" content='{{.RequestedURL}}embed/video' />
<meta property="twitter:player:width" content="560" />
<meta property="twitter:player:height" content="315" />
<link rel="apple-touch-icon" sizes="57x57" href="/img/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/img/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/img/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/img/favicon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/img/favicon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/img/favicon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/img/favicon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/img/favicon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/img/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/img/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="authorization_endpoint" href="/api/auth/provider/indieauth">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/img/favicon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
</head>
<body>
<h1>{{.Name}}</h1>
<center>
<img src="{{.Thumbnail}}" width=10% />
</center>
<h3>{{.Summary}}</h3>
{{range .Tags}}
<li>{{.}}</li>
{{end}}
<br/>
<h3>Links for {{.Name}}:</h3>
{{range .SocialHandles}}
<li><a href="{{.URL}}">{{.Platform}}</a></li>
{{end}}
</body>
</html>

11
static/static.go vendored

@ -76,3 +76,14 @@ func getFileSystemStaticFileOrDefault(path string, defaultData []byte) []byte { @@ -76,3 +76,14 @@ func getFileSystemStaticFileOrDefault(path string, defaultData []byte) []byte {
return data
}
//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
}

6
static/web/404.html vendored

File diff suppressed because one or more lines are too long

6
static/web/404/index.html vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/RVMtYusC1XoMhDVY7dZn8/_buildManifest.js

File diff suppressed because one or more lines are too long

0
static/web/_next/static/os7UB0OmoOFU092vTD7Hy/_ssgManifest.js → static/web/_next/static/RVMtYusC1XoMhDVY7dZn8/_ssgManifest.js

1
static/web/_next/static/chunks/1130-54a10975faad53e6.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/1130-a8f4f1269cf500ba.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/1390.54a41f441aeb5900.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/1390.74b7d711d1b567d8.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/1683.9d90dd9cd2cb84c3.js → static/web/_next/static/chunks/1683.bff1967e93ef8f2d.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/1700-4d186ad54316e8e3.js → static/web/_next/static/chunks/1700-20ee1212d9d53d1f.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/1749-95d306e8dcbf18c9.js → static/web/_next/static/chunks/1749-d0c269de311cb336.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/1822-942470a32947d41e.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/2466-450f21ebfadef131.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/2466-b66877346c385e8f.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/3595-74f53641dfc123ea.js → static/web/_next/static/chunks/3595-eff990a9d59bf5b3.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/3662.d534a1ec312638f7.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/4060-d6809300755291ca.js → static/web/_next/static/chunks/4060-83fee072e10f441e.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/4261-1c69da29574680fc.js → static/web/_next/static/chunks/4261-085f9ba452c6494b.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/4938-0f8d85099b1dab44.js → static/web/_next/static/chunks/4938-d2954cc1d2f2cba9.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/4954-cfdba31aeaa0bc8c.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/4ad82c5e-4ee9ba5c4a1b7736.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/4ad82c5e-8a2b7078d7edfbd1.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/5056-699ea3f8794bf8ba.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/5056-c1d8c74ee2885189.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/5197-71e496257358d5de.js → static/web/_next/static/chunks/5197-5e1c7d67b2ee932d.js vendored

File diff suppressed because one or more lines are too long

11
static/web/_next/static/chunks/5283-188133a899b70038.js vendored

File diff suppressed because one or more lines are too long

11
static/web/_next/static/chunks/5283-4fd930101bbd38a5.js vendored

File diff suppressed because one or more lines are too long

8
static/web/_next/static/chunks/5888-1bc8318a305596b9.js → static/web/_next/static/chunks/5888-7e4d7abd251e35dc.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/6017-70ec08da49208e7d.js → static/web/_next/static/chunks/6017-bae7d9fa64638d6a.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6108-0aaf6564c29833d0.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6183-34eaa0ed72f73429.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6430.5409e1c56048d9fb.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6430.aaf983756cf43d2c.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6627-33c5c227d9c91eb1.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6627-cd5f0f07f5fd191a.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6697-674d0ad6b6cf6f94.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6697-daaa18ed4a7fbeb3.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/686-ea00a8252cf77572.js → static/web/_next/static/chunks/686-e54e0686e8b68d42.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/6971.20c757ff41adcc55.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/7116.4d08cf7bf648c40e.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/7116.e553286894fa42f5.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/7423-e0d52e40973c386e.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/8187-e64e7075c24e044c.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/8465-d4471aff35635f2e.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/8465-e89ae12ec840d331.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/8966-1e438ee19e03d0e0.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/8966-4955acf88b3c6b8a.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/9266.024e8c7e247613ac.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/9266.994c0009cc4d1f4b.js vendored

File diff suppressed because one or more lines are too long

31
static/web/_next/static/chunks/9403-700f4f2fc88e1060.js vendored

File diff suppressed because one or more lines are too long

31
static/web/_next/static/chunks/9403-d68ad167a199814a.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/9456-4101ce4fe4ac44c6.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/9552-ef0c91f94d86e6e4.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/9614.51ab003492e1b36c.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/9614.e05165953361b1cb.js vendored

@ -1 +0,0 @@ @@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9614],{29614:function(e,l,n){"use strict";n.r(l),n.d(l,{Modal:function(){return p}});var o=n(85893),a=n(89552),t=n(56697),s=n(67294),i=n(4511),d=n(69183),r=n(70051),c=n.n(r);let p=e=>{let{title:l,url:n,open:r,handleOk:p,handleCancel:h,afterClose:u,height:m,width:f,children:x}=e,[_,v]=(0,s.useState)(!!n),g="100%",k="520px";n&&(g="70vh",k="900px");let w={padding:"0px",minHeight:m,height:null!=m?m:g},y=n&&(0,o.jsx)("iframe",{title:l,src:n,width:"100%",height:"100%",sandbox:"allow-same-origin allow-scripts allow-popups allow-forms",frameBorder:"0",allowFullScreen:!0,style:{display:"block"},onLoad:()=>v(!1)});return(0,o.jsx)(t.default,{title:l,open:r,onOk:p,onCancel:h,afterClose:u,bodyStyle:w,width:null!=f?f:k,zIndex:999,footer:null,centered:!0,destroyOnClose:!0,className:c().modal,children:(0,o.jsx)(i.SV,{fallbackRender:e=>{let{error:l,resetErrorBoundary:n}=e;return(0,o.jsx)(d.A,{componentName:"Modal",message:l.message,retryFunction:n})},children:(0,o.jsxs)("div",{id:"modal-container",style:{height:"100%"},children:[y&&(0,o.jsx)("div",{style:{display:_?"none":"inline"},children:y}),x&&(0,o.jsx)("div",{className:c().content,children:x}),_&&(0,o.jsx)(a.default,{className:c().spinner,spinning:_,size:"large",tip:l})]})})})};p.defaultProps={url:void 0,children:void 0,handleOk:void 0,handleCancel:void 0,afterClose:void 0}},70051:function(e){e.exports={spinner:"Modal_spinner__vORWo",content:"Modal_content__mZkaV",modal:"Modal_modal___vOcW"}}}]);

6
static/web/_next/static/chunks/ee8b1517-e173562e6d8a9a7c.js vendored

File diff suppressed because one or more lines are too long

6
static/web/_next/static/chunks/ee8b1517-e269878be425639b.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/main-4582c4685dd3183e.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/main-a0ab4896564e9aa0.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin-7d7469230bfb9028.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin-925770471a2e20ea.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/access-tokens-849b6571ec1cb0d3.js → static/web/_next/static/chunks/pages/admin/access-tokens-7b49f24b06be9bae.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/actions-593097ad9518fc88.js → static/web/_next/static/chunks/pages/admin/actions-a8b8661938df51f6.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/chat/emojis-30ffad910eb27e1a.js → static/web/_next/static/chunks/pages/admin/chat/emojis-9a2dcb2fd5e31f36.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/chat/messages-1a47f637b4352ca7.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/chat/messages-6238d486b783c717.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/chat/users-b948e828df595276.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/chat/users-bbb4a3a7c9910952.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/config-social-items-e249e56cefe7badc.js → static/web/_next/static/chunks/pages/admin/config-social-items-193d1b325efc2e41.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/config-video-21561df776c7ab49.js → static/web/_next/static/chunks/pages/admin/config-video-e3d0d9cdb6c2769d.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/config/general-25b8e527665dc30b.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/config/general-5131173209a8f9ac.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/config/server-8a01c4c5df1fd783.js → static/web/_next/static/chunks/pages/admin/config/server-a30c71383907d9f2.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/federation/actions-37f83ccccc439d69.js → static/web/_next/static/chunks/pages/admin/federation/actions-190df41867823a3d.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/federation/followers-bde124bf6e2cba22.js → static/web/_next/static/chunks/pages/admin/federation/followers-1b3001aa48d021e4.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/hardware-info-0a6d77731a8a4e12.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/hardware-info-ebde85da283d92fe.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/help-bedea22d9d1b797e.js → static/web/_next/static/chunks/pages/admin/help-e51ea2b960e8ce58.js vendored

File diff suppressed because one or more lines are too long

2
static/web/_next/static/chunks/pages/admin/logs-22f16a57ba5ffab0.js → static/web/_next/static/chunks/pages/admin/logs-5a8813b8cb2b3bbc.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/stream-health-4d4b58b11afc93e4.js vendored

File diff suppressed because one or more lines are too long

1
static/web/_next/static/chunks/pages/admin/stream-health-5620d18e5d123967.js vendored

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save