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" | ||||
|  | ||||
| 	"github.com/owncast/owncast/metrics" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // GetHardwareStats will return hardware utilization over time. | ||||
| @ -12,5 +13,8 @@ func GetHardwareStats(w http.ResponseWriter, r *http.Request) { | ||||
| 	m := metrics.Metrics | ||||
|  | ||||
| 	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) | ||||
| 	if err != nil { | ||||
| 		log.Warnln(err, path) | ||||
| 		errorHandler(w, r, http.StatusNotFound) | ||||
| 		errorHandler(w, http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -39,9 +39,11 @@ func ServeAdmin(w http.ResponseWriter, r *http.Request) { | ||||
|  | ||||
| 	mimeType := mime.TypeByExtension(filepath.Ext(path)) | ||||
| 	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) | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,7 @@ import ( | ||||
|  | ||||
| 	"github.com/owncast/owncast/logging" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // GetLogs will return all logs. | ||||
| @ -19,7 +20,10 @@ func GetLogs(w http.ResponseWriter, r *http.Request) { | ||||
| 	} | ||||
|  | ||||
| 	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. | ||||
| @ -32,7 +36,10 @@ func GetWarnings(w http.ResponseWriter, r *http.Request) { | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
|  | ||||
| @ -5,6 +5,7 @@ import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/owncast/owncast/config" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // 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") | ||||
| 	json.NewEncoder(w).Encode(response) | ||||
| 	err := json.NewEncoder(w).Encode(response) | ||||
| 	if err != nil { | ||||
| 		log.Errorln(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type serverConfigAdminResponse struct { | ||||
|  | ||||
| @ -7,6 +7,7 @@ import ( | ||||
| 	"github.com/owncast/owncast/config" | ||||
| 	"github.com/owncast/owncast/core" | ||||
| 	"github.com/owncast/owncast/models" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // 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") | ||||
| 	json.NewEncoder(w).Encode(response) | ||||
| 	err := json.NewEncoder(w).Encode(response) | ||||
| 	if err != nil { | ||||
| 		log.Errorln(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type adminStatusResponse struct { | ||||
|  | ||||
| @ -5,11 +5,15 @@ import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/owncast/owncast/metrics" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // GetViewersOverTime will return the number of viewers at points in time. | ||||
| func GetViewersOverTime(w http.ResponseWriter, r *http.Request) { | ||||
| 	viewersOverTime := metrics.Metrics.Viewers | ||||
| 	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/models" | ||||
| 	"github.com/owncast/owncast/router/middleware" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // GetChatMessages gets all of the chat messages. | ||||
| @ -17,7 +18,10 @@ func GetChatMessages(w http.ResponseWriter, r *http.Request) { | ||||
| 	case http.MethodGet: | ||||
| 		messages := core.GetAllChatMessages() | ||||
|  | ||||
| 		json.NewEncoder(w).Encode(messages) | ||||
| 		err := json.NewEncoder(w).Encode(messages) | ||||
| 		if err != nil { | ||||
| 			log.Errorln(err) | ||||
| 		} | ||||
| 	case http.MethodPost: | ||||
| 		var message models.ChatMessage | ||||
| 		if err := json.NewDecoder(r.Body).Decode(&message); err != nil { | ||||
| @ -30,9 +34,14 @@ func GetChatMessages(w http.ResponseWriter, r *http.Request) { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		json.NewEncoder(w).Encode(j{"success": true}) | ||||
| 		if err := json.NewEncoder(w).Encode(j{"success": true}); err != nil { | ||||
| 			internalErrorHandler(w, err) | ||||
| 			return | ||||
| 		} | ||||
| 	default: | ||||
| 		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.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() | ||||
| 	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) | ||||
| 	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) { | ||||
| @ -24,7 +26,9 @@ func badRequestHandler(w http.ResponseWriter, err error) { | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| @ -33,5 +37,7 @@ func WriteSimpleResponse(w http.ResponseWriter, success bool, message string) { | ||||
| 		Message: message, | ||||
| 	} | ||||
| 	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 { | ||||
| 		name := strings.TrimSuffix(f.Name(), path.Ext(f.Name())) | ||||
| 		emojiPath := filepath.Join(emojiDir, f.Name()) | ||||
| 		singleEmoji := models.CustomEmoji{name, emojiPath} | ||||
| 		singleEmoji := models.CustomEmoji{Name: name, Emoji: emojiPath} | ||||
| 		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() | ||||
| 	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,12 +40,9 @@ func Start() error { | ||||
|  | ||||
| 	ticker := time.NewTicker(30 * time.Second) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-ticker.C: | ||||
| 		for range ticker.C { | ||||
| 			_server.ping() | ||||
| 		} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	_server.Listen() | ||||
|  | ||||
| @ -94,13 +94,21 @@ func (c *Client) listenWrite() { | ||||
| 		select { | ||||
| 		// Send a PING keepalive | ||||
| 		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 | ||||
| 		case msg := <-c.ch: | ||||
| 			// log.Println("Send:", msg) | ||||
| 			websocket.JSON.Send(c.ws, msg) | ||||
| 			err := websocket.JSON.Send(c.ws, msg) | ||||
| 			if err != nil { | ||||
| 				log.Errorln(err) | ||||
| 			} | ||||
| 		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 | ||||
| 		case <-c.doneCh: | ||||
| 			_server.remove(c) | ||||
| @ -114,7 +122,6 @@ func (c *Client) listenWrite() { | ||||
| func (c *Client) listenRead() { | ||||
| 	for { | ||||
| 		select { | ||||
|  | ||||
| 		// receive done request | ||||
| 		case <-c.doneCh: | ||||
| 			_server.remove(c) | ||||
|  | ||||
| @ -32,7 +32,10 @@ func createTable() { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	defer stmt.Close() | ||||
| 	stmt.Exec() | ||||
| 	_, err = stmt.Exec() | ||||
| 	if err != nil { | ||||
| 		log.Warnln(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func addMessage(message models.ChatMessage) { | ||||
| @ -41,16 +44,20 @@ func addMessage(message models.ChatMessage) { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	stmt, err := tx.Prepare("INSERT INTO messages(id, author, body, messageType, visible, timestamp) values(?, ?, ?, ?, ?, ?)") | ||||
|  | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	defer stmt.Close() | ||||
|  | ||||
| 	_, err = stmt.Exec(message.ID, message.Author, message.Body, message.MessageType, 1, message.Timestamp) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	tx.Commit() | ||||
|  | ||||
| 	defer stmt.Close() | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getChatHistory() []models.ChatMessage { | ||||
| @ -89,5 +96,9 @@ func getChatHistory() []models.ChatMessage { | ||||
| 		history = append(history, message) | ||||
| 	} | ||||
|  | ||||
| 	if err := rows.Err(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	return history | ||||
| } | ||||
|  | ||||
| @ -46,11 +46,6 @@ func (s *server) SendToAll(msg models.ChatMessage) { | ||||
| 	s.sendAllCh <- msg | ||||
| } | ||||
|  | ||||
| // Done marks the server as done. | ||||
| func (s *server) done() { | ||||
| 	s.doneCh <- true | ||||
| } | ||||
|  | ||||
| // Err handles an error. | ||||
| func (s *server) err(err error) { | ||||
| 	s.errCh <- err | ||||
| @ -140,18 +135,7 @@ func (s *server) sendWelcomeMessageToClient(c *Client) { | ||||
| 		time.Sleep(7 * time.Second) | ||||
|  | ||||
| 		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) | ||||
| 	}() | ||||
|  | ||||
| } | ||||
|  | ||||
| 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() | ||||
|  | ||||
| 	// 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 | ||||
| 	os.Remove(path.Join(config.WebRoot, "preview.gif")) | ||||
| @ -111,8 +114,15 @@ func resetDirectories() { | ||||
| 	// Wipe the public, web-accessible hls data directory | ||||
| 	os.RemoveAll(config.PublicHLSStoragePath) | ||||
| 	os.RemoveAll(config.PrivateHLSStoragePath) | ||||
| 	os.MkdirAll(config.PublicHLSStoragePath, 0777) | ||||
| 	os.MkdirAll(config.PrivateHLSStoragePath, 0777) | ||||
| 	err := os.MkdirAll(config.PublicHLSStoragePath, 0777) | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err) | ||||
| 	} | ||||
|  | ||||
| 	err = os.MkdirAll(config.PrivateHLSStoragePath, 0777) | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err) | ||||
| 	} | ||||
|  | ||||
| 	// Remove the previous thumbnail | ||||
| 	os.Remove(filepath.Join(config.WebRoot, "thumbnail.jpg")) | ||||
| @ -120,14 +130,31 @@ func resetDirectories() { | ||||
| 	// Create private hls data dirs | ||||
| 	if len(config.Config.VideoSettings.StreamQualities) != 0 { | ||||
| 		for index := range config.Config.VideoSettings.StreamQualities { | ||||
| 			os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0777) | ||||
| 			os.MkdirAll(path.Join(config.PublicHLSStoragePath, strconv.Itoa(index)), 0777) | ||||
| 			err = os.MkdirAll(path.Join(config.PrivateHLSStoragePath, 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 { | ||||
| 		os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(0)), 0777) | ||||
| 		os.MkdirAll(path.Join(config.PublicHLSStoragePath, strconv.Itoa(0)), 0777) | ||||
| 		err = os.MkdirAll(path.Join(config.PrivateHLSStoragePath, 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 | ||||
| 	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) | ||||
| 	if err != nil { | ||||
| 		returnError(err, w, r) | ||||
| 		returnError(err, w) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	defer f.Close() | ||||
| 	_, err = f.Write(data) | ||||
| 	if err != nil { | ||||
| 		returnError(err, w, r) | ||||
| 		returnError(err, w) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -82,7 +82,6 @@ func (s *FileWriterReceiverService) fileWritten(path string) { | ||||
|  | ||||
| 	if utils.GetRelativePathFromAbsolutePath(path) == "hls/stream.m3u8" { | ||||
| 		s.callbacks.MasterPlaylistWritten(path) | ||||
|  | ||||
| 	} else if strings.HasSuffix(path, ".ts") { | ||||
| 		performanceMonitorKey := "segmentWritten-" + index | ||||
| 		averagePerformance := utils.GetAveragePerformance(performanceMonitorKey) | ||||
| @ -98,13 +97,12 @@ func (s *FileWriterReceiverService) fileWritten(path string) { | ||||
| 		} else { | ||||
| 			_inWarningState = false | ||||
| 		} | ||||
|  | ||||
| 	} else if strings.HasSuffix(path, ".m3u8") { | ||||
| 		s.callbacks.VariantPlaylistWritten(path) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func returnError(err error, w http.ResponseWriter, r *http.Request) { | ||||
| func returnError(err error, w http.ResponseWriter) { | ||||
| 	log.Errorln(err) | ||||
| 	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.") | ||||
|  | ||||
| 	pipePath := utils.GetTemporaryPipePath() | ||||
| 	syscall.Mkfifo(pipePath, 0666) | ||||
| 	if !utils.DoesFileExists(pipePath) { | ||||
| 		err := syscall.Mkfifo(pipePath, 0666) | ||||
| 		if err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	_hasInboundRTMPConnection = true | ||||
| 	_setStreamAsConnected() | ||||
| @ -119,7 +124,6 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func handleDisconnect(conn net.Conn) { | ||||
|  | ||||
| @ -6,8 +6,8 @@ import ( | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
|  | ||||
| 	"github.com/owncast/owncast/models" | ||||
| 	"github.com/nareix/joy5/format/flv/flvio" | ||||
| 	"github.com/owncast/owncast/models" | ||||
| ) | ||||
|  | ||||
| func getInboundDetailsFromMetadata(metadata []interface{}) (models.RTMPStreamMetadata, error) { | ||||
| @ -21,8 +21,8 @@ func getInboundDetailsFromMetadata(metadata []interface{}) (models.RTMPStreamMet | ||||
|  | ||||
| 	metadataJSONString := submatchall[0] | ||||
| 	var details models.RTMPStreamMetadata | ||||
| 	json.Unmarshal([]byte(metadataJSONString), &details) | ||||
| 	return details, nil | ||||
| 	err := json.Unmarshal([]byte(metadataJSONString), &details) | ||||
| 	return details, err | ||||
| } | ||||
|  | ||||
| func getAudioCodec(codec interface{}) string { | ||||
|  | ||||
| @ -29,14 +29,11 @@ func setupStats() error { | ||||
|  | ||||
| 	statsSaveTimer := time.NewTicker(1 * time.Minute) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-statsSaveTimer.C: | ||||
| 		for range statsSaveTimer.C { | ||||
| 			if err := saveStatsToFile(); err != nil { | ||||
| 				panic(err) | ||||
| 			} | ||||
| 		} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return nil | ||||
| @ -137,12 +134,12 @@ func getSavedStats() (models.Stats, error) { | ||||
|  | ||||
| 	jsonFile, err := ioutil.ReadFile(config.StatsFile) | ||||
| 	if err != nil { | ||||
| 		return result, nil | ||||
| 		return result, err | ||||
| 	} | ||||
|  | ||||
| 	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" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	usingExternalStorage = false | ||||
| ) | ||||
|  | ||||
| func setupStorage() error { | ||||
| 	handler.Storage = _storage | ||||
|  | ||||
|  | ||||
| @ -23,19 +23,19 @@ func (s *LocalStorage) Setup() error { | ||||
| 	// as all HLS segments have to be publicly available on disk to keep a recording of them. | ||||
| 	_onlineCleanupTicker = time.NewTicker(1 * time.Minute) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-_onlineCleanupTicker.C: | ||||
| 		for range _onlineCleanupTicker.C { | ||||
| 			ffmpeg.CleanupOldContent(config.PublicHLSStoragePath) | ||||
| 		} | ||||
| 		} | ||||
| 	}() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SegmentWritten is called when a single segment of video is written. | ||||
| 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. | ||||
| @ -49,7 +49,10 @@ func (s *LocalStorage) VariantPlaylistWritten(localFilePath string) { | ||||
|  | ||||
| // MasterPlaylistWritten is called when the master hls playlist is written. | ||||
| 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. | ||||
| @ -63,7 +66,6 @@ func (s *LocalStorage) Save(filePath string, retryCount int) (string, error) { | ||||
| 		newPath = filepath.Join(config.WebRoot, filePath) | ||||
| 	} | ||||
|  | ||||
| 	utils.Copy(filePath, newPath) | ||||
|  | ||||
| 	return newPath, nil | ||||
| 	err := utils.Copy(filePath, newPath) | ||||
| 	return newPath, err | ||||
| } | ||||
|  | ||||
| @ -22,7 +22,7 @@ import ( | ||||
|  | ||||
| // If we try to upload a playlist but it is not yet on disk | ||||
| // 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. | ||||
| type S3Storage struct { | ||||
| @ -118,7 +118,10 @@ 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 | ||||
| 	s.rewriteRemotePlaylist(localFilePath) | ||||
| 	err := s.rewriteRemotePlaylist(localFilePath) | ||||
| 	if err != nil { | ||||
| 		log.Warnln(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Save saves the file to the s3 bucket. | ||||
| @ -192,6 +195,9 @@ func (s *S3Storage) rewriteRemotePlaylist(filePath string) error { | ||||
|  | ||||
| 	p := m3u8.NewMasterPlaylist() | ||||
| 	err = p.DecodeFrom(bufio.NewReader(f), false) | ||||
| 	if err != nil { | ||||
| 		log.Warnln(err) | ||||
| 	} | ||||
|  | ||||
| 	for _, item := range p.Variants { | ||||
| 		item.URI = s.host + filepath.Join("/hls", item.URI) | ||||
|  | ||||
| @ -26,8 +26,8 @@ var _onlineCleanupTicker *time.Ticker | ||||
| // setStreamAsConnected sets the stream as connected. | ||||
| func setStreamAsConnected() { | ||||
| 	_stats.StreamConnected = true | ||||
| 	_stats.LastConnectTime = utils.NullTime{time.Now(), true} | ||||
| 	_stats.LastDisconnectTime = utils.NullTime{time.Now(), false} | ||||
| 	_stats.LastConnectTime = utils.NullTime{Time: time.Now(), Valid: true} | ||||
| 	_stats.LastDisconnectTime = utils.NullTime{Time: time.Now(), Valid: false} | ||||
|  | ||||
| 	StopOfflineCleanupTimer() | ||||
| 	startOnlineCleanupTimer() | ||||
| @ -44,7 +44,6 @@ func setStreamAsConnected() { | ||||
| 	go func() { | ||||
| 		_transcoder = ffmpeg.NewTranscoder() | ||||
| 		_transcoder.TranscoderCompleted = func(error) { | ||||
|  | ||||
| 			SetStreamAsDisconnected() | ||||
| 		} | ||||
| 		_transcoder.Start() | ||||
| @ -56,7 +55,7 @@ func setStreamAsConnected() { | ||||
| // SetStreamAsDisconnected sets the stream as disconnected. | ||||
| func SetStreamAsDisconnected() { | ||||
| 	_stats.StreamConnected = false | ||||
| 	_stats.LastDisconnectTime = utils.NullTime{time.Now(), true} | ||||
| 	_stats.LastDisconnectTime = utils.NullTime{Time: time.Now(), Valid: true} | ||||
| 	_broadcaster = nil | ||||
|  | ||||
| 	offlineFilename := "offline.ts" | ||||
| @ -73,9 +72,14 @@ func SetStreamAsDisconnected() { | ||||
| 		playlistFilePath := fmt.Sprintf(filepath.Join(config.PrivateHLSStoragePath, "%d/stream.m3u8"), index) | ||||
| 		segmentFilePath := fmt.Sprintf(filepath.Join(config.PrivateHLSStoragePath, "%d/%s"), index, offlineFilename) | ||||
|  | ||||
| 		utils.Copy(offlineFilePath, segmentFilePath) | ||||
| 		_storage.Save(segmentFilePath, 0) | ||||
|  | ||||
| 		err := utils.Copy(offlineFilePath, segmentFilePath) | ||||
| 		if err != nil { | ||||
| 			log.Warnln(err) | ||||
| 		} | ||||
| 		_, err = _storage.Save(segmentFilePath, 0) | ||||
| 		if err != nil { | ||||
| 			log.Warnln(err) | ||||
| 		} | ||||
| 		if utils.DoesFileExists(playlistFilePath) { | ||||
| 			f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm) | ||||
| 			if err != nil { | ||||
| @ -84,13 +88,23 @@ func SetStreamAsDisconnected() { | ||||
| 			defer f.Close() | ||||
|  | ||||
| 			playlist, _, err := m3u8.DecodeFrom(bufio.NewReader(f), true) | ||||
| 			if err != nil { | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
|  | ||||
| 			variantPlaylist := playlist.(*m3u8.MediaPlaylist) | ||||
| 			if len(variantPlaylist.Segments) > config.Config.GetMaxNumberOfReferencedSegmentsInPlaylist() { | ||||
| 				variantPlaylist.Segments = variantPlaylist.Segments[:len(variantPlaylist.Segments)] | ||||
| 			} | ||||
|  | ||||
| 			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) | ||||
| 			if err != nil { | ||||
| 				log.Errorln(err) | ||||
| @ -118,7 +132,10 @@ func SetStreamAsDisconnected() { | ||||
| 				log.Errorln(err) | ||||
| 			} | ||||
| 		} | ||||
| 		_storage.Save(playlistFilePath, 0) | ||||
| 		_, err = _storage.Save(playlistFilePath, 0) | ||||
| 		if err != nil { | ||||
| 			log.Warnln(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	StartOfflineCleanupTimer() | ||||
| @ -129,15 +146,12 @@ func SetStreamAsDisconnected() { | ||||
| func StartOfflineCleanupTimer() { | ||||
| 	_offlineCleanupTimer = time.NewTimer(5 * time.Minute) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-_offlineCleanupTimer.C: | ||||
| 		for range _offlineCleanupTimer.C { | ||||
| 			// Reset the session count since the session is over | ||||
| 			_stats.SessionMaxViewerCount = 0 | ||||
| 			resetDirectories() | ||||
| 			transitionToOfflineVideoStreamContent() | ||||
| 		} | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| @ -151,12 +165,9 @@ func StopOfflineCleanupTimer() { | ||||
| func startOnlineCleanupTimer() { | ||||
| 	_onlineCleanupTicker = time.NewTicker(1 * time.Minute) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-_onlineCleanupTicker.C: | ||||
| 		for range _onlineCleanupTicker.C { | ||||
| 			ffmpeg.CleanupOldContent(config.PrivateHLSStoragePath) | ||||
| 		} | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -80,5 +80,4 @@ func FetchGeoForIP(ip string) { | ||||
|  | ||||
| 		_geoIPCache[ip] = response | ||||
| 	}() | ||||
|  | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								main.go
									
									
									
									
									
								
							| @ -31,7 +31,7 @@ func main() { | ||||
|  | ||||
| 	log.Infoln(getReleaseString()) | ||||
| 	// 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") | ||||
| 	dbFile := flag.String("database", "", "Path to the database file.") | ||||
| @ -67,13 +67,11 @@ func main() { | ||||
|  | ||||
| 	// starts the core | ||||
| 	if err := core.Start(); err != nil { | ||||
| 		log.Error("failed to start the core package") | ||||
| 		panic(err) | ||||
| 		log.Fatalln("failed to start the core package", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := router.Start(); err != nil { | ||||
| 		log.Error("failed to start/run the router") | ||||
| 		panic(err) | ||||
| 		log.Fatalln("failed to start/run the router", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -51,5 +51,5 @@ func handleDiskAlerting() { | ||||
| } | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 	// websocket chat server | ||||
| 	go chat.Start() | ||||
| 	go func() { | ||||
| 		err := chat.Start() | ||||
| 		if err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// chat rest api | ||||
| 	http.HandleFunc("/api/chat", controllers.GetChatMessages) | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"crypto/md5" | ||||
| 	"crypto/md5" //nolint | ||||
| 	"encoding/hex" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| @ -18,9 +18,8 @@ func GenerateClientIDFromRequest(req *http.Request) string { | ||||
| 	clientID := strings.Join(ipAddressComponents, ":") + req.UserAgent() | ||||
|  | ||||
| 	// Create a MD5 hash of this ip + useragent | ||||
| 	hasher := md5.New() | ||||
| 	hasher.Write([]byte(clientID)) | ||||
| 	return hex.EncodeToString(hasher.Sum(nil)) | ||||
| 	b := md5.Sum([]byte(clientID)) // nolint | ||||
| 	return hex.EncodeToString(b[:]) | ||||
| } | ||||
|  | ||||
| // GetIPAddressFromRequest returns the IP address from a http request. | ||||
|  | ||||
| @ -17,7 +17,7 @@ func (nt *NullTime) Scan(value interface{}) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Value implements the driver Valuer interface. | ||||
| // Value implements the driver Value interface. | ||||
| func (nt NullTime) Value() (driver.Value, error) { | ||||
| 	if !nt.Valid { | ||||
| 		return nil, nil | ||||
| @ -32,3 +32,19 @@ func (nt NullTime) MarshalJSON() ([]byte, error) { | ||||
| 	val := fmt.Sprintf("\"%s\"", nt.Time.Format(time.RFC3339)) | ||||
| 	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 ioutil.WriteFile(destination, input, 0644) | ||||
| 	return ioutil.WriteFile(destination, input, 0600) | ||||
| } | ||||
|  | ||||
| // Move moves the file to destination. | ||||
|  | ||||
| @ -6,6 +6,7 @@ import ( | ||||
|  | ||||
| 	"github.com/owncast/owncast/config" | ||||
| 	"github.com/owncast/owncast/utils" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| type ypDetailsResponse struct { | ||||
| @ -41,6 +42,8 @@ func GetYPResponse(w http.ResponseWriter, r *http.Request) { | ||||
|  | ||||
| 	w.Header().Set("Content-Type", "application/json") | ||||
|  | ||||
| 	json.NewEncoder(w).Encode(response) | ||||
|  | ||||
| 	err := json.NewEncoder(w).Encode(response) | ||||
| 	if err != nil { | ||||
| 		log.Errorln(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										15
									
								
								yp/yp.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								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. | ||||
| func (yp *YP) Start() { | ||||
| 	yp.timer = time.NewTicker(pingInterval) | ||||
|  | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-yp.timer.C: | ||||
| 	for range yp.timer.C { | ||||
| 		yp.ping() | ||||
| 	} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	yp.ping() | ||||
| } | ||||
| @ -92,7 +86,7 @@ func (yp *YP) 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 { | ||||
| 		log.Errorln(err) | ||||
| 		return | ||||
| @ -105,7 +99,10 @@ func (yp *YP) ping() { | ||||
| 	} | ||||
|  | ||||
| 	pingResponse := ypPingResponse{} | ||||
| 	json.Unmarshal(body, &pingResponse) | ||||
| 	err = json.Unmarshal(body, &pingResponse) | ||||
| 	if err != nil { | ||||
| 		log.Errorln(err) | ||||
| 	} | ||||
|  | ||||
| 	if !pingResponse.Success { | ||||
| 		if !_inErrorState { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Gabe Kangas
					Gabe Kangas