2020-10-02 08:55:38 +02:00
package yp
import (
"bytes"
"io/ioutil"
"net/http"
2020-11-05 18:20:33 +01:00
"net/url"
2020-10-02 08:55:38 +02:00
"os"
"time"
"encoding/json"
2020-10-05 19:07:09 +02:00
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
2020-10-02 08:55:38 +02:00
log "github.com/sirupsen/logrus"
)
const pingInterval = 4 * time . Minute
var getStatus func ( ) models . Status
2020-11-05 18:14:47 +01:00
var _inErrorState = false
2020-10-02 08:55:38 +02:00
//YP is a service for handling listing in the Owncast directory.
type YP struct {
timer * time . Ticker
}
type ypPingResponse struct {
Key string ` json:"key" `
Success bool ` json:"success" `
Error string ` json:"error" `
ErrorCode int ` json:"errorCode" `
}
type ypPingRequest struct {
Key string ` json:"key" `
URL string ` json:"url" `
}
2020-11-13 00:14:59 +01:00
// NewYP creates a new instance of the YP service handler.
2020-10-02 08:55:38 +02:00
func NewYP ( getStatusFunc func ( ) models . Status ) * YP {
getStatus = getStatusFunc
return & YP { }
}
2020-11-13 00:14:59 +01:00
// Start is run when a live stream begins to start pinging YP.
2020-10-02 08:55:38 +02:00
func ( yp * YP ) Start ( ) {
yp . timer = time . NewTicker ( pingInterval )
go func ( ) {
for {
select {
case <- yp . timer . C :
yp . ping ( )
}
}
} ( )
yp . ping ( )
}
2020-11-13 00:14:59 +01:00
// Stop stops the pinging of YP.
2020-10-02 08:55:38 +02:00
func ( yp * YP ) Stop ( ) {
yp . timer . Stop ( )
}
func ( yp * YP ) ping ( ) {
myInstanceURL := config . Config . YP . InstanceURL
2020-11-05 18:20:33 +01:00
isValidInstanceURL := isUrl ( myInstanceURL )
if myInstanceURL == "" || ! isValidInstanceURL {
if ! _inErrorState {
log . Warnln ( "YP Error: unable to use" , myInstanceURL , "as a public instance URL. Fix this value in your configuration." )
}
_inErrorState = true
return
}
2020-10-02 08:55:38 +02:00
key := yp . getSavedKey ( )
log . Traceln ( "Pinging YP as: " , config . Config . InstanceDetails . Name )
request := ypPingRequest {
Key : key ,
URL : myInstanceURL ,
}
req , err := json . Marshal ( request )
if err != nil {
log . Errorln ( err )
return
}
pingURL := config . Config . GetYPServiceHost ( ) + "/ping"
resp , err := http . Post ( pingURL , "application/json" , bytes . NewBuffer ( req ) )
if err != nil {
log . Errorln ( err )
return
}
defer resp . Body . Close ( )
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
log . Errorln ( err )
}
pingResponse := ypPingResponse { }
json . Unmarshal ( body , & pingResponse )
if ! pingResponse . Success {
2020-11-05 18:14:47 +01:00
if ! _inErrorState {
log . Warnln ( "YP Ping error returned from service:" , pingResponse . Error )
}
_inErrorState = true
2020-10-02 08:55:38 +02:00
return
}
2020-11-05 18:14:47 +01:00
_inErrorState = false
2020-10-02 08:55:38 +02:00
if pingResponse . Key != key {
yp . writeSavedKey ( pingResponse . Key )
}
}
func ( yp * YP ) writeSavedKey ( key string ) {
f , err := os . Create ( ".yp.key" )
if err != nil {
log . Errorln ( err )
return
}
2020-10-17 00:04:31 +02:00
defer f . Close ( )
2020-10-02 08:55:38 +02:00
_ , err = f . WriteString ( key )
if err != nil {
log . Errorln ( err )
return
}
}
func ( yp * YP ) getSavedKey ( ) string {
fileBytes , err := ioutil . ReadFile ( ".yp.key" )
if err != nil {
return ""
}
return string ( fileBytes )
}
// DisplayInstructions will let the user know they are not in the directory by default and
// how they can enable the feature.
func DisplayInstructions ( ) {
2020-10-30 02:26:10 +01:00
text := "Your instance can be listed on the Owncast directory at http://directory.owncast.online by enabling YP in your config. Learn more at https://directory.owncast.online/get-listed."
2020-10-02 08:55:38 +02:00
log . Debugln ( text )
}
2020-11-05 18:20:33 +01:00
func isUrl ( str string ) bool {
u , err := url . Parse ( str )
return err == nil && u . Scheme != "" && u . Host != ""
}