mirror of
https://github.com/owncast/owncast.git
synced 2025-11-02 20:23:29 +08:00
Support using the custom video serving endpoint even if you don't use object storage (#2924)
* feat(video): refactor video serving endpoint It can now be used without an object storage provider. Closes #2785 * fix: remove debug log
This commit is contained in:
@ -771,6 +771,28 @@ func SetDisableSearchIndexing(w http.ResponseWriter, r *http.Request) {
|
|||||||
controllers.WriteSimpleResponse(w, true, "search indexing support updated")
|
controllers.WriteSimpleResponse(w, true, "search indexing support updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetVideoServingEndpoint will save the video serving endpoint.
|
||||||
|
func SetVideoServingEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
endpoint, success := getValueFromRequest(w, r)
|
||||||
|
if !success {
|
||||||
|
controllers.WriteSimpleResponse(w, false, "unable to update custom video serving endpoint")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value, ok := endpoint.Value.(string)
|
||||||
|
if !ok {
|
||||||
|
controllers.WriteSimpleResponse(w, false, "unable to update custom video serving endpoint")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := data.SetVideoServingEndpoint(value); err != nil {
|
||||||
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
controllers.WriteSimpleResponse(w, true, "custom video serving endpoint updated")
|
||||||
|
}
|
||||||
|
|
||||||
func requirePOST(w http.ResponseWriter, r *http.Request) bool {
|
func requirePOST(w http.ResponseWriter, r *http.Request) bool {
|
||||||
if r.Method != controllers.POST {
|
if r.Method != controllers.POST {
|
||||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
|
|||||||
@ -59,6 +59,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
ChatDisabled: data.GetChatDisabled(),
|
ChatDisabled: data.GetChatDisabled(),
|
||||||
ChatJoinMessagesEnabled: data.GetChatJoinMessagesEnabled(),
|
ChatJoinMessagesEnabled: data.GetChatJoinMessagesEnabled(),
|
||||||
SocketHostOverride: data.GetWebsocketOverrideHost(),
|
SocketHostOverride: data.GetWebsocketOverrideHost(),
|
||||||
|
VideoServingEndpoint: data.GetVideoServingEndpoint(),
|
||||||
ChatEstablishedUserMode: data.GetChatEstbalishedUsersOnlyMode(),
|
ChatEstablishedUserMode: data.GetChatEstbalishedUsersOnlyMode(),
|
||||||
HideViewerCount: data.GetHideViewerCount(),
|
HideViewerCount: data.GetHideViewerCount(),
|
||||||
DisableSearchIndexing: data.GetDisableSearchIndexing(),
|
DisableSearchIndexing: data.GetDisableSearchIndexing(),
|
||||||
@ -107,6 +108,7 @@ type serverConfigAdminResponse struct {
|
|||||||
SocketHostOverride string `json:"socketHostOverride,omitempty"`
|
SocketHostOverride string `json:"socketHostOverride,omitempty"`
|
||||||
WebServerIP string `json:"webServerIP"`
|
WebServerIP string `json:"webServerIP"`
|
||||||
VideoCodec string `json:"videoCodec"`
|
VideoCodec string `json:"videoCodec"`
|
||||||
|
VideoServingEndpoint string `json:"videoServingEndpoint"`
|
||||||
S3 models.S3 `json:"s3"`
|
S3 models.S3 `json:"s3"`
|
||||||
Federation federationConfigResponse `json:"federation"`
|
Federation federationConfigResponse `json:"federation"`
|
||||||
SupportedCodecs []string `json:"supportedCodecs"`
|
SupportedCodecs []string `json:"supportedCodecs"`
|
||||||
@ -120,9 +122,9 @@ type serverConfigAdminResponse struct {
|
|||||||
ChatDisabled bool `json:"chatDisabled"`
|
ChatDisabled bool `json:"chatDisabled"`
|
||||||
ChatJoinMessagesEnabled bool `json:"chatJoinMessagesEnabled"`
|
ChatJoinMessagesEnabled bool `json:"chatJoinMessagesEnabled"`
|
||||||
ChatEstablishedUserMode bool `json:"chatEstablishedUserMode"`
|
ChatEstablishedUserMode bool `json:"chatEstablishedUserMode"`
|
||||||
|
DisableSearchIndexing bool `json:"disableSearchIndexing"`
|
||||||
StreamKeyOverridden bool `json:"streamKeyOverridden"`
|
StreamKeyOverridden bool `json:"streamKeyOverridden"`
|
||||||
HideViewerCount bool `json:"hideViewerCount"`
|
HideViewerCount bool `json:"hideViewerCount"`
|
||||||
DisableSearchIndexing bool `json:"disableSearchIndexing"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type videoSettings struct {
|
type videoSettings struct {
|
||||||
|
|||||||
@ -70,6 +70,7 @@ const (
|
|||||||
customColorVariableValuesKey = "custom_color_variable_values"
|
customColorVariableValuesKey = "custom_color_variable_values"
|
||||||
streamKeysKey = "stream_keys"
|
streamKeysKey = "stream_keys"
|
||||||
disableSearchIndexingKey = "disable_search_indexing"
|
disableSearchIndexingKey = "disable_search_indexing"
|
||||||
|
videoServingEndpointKey = "video_serving_endpoint"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetExtraPageBodyContent will return the user-supplied body content.
|
// GetExtraPageBodyContent will return the user-supplied body content.
|
||||||
@ -974,3 +975,14 @@ func GetDisableSearchIndexing() bool {
|
|||||||
}
|
}
|
||||||
return disableSearchIndexing
|
return disableSearchIndexing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetVideoServingEndpoint returns the custom video endpont.
|
||||||
|
func GetVideoServingEndpoint() string {
|
||||||
|
message, _ := _datastore.GetString(videoServingEndpointKey)
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetVideoServingEndpoint sets the custom video endpoint.
|
||||||
|
func SetVideoServingEndpoint(message string) error {
|
||||||
|
return _datastore.SetString(videoServingEndpointKey, message)
|
||||||
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
datastoreValuesVersion = 2
|
datastoreValuesVersion = 3
|
||||||
datastoreValueVersionKey = "DATA_STORE_VERSION"
|
datastoreValueVersionKey = "DATA_STORE_VERSION"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,6 +25,8 @@ func migrateDatastoreValues(datastore *Datastore) {
|
|||||||
migrateToDatastoreValues1(datastore)
|
migrateToDatastoreValues1(datastore)
|
||||||
case 1:
|
case 1:
|
||||||
migrateToDatastoreValues2(datastore)
|
migrateToDatastoreValues2(datastore)
|
||||||
|
case 2:
|
||||||
|
migrateToDatastoreValues3ServingEndpoint3(datastore)
|
||||||
default:
|
default:
|
||||||
log.Fatalln("missing datastore values migration step")
|
log.Fatalln("missing datastore values migration step")
|
||||||
}
|
}
|
||||||
@ -61,3 +63,13 @@ func migrateToDatastoreValues2(datastore *Datastore) {
|
|||||||
{Key: oldAdminPassword, Comment: "Default stream key"},
|
{Key: oldAdminPassword, Comment: "Default stream key"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func migrateToDatastoreValues3ServingEndpoint3(_ *Datastore) {
|
||||||
|
s3Config := GetS3Config()
|
||||||
|
|
||||||
|
if !s3Config.Enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = SetVideoServingEndpoint(s3Config.ServingEndpoint)
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/core/transcoder"
|
"github.com/owncast/owncast/core/transcoder"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
type LocalStorage struct {
|
type LocalStorage struct {
|
||||||
// Cleanup old public HLS content every N min from the webroot.
|
// Cleanup old public HLS content every N min from the webroot.
|
||||||
onlineCleanupTicker *time.Ticker
|
onlineCleanupTicker *time.Ticker
|
||||||
|
customVideoServingEndpoint string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalStorage returns a new LocalStorage instance.
|
// NewLocalStorage returns a new LocalStorage instance.
|
||||||
@ -22,6 +24,10 @@ func NewLocalStorage() *LocalStorage {
|
|||||||
|
|
||||||
// Setup configures this storage provider.
|
// Setup configures this storage provider.
|
||||||
func (s *LocalStorage) Setup() error {
|
func (s *LocalStorage) Setup() error {
|
||||||
|
if data.GetVideoServingEndpoint() != "" {
|
||||||
|
s.customVideoServingEndpoint = data.GetVideoServingEndpoint()
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: This cleanup timer will have to be disabled to support recordings in the future
|
// NOTE: This cleanup timer will have to be disabled to support recordings in the future
|
||||||
// as all HLS segments have to be publicly available on disk to keep a recording of them.
|
// as all HLS segments have to be publicly available on disk to keep a recording of them.
|
||||||
s.onlineCleanupTicker = time.NewTicker(1 * time.Minute)
|
s.onlineCleanupTicker = time.NewTicker(1 * time.Minute)
|
||||||
@ -50,7 +56,12 @@ func (s *LocalStorage) VariantPlaylistWritten(localFilePath string) {
|
|||||||
|
|
||||||
// MasterPlaylistWritten is called when the master hls playlist is written.
|
// MasterPlaylistWritten is called when the master hls playlist is written.
|
||||||
func (s *LocalStorage) MasterPlaylistWritten(localFilePath string) {
|
func (s *LocalStorage) MasterPlaylistWritten(localFilePath string) {
|
||||||
if _, err := s.Save(localFilePath, 0); err != nil {
|
if s.customVideoServingEndpoint != "" {
|
||||||
|
// Rewrite the playlist to use custom absolute remote URLs
|
||||||
|
if err := rewriteRemotePlaylist(localFilePath, s.customVideoServingEndpoint); err != nil {
|
||||||
|
log.Warnln(err)
|
||||||
|
}
|
||||||
|
} else if _, err := s.Save(localFilePath, 0); err != nil {
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
core/storageproviders/rewriteLocalPlaylist.go
Normal file
36
core/storageproviders/rewriteLocalPlaylist.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package storageproviders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/grafov/m3u8"
|
||||||
|
"github.com/owncast/owncast/config"
|
||||||
|
"github.com/owncast/owncast/core/playlist"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rewriteRemotePlaylist will take a local playlist and rewrite it to have absolute URLs to remote locations.
|
||||||
|
func rewriteRemotePlaylist(localFilePath, remoteServingEndpoint string) error {
|
||||||
|
f, err := os.Open(localFilePath) // nolint
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := m3u8.NewMasterPlaylist()
|
||||||
|
if err := p.DecodeFrom(bufio.NewReader(f), false); err != nil {
|
||||||
|
log.Warnln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range p.Variants {
|
||||||
|
item.URI = remoteServingEndpoint + filepath.Join("/hls", item.URI)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicPath := filepath.Join(config.HLSStoragePath, filepath.Base(localFilePath))
|
||||||
|
|
||||||
|
newPlaylist := p.String()
|
||||||
|
|
||||||
|
return playlist.WritePlaylist(newPlaylist, publicPath)
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package storageproviders
|
package storageproviders
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -11,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/core/playlist"
|
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
@ -21,8 +19,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
|
|
||||||
"github.com/grafov/m3u8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// S3Storage is the s3 implementation of a storage provider.
|
// S3Storage is the s3 implementation of a storage provider.
|
||||||
@ -58,8 +54,9 @@ func (s *S3Storage) Setup() error {
|
|||||||
log.Trace("Setting up S3 for external storage of video...")
|
log.Trace("Setting up S3 for external storage of video...")
|
||||||
|
|
||||||
s3Config := data.GetS3Config()
|
s3Config := data.GetS3Config()
|
||||||
if s3Config.ServingEndpoint != "" {
|
customVideoServingEndpoint := data.GetVideoServingEndpoint()
|
||||||
s.host = s3Config.ServingEndpoint
|
if customVideoServingEndpoint != "" {
|
||||||
|
s.host = customVideoServingEndpoint
|
||||||
} else {
|
} else {
|
||||||
s.host = fmt.Sprintf("%s/%s", s3Config.Endpoint, s3Config.Bucket)
|
s.host = fmt.Sprintf("%s/%s", s3Config.Endpoint, s3Config.Bucket)
|
||||||
}
|
}
|
||||||
@ -130,7 +127,7 @@ func (s *S3Storage) VariantPlaylistWritten(localFilePath string) {
|
|||||||
// MasterPlaylistWritten is called when the master hls playlist is written.
|
// MasterPlaylistWritten is called when the master hls playlist is written.
|
||||||
func (s *S3Storage) MasterPlaylistWritten(localFilePath string) {
|
func (s *S3Storage) MasterPlaylistWritten(localFilePath string) {
|
||||||
// Rewrite the playlist to use absolute remote S3 URLs
|
// Rewrite the playlist to use absolute remote S3 URLs
|
||||||
if err := s.rewriteRemotePlaylist(localFilePath); err != nil {
|
if err := rewriteRemotePlaylist(localFilePath, s.host); err != nil {
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,26 +213,3 @@ func (s *S3Storage) connectAWS() *session.Session {
|
|||||||
}
|
}
|
||||||
return sess
|
return sess
|
||||||
}
|
}
|
||||||
|
|
||||||
// rewriteRemotePlaylist will take a local playlist and rewrite it to have absolute URLs to remote locations.
|
|
||||||
func (s *S3Storage) rewriteRemotePlaylist(filePath string) error {
|
|
||||||
f, err := os.Open(filePath) // nolint
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := m3u8.NewMasterPlaylist()
|
|
||||||
if err := p.DecodeFrom(bufio.NewReader(f), false); err != nil {
|
|
||||||
log.Warnln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range p.Variants {
|
|
||||||
item.URI = s.host + filepath.Join("/hls", item.URI)
|
|
||||||
}
|
|
||||||
|
|
||||||
publicPath := filepath.Join(config.HLSStoragePath, filepath.Base(filePath))
|
|
||||||
|
|
||||||
newPlaylist := p.String()
|
|
||||||
|
|
||||||
return playlist.WritePlaylist(newPlaylist, publicPath)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,13 +2,17 @@ package models
|
|||||||
|
|
||||||
// S3 is the storage configuration.
|
// S3 is the storage configuration.
|
||||||
type S3 struct {
|
type S3 struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
ServingEndpoint string `json:"servingEndpoint,omitempty"`
|
|
||||||
AccessKey string `json:"accessKey,omitempty"`
|
AccessKey string `json:"accessKey,omitempty"`
|
||||||
Secret string `json:"secret,omitempty"`
|
Secret string `json:"secret,omitempty"`
|
||||||
Bucket string `json:"bucket,omitempty"`
|
Bucket string `json:"bucket,omitempty"`
|
||||||
Region string `json:"region,omitempty"`
|
Region string `json:"region,omitempty"`
|
||||||
ACL string `json:"acl,omitempty"`
|
ACL string `json:"acl,omitempty"`
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
ForcePathStyle bool `json:"forcePathStyle"`
|
ForcePathStyle bool `json:"forcePathStyle"`
|
||||||
|
|
||||||
|
// This property is no longer used as of v0.1.1. See the standalone
|
||||||
|
// property that was pulled out of here instead. It's only left here
|
||||||
|
// to allow the migration to take place without data loss.
|
||||||
|
ServingEndpoint string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -291,6 +291,9 @@ func Start() error {
|
|||||||
// Websocket host override
|
// Websocket host override
|
||||||
http.HandleFunc("/api/admin/config/sockethostoverride", middleware.RequireAdminAuth(admin.SetSocketHostOverride))
|
http.HandleFunc("/api/admin/config/sockethostoverride", middleware.RequireAdminAuth(admin.SetSocketHostOverride))
|
||||||
|
|
||||||
|
// Custom video serving endpoint
|
||||||
|
http.HandleFunc("/api/admin/config/videoservingendpoint", middleware.RequireAdminAuth(admin.SetVideoServingEndpoint))
|
||||||
|
|
||||||
// Is server marked as NSFW
|
// Is server marked as NSFW
|
||||||
http.HandleFunc("/api/admin/config/nsfw", middleware.RequireAdminAuth(admin.SetNSFW))
|
http.HandleFunc("/api/admin/config/nsfw", middleware.RequireAdminAuth(admin.SetNSFW))
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
TEXTFIELD_PROPS_SOCKET_HOST_OVERRIDE,
|
TEXTFIELD_PROPS_SOCKET_HOST_OVERRIDE,
|
||||||
TEXTFIELD_PROPS_ADMIN_PASSWORD,
|
TEXTFIELD_PROPS_ADMIN_PASSWORD,
|
||||||
TEXTFIELD_PROPS_WEB_PORT,
|
TEXTFIELD_PROPS_WEB_PORT,
|
||||||
|
TEXTFIELD_PROPS_VIDEO_SERVING_ENDPOINT,
|
||||||
} from '../../utils/config-constants';
|
} from '../../utils/config-constants';
|
||||||
import { UpdateArgs } from '../../types/config-section';
|
import { UpdateArgs } from '../../types/config-section';
|
||||||
import { ResetYP } from './ResetYP';
|
import { ResetYP } from './ResetYP';
|
||||||
@ -24,8 +25,15 @@ export default function EditInstanceDetails() {
|
|||||||
|
|
||||||
const { serverConfig } = serverStatusData || {};
|
const { serverConfig } = serverStatusData || {};
|
||||||
|
|
||||||
const { adminPassword, ffmpegPath, rtmpServerPort, webServerPort, yp, socketHostOverride } =
|
const {
|
||||||
serverConfig;
|
adminPassword,
|
||||||
|
ffmpegPath,
|
||||||
|
rtmpServerPort,
|
||||||
|
webServerPort,
|
||||||
|
yp,
|
||||||
|
socketHostOverride,
|
||||||
|
videoServingEndpoint,
|
||||||
|
} = serverConfig;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFormDataValues({
|
setFormDataValues({
|
||||||
@ -34,6 +42,7 @@ export default function EditInstanceDetails() {
|
|||||||
rtmpServerPort,
|
rtmpServerPort,
|
||||||
webServerPort,
|
webServerPort,
|
||||||
socketHostOverride,
|
socketHostOverride,
|
||||||
|
videoServingEndpoint,
|
||||||
});
|
});
|
||||||
}, [serverConfig]);
|
}, [serverConfig]);
|
||||||
|
|
||||||
@ -119,6 +128,15 @@ export default function EditInstanceDetails() {
|
|||||||
type={TEXTFIELD_TYPE_URL}
|
type={TEXTFIELD_TYPE_URL}
|
||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextFieldWithSubmit
|
||||||
|
fieldName="videoServingEndpoint"
|
||||||
|
{...TEXTFIELD_PROPS_VIDEO_SERVING_ENDPOINT}
|
||||||
|
value={formDataValues.videoServingEndpoint}
|
||||||
|
initialValue={videoServingEndpoint || ''}
|
||||||
|
type={TEXTFIELD_TYPE_URL}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
{yp.enabled && <ResetYP />}
|
{yp.enabled && <ResetYP />}
|
||||||
</Panel>
|
</Panel>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|||||||
@ -28,17 +28,7 @@ const { Panel } = Collapse;
|
|||||||
// we could probably add more detailed checks here
|
// we could probably add more detailed checks here
|
||||||
// `currentValues` is what's currently in the global store and in the db
|
// `currentValues` is what's currently in the global store and in the db
|
||||||
function checkSaveable(formValues: any, currentValues: any) {
|
function checkSaveable(formValues: any, currentValues: any) {
|
||||||
const {
|
const { endpoint, accessKey, secret, bucket, region, enabled, acl, forcePathStyle } = formValues;
|
||||||
endpoint,
|
|
||||||
accessKey,
|
|
||||||
secret,
|
|
||||||
bucket,
|
|
||||||
region,
|
|
||||||
enabled,
|
|
||||||
servingEndpoint,
|
|
||||||
acl,
|
|
||||||
forcePathStyle,
|
|
||||||
} = formValues;
|
|
||||||
// if fields are filled out and different from what's in store, then return true
|
// if fields are filled out and different from what's in store, then return true
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!!endpoint && isValidUrl(endpoint) && !!accessKey && !!secret && !!bucket && !!region) {
|
if (!!endpoint && isValidUrl(endpoint) && !!accessKey && !!secret && !!bucket && !!region) {
|
||||||
@ -49,8 +39,6 @@ function checkSaveable(formValues: any, currentValues: any) {
|
|||||||
secret !== currentValues.secret ||
|
secret !== currentValues.secret ||
|
||||||
bucket !== currentValues.bucket ||
|
bucket !== currentValues.bucket ||
|
||||||
region !== currentValues.region ||
|
region !== currentValues.region ||
|
||||||
(!currentValues.servingEndpoint && servingEndpoint !== '') ||
|
|
||||||
(!!currentValues.servingEndpoint && servingEndpoint !== currentValues.servingEndpoint) ||
|
|
||||||
(!currentValues.acl && acl !== '') ||
|
(!currentValues.acl && acl !== '') ||
|
||||||
(!!currentValues.acl && acl !== currentValues.acl) ||
|
(!!currentValues.acl && acl !== currentValues.acl) ||
|
||||||
forcePathStyle !== currentValues.forcePathStyle
|
forcePathStyle !== currentValues.forcePathStyle
|
||||||
@ -84,7 +72,6 @@ export default function EditStorage() {
|
|||||||
endpoint = '',
|
endpoint = '',
|
||||||
region = '',
|
region = '',
|
||||||
secret = '',
|
secret = '',
|
||||||
servingEndpoint = '',
|
|
||||||
forcePathStyle = false,
|
forcePathStyle = false,
|
||||||
} = s3;
|
} = s3;
|
||||||
|
|
||||||
@ -97,7 +84,6 @@ export default function EditStorage() {
|
|||||||
endpoint,
|
endpoint,
|
||||||
region,
|
region,
|
||||||
secret,
|
secret,
|
||||||
servingEndpoint,
|
|
||||||
forcePathStyle,
|
forcePathStyle,
|
||||||
});
|
});
|
||||||
setShouldDisplayForm(enabled);
|
setShouldDisplayForm(enabled);
|
||||||
@ -232,13 +218,7 @@ export default function EditStorage() {
|
|||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="field-container">
|
|
||||||
<TextField
|
|
||||||
{...S3_TEXT_FIELDS_INFO.servingEndpoint}
|
|
||||||
value={formDataValues.servingEndpoint}
|
|
||||||
onChange={handleFieldChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="enable-switch">
|
<div className="enable-switch">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
{...S3_TEXT_FIELDS_INFO.forcePathStyle}
|
{...S3_TEXT_FIELDS_INFO.forcePathStyle}
|
||||||
|
|||||||
@ -81,7 +81,6 @@ export interface S3Field {
|
|||||||
endpoint: string;
|
endpoint: string;
|
||||||
region: string;
|
region: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
servingEndpoint?: string;
|
|
||||||
forcePathStyle: boolean;
|
forcePathStyle: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +144,7 @@ export interface ConfigDetails {
|
|||||||
videoSettings: VideoSettingsFields;
|
videoSettings: VideoSettingsFields;
|
||||||
webServerPort: string;
|
webServerPort: string;
|
||||||
socketHostOverride: string;
|
socketHostOverride: string;
|
||||||
|
videoServingEndpoint: string;
|
||||||
yp: ConfigDirectoryFields;
|
yp: ConfigDirectoryFields;
|
||||||
supportedCodecs: string[];
|
supportedCodecs: string[];
|
||||||
videoCodec: string;
|
videoCodec: string;
|
||||||
|
|||||||
@ -40,6 +40,7 @@ const API_CHAT_JOIN_MESSAGES_ENABLED = '/chat/joinmessagesenabled';
|
|||||||
const API_CHAT_ESTABLISHED_MODE = '/chat/establishedusermode';
|
const API_CHAT_ESTABLISHED_MODE = '/chat/establishedusermode';
|
||||||
const API_DISABLE_SEARCH_INDEXING = '/disablesearchindexing';
|
const API_DISABLE_SEARCH_INDEXING = '/disablesearchindexing';
|
||||||
const API_SOCKET_HOST_OVERRIDE = '/sockethostoverride';
|
const API_SOCKET_HOST_OVERRIDE = '/sockethostoverride';
|
||||||
|
const API_VIDEO_SERVING_ENDPOINT = '/videoservingendpoint';
|
||||||
|
|
||||||
// Federation
|
// Federation
|
||||||
const API_FEDERATION_ENABLED = '/federation/enable';
|
const API_FEDERATION_ENABLED = '/federation/enable';
|
||||||
@ -180,6 +181,18 @@ export const TEXTFIELD_PROPS_SOCKET_HOST_OVERRIDE = {
|
|||||||
useTrim: true,
|
useTrim: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TEXTFIELD_PROPS_VIDEO_SERVING_ENDPOINT = {
|
||||||
|
apiPath: API_VIDEO_SERVING_ENDPOINT,
|
||||||
|
fieldName: 'videoServingEndpoint',
|
||||||
|
label: 'Serving Endpoint',
|
||||||
|
maxLength: 255,
|
||||||
|
placeholder: 'http://cdn.provider.endpoint.com',
|
||||||
|
tip: 'Optional URL that video content should be accessed from instead of the default. Used with CDNs and specific storage providers. Generally not required.',
|
||||||
|
type: TEXTFIELD_TYPE_URL,
|
||||||
|
pattern: DEFAULT_TEXTFIELD_URL_PATTERN,
|
||||||
|
useTrim: true,
|
||||||
|
};
|
||||||
|
|
||||||
// MISC FIELDS
|
// MISC FIELDS
|
||||||
export const FIELD_PROPS_TAGS = {
|
export const FIELD_PROPS_TAGS = {
|
||||||
apiPath: API_TAGS,
|
apiPath: API_TAGS,
|
||||||
@ -521,16 +534,6 @@ export const S3_TEXT_FIELDS_INFO = {
|
|||||||
placeholder: 'your secret key',
|
placeholder: 'your secret key',
|
||||||
tip: '',
|
tip: '',
|
||||||
},
|
},
|
||||||
servingEndpoint: {
|
|
||||||
fieldName: 'servingEndpoint',
|
|
||||||
label: 'Serving Endpoint',
|
|
||||||
maxLength: 255,
|
|
||||||
placeholder: 'http://cdn.ss3.provider.endpoint.com',
|
|
||||||
tip: 'Optional URL that content should be accessed from instead of the default. Used with CDNs and specific storage providers. Generally not required.',
|
|
||||||
type: TEXTFIELD_TYPE_URL,
|
|
||||||
pattern: DEFAULT_TEXTFIELD_URL_PATTERN,
|
|
||||||
useTrim: true,
|
|
||||||
},
|
|
||||||
forcePathStyle: {
|
forcePathStyle: {
|
||||||
fieldName: 'forcePathStyle',
|
fieldName: 'forcePathStyle',
|
||||||
label: 'Force path-style',
|
label: 'Force path-style',
|
||||||
|
|||||||
@ -30,6 +30,7 @@ const initialServerConfigState: ConfigDetails = {
|
|||||||
rtmpServerPort: '',
|
rtmpServerPort: '',
|
||||||
webServerPort: '',
|
webServerPort: '',
|
||||||
socketHostOverride: null,
|
socketHostOverride: null,
|
||||||
|
videoServingEndpoint: '',
|
||||||
s3: {
|
s3: {
|
||||||
accessKey: '',
|
accessKey: '',
|
||||||
acl: '',
|
acl: '',
|
||||||
@ -38,7 +39,6 @@ const initialServerConfigState: ConfigDetails = {
|
|||||||
endpoint: '',
|
endpoint: '',
|
||||||
region: '',
|
region: '',
|
||||||
secret: '',
|
secret: '',
|
||||||
servingEndpoint: '',
|
|
||||||
forcePathStyle: false,
|
forcePathStyle: false,
|
||||||
},
|
},
|
||||||
yp: {
|
yp: {
|
||||||
|
|||||||
Reference in New Issue
Block a user