Browse Source
* Add support for ending the inbound stream. Closes #191 * Add a simple success response to API requests * Connected clients API with geo details * Post-rebase cleanup * Make setting and reading geo details separate operations to unblock and speed up * Rename file * Fire geoip api call behind goroutine * Add comment * Post-rebase fixes * Add support for the MaxMind GeoLite2 GeoIP databasepull/231/head
21 changed files with 1926 additions and 37 deletions
@ -0,0 +1,60 @@ |
|||||||
|
instanceDetails: |
||||||
|
name: Localhost Test Instance |
||||||
|
title: Owncast Demo Server |
||||||
|
summary: "This is Gabe's localhost instance of Owncast." |
||||||
|
|
||||||
|
logo: |
||||||
|
small: /img/logo128.png |
||||||
|
large: /img/logo256.png |
||||||
|
|
||||||
|
tags: |
||||||
|
- software |
||||||
|
- music |
||||||
|
- animal crossing |
||||||
|
|
||||||
|
# https://github.com/gabek/owncast/blob/master/doc/configuration.md#customization |
||||||
|
# for full list of supported social links. All optional. |
||||||
|
socialHandles: |
||||||
|
- platform: twitter |
||||||
|
url: http://twitter.com/owncast |
||||||
|
- platform: instagram |
||||||
|
url: http://instagram.biz/owncast |
||||||
|
- platform: facebook |
||||||
|
url: http://facebook.gov/owncast |
||||||
|
|
||||||
|
videoSettings: |
||||||
|
# Change this value and keep it secure. Treat it like a password to your live stream. |
||||||
|
streamingKey: abc123 |
||||||
|
|
||||||
|
# Determine the bitrate of your stream variants. |
||||||
|
# See https://github.com/gabek/owncast/blob/master/doc/configuration.md#video-quality for details. |
||||||
|
streamQualities: |
||||||
|
- high: |
||||||
|
videoBitrate: 2000 |
||||||
|
|
||||||
|
- medium: |
||||||
|
videoBitrate: 800 |
||||||
|
|
||||||
|
|
||||||
|
# s3: |
||||||
|
# enabled: true |
||||||
|
# endpoint: https://gabevideo.us-east-1.linodeobjects.com |
||||||
|
# accessKey: TM24VRAB57SLH72CS0XA |
||||||
|
# secret: zKpuJHRNLmOVnzh9gsoQHbRhpYAQt94xCb3Y7pou |
||||||
|
# region: us-east-1 |
||||||
|
# bucket: gabevideo |
||||||
|
|
||||||
|
s3: |
||||||
|
enabled: true |
||||||
|
endpoint: https://gabevideo.s3-us-west-2.amazonaws.com |
||||||
|
accessKey: AKIAZVILNW6ECSTICSPM |
||||||
|
secret: 5t34rWZqCMgNAk3B3dzgsQuZWuzZvylBiWvb1oYD |
||||||
|
region: us-west-2 |
||||||
|
bucket: gabevideo |
||||||
|
acl: public-read |
||||||
|
|
||||||
|
# Enable YP to be listed in the Owncast directory and let people discover your instance. |
||||||
|
yp: |
||||||
|
enabled: true |
||||||
|
ypServiceURL: https://owncast-yp-test.gabek.vercel.app |
||||||
|
instanceURL: http://localhost:8080 |
@ -0,0 +1,13 @@ |
|||||||
|
package controllers |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"github.com/owncast/owncast/core/rtmp" |
||||||
|
) |
||||||
|
|
||||||
|
// DisconnectInboundConnection will force-disconnect an inbound stream
|
||||||
|
func DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) { |
||||||
|
rtmp.Disconnect() |
||||||
|
w.WriteHeader(http.StatusOK) |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package controllers |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"github.com/owncast/owncast/core" |
||||||
|
) |
||||||
|
|
||||||
|
// GetConnectedClients returns currently connected clients
|
||||||
|
func GetConnectedClients(w http.ResponseWriter, r *http.Request) { |
||||||
|
clients := core.GetClients() |
||||||
|
w.Header().Set("Content-Type", "application/json") |
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(clients) |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
// This package utilizes the MaxMind GeoLite2 GeoIP database https://dev.maxmind.com/geoip/geoip2/geolite2/.
|
||||||
|
// You must provide your own copy of this database for it to work.
|
||||||
|
// Read more about how this works at http://owncast.online/docs/geoip
|
||||||
|
|
||||||
|
package geoip |
||||||
|
|
||||||
|
import ( |
||||||
|
"net" |
||||||
|
|
||||||
|
"github.com/oschwald/geoip2-golang" |
||||||
|
"github.com/owncast/owncast/config" |
||||||
|
log "github.com/sirupsen/logrus" |
||||||
|
) |
||||||
|
|
||||||
|
var _geoIPCache = map[string]GeoDetails{} |
||||||
|
var _enabled = true // Try to use GeoIP support it by default.
|
||||||
|
|
||||||
|
// GeoDetails stores details about a location
|
||||||
|
type GeoDetails struct { |
||||||
|
CountryCode string `json:"countryCode"` |
||||||
|
RegionName string `json:"regionName"` |
||||||
|
TimeZone string `json:"timeZone"` |
||||||
|
} |
||||||
|
|
||||||
|
// GetGeoFromIP returns geo details associated with an IP address if we
|
||||||
|
// have previously fetched it.
|
||||||
|
func GetGeoFromIP(ip string) *GeoDetails { |
||||||
|
if cachedGeoDetails, ok := _geoIPCache[ip]; ok { |
||||||
|
return &cachedGeoDetails |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// FetchGeoForIP makes an API call to get geo details for an IP address.
|
||||||
|
func FetchGeoForIP(ip string) { |
||||||
|
// If GeoIP has been disabled then don't try to access it.
|
||||||
|
if !_enabled { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Don't re-fetch if we already have it.
|
||||||
|
if _, ok := _geoIPCache[ip]; ok { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
go func() { |
||||||
|
db, err := geoip2.Open(config.GeoIPDatabasePath) |
||||||
|
if err != nil { |
||||||
|
log.Traceln("GeoIP support is disabled. visit http://owncast.online/docs/geoip to learn how to enable.", err) |
||||||
|
_enabled = false |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
defer db.Close() |
||||||
|
|
||||||
|
ipObject := net.ParseIP(ip) |
||||||
|
|
||||||
|
record, err := db.City(ipObject) |
||||||
|
if err != nil { |
||||||
|
log.Warnln(err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// If no country is available then exit
|
||||||
|
if record.Country.IsoCode == "" { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// If we believe this IP to be anonymous then no reason to report it
|
||||||
|
if record.Traits.IsAnonymousProxy { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
response := GeoDetails{ |
||||||
|
CountryCode: record.Country.IsoCode, |
||||||
|
RegionName: record.Subdivisions[0].Names["en"], |
||||||
|
TimeZone: record.Location.TimeZone, |
||||||
|
} |
||||||
|
|
||||||
|
_geoIPCache[ip] = response |
||||||
|
}() |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
file '/Users/gabek/Downloads/MegaManNetworkTransmission_SS_5633_HQ.mp4' |
||||||
|
file '/Users/gabek/Downloads/SampleVideo_720x480_10mb.mp4' |
||||||
|
file '/Users/gabek/Downloads/big_buck_bunny_720p_surround.mp4' |
||||||
|
file '/Users/gabek/Downloads/ed_hd.mp4' |
||||||
|
file '/Users/gabek/Downloads/god.mp4' |
||||||
|
file '/Users/gabek/Downloads/mixkit-a-boy-and-a-girl-with-a-mask-dancing-nearby-8689.mp4' |
||||||
|
file '/Users/gabek/Downloads/mixkit-couple-on-the-dance-floor-having-fun-344.mp4' |
||||||
|
file '/Users/gabek/Downloads/mixkit-disco-ball-spinning-1356.mp4' |
||||||
|
file '/Users/gabek/Downloads/mixkit-girl-dancing-in-nightclub-302.mp4' |
||||||
|
file '/Users/gabek/Downloads/mixkit-hands-raised-high-on-a-nightclub-dance-floor-341.mp4' |
||||||
|
file '/Users/gabek/Downloads/mixkit-popping-dancer-wearing-a-mask-with-neon-lights-3613.mp4' |
||||||
|
file '/Users/gabek/Downloads/randomdjset.mp4' |
||||||
|
file '/Users/gabek/Downloads/testtrailers.mp4' |
@ -0,0 +1,36 @@ |
|||||||
|
package models |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/owncast/owncast/geoip" |
||||||
|
"github.com/owncast/owncast/utils" |
||||||
|
) |
||||||
|
|
||||||
|
type ConnectedClientsResponse struct { |
||||||
|
Clients []Client `json:"clients"` |
||||||
|
} |
||||||
|
|
||||||
|
type Client struct { |
||||||
|
ConnectedAt time.Time `json:"connectedAt"` |
||||||
|
LastSeen time.Time `json:"-"` |
||||||
|
MessageCount int `json:"messageCount"` |
||||||
|
UserAgent string `json:"userAgent"` |
||||||
|
IPAddress string `json:"ipAddress"` |
||||||
|
Username *string `json:"username"` |
||||||
|
ClientID string `json:"clientID"` |
||||||
|
Geo *geoip.GeoDetails `json:"geo"` |
||||||
|
} |
||||||
|
|
||||||
|
func GenerateClientFromRequest(req *http.Request) Client { |
||||||
|
return Client{ |
||||||
|
ConnectedAt: time.Now(), |
||||||
|
LastSeen: time.Now(), |
||||||
|
MessageCount: 0, |
||||||
|
UserAgent: req.UserAgent(), |
||||||
|
IPAddress: utils.GetIPAddressFromRequest(req), |
||||||
|
Username: nil, |
||||||
|
ClientID: utils.GenerateClientIDFromRequest(req), |
||||||
|
} |
||||||
|
} |
@ -1,25 +1,41 @@ |
|||||||
package utils |
package utils |
||||||
|
|
||||||
import ( |
import ( |
||||||
|
"crypto/md5" |
||||||
|
"encoding/hex" |
||||||
|
"net" |
||||||
"net/http" |
"net/http" |
||||||
"strings" |
"strings" |
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus" |
||||||
) |
) |
||||||
|
|
||||||
//GenerateClientIDFromRequest generates a client id from the provided request
|
//GenerateClientIDFromRequest generates a client id from the provided request
|
||||||
func GenerateClientIDFromRequest(req *http.Request) string { |
func GenerateClientIDFromRequest(req *http.Request) string { |
||||||
var clientID string |
ipAddress := GetIPAddressFromRequest(req) |
||||||
|
ipAddressComponents := strings.Split(ipAddress, ":") |
||||||
|
ipAddressComponents[len(ipAddressComponents)-1] = "" |
||||||
|
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)) |
||||||
|
} |
||||||
|
|
||||||
|
// GetIPAddressFromRequest returns the IP address from a http request
|
||||||
|
func GetIPAddressFromRequest(req *http.Request) string { |
||||||
|
ipAddressString := req.RemoteAddr |
||||||
xForwardedFor := req.Header.Get("X-FORWARDED-FOR") |
xForwardedFor := req.Header.Get("X-FORWARDED-FOR") |
||||||
if xForwardedFor != "" { |
if xForwardedFor != "" { |
||||||
clientID = xForwardedFor |
ipAddressString = xForwardedFor |
||||||
} else { |
|
||||||
ipAddressString := req.RemoteAddr |
|
||||||
ipAddressComponents := strings.Split(ipAddressString, ":") |
|
||||||
ipAddressComponents[len(ipAddressComponents)-1] = "" |
|
||||||
clientID = strings.Join(ipAddressComponents, ":") |
|
||||||
} |
} |
||||||
|
|
||||||
// fmt.Println("IP address determined to be", ipAddress)
|
ip, _, err := net.SplitHostPort(ipAddressString) |
||||||
|
if err != nil { |
||||||
|
log.Errorln(err) |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
return clientID + req.UserAgent() |
return ip |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue