mirror of
				https://github.com/owncast/owncast.git
				synced 2025-11-01 02:44:31 +08:00 
			
		
		
		
	Start cleaning up linter errors. (#358)
* Start cleaning up linter errors. For #357 * Fix unmarshalling NullTime values * More linter fixes * Remove commented code * Move defer up * Consolidate error check lines * Move error check to make sure row iteration was successful * Cleaner error check + do not recreate pipe if it exists * Consolidate hashing to generate client id
This commit is contained in:
		| @ -5,6 +5,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"github.com/owncast/owncast/metrics" | 	"github.com/owncast/owncast/metrics" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // GetHardwareStats will return hardware utilization over time. | // GetHardwareStats will return hardware utilization over time. | ||||||
| @ -12,5 +13,8 @@ func GetHardwareStats(w http.ResponseWriter, r *http.Request) { | |||||||
| 	m := metrics.Metrics | 	m := metrics.Metrics | ||||||
|  |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	json.NewEncoder(w).Encode(m) | 	err := json.NewEncoder(w).Encode(m) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ func ServeAdmin(w http.ResponseWriter, r *http.Request) { | |||||||
| 	f, err := pkger.Open(path) | 	f, err := pkger.Open(path) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Warnln(err, path) | 		log.Warnln(err, path) | ||||||
| 		errorHandler(w, r, http.StatusNotFound) | 		errorHandler(w, http.StatusNotFound) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -39,9 +39,11 @@ func ServeAdmin(w http.ResponseWriter, r *http.Request) { | |||||||
|  |  | ||||||
| 	mimeType := mime.TypeByExtension(filepath.Ext(path)) | 	mimeType := mime.TypeByExtension(filepath.Ext(path)) | ||||||
| 	w.Header().Set("Content-Type", mimeType) | 	w.Header().Set("Content-Type", mimeType) | ||||||
| 	w.Write(b) | 	if _, err = w.Write(b); err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func errorHandler(w http.ResponseWriter, r *http.Request, status int) { | func errorHandler(w http.ResponseWriter, status int) { | ||||||
| 	w.WriteHeader(status) | 	w.WriteHeader(status) | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/owncast/owncast/logging" | 	"github.com/owncast/owncast/logging" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // GetLogs will return all logs. | // GetLogs will return all logs. | ||||||
| @ -19,7 +20,10 @@ func GetLogs(w http.ResponseWriter, r *http.Request) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	json.NewEncoder(w).Encode(response) | 	err := json.NewEncoder(w).Encode(response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetWarnings will return only warning and error logs. | // GetWarnings will return only warning and error logs. | ||||||
| @ -32,7 +36,10 @@ func GetWarnings(w http.ResponseWriter, r *http.Request) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	json.NewEncoder(w).Encode(response) | 	err := json.NewEncoder(w).Encode(response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| type logsResponse struct { | type logsResponse struct { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"github.com/owncast/owncast/config" | 	"github.com/owncast/owncast/config" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // GetServerConfig gets the config details of the server. | // GetServerConfig gets the config details of the server. | ||||||
| @ -35,7 +36,10 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	json.NewEncoder(w).Encode(response) | 	err := json.NewEncoder(w).Encode(response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| type serverConfigAdminResponse struct { | type serverConfigAdminResponse struct { | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import ( | |||||||
| 	"github.com/owncast/owncast/config" | 	"github.com/owncast/owncast/config" | ||||||
| 	"github.com/owncast/owncast/core" | 	"github.com/owncast/owncast/core" | ||||||
| 	"github.com/owncast/owncast/models" | 	"github.com/owncast/owncast/models" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Status gets the details of the inbound broadcaster. | // Status gets the details of the inbound broadcaster. | ||||||
| @ -25,7 +26,10 @@ func Status(w http.ResponseWriter, r *http.Request) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	json.NewEncoder(w).Encode(response) | 	err := json.NewEncoder(w).Encode(response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| type adminStatusResponse struct { | type adminStatusResponse struct { | ||||||
|  | |||||||
| @ -5,11 +5,15 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"github.com/owncast/owncast/metrics" | 	"github.com/owncast/owncast/metrics" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // GetViewersOverTime will return the number of viewers at points in time. | // GetViewersOverTime will return the number of viewers at points in time. | ||||||
| func GetViewersOverTime(w http.ResponseWriter, r *http.Request) { | func GetViewersOverTime(w http.ResponseWriter, r *http.Request) { | ||||||
| 	viewersOverTime := metrics.Metrics.Viewers | 	viewersOverTime := metrics.Metrics.Viewers | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	json.NewEncoder(w).Encode(viewersOverTime) | 	err := json.NewEncoder(w).Encode(viewersOverTime) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import ( | |||||||
| 	"github.com/owncast/owncast/core" | 	"github.com/owncast/owncast/core" | ||||||
| 	"github.com/owncast/owncast/models" | 	"github.com/owncast/owncast/models" | ||||||
| 	"github.com/owncast/owncast/router/middleware" | 	"github.com/owncast/owncast/router/middleware" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // GetChatMessages gets all of the chat messages. | // GetChatMessages gets all of the chat messages. | ||||||
| @ -17,7 +18,10 @@ func GetChatMessages(w http.ResponseWriter, r *http.Request) { | |||||||
| 	case http.MethodGet: | 	case http.MethodGet: | ||||||
| 		messages := core.GetAllChatMessages() | 		messages := core.GetAllChatMessages() | ||||||
|  |  | ||||||
| 		json.NewEncoder(w).Encode(messages) | 		err := json.NewEncoder(w).Encode(messages) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Errorln(err) | ||||||
|  | 		} | ||||||
| 	case http.MethodPost: | 	case http.MethodPost: | ||||||
| 		var message models.ChatMessage | 		var message models.ChatMessage | ||||||
| 		if err := json.NewDecoder(r.Body).Decode(&message); err != nil { | 		if err := json.NewDecoder(r.Body).Decode(&message); err != nil { | ||||||
| @ -30,9 +34,14 @@ func GetChatMessages(w http.ResponseWriter, r *http.Request) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		json.NewEncoder(w).Encode(j{"success": true}) | 		if err := json.NewEncoder(w).Encode(j{"success": true}); err != nil { | ||||||
|  | 			internalErrorHandler(w, err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
| 	default: | 	default: | ||||||
| 		w.WriteHeader(http.StatusNotImplemented) | 		w.WriteHeader(http.StatusNotImplemented) | ||||||
| 		json.NewEncoder(w).Encode(j{"error": "method not implemented (PRs are accepted)"}) | 		if err := json.NewEncoder(w).Encode(j{"error": "method not implemented (PRs are accepted)"}); err != nil { | ||||||
|  | 			internalErrorHandler(w, err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,5 +14,7 @@ func GetWebConfig(w http.ResponseWriter, r *http.Request) { | |||||||
|  |  | ||||||
| 	configuration := config.Config.InstanceDetails | 	configuration := config.Config.InstanceDetails | ||||||
| 	configuration.Version = config.Config.VersionInfo | 	configuration.Version = config.Config.VersionInfo | ||||||
| 	json.NewEncoder(w).Encode(configuration) | 	if err := json.NewEncoder(w).Encode(configuration); err != nil { | ||||||
|  | 		badRequestHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,5 +12,7 @@ func GetConnectedClients(w http.ResponseWriter, r *http.Request) { | |||||||
| 	clients := core.GetClients() | 	clients := core.GetClients() | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
|  |  | ||||||
| 	json.NewEncoder(w).Encode(clients) | 	if err := json.NewEncoder(w).Encode(clients); err != nil { | ||||||
|  | 		internalErrorHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,7 +15,9 @@ func internalErrorHandler(w http.ResponseWriter, err error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	w.WriteHeader(http.StatusInternalServerError) | 	w.WriteHeader(http.StatusInternalServerError) | ||||||
| 	json.NewEncoder(w).Encode(j{"error": err.Error()}) | 	if err := json.NewEncoder(w).Encode(j{"error": err.Error()}); err != nil { | ||||||
|  | 		internalErrorHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func badRequestHandler(w http.ResponseWriter, err error) { | func badRequestHandler(w http.ResponseWriter, err error) { | ||||||
| @ -24,7 +26,9 @@ func badRequestHandler(w http.ResponseWriter, err error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	w.WriteHeader(http.StatusBadRequest) | 	w.WriteHeader(http.StatusBadRequest) | ||||||
| 	json.NewEncoder(w).Encode(j{"error": err.Error()}) | 	if err := json.NewEncoder(w).Encode(j{"error": err.Error()}); err != nil { | ||||||
|  | 		internalErrorHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func WriteSimpleResponse(w http.ResponseWriter, success bool, message string) { | func WriteSimpleResponse(w http.ResponseWriter, success bool, message string) { | ||||||
| @ -33,5 +37,7 @@ func WriteSimpleResponse(w http.ResponseWriter, success bool, message string) { | |||||||
| 		Message: message, | 		Message: message, | ||||||
| 	} | 	} | ||||||
| 	w.WriteHeader(http.StatusOK) | 	w.WriteHeader(http.StatusOK) | ||||||
| 	json.NewEncoder(w).Encode(response) | 	if err := json.NewEncoder(w).Encode(response); err != nil { | ||||||
|  | 		internalErrorHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,9 +35,11 @@ func GetCustomEmoji(w http.ResponseWriter, r *http.Request) { | |||||||
| 	for _, f := range files { | 	for _, f := range files { | ||||||
| 		name := strings.TrimSuffix(f.Name(), path.Ext(f.Name())) | 		name := strings.TrimSuffix(f.Name(), path.Ext(f.Name())) | ||||||
| 		emojiPath := filepath.Join(emojiDir, f.Name()) | 		emojiPath := filepath.Join(emojiDir, f.Name()) | ||||||
| 		singleEmoji := models.CustomEmoji{name, emojiPath} | 		singleEmoji := models.CustomEmoji{Name: name, Emoji: emojiPath} | ||||||
| 		emojiList = append(emojiList, singleEmoji) | 		emojiList = append(emojiList, singleEmoji) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	json.NewEncoder(w).Encode(emojiList) | 	if err := json.NewEncoder(w).Encode(emojiList); err != nil { | ||||||
|  | 		internalErrorHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,5 +15,7 @@ func GetStatus(w http.ResponseWriter, r *http.Request) { | |||||||
| 	status := core.GetStatus() | 	status := core.GetStatus() | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
|  |  | ||||||
| 	json.NewEncoder(w).Encode(status) | 	if err := json.NewEncoder(w).Encode(status); err != nil { | ||||||
|  | 		internalErrorHandler(w, err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -40,11 +40,8 @@ func Start() error { | |||||||
|  |  | ||||||
| 	ticker := time.NewTicker(30 * time.Second) | 	ticker := time.NewTicker(30 * time.Second) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for range ticker.C { | ||||||
| 			select { | 			_server.ping() | ||||||
| 			case <-ticker.C: |  | ||||||
| 				_server.ping() |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|  | |||||||
| @ -94,13 +94,21 @@ func (c *Client) listenWrite() { | |||||||
| 		select { | 		select { | ||||||
| 		// Send a PING keepalive | 		// Send a PING keepalive | ||||||
| 		case msg := <-c.pingch: | 		case msg := <-c.pingch: | ||||||
| 			websocket.JSON.Send(c.ws, msg) | 			err := websocket.JSON.Send(c.ws, msg) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Errorln(err) | ||||||
|  | 			} | ||||||
| 		// send message to the client | 		// send message to the client | ||||||
| 		case msg := <-c.ch: | 		case msg := <-c.ch: | ||||||
| 			// log.Println("Send:", msg) | 			err := websocket.JSON.Send(c.ws, msg) | ||||||
| 			websocket.JSON.Send(c.ws, msg) | 			if err != nil { | ||||||
|  | 				log.Errorln(err) | ||||||
|  | 			} | ||||||
| 		case msg := <-c.usernameChangeChannel: | 		case msg := <-c.usernameChangeChannel: | ||||||
| 			websocket.JSON.Send(c.ws, msg) | 			err := websocket.JSON.Send(c.ws, msg) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Errorln(err) | ||||||
|  | 			} | ||||||
| 		// receive done request | 		// receive done request | ||||||
| 		case <-c.doneCh: | 		case <-c.doneCh: | ||||||
| 			_server.remove(c) | 			_server.remove(c) | ||||||
| @ -114,7 +122,6 @@ func (c *Client) listenWrite() { | |||||||
| func (c *Client) listenRead() { | func (c *Client) listenRead() { | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
|  |  | ||||||
| 		// receive done request | 		// receive done request | ||||||
| 		case <-c.doneCh: | 		case <-c.doneCh: | ||||||
| 			_server.remove(c) | 			_server.remove(c) | ||||||
|  | |||||||
| @ -32,7 +32,10 @@ func createTable() { | |||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	defer stmt.Close() | 	defer stmt.Close() | ||||||
| 	stmt.Exec() | 	_, err = stmt.Exec() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func addMessage(message models.ChatMessage) { | func addMessage(message models.ChatMessage) { | ||||||
| @ -41,16 +44,20 @@ func addMessage(message models.ChatMessage) { | |||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	stmt, err := tx.Prepare("INSERT INTO messages(id, author, body, messageType, visible, timestamp) values(?, ?, ?, ?, ?, ?)") | 	stmt, err := tx.Prepare("INSERT INTO messages(id, author, body, messageType, visible, timestamp) values(?, ?, ?, ?, ?, ?)") | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  | 	defer stmt.Close() | ||||||
|  |  | ||||||
| 	_, err = stmt.Exec(message.ID, message.Author, message.Body, message.MessageType, 1, message.Timestamp) | 	_, err = stmt.Exec(message.ID, message.Author, message.Body, message.MessageType, 1, message.Timestamp) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	tx.Commit() | 	err = tx.Commit() | ||||||
|  | 	if err != nil { | ||||||
| 	defer stmt.Close() | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func getChatHistory() []models.ChatMessage { | func getChatHistory() []models.ChatMessage { | ||||||
| @ -89,5 +96,9 @@ func getChatHistory() []models.ChatMessage { | |||||||
| 		history = append(history, message) | 		history = append(history, message) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err := rows.Err(); err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return history | 	return history | ||||||
| } | } | ||||||
|  | |||||||
| @ -46,11 +46,6 @@ func (s *server) SendToAll(msg models.ChatMessage) { | |||||||
| 	s.sendAllCh <- msg | 	s.sendAllCh <- msg | ||||||
| } | } | ||||||
|  |  | ||||||
| // Done marks the server as done. |  | ||||||
| func (s *server) done() { |  | ||||||
| 	s.doneCh <- true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Err handles an error. | // Err handles an error. | ||||||
| func (s *server) err(err error) { | func (s *server) err(err error) { | ||||||
| 	s.errCh <- err | 	s.errCh <- err | ||||||
| @ -140,18 +135,7 @@ func (s *server) sendWelcomeMessageToClient(c *Client) { | |||||||
| 		time.Sleep(7 * time.Second) | 		time.Sleep(7 * time.Second) | ||||||
|  |  | ||||||
| 		initialChatMessageText := fmt.Sprintf("Welcome to %s! %s", config.Config.InstanceDetails.Title, config.Config.InstanceDetails.Summary) | 		initialChatMessageText := fmt.Sprintf("Welcome to %s! %s", config.Config.InstanceDetails.Title, config.Config.InstanceDetails.Summary) | ||||||
| 		initialMessage := models.ChatMessage{"owncast-server", config.Config.InstanceDetails.Name, initialChatMessageText, "initial-message-1", "SYSTEM", true, time.Now()} | 		initialMessage := models.ChatMessage{ClientID: "owncast-server", Author: config.Config.InstanceDetails.Name, Body: initialChatMessageText, ID: "initial-message-1", MessageType: "SYSTEM", Visible: true, Timestamp: time.Now()} | ||||||
| 		c.Write(initialMessage) | 		c.Write(initialMessage) | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *server) getClientForClientID(clientID string) *Client { |  | ||||||
| 	for _, client := range s.Clients { |  | ||||||
| 		if client.ClientID == clientID { |  | ||||||
| 			return client |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								core/core.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								core/core.go
									
									
									
									
									
								
							| @ -99,7 +99,10 @@ func transitionToOfflineVideoStreamContent() { | |||||||
| 	_transcoder.Start() | 	_transcoder.Start() | ||||||
|  |  | ||||||
| 	// Copy the logo to be the thumbnail | 	// Copy the logo to be the thumbnail | ||||||
| 	utils.Copy(filepath.Join("webroot", config.Config.InstanceDetails.Logo.Large), "webroot/thumbnail.jpg") | 	err := utils.Copy(filepath.Join("webroot", config.Config.InstanceDetails.Logo.Large), "webroot/thumbnail.jpg") | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Delete the preview Gif | 	// Delete the preview Gif | ||||||
| 	os.Remove(path.Join(config.WebRoot, "preview.gif")) | 	os.Remove(path.Join(config.WebRoot, "preview.gif")) | ||||||
| @ -111,8 +114,15 @@ func resetDirectories() { | |||||||
| 	// Wipe the public, web-accessible hls data directory | 	// Wipe the public, web-accessible hls data directory | ||||||
| 	os.RemoveAll(config.PublicHLSStoragePath) | 	os.RemoveAll(config.PublicHLSStoragePath) | ||||||
| 	os.RemoveAll(config.PrivateHLSStoragePath) | 	os.RemoveAll(config.PrivateHLSStoragePath) | ||||||
| 	os.MkdirAll(config.PublicHLSStoragePath, 0777) | 	err := os.MkdirAll(config.PublicHLSStoragePath, 0777) | ||||||
| 	os.MkdirAll(config.PrivateHLSStoragePath, 0777) | 	if err != nil { | ||||||
|  | 		log.Fatalln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = os.MkdirAll(config.PrivateHLSStoragePath, 0777) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Remove the previous thumbnail | 	// Remove the previous thumbnail | ||||||
| 	os.Remove(filepath.Join(config.WebRoot, "thumbnail.jpg")) | 	os.Remove(filepath.Join(config.WebRoot, "thumbnail.jpg")) | ||||||
| @ -120,14 +130,31 @@ func resetDirectories() { | |||||||
| 	// Create private hls data dirs | 	// Create private hls data dirs | ||||||
| 	if len(config.Config.VideoSettings.StreamQualities) != 0 { | 	if len(config.Config.VideoSettings.StreamQualities) != 0 { | ||||||
| 		for index := range config.Config.VideoSettings.StreamQualities { | 		for index := range config.Config.VideoSettings.StreamQualities { | ||||||
| 			os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0777) | 			err = os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0777) | ||||||
| 			os.MkdirAll(path.Join(config.PublicHLSStoragePath, strconv.Itoa(index)), 0777) | 			if err != nil { | ||||||
|  | 				log.Fatalln(err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			err = os.MkdirAll(path.Join(config.PublicHLSStoragePath, strconv.Itoa(index)), 0777) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Fatalln(err) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(0)), 0777) | 		err = os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(0)), 0777) | ||||||
| 		os.MkdirAll(path.Join(config.PublicHLSStoragePath, strconv.Itoa(0)), 0777) | 		if err != nil { | ||||||
|  | 			log.Fatalln(err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = os.MkdirAll(path.Join(config.PublicHLSStoragePath, strconv.Itoa(0)), 0777) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatalln(err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Remove the previous thumbnail | 	// Remove the previous thumbnail | ||||||
| 	utils.Copy(config.Config.InstanceDetails.Logo.Large, "webroot/thumbnail.jpg") | 	err = utils.Copy(path.Join(config.WebRoot, config.Config.InstanceDetails.Logo.Large), "webroot/thumbnail.jpg") | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -60,14 +60,14 @@ func (s *FileWriterReceiverService) uploadHandler(w http.ResponseWriter, r *http | |||||||
|  |  | ||||||
| 	f, err := os.Create(writePath) | 	f, err := os.Create(writePath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		returnError(err, w, r) | 		returnError(err, w) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	defer f.Close() | 	defer f.Close() | ||||||
| 	_, err = f.Write(data) | 	_, err = f.Write(data) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		returnError(err, w, r) | 		returnError(err, w) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -82,7 +82,6 @@ func (s *FileWriterReceiverService) fileWritten(path string) { | |||||||
|  |  | ||||||
| 	if utils.GetRelativePathFromAbsolutePath(path) == "hls/stream.m3u8" { | 	if utils.GetRelativePathFromAbsolutePath(path) == "hls/stream.m3u8" { | ||||||
| 		s.callbacks.MasterPlaylistWritten(path) | 		s.callbacks.MasterPlaylistWritten(path) | ||||||
|  |  | ||||||
| 	} else if strings.HasSuffix(path, ".ts") { | 	} else if strings.HasSuffix(path, ".ts") { | ||||||
| 		performanceMonitorKey := "segmentWritten-" + index | 		performanceMonitorKey := "segmentWritten-" + index | ||||||
| 		averagePerformance := utils.GetAveragePerformance(performanceMonitorKey) | 		averagePerformance := utils.GetAveragePerformance(performanceMonitorKey) | ||||||
| @ -98,13 +97,12 @@ func (s *FileWriterReceiverService) fileWritten(path string) { | |||||||
| 		} else { | 		} else { | ||||||
| 			_inWarningState = false | 			_inWarningState = false | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} else if strings.HasSuffix(path, ".m3u8") { | 	} else if strings.HasSuffix(path, ".m3u8") { | ||||||
| 		s.callbacks.VariantPlaylistWritten(path) | 		s.callbacks.VariantPlaylistWritten(path) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func returnError(err error, w http.ResponseWriter, r *http.Request) { | func returnError(err error, w http.ResponseWriter) { | ||||||
| 	log.Errorln(err) | 	log.Errorln(err) | ||||||
| 	http.Error(w, http.StatusText(http.StatusInternalServerError)+": "+err.Error(), http.StatusInternalServerError) | 	http.Error(w, http.StatusText(http.StatusInternalServerError)+": "+err.Error(), http.StatusInternalServerError) | ||||||
| } | } | ||||||
|  | |||||||
| @ -90,7 +90,12 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { | |||||||
| 	log.Infoln("Incoming RTMP connected.") | 	log.Infoln("Incoming RTMP connected.") | ||||||
|  |  | ||||||
| 	pipePath := utils.GetTemporaryPipePath() | 	pipePath := utils.GetTemporaryPipePath() | ||||||
| 	syscall.Mkfifo(pipePath, 0666) | 	if !utils.DoesFileExists(pipePath) { | ||||||
|  | 		err := syscall.Mkfifo(pipePath, 0666) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatalln(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	_hasInboundRTMPConnection = true | 	_hasInboundRTMPConnection = true | ||||||
| 	_setStreamAsConnected() | 	_setStreamAsConnected() | ||||||
| @ -119,7 +124,6 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { | |||||||
| 			panic(err) | 			panic(err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func handleDisconnect(conn net.Conn) { | func handleDisconnect(conn net.Conn) { | ||||||
|  | |||||||
| @ -6,8 +6,8 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"regexp" | 	"regexp" | ||||||
|  |  | ||||||
| 	"github.com/owncast/owncast/models" |  | ||||||
| 	"github.com/nareix/joy5/format/flv/flvio" | 	"github.com/nareix/joy5/format/flv/flvio" | ||||||
|  | 	"github.com/owncast/owncast/models" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func getInboundDetailsFromMetadata(metadata []interface{}) (models.RTMPStreamMetadata, error) { | func getInboundDetailsFromMetadata(metadata []interface{}) (models.RTMPStreamMetadata, error) { | ||||||
| @ -21,8 +21,8 @@ func getInboundDetailsFromMetadata(metadata []interface{}) (models.RTMPStreamMet | |||||||
|  |  | ||||||
| 	metadataJSONString := submatchall[0] | 	metadataJSONString := submatchall[0] | ||||||
| 	var details models.RTMPStreamMetadata | 	var details models.RTMPStreamMetadata | ||||||
| 	json.Unmarshal([]byte(metadataJSONString), &details) | 	err := json.Unmarshal([]byte(metadataJSONString), &details) | ||||||
| 	return details, nil | 	return details, err | ||||||
| } | } | ||||||
|  |  | ||||||
| func getAudioCodec(codec interface{}) string { | func getAudioCodec(codec interface{}) string { | ||||||
|  | |||||||
| @ -29,12 +29,9 @@ func setupStats() error { | |||||||
|  |  | ||||||
| 	statsSaveTimer := time.NewTicker(1 * time.Minute) | 	statsSaveTimer := time.NewTicker(1 * time.Minute) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for range statsSaveTimer.C { | ||||||
| 			select { | 			if err := saveStatsToFile(); err != nil { | ||||||
| 			case <-statsSaveTimer.C: | 				panic(err) | ||||||
| 				if err := saveStatsToFile(); err != nil { |  | ||||||
| 					panic(err) |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| @ -137,12 +134,12 @@ func getSavedStats() (models.Stats, error) { | |||||||
|  |  | ||||||
| 	jsonFile, err := ioutil.ReadFile(config.StatsFile) | 	jsonFile, err := ioutil.ReadFile(config.StatsFile) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return result, nil | 		return result, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := json.Unmarshal(jsonFile, &result); err != nil { | 	if err := json.Unmarshal(jsonFile, &result); err != nil { | ||||||
| 		return result, nil | 		return result, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return result, nil | 	return result, err | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,10 +5,6 @@ import ( | |||||||
| 	"github.com/owncast/owncast/core/storageproviders" | 	"github.com/owncast/owncast/core/storageproviders" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	usingExternalStorage = false |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func setupStorage() error { | func setupStorage() error { | ||||||
| 	handler.Storage = _storage | 	handler.Storage = _storage | ||||||
|  |  | ||||||
|  | |||||||
| @ -23,11 +23,8 @@ func (s *LocalStorage) Setup() error { | |||||||
| 	// 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. | ||||||
| 	_onlineCleanupTicker = time.NewTicker(1 * time.Minute) | 	_onlineCleanupTicker = time.NewTicker(1 * time.Minute) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for range _onlineCleanupTicker.C { | ||||||
| 			select { | 			ffmpeg.CleanupOldContent(config.PublicHLSStoragePath) | ||||||
| 			case <-_onlineCleanupTicker.C: |  | ||||||
| 				ffmpeg.CleanupOldContent(config.PublicHLSStoragePath) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 	return nil | 	return nil | ||||||
| @ -35,7 +32,10 @@ func (s *LocalStorage) Setup() error { | |||||||
|  |  | ||||||
| // SegmentWritten is called when a single segment of video is written. | // SegmentWritten is called when a single segment of video is written. | ||||||
| func (s *LocalStorage) SegmentWritten(localFilePath string) { | func (s *LocalStorage) SegmentWritten(localFilePath string) { | ||||||
| 	s.Save(localFilePath, 0) | 	_, err := s.Save(localFilePath, 0) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // VariantPlaylistWritten is called when a variant hls playlist is written. | // VariantPlaylistWritten is called when a variant hls playlist is written. | ||||||
| @ -49,7 +49,10 @@ 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) { | ||||||
| 	s.Save(localFilePath, 0) | 	_, err := s.Save(localFilePath, 0) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Save will save a local filepath using the storage provider. | // Save will save a local filepath using the storage provider. | ||||||
| @ -63,7 +66,6 @@ func (s *LocalStorage) Save(filePath string, retryCount int) (string, error) { | |||||||
| 		newPath = filepath.Join(config.WebRoot, filePath) | 		newPath = filepath.Join(config.WebRoot, filePath) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	utils.Copy(filePath, newPath) | 	err := utils.Copy(filePath, newPath) | ||||||
|  | 	return newPath, err | ||||||
| 	return newPath, nil |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ import ( | |||||||
|  |  | ||||||
| // If we try to upload a playlist but it is not yet on disk | // If we try to upload a playlist but it is not yet on disk | ||||||
| // then keep a reference to it here. | // then keep a reference to it here. | ||||||
| var _queuedPlaylistUpdates = make(map[string]string, 0) | var _queuedPlaylistUpdates = make(map[string]string) | ||||||
|  |  | ||||||
| // S3Storage is the s3 implementation of the ChunkStorageProvider. | // S3Storage is the s3 implementation of the ChunkStorageProvider. | ||||||
| type S3Storage struct { | type S3Storage struct { | ||||||
| @ -118,7 +118,10 @@ 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 | ||||||
| 	s.rewriteRemotePlaylist(localFilePath) | 	err := s.rewriteRemotePlaylist(localFilePath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Save saves the file to the s3 bucket. | // Save saves the file to the s3 bucket. | ||||||
| @ -192,6 +195,9 @@ func (s *S3Storage) rewriteRemotePlaylist(filePath string) error { | |||||||
|  |  | ||||||
| 	p := m3u8.NewMasterPlaylist() | 	p := m3u8.NewMasterPlaylist() | ||||||
| 	err = p.DecodeFrom(bufio.NewReader(f), false) | 	err = p.DecodeFrom(bufio.NewReader(f), false) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Warnln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for _, item := range p.Variants { | 	for _, item := range p.Variants { | ||||||
| 		item.URI = s.host + filepath.Join("/hls", item.URI) | 		item.URI = s.host + filepath.Join("/hls", item.URI) | ||||||
|  | |||||||
| @ -26,8 +26,8 @@ var _onlineCleanupTicker *time.Ticker | |||||||
| // setStreamAsConnected sets the stream as connected. | // setStreamAsConnected sets the stream as connected. | ||||||
| func setStreamAsConnected() { | func setStreamAsConnected() { | ||||||
| 	_stats.StreamConnected = true | 	_stats.StreamConnected = true | ||||||
| 	_stats.LastConnectTime = utils.NullTime{time.Now(), true} | 	_stats.LastConnectTime = utils.NullTime{Time: time.Now(), Valid: true} | ||||||
| 	_stats.LastDisconnectTime = utils.NullTime{time.Now(), false} | 	_stats.LastDisconnectTime = utils.NullTime{Time: time.Now(), Valid: false} | ||||||
|  |  | ||||||
| 	StopOfflineCleanupTimer() | 	StopOfflineCleanupTimer() | ||||||
| 	startOnlineCleanupTimer() | 	startOnlineCleanupTimer() | ||||||
| @ -44,7 +44,6 @@ func setStreamAsConnected() { | |||||||
| 	go func() { | 	go func() { | ||||||
| 		_transcoder = ffmpeg.NewTranscoder() | 		_transcoder = ffmpeg.NewTranscoder() | ||||||
| 		_transcoder.TranscoderCompleted = func(error) { | 		_transcoder.TranscoderCompleted = func(error) { | ||||||
|  |  | ||||||
| 			SetStreamAsDisconnected() | 			SetStreamAsDisconnected() | ||||||
| 		} | 		} | ||||||
| 		_transcoder.Start() | 		_transcoder.Start() | ||||||
| @ -56,7 +55,7 @@ func setStreamAsConnected() { | |||||||
| // SetStreamAsDisconnected sets the stream as disconnected. | // SetStreamAsDisconnected sets the stream as disconnected. | ||||||
| func SetStreamAsDisconnected() { | func SetStreamAsDisconnected() { | ||||||
| 	_stats.StreamConnected = false | 	_stats.StreamConnected = false | ||||||
| 	_stats.LastDisconnectTime = utils.NullTime{time.Now(), true} | 	_stats.LastDisconnectTime = utils.NullTime{Time: time.Now(), Valid: true} | ||||||
| 	_broadcaster = nil | 	_broadcaster = nil | ||||||
|  |  | ||||||
| 	offlineFilename := "offline.ts" | 	offlineFilename := "offline.ts" | ||||||
| @ -73,9 +72,14 @@ func SetStreamAsDisconnected() { | |||||||
| 		playlistFilePath := fmt.Sprintf(filepath.Join(config.PrivateHLSStoragePath, "%d/stream.m3u8"), index) | 		playlistFilePath := fmt.Sprintf(filepath.Join(config.PrivateHLSStoragePath, "%d/stream.m3u8"), index) | ||||||
| 		segmentFilePath := fmt.Sprintf(filepath.Join(config.PrivateHLSStoragePath, "%d/%s"), index, offlineFilename) | 		segmentFilePath := fmt.Sprintf(filepath.Join(config.PrivateHLSStoragePath, "%d/%s"), index, offlineFilename) | ||||||
|  |  | ||||||
| 		utils.Copy(offlineFilePath, segmentFilePath) | 		err := utils.Copy(offlineFilePath, segmentFilePath) | ||||||
| 		_storage.Save(segmentFilePath, 0) | 		if err != nil { | ||||||
|  | 			log.Warnln(err) | ||||||
|  | 		} | ||||||
|  | 		_, err = _storage.Save(segmentFilePath, 0) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Warnln(err) | ||||||
|  | 		} | ||||||
| 		if utils.DoesFileExists(playlistFilePath) { | 		if utils.DoesFileExists(playlistFilePath) { | ||||||
| 			f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm) | 			f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| @ -84,13 +88,23 @@ func SetStreamAsDisconnected() { | |||||||
| 			defer f.Close() | 			defer f.Close() | ||||||
|  |  | ||||||
| 			playlist, _, err := m3u8.DecodeFrom(bufio.NewReader(f), true) | 			playlist, _, err := m3u8.DecodeFrom(bufio.NewReader(f), true) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Fatalln(err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			variantPlaylist := playlist.(*m3u8.MediaPlaylist) | 			variantPlaylist := playlist.(*m3u8.MediaPlaylist) | ||||||
| 			if len(variantPlaylist.Segments) > config.Config.GetMaxNumberOfReferencedSegmentsInPlaylist() { | 			if len(variantPlaylist.Segments) > config.Config.GetMaxNumberOfReferencedSegmentsInPlaylist() { | ||||||
| 				variantPlaylist.Segments = variantPlaylist.Segments[:len(variantPlaylist.Segments)] | 				variantPlaylist.Segments = variantPlaylist.Segments[:len(variantPlaylist.Segments)] | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = variantPlaylist.Append(offlineFilename, 8.0, "") | 			err = variantPlaylist.Append(offlineFilename, 8.0, "") | ||||||
| 			variantPlaylist.SetDiscontinuity() | 			if err != nil { | ||||||
|  | 				log.Fatalln(err) | ||||||
|  | 			} | ||||||
|  | 			err = variantPlaylist.SetDiscontinuity() | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Fatalln(err) | ||||||
|  | 			} | ||||||
| 			_, err = f.WriteAt(variantPlaylist.Encode().Bytes(), 0) | 			_, err = f.WriteAt(variantPlaylist.Encode().Bytes(), 0) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Errorln(err) | 				log.Errorln(err) | ||||||
| @ -118,7 +132,10 @@ func SetStreamAsDisconnected() { | |||||||
| 				log.Errorln(err) | 				log.Errorln(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		_storage.Save(playlistFilePath, 0) | 		_, err = _storage.Save(playlistFilePath, 0) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Warnln(err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	StartOfflineCleanupTimer() | 	StartOfflineCleanupTimer() | ||||||
| @ -129,14 +146,11 @@ func SetStreamAsDisconnected() { | |||||||
| func StartOfflineCleanupTimer() { | func StartOfflineCleanupTimer() { | ||||||
| 	_offlineCleanupTimer = time.NewTimer(5 * time.Minute) | 	_offlineCleanupTimer = time.NewTimer(5 * time.Minute) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for range _offlineCleanupTimer.C { | ||||||
| 			select { | 			// Reset the session count since the session is over | ||||||
| 			case <-_offlineCleanupTimer.C: | 			_stats.SessionMaxViewerCount = 0 | ||||||
| 				// Reset the session count since the session is over | 			resetDirectories() | ||||||
| 				_stats.SessionMaxViewerCount = 0 | 			transitionToOfflineVideoStreamContent() | ||||||
| 				resetDirectories() |  | ||||||
| 				transitionToOfflineVideoStreamContent() |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| } | } | ||||||
| @ -151,11 +165,8 @@ func StopOfflineCleanupTimer() { | |||||||
| func startOnlineCleanupTimer() { | func startOnlineCleanupTimer() { | ||||||
| 	_onlineCleanupTicker = time.NewTicker(1 * time.Minute) | 	_onlineCleanupTicker = time.NewTicker(1 * time.Minute) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for range _onlineCleanupTicker.C { | ||||||
| 			select { | 			ffmpeg.CleanupOldContent(config.PrivateHLSStoragePath) | ||||||
| 			case <-_onlineCleanupTicker.C: |  | ||||||
| 				ffmpeg.CleanupOldContent(config.PrivateHLSStoragePath) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| } | } | ||||||
|  | |||||||
| @ -80,5 +80,4 @@ func FetchGeoForIP(ip string) { | |||||||
|  |  | ||||||
| 		_geoIPCache[ip] = response | 		_geoIPCache[ip] = response | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								main.go
									
									
									
									
									
								
							| @ -31,7 +31,7 @@ func main() { | |||||||
|  |  | ||||||
| 	log.Infoln(getReleaseString()) | 	log.Infoln(getReleaseString()) | ||||||
| 	// Enable bundling of admin assets | 	// Enable bundling of admin assets | ||||||
| 	pkger.Include("/admin") | 	_ = pkger.Include("/admin") | ||||||
|  |  | ||||||
| 	configFile := flag.String("configFile", "config.yaml", "Config File full path. Defaults to current folder") | 	configFile := flag.String("configFile", "config.yaml", "Config File full path. Defaults to current folder") | ||||||
| 	dbFile := flag.String("database", "", "Path to the database file.") | 	dbFile := flag.String("database", "", "Path to the database file.") | ||||||
| @ -67,13 +67,11 @@ func main() { | |||||||
|  |  | ||||||
| 	// starts the core | 	// starts the core | ||||||
| 	if err := core.Start(); err != nil { | 	if err := core.Start(); err != nil { | ||||||
| 		log.Error("failed to start the core package") | 		log.Fatalln("failed to start the core package", err) | ||||||
| 		panic(err) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := router.Start(); err != nil { | 	if err := router.Start(); err != nil { | ||||||
| 		log.Error("failed to start/run the router") | 		log.Fatalln("failed to start/run the router", err) | ||||||
| 		panic(err) |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -51,5 +51,5 @@ func handleDiskAlerting() { | |||||||
| } | } | ||||||
|  |  | ||||||
| func recentAverage(values []timestampedValue) int { | func recentAverage(values []timestampedValue) int { | ||||||
| 	return int((values[len(values)-1].Value + values[len(values)-2].Value) / 2) | 	return (values[len(values)-1].Value + values[len(values)-2].Value) / 2 | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,7 +29,12 @@ func Start() error { | |||||||
| 	http.HandleFunc("/api/emoji", controllers.GetCustomEmoji) | 	http.HandleFunc("/api/emoji", controllers.GetCustomEmoji) | ||||||
|  |  | ||||||
| 	// websocket chat server | 	// websocket chat server | ||||||
| 	go chat.Start() | 	go func() { | ||||||
|  | 		err := chat.Start() | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatalln(err) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
| 	// chat rest api | 	// chat rest api | ||||||
| 	http.HandleFunc("/api/chat", controllers.GetChatMessages) | 	http.HandleFunc("/api/chat", controllers.GetChatMessages) | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| package utils | package utils | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"crypto/md5" | 	"crypto/md5" //nolint | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @ -18,9 +18,8 @@ func GenerateClientIDFromRequest(req *http.Request) string { | |||||||
| 	clientID := strings.Join(ipAddressComponents, ":") + req.UserAgent() | 	clientID := strings.Join(ipAddressComponents, ":") + req.UserAgent() | ||||||
|  |  | ||||||
| 	// Create a MD5 hash of this ip + useragent | 	// Create a MD5 hash of this ip + useragent | ||||||
| 	hasher := md5.New() | 	b := md5.Sum([]byte(clientID)) // nolint | ||||||
| 	hasher.Write([]byte(clientID)) | 	return hex.EncodeToString(b[:]) | ||||||
| 	return hex.EncodeToString(hasher.Sum(nil)) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetIPAddressFromRequest returns the IP address from a http request. | // GetIPAddressFromRequest returns the IP address from a http request. | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ func (nt *NullTime) Scan(value interface{}) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Value implements the driver Valuer interface. | // Value implements the driver Value interface. | ||||||
| func (nt NullTime) Value() (driver.Value, error) { | func (nt NullTime) Value() (driver.Value, error) { | ||||||
| 	if !nt.Valid { | 	if !nt.Valid { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| @ -32,3 +32,19 @@ func (nt NullTime) MarshalJSON() ([]byte, error) { | |||||||
| 	val := fmt.Sprintf("\"%s\"", nt.Time.Format(time.RFC3339)) | 	val := fmt.Sprintf("\"%s\"", nt.Time.Format(time.RFC3339)) | ||||||
| 	return []byte(val), nil | 	return []byte(val), nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (nt NullTime) UnmarshalJSON(data []byte) error { | ||||||
|  | 	dateString := string(data) | ||||||
|  | 	if dateString == "null" { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dateStringWithoutQuotes := dateString[1 : len(dateString)-1] | ||||||
|  | 	parsedDateTime, err := time.Parse(time.RFC3339, dateStringWithoutQuotes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	nt.Time = parsedDateTime | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ func Copy(source, destination string) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return ioutil.WriteFile(destination, input, 0644) | 	return ioutil.WriteFile(destination, input, 0600) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Move moves the file to destination. | // Move moves the file to destination. | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/owncast/owncast/config" | 	"github.com/owncast/owncast/config" | ||||||
| 	"github.com/owncast/owncast/utils" | 	"github.com/owncast/owncast/utils" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type ypDetailsResponse struct { | type ypDetailsResponse struct { | ||||||
| @ -41,6 +42,8 @@ func GetYPResponse(w http.ResponseWriter, r *http.Request) { | |||||||
|  |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
|  |  | ||||||
| 	json.NewEncoder(w).Encode(response) | 	err := json.NewEncoder(w).Encode(response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								yp/yp.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								yp/yp.go
									
									
									
									
									
								
							| @ -47,15 +47,9 @@ func NewYP(getStatusFunc func() models.Status) *YP { | |||||||
| // Start is run when a live stream begins to start pinging YP. | // Start is run when a live stream begins to start pinging YP. | ||||||
| func (yp *YP) Start() { | func (yp *YP) Start() { | ||||||
| 	yp.timer = time.NewTicker(pingInterval) | 	yp.timer = time.NewTicker(pingInterval) | ||||||
|  | 	for range yp.timer.C { | ||||||
| 	go func() { | 		yp.ping() | ||||||
| 		for { | 	} | ||||||
| 			select { |  | ||||||
| 			case <-yp.timer.C: |  | ||||||
| 				yp.ping() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	yp.ping() | 	yp.ping() | ||||||
| } | } | ||||||
| @ -92,7 +86,7 @@ func (yp *YP) ping() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pingURL := config.Config.GetYPServiceHost() + "/ping" | 	pingURL := config.Config.GetYPServiceHost() + "/ping" | ||||||
| 	resp, err := http.Post(pingURL, "application/json", bytes.NewBuffer(req)) | 	resp, err := http.Post(pingURL, "application/json", bytes.NewBuffer(req)) //nolint | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Errorln(err) | 		log.Errorln(err) | ||||||
| 		return | 		return | ||||||
| @ -105,7 +99,10 @@ func (yp *YP) ping() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pingResponse := ypPingResponse{} | 	pingResponse := ypPingResponse{} | ||||||
| 	json.Unmarshal(body, &pingResponse) | 	err = json.Unmarshal(body, &pingResponse) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Errorln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if !pingResponse.Success { | 	if !pingResponse.Success { | ||||||
| 		if !_inErrorState { | 		if !_inErrorState { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Gabe Kangas
					Gabe Kangas