2020-06-23 03:11:56 +02:00
package config
import (
"errors"
"io/ioutil"
2020-11-10 04:35:01 +01:00
"os/exec"
"strings"
2020-06-23 03:11:56 +02:00
2020-10-05 19:07:09 +02:00
"github.com/owncast/owncast/utils"
2020-06-23 03:11:56 +02:00
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
2020-11-13 00:14:59 +01:00
// Config contains a reference to the configuration.
2020-06-23 03:11:56 +02:00
var Config * config
2020-07-18 03:27:00 +02:00
var _default config
2020-06-23 03:11:56 +02:00
type config struct {
2020-11-06 03:29:16 +01:00
DatabaseFilePath string ` yaml:"databaseFile" `
EnableDebugFeatures bool ` yaml:"-" `
FFMpegPath string ` yaml:"ffmpegPath" `
Files files ` yaml:"files" `
InstanceDetails InstanceDetails ` yaml:"instanceDetails" `
S3 S3 ` yaml:"s3" `
VersionInfo string ` yaml:"-" ` // For storing the version/build number
VersionNumber string ` yaml:"-" `
VideoSettings videoSettings ` yaml:"videoSettings" `
WebServerPort int ` yaml:"webServerPort" `
2020-12-17 18:56:04 +01:00
RTMPServerPort int ` yaml:"rtmpServerPort" `
2020-11-06 03:29:16 +01:00
DisableUpgradeChecks bool ` yaml:"disableUpgradeChecks" `
YP YP ` yaml:"yp" `
2020-06-29 00:10:00 +02:00
}
// InstanceDetails defines the user-visible information about this particular instance.
type InstanceDetails struct {
2020-11-18 00:12:54 +01:00
Name string ` yaml:"name" json:"name" `
Title string ` yaml:"title" json:"title" `
Summary string ` yaml:"summary" json:"summary" `
// Logo logo `yaml:"logo" json:"logo"`
Logo string ` yaml:"logo" json:"logo" `
2020-10-14 01:45:52 +02:00
Tags [ ] string ` yaml:"tags" json:"tags" `
SocialHandles [ ] socialHandle ` yaml:"socialHandles" json:"socialHandles" `
Version string ` json:"version" `
NSFW bool ` yaml:"nsfw" json:"nsfw" `
2020-10-14 18:38:48 +02:00
ExtraPageContent string ` json:"extraPageContent" `
2020-10-02 08:55:38 +02:00
}
2020-11-18 00:12:54 +01:00
// type logo struct {
// Large string `yaml:"large" json:"large"`
// Small string `yaml:"small" json:"small"`
// }
2020-06-23 03:11:56 +02:00
2020-07-01 02:11:24 +02:00
type socialHandle struct {
Platform string ` yaml:"platform" json:"platform" `
URL string ` yaml:"url" json:"url" `
2021-01-09 21:59:43 +01:00
Icon string ` yaml:"icon" json:"icon" `
2020-07-01 02:11:24 +02:00
}
2020-06-23 03:11:56 +02:00
type videoSettings struct {
2020-07-07 04:45:58 +02:00
ChunkLengthInSeconds int ` yaml:"chunkLengthInSeconds" `
StreamingKey string ` yaml:"streamingKey" `
StreamQualities [ ] StreamQuality ` yaml:"streamQualities" `
2020-10-02 08:55:38 +02:00
HighestQualityStreamIndex int ` yaml:"-" `
}
2020-10-08 07:42:14 +02:00
// YP allows registration to the central Owncast YP (Yellow pages) service operating as a directory.
type YP struct {
Enabled bool ` yaml:"enabled" json:"enabled" `
InstanceURL string ` yaml:"instanceURL" json:"instanceUrl" ` // The public URL the directory should link to
YPServiceURL string ` yaml:"ypServiceURL" json:"-" ` // The base URL to the YP API to register with (optional)
2020-06-23 03:11:56 +02:00
}
2020-06-29 00:10:00 +02:00
// StreamQuality defines the specifics of a single HLS stream variant.
2020-06-26 02:44:47 +02:00
type StreamQuality struct {
// Enable passthrough to copy the video and/or audio directly from the
// incoming stream and disable any transcoding. It will ignore any of
// the below settings.
2020-10-02 09:02:42 +02:00
IsVideoPassthrough bool ` yaml:"videoPassthrough" json:"videoPassthrough" `
IsAudioPassthrough bool ` yaml:"audioPassthrough" json:"audioPassthrough" `
2020-06-26 02:44:47 +02:00
2020-10-02 09:02:42 +02:00
VideoBitrate int ` yaml:"videoBitrate" json:"videoBitrate" `
AudioBitrate int ` yaml:"audioBitrate" json:"audioBitrate" `
2020-06-26 02:44:47 +02:00
// Set only one of these in order to keep your current aspect ratio.
// Or set neither to not scale the video.
2020-10-02 09:02:42 +02:00
ScaledWidth int ` yaml:"scaledWidth" json:"scaledWidth,omitempty" `
ScaledHeight int ` yaml:"scaledHeight" json:"scaledHeight,omitempty" `
2020-06-26 02:44:47 +02:00
2020-10-02 09:02:42 +02:00
Framerate int ` yaml:"framerate" json:"framerate" `
EncoderPreset string ` yaml:"encoderPreset" json:"encoderPreset" `
2020-06-23 03:11:56 +02:00
}
type files struct {
MaxNumberInPlaylist int ` yaml:"maxNumberInPlaylist" `
}
2020-11-13 00:14:59 +01:00
// S3 is for configuring the S3 integration.
2020-10-02 09:02:42 +02:00
type S3 struct {
Enabled bool ` yaml:"enabled" json:"enabled" `
Endpoint string ` yaml:"endpoint" json:"endpoint,omitempty" `
ServingEndpoint string ` yaml:"servingEndpoint" json:"servingEndpoint,omitempty" `
AccessKey string ` yaml:"accessKey" json:"accessKey,omitempty" `
Secret string ` yaml:"secret" json:"secret,omitempty" `
Bucket string ` yaml:"bucket" json:"bucket,omitempty" `
Region string ` yaml:"region" json:"region,omitempty" `
ACL string ` yaml:"acl" json:"acl,omitempty" `
2020-06-23 03:11:56 +02:00
}
func ( c * config ) load ( filePath string ) error {
if ! utils . DoesFileExists ( filePath ) {
2020-11-19 17:48:33 +01:00
log . Fatal ( "ERROR: valid config.yaml is required. Copy config-default.yaml to config.yaml and edit" )
2020-06-23 03:11:56 +02:00
}
yamlFile , err := ioutil . ReadFile ( filePath )
if err != nil {
log . Printf ( "yamlFile.Get err #%v " , err )
return err
}
if err := yaml . Unmarshal ( yamlFile , c ) ; err != nil {
2020-11-18 00:21:53 +01:00
log . Fatalf ( "Error reading the config file.\nHave you recently updated your version of Owncast?\nIf so there may be changes to the config.\nPlease read the change log for your version at https://owncast.online/posts/\n%v" , err )
2020-06-23 03:11:56 +02:00
return err
}
2020-07-07 04:45:58 +02:00
c . VideoSettings . HighestQualityStreamIndex = findHighestQuality ( c . VideoSettings . StreamQualities )
2020-10-14 01:45:52 +02:00
// Add custom page content to the instance details.
customContentMarkdownData , err := ioutil . ReadFile ( ExtraInfoFile )
if err == nil {
customContentMarkdownString := string ( customContentMarkdownData )
2020-10-14 18:38:48 +02:00
c . InstanceDetails . ExtraPageContent = utils . RenderSimpleMarkdown ( customContentMarkdownString )
2020-10-14 01:45:52 +02:00
}
2020-06-23 03:11:56 +02:00
return nil
}
func ( c * config ) verifySettings ( ) error {
2020-07-19 02:46:18 +02:00
if c . VideoSettings . StreamingKey == "" {
return errors . New ( "No stream key set. Please set one in your config file." )
}
2020-06-23 03:11:56 +02:00
if c . S3 . Enabled {
if c . S3 . AccessKey == "" || c . S3 . Secret == "" {
return errors . New ( "s3 support requires an access key and secret" )
}
if c . S3 . Region == "" || c . S3 . Endpoint == "" {
return errors . New ( "s3 support requires a region and endpoint" )
}
if c . S3 . Bucket == "" {
return errors . New ( "s3 support requires a bucket created for storing public video segments" )
}
}
2020-10-02 08:55:38 +02:00
if c . YP . Enabled && c . YP . InstanceURL == "" {
return errors . New ( "YP is enabled but instance url is not set" )
}
2020-07-13 23:32:12 +02:00
return nil
}
2020-06-23 03:11:56 +02:00
2020-07-13 23:39:44 +02:00
func ( c * config ) GetVideoSegmentSecondsLength ( ) int {
if c . VideoSettings . ChunkLengthInSeconds != 0 {
return c . VideoSettings . ChunkLengthInSeconds
}
2020-07-18 03:27:00 +02:00
return _default . GetVideoSegmentSecondsLength ( )
2020-07-13 23:39:44 +02:00
}
2020-07-13 23:48:56 +02:00
func ( c * config ) GetPublicWebServerPort ( ) int {
if c . WebServerPort != 0 {
return c . WebServerPort
}
2020-07-18 03:27:00 +02:00
return _default . WebServerPort
2020-07-13 23:48:56 +02:00
}
2020-12-17 18:56:04 +01:00
func ( c * config ) GetRTMPServerPort ( ) int {
if c . RTMPServerPort != 0 {
return c . RTMPServerPort
}
return _default . RTMPServerPort
}
2020-07-13 23:55:21 +02:00
func ( c * config ) GetMaxNumberOfReferencedSegmentsInPlaylist ( ) int {
if c . Files . MaxNumberInPlaylist > 0 {
return c . Files . MaxNumberInPlaylist
}
2020-07-18 03:27:00 +02:00
return _default . GetMaxNumberOfReferencedSegmentsInPlaylist ( )
2020-07-13 23:55:21 +02:00
}
2020-07-18 03:27:00 +02:00
func ( c * config ) GetFFMpegPath ( ) string {
if c . FFMpegPath != "" {
2020-12-22 07:02:05 +01:00
if err := verifyFFMpegPath ( c . FFMpegPath ) ; err == nil {
return c . FFMpegPath
} else {
log . Errorln ( c . FFMpegPath , "is an invalid path to ffmpeg. Will try to use a copy in your path, if possible." )
}
2020-07-18 03:27:00 +02:00
}
2020-11-10 04:35:01 +01:00
// First look to see if ffmpeg is in the current working directory
localCopy := "./ffmpeg"
hasLocalCopyError := verifyFFMpegPath ( localCopy )
if hasLocalCopyError == nil {
// No error, so all is good. Use the local copy.
return localCopy
}
cmd := exec . Command ( "which" , "ffmpeg" )
out , err := cmd . CombinedOutput ( )
if err != nil {
2021-01-02 02:01:36 +01:00
log . Fatalln ( "Unable to determine path to ffmpeg. Please specify it in the config file." )
2020-11-10 04:35:01 +01:00
}
path := strings . TrimSpace ( string ( out ) )
2021-01-08 07:45:29 +01:00
if err := verifyFFMpegPath ( path ) ; err != nil {
log . Warnln ( err )
}
2020-11-10 04:35:01 +01:00
return path
2020-07-18 03:27:00 +02:00
}
2020-10-02 08:55:38 +02:00
func ( c * config ) GetYPServiceHost ( ) string {
if c . YP . YPServiceURL != "" {
return c . YP . YPServiceURL
}
return _default . YP . YPServiceURL
}
2020-10-26 16:55:31 +01:00
func ( c * config ) GetDataFilePath ( ) string {
if c . DatabaseFilePath != "" {
return c . DatabaseFilePath
}
return _default . DatabaseFilePath
}
2020-07-18 03:27:00 +02:00
func ( c * config ) GetVideoStreamQualities ( ) [ ] StreamQuality {
if len ( c . VideoSettings . StreamQualities ) > 0 {
return c . VideoSettings . StreamQualities
}
return _default . VideoSettings . StreamQualities
2020-07-14 00:13:24 +02:00
}
2020-11-13 00:14:59 +01:00
// GetFramerate returns the framerate or default.
2020-08-06 21:19:35 +02:00
func ( q * StreamQuality ) GetFramerate ( ) int {
2020-11-20 07:07:28 +01:00
if q . IsVideoPassthrough {
return 0
}
2020-08-06 21:19:35 +02:00
if q . Framerate > 0 {
return q . Framerate
}
return _default . VideoSettings . StreamQualities [ 0 ] . Framerate
}
2020-11-13 00:14:59 +01:00
// GetEncoderPreset returns the preset or default.
2020-10-26 17:12:44 +01:00
func ( q * StreamQuality ) GetEncoderPreset ( ) string {
2020-11-20 07:07:28 +01:00
if q . IsVideoPassthrough {
return ""
}
2020-10-26 17:12:44 +01:00
if q . EncoderPreset != "" {
return q . EncoderPreset
}
return _default . VideoSettings . StreamQualities [ 0 ] . EncoderPreset
}
2020-11-20 07:07:28 +01:00
func ( q * StreamQuality ) GetIsAudioPassthrough ( ) bool {
if q . IsAudioPassthrough {
return true
}
if q . AudioBitrate == 0 {
return true
}
return false
}
2020-11-13 00:14:59 +01:00
// Load tries to load the configuration file.
2020-11-04 02:34:25 +01:00
func Load ( filePath string , versionInfo string , versionNumber string ) error {
2020-06-23 03:11:56 +02:00
Config = new ( config )
2020-07-18 03:27:00 +02:00
_default = getDefaults ( )
2020-06-23 03:11:56 +02:00
if err := Config . load ( filePath ) ; err != nil {
return err
}
2020-07-01 02:48:26 +02:00
Config . VersionInfo = versionInfo
2020-11-04 02:34:25 +01:00
Config . VersionNumber = versionNumber
2020-06-23 03:11:56 +02:00
return Config . verifySettings ( )
}