func GetViewMypage(c *gin.Context) {
- c.HTML(200, "mypage.html", gin.H{})
+ c.HTML(200, "mypage/index.html", gin.H{})
}
-func GetViewContentArticle(c *gin.Context) {
+func GetViewMypageArticle(c *gin.Context) {
- c.HTML(200, "content/article.html", gin.H{})
+ c.HTML(200, "mypage/article.html", gin.H{})
+
+}
+
+func GetViewMypageVideo(c *gin.Context) {
+
+ c.HTML(200, "mypage/video.html", gin.H{})
+
+}
+
+func GetViewMypageRoom(c *gin.Context) {
+
+ c.HTML(200, "mypage/room.html", gin.H{})
}
-func GetViewContentPeers(c *gin.Context) {
+func GetViewContentArticle(c *gin.Context) {
- c.HTML(200, "content/peers.html", gin.H{})
+ c.HTML(200, "content/article.html", gin.H{})
}
c.HTML(200, "content/video.html", gin.H{})
}
+
+func GetViewRoom(c *gin.Context) {
+
+ c.HTML(200, "room/index.html", gin.H{})
+
+}
ServeAddr string `yaml:"serveAddr"`
ServePort int `yaml:"servePort"`
MaxFileSize int64 `yaml:"maxFileSize"`
- Stream struct {
+ Com struct {
+ ChannelPort int `yaml:"channelPort"`
+ ChannelPortExternal int `yaml:"channelPortExternal"`
+ } `yaml:"channel"`
+ Edition struct {
+ ExtAllowList []string `yaml:"extAllowList"`
+ } `yaml:"edition"`
+ Stream struct {
TurnServerAddr []struct {
Addr string `yaml:"addr"`
Id string `yaml:"id"`
Pw string `yaml:"pw"`
} `yaml:"turnServerAddr"`
- PeerSignalAddr string `yaml:"peerSignalAddr"`
- RtcpPLIInterval int `yaml:"rtcpPLIInterval"`
- ExtAllowList []string `yaml:"extAllowList"`
- UdpBufferByteSize int `yaml:"udpBufferByteSize"`
- UdpMuxPort int `yaml:"udpMuxPort"`
- UdpEphemeralPortMin int `yaml:"udpEphemeralPortMin"`
- UdpEphemeralPortMax int `yaml:"udpEphemeralPortMax"`
- SignalPort int `yaml:"signalPort"`
- SignalPortExternal int `yaml:"signalPortExternal"`
- RtpReceivePort int `yaml:"rtpReceivePort"`
- RtpReceivePortExternal int `yaml:"rtpReceivePortExternal"`
+ PeerSignalAddr string `yaml:"peerSignalAddr"`
+ RtcpPLIInterval int `yaml:"rtcpPLIInterval"`
+ UdpBufferByteSize int `yaml:"udpBufferByteSize"`
+ UdpMuxPort int `yaml:"udpMuxPort"`
+ UdpEphemeralPortMin int `yaml:"udpEphemeralPortMin"`
+ UdpEphemeralPortMax int `yaml:"udpEphemeralPortMax"`
+ RtpReceivePort int `yaml:"rtpReceivePort"`
+ RtpReceivePortExternal int `yaml:"rtpReceivePortExternal"`
} `yaml:"stream"`
Utils struct {
UseCompress bool `yaml:"useCompress"`
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
pkgauth "github.com/seantywork/sorrylinus-again/pkg/auth"
+ pkgcom "github.com/seantywork/sorrylinus-again/pkg/com"
+ pkgedition "github.com/seantywork/sorrylinus-again/pkg/edition"
pkgstream "github.com/seantywork/sorrylinus-again/pkg/stream"
pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
)
pkgstream.INTERNAL_URL = CONF.InternalUrl
+ pkgcom.CHANNEL_ADDR = CONF.ServeAddr
+ pkgcom.CHANNEL_PORT = fmt.Sprintf("%d", CONF.Com.ChannelPort)
+ pkgcom.CHANNEL_PORT_EXTERNAL = fmt.Sprintf("%d", CONF.Com.ChannelPortExternal)
+
+ pkgedition.EXTENSION_ALLOWLIST = CONF.Edition.ExtAllowList
+
for i := 0; i < len(CONF.Stream.TurnServerAddr); i++ {
tmp := struct {
pkgstream.PEERS_SIGNAL_PATH = CONF.Stream.PeerSignalAddr
pkgstream.RTCP_PLI_INTERVAL = time.Second * time.Duration(CONF.Stream.RtcpPLIInterval)
- pkgstream.EXTENSION_ALLOWLIST = CONF.Stream.ExtAllowList
pkgstream.UDP_BUFFER_BYTE_SIZE = CONF.Stream.UdpBufferByteSize
pkgstream.UDP_MUX_PORT = CONF.Stream.UdpMuxPort
pkgstream.UDP_EPHEMERAL_PORT_MIN = CONF.Stream.UdpEphemeralPortMin
pkgstream.UDP_EPHEMERAL_PORT_MAX = CONF.Stream.UdpEphemeralPortMax
- pkgstream.SIGNAL_ADDR = CONF.ServeAddr
- pkgstream.SIGNAL_PORT = fmt.Sprintf("%d", CONF.Stream.SignalPort)
- pkgstream.SIGNAL_PORT_EXTERNAL = fmt.Sprintf("%d", CONF.Stream.SignalPortExternal)
-
pkgstream.RTP_RECEIVE_ADDR = CONF.ServeAddr
pkgstream.RTP_RECEIVE_PORT = fmt.Sprintf("%d", CONF.Stream.RtpReceivePort)
pkgstream.RTP_RECEIVE_PORT_EXTERNAL = fmt.Sprintf("%d", CONF.Stream.RtpReceivePortExternal)
e.GET("/mypage", GetViewMypage)
- e.GET("/content/article/:articleId", GetViewContentArticle)
+ e.GET("/mypage/article", GetViewMypage)
- e.GET("/content/peers/:peersId", GetViewContentPeers)
+ e.GET("/mypage/video", GetViewMypage)
+
+ e.GET("/mypage/room", GetViewMypage)
+
+ e.GET("/content/article/:articleId", GetViewContentArticle)
e.GET("/content/video/:videoId", GetViewContentVideo)
+ e.GET("/room/:roomId", GetViewRoom)
+
// auth
e.GET("/api/oauth2/google/signin", pkgauth.OauthGoogleLogin)
e.GET("/oauth2/google/callback", pkgauth.OauthGoogleCallback)
- // e.POST("/api/auth/user/add", pkgauth.UserAdd)
+ e.POST("/api/auth/user/add", pkgauth.UserAdd)
- // e.GET("/api/auth/signin", pkgauth.Login)
+ e.POST("/api/auth/user/remove", pkgauth.UserRemove)
- // e.GET("/api/auth/signout", pkgauth.Logout)
+ e.GET("/api/auth/signin", pkgauth.Login)
+
+ e.GET("/api/auth/signout", pkgauth.Logout)
pkgauth.InitAuth()
// e.POST("/api/article/upload", pkgedition.PostArticleUpload)
- // e.GET("/api/article/c/:contentId", pkgeditioni.GetArticleContentById)
+ // e.POST("/api/article/delete", pkgedition.PostArticleDelete)
+
+ // e.GET("/api/article/c/:contentId", pkgedition.GetArticleContentById)
// e.POST("/api/image/upload", pkgedition.PostImageUpload)
// e.GET("/api/image/c/:contentId", pkgedition.GetImageContentById)
+ e.POST("/api/video/upload", pkgedition.PostVideoUpload)
+
+ e.POST("/api/video/delete", pkgedition.PostVideoDelete)
+
+ e.GET("/api/video/c/:contentId", pkgedition.GetVideoContentByID)
+
// stream
pkgstream.InitWebRTCApi()
go pkgstream.InitRTMPServer()
- e.POST("/api/video/upload", pkgstream.PostVideoUpload)
+ e.GET("/api/peers/signal/address", pkgstream.GetPeersSignalAddress)
- e.GET("/api/video/c/:contentId", pkgstream.GetVideoContentByID)
+ // channel
- e.GET("/api/peers/signal/address", pkgstream.GetPeersSignalAddress)
+ pkgcom.AddChannelHandler(CONF.Stream.PeerSignalAddr, pkgstream.RoomSignalHandler)
- pkgstream.AddSignalHandler(CONF.Stream.PeerSignalAddr, pkgstream.RoomSignalHandler)
+ pkgcom.AddChannelCallback(pkgstream.SignalDispatcher)
- go pkgstream.StartSignalHandler()
+ go pkgcom.StartAllChannelHandlers()
}
import (
"encoding/json"
"fmt"
+ "net/http"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
+ "github.com/seantywork/sorrylinus-again/pkg/com"
+ "github.com/seantywork/sorrylinus-again/pkg/dbquery"
+ pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
)
var DEBUG bool = false
+type UserCreate struct {
+ Passphrase string `json:"passphrase"`
+ Id string `json:"id"`
+ DurationSeconds int `json:"duration_seconds"`
+}
+
+type UserLogin struct {
+ Id string `json:"id"`
+ Passphrase string `json:"passphrase"`
+}
+
+func GenerateStateAuthCookie(c *gin.Context) string {
+
+ state, _ := pkgutils.GetRandomHex(64)
+
+ session := sessions.Default(c)
+
+ session.Set("SOLIAGAIN", state)
+ session.Save()
+
+ return state
+}
+
func OauthGoogleLogin(c *gin.Context) {
+ my_key, my_type, _ := WhoAmI(c)
+
+ if my_key != "" && my_type != "" {
+
+ fmt.Printf("oauth login: already logged in\n")
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "already logged in"})
+
+ return
+
+ }
+
oauth_state := GenerateStateAuthCookie(c)
u := GoogleOauthConfig.AuthCodeURL(oauth_state)
func OauthGoogleCallback(c *gin.Context) {
+ my_key, my_type, _ := WhoAmI(c)
+
+ if my_key != "" && my_type != "" {
+
+ fmt.Printf("oauth callback: already logged in\n")
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "already logged in"})
+
+ return
+
+ }
+
session := sessions.Default(c)
var session_id string
return
}
+ as, err := dbquery.GetByIdFromAdmin(oauth_struct.EMAIL)
+
+ if as == nil {
+
+ fmt.Printf("access auth failed: %s", err.Error())
+
+ c.Redirect(302, "/signin")
+
+ return
+
+ }
+
+ err = dbquery.MakeSessionForAdmin(session_id, oauth_struct.EMAIL)
+
+ if err != nil {
+
+ fmt.Printf("make session failed for admin: %s", err.Error())
+
+ c.Redirect(302, "/signin")
+
+ return
+
+ }
+
fmt.Println("oauth sign in success")
c.Redirect(302, "/")
}
+
+func UserAdd(c *gin.Context) {
+
+ _, my_type, _ := WhoAmI(c)
+
+ if my_type != "admin" {
+
+ fmt.Printf("user add: not admin\n")
+
+ c.JSON(http.StatusForbidden, com.SERVER_RE{Status: "error", Reply: "you're not admin"})
+
+ return
+
+ }
+
+ var req com.CLIENT_REQ
+
+ var u_create UserCreate
+
+ if err := c.BindJSON(&req); err != nil {
+
+ fmt.Printf("user add: failed to bind: %s\n", err.Error())
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+ }
+
+ err := json.Unmarshal([]byte(req.Data), &u_create)
+
+ if err != nil {
+
+ fmt.Printf("user add: failed to unmarshal: %s\n", err.Error())
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+
+ }
+
+ err = dbquery.MakeUser(u_create.Id, u_create.Passphrase, u_create.DurationSeconds)
+
+ if err != nil {
+
+ fmt.Printf("user add: failed to make: %s", err.Error())
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed to make user"})
+
+ return
+ }
+
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "success", Reply: fmt.Sprintf("id: %s: created", u_create.Id)})
+
+}
+
+func UserRemove(c *gin.Context) {
+
+ _, my_type, _ := WhoAmI(c)
+
+ if my_type != "admin" {
+
+ fmt.Printf("user remove: not admin\n")
+
+ c.JSON(http.StatusForbidden, com.SERVER_RE{Status: "error", Reply: "you're not admin"})
+
+ return
+
+ }
+
+ var req com.CLIENT_REQ
+
+ if err := c.BindJSON(&req); err != nil {
+
+ fmt.Printf("user add: failed to bind: %s\n", err.Error())
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+ }
+
+ err := dbquery.RemoveUser(req.Data)
+
+ if err != nil {
+
+ fmt.Printf("user add: failed to remove: %s", err.Error())
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed to remove user"})
+
+ return
+ }
+
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "success", Reply: fmt.Sprintf("id: %s: deleted", req.Data)})
+
+}
+
+func Login(c *gin.Context) {
+
+ my_key, my_type, _ := WhoAmI(c)
+
+ if my_key != "" && my_type != "" {
+
+ fmt.Printf("user login: already logged in\n")
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "already logged in"})
+
+ return
+
+ }
+
+ var req com.CLIENT_REQ
+
+ var u_login UserLogin
+
+ if err := c.BindJSON(&req); err != nil {
+
+ fmt.Printf("user login: failed to bind: %s\n", err.Error())
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+ }
+
+ us, err := dbquery.GetByIdFromUser(u_login.Id)
+
+ if err != nil {
+
+ fmt.Printf("user login: failed to get from user: %s", err.Error())
+
+ c.JSON(http.StatusForbidden, com.SERVER_RE{Status: "error", Reply: "id doesn't exist"})
+
+ return
+ }
+
+ if us.Passphrase != u_login.Passphrase {
+
+ fmt.Printf("user login: passphrase: %s", "not matching")
+
+ c.JSON(http.StatusForbidden, com.SERVER_RE{Status: "error", Reply: "passphrase not matching"})
+
+ return
+
+ }
+
+ session_key := GenerateStateAuthCookie(c)
+
+ err = dbquery.MakeSessionForUser(session_key, u_login.Id, us.DurationSeconds)
+
+ if err != nil {
+
+ fmt.Printf("user login: failed to get from user: %s", err.Error())
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed to login"})
+
+ return
+
+ }
+
+}
+
+func Logout(c *gin.Context) {
+
+ my_key, my_type, _ := WhoAmI(c)
+
+ if my_key == "" && my_type == "" {
+
+ fmt.Printf("user logout: not logged in\n")
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "not logged in"})
+
+ return
+
+ }
+
+ if my_type == "admin" || my_type == "user" {
+
+ err := dbquery.RemoveSessionKeyFromSession(my_key)
+
+ if err != nil {
+
+ fmt.Printf("user logout: failed to remove session key: %s", err.Error())
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed to logout"})
+
+ return
+
+ }
+
+ } else {
+
+ fmt.Printf("user logout: what the hell is this type?: %s", my_type)
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed to logout"})
+
+ return
+
+ }
+
+}
--- /dev/null
+package auth
+
+import "unicode"
+
+func SanitizePlainNameValue(raw string) string {
+
+ var result string = ""
+
+ for _, c := range raw {
+
+ if unicode.IsLetter(c) {
+
+ result = result + string(c)
+
+ } else if unicode.IsDigit(c) {
+
+ result = result + string(c)
+
+ } else {
+
+ result = result + "-"
+
+ }
+
+ }
+
+ return result
+
+}
+
+func VerifyCodeNameValue(raw string) bool {
+
+ for _, c := range raw {
+
+ if unicode.IsLetter(c) {
+
+ continue
+
+ } else if unicode.IsDigit(c) {
+
+ continue
+
+ } else {
+
+ return false
+ }
+
+ }
+
+ return true
+}
import (
"context"
- "crypto/rand"
- "encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
}
-func GenerateStateAuthCookie(c *gin.Context) string {
-
- b := make([]byte, 64)
- rand.Read(b)
-
- state := base64.URLEncoding.EncodeToString(b)
-
- session := sessions.Default(c)
-
- session.Set("SOLIAGAIN", state)
- session.Save()
-
- return state
-}
-
func GetUserDataFromGoogle(code string) ([]byte, error) {
token, err := GoogleOauthConfig.Exchange(context.Background(), code)
--- /dev/null
+package auth
+
+import (
+ "fmt"
+
+ "github.com/gin-gonic/contrib/sessions"
+ "github.com/gin-gonic/gin"
+ "github.com/seantywork/sorrylinus-again/pkg/dbquery"
+)
+
+func WhoAmI(c *gin.Context) (string, string, string) {
+
+ var your_key string = ""
+
+ var your_type string = ""
+
+ var your_id string = ""
+
+ session := sessions.Default(c)
+
+ var session_id string
+
+ v := session.Get("SOLIAGAIN")
+
+ if v == nil {
+
+ fmt.Printf("you: %s\n", "nobody-yet")
+ return "", "", ""
+
+ } else {
+
+ session_id = v.(string)
+
+ }
+
+ ss, err := dbquery.GetBySessionKeyFromSession(session_id)
+
+ if err != nil {
+
+ fmt.Printf("you: %s\n", "nodody-no-session")
+
+ return "", "", ""
+ }
+
+ your_key = session_id
+ your_type = ss.Type
+ your_id = ss.Id
+
+ return your_key, your_type, your_id
+}
--- /dev/null
+package com
+
+import (
+ "log"
+ "net/http"
+ "sync"
+
+ "github.com/gorilla/websocket"
+)
+
+type CLIENT_REQ struct {
+ Data string `json:"data"`
+}
+
+type SERVER_RE struct {
+ Status string `json:"status"`
+ Reply string `json:"reply"`
+}
+
+var CHANNEL_ADDR string
+
+var CHANNEL_PORT string
+
+var CHANNEL_PORT_EXTERNAL string
+
+var USER_CHANNEL = make(map[string]*websocket.Conn)
+
+var CH_CALLBACKS []func()
+
+var ListLock sync.RWMutex
+
+type ThreadSafeWriter struct {
+ *websocket.Conn
+ sync.Mutex
+}
+
+func (t *ThreadSafeWriter) WriteJSON(v interface{}) error {
+ t.Lock()
+ defer t.Unlock()
+
+ return t.Conn.WriteJSON(v)
+}
+
+func AddChannelHandler(channelPath string, channelHandler func(w http.ResponseWriter, r *http.Request)) {
+
+ http.HandleFunc(channelPath, channelHandler)
+
+}
+
+func AddChannelCallback(channelFunction func()) {
+
+ CH_CALLBACKS = append(CH_CALLBACKS, channelFunction)
+}
+
+func StartAllChannelHandlers() {
+
+ callback_count := len(CH_CALLBACKS)
+
+ for i := 0; i < callback_count; i++ {
+
+ go CH_CALLBACKS[i]()
+
+ }
+
+ channel_addr := CHANNEL_ADDR + ":" + CHANNEL_PORT
+
+ log.Fatal(http.ListenAndServe(channel_addr, nil))
+
+}
package dbquery
import (
+ "encoding/json"
"fmt"
"mime/multipart"
"os"
- "path"
+ "strings"
+ "time"
"github.com/gin-gonic/gin"
)
+type AdminStruct struct {
+}
+
+type UserStruct struct {
+ Passphrase string `json:"passphrase"`
+ DurationSeconds int `json:"duration_seconds"`
+}
+
+type SessionStruct struct {
+ Type string `json:"type"`
+ Id string `json:"id"`
+ StartTime string `json:"start_time"`
+ DurationSeconds int `json:"duration_seconds"`
+}
+
+type MediaStruct struct {
+ ISPublic bool `json:"is_public"`
+ Type string `json:"type"`
+ Extension string `json:"extension"`
+ PlainName string `json:"plain_name"`
+ AllowedId []string `json:"allowed_id"`
+}
+
+var adminPath string = "./data/admin/"
+
+var userPath string = "./data/user/"
+
+var sessionPath string = "./data/session/"
+
var mediaPath string = "./data/media/"
-var videoPath string = "./data/media_video/"
+var articlePath string = "./data/media/article/"
+
+var imagePath string = "./data/media/image/"
+
+var videoPath string = "./data/media/video/"
+
+func GetByIdFromAdmin(email string) (*AdminStruct, error) {
+
+ var as AdminStruct
+
+ this_file_path := adminPath + email + ".json"
+
+ file_b, err := os.ReadFile(this_file_path)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("failed to get from admin: %s", err.Error())
+ }
+
+ err = json.Unmarshal(file_b, &as)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("failed to get from admin: %s", err.Error())
+ }
+
+ return &as, nil
+
+}
+
+func GetByIdFromUser(id string) (*UserStruct, error) {
+
+ var us UserStruct
+
+ this_file_path := userPath + id + ".json"
+
+ file_b, err := os.ReadFile(this_file_path)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("failed to get from user: %s", err.Error())
+
+ }
+
+ err = json.Unmarshal(file_b, &us)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("failed to get from user: %s", err.Error())
+ }
+
+ return &us, nil
+
+}
+
+func GetBySessionKeyFromSession(session_key string) (*SessionStruct, error) {
+
+ var ss SessionStruct
+
+ this_file_path := sessionPath + session_key + ".json"
+
+ file_b, err := os.ReadFile(this_file_path)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("session pool: failed to read file: %s", err.Error())
+
+ }
+
+ err = json.Unmarshal(file_b, &ss)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("session pool: failed to marshal: %s", err.Error())
+
+ }
+
+ t_now := time.Now()
+
+ t, _ := time.Parse("2006-01-02-15-04-05", ss.StartTime)
+
+ diff := t_now.Sub(t)
+
+ if ss.DurationSeconds == 0 || (diff.Seconds() < float64(ss.DurationSeconds)) {
+
+ return &ss, nil
+
+ } else {
+
+ err = RemoveSessionKeyFromSession(session_key)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("session: failed to remove: %s", err.Error())
+
+ }
-func UploadVideo(c *gin.Context, file *multipart.FileHeader, filename string, new_filename string) error {
+ return nil, fmt.Errorf("id: %s: expired", ss.Id)
- /*
+ }
+
+}
+
+func GetByMediaKeyFromMedia(media_key string) (*MediaStruct, error) {
+
+ var ms MediaStruct
+
+ this_file_path := mediaPath + media_key + ".json"
+
+ file_b, err := os.ReadFile(this_file_path)
+
+ if err != nil {
- TODO:
- save media index
+ return nil, fmt.Errorf("media: failed to read file: %s", err.Error())
- */
+ }
+
+ err = json.Unmarshal(file_b, &ms)
+
+ if err != nil {
+
+ return nil, fmt.Errorf("media: failed to marshal: %s", err.Error())
+ }
+
+ return &ms, nil
+
+}
- upload_path := path.Join(videoPath, new_filename)
+func GetByIdFromSession(email string) (string, *SessionStruct, error) {
- err := c.SaveUploadedFile(file, upload_path)
+ files, err := os.ReadDir(sessionPath)
+
+ if err != nil {
+
+ return "", nil, fmt.Errorf("session pool: failed to read dir: %s", err.Error())
+
+ }
+
+ for _, f := range files {
+
+ ss := SessionStruct{}
+
+ f_name := f.Name()
+
+ if !strings.Contains(f_name, ".json") {
+ continue
+ }
+
+ key_name := strings.ReplaceAll(f_name, ".json", "")
+
+ this_file_path := sessionPath + f_name
+
+ file_b, err := os.ReadFile(this_file_path)
+
+ if err != nil {
+
+ return "", nil, fmt.Errorf("session pool: failed to read file: %s", err.Error())
+
+ }
+
+ err = json.Unmarshal(file_b, &ss)
+
+ if err != nil {
+
+ return "", nil, fmt.Errorf("session pool: failed to marshal: %s", err.Error())
+ }
+
+ t_now := time.Now()
+
+ t, _ := time.Parse("2006-01-02-15-04-05", ss.StartTime)
+
+ diff := t_now.Sub(t)
+
+ if ss.Id == email {
+
+ if ss.DurationSeconds == 0 || (diff.Seconds() < float64(ss.DurationSeconds)) {
+
+ return key_name, &ss, nil
+
+ } else {
+
+ err = RemoveSessionKeyFromSession(key_name)
+
+ if err != nil {
+
+ return "", nil, fmt.Errorf("session pool: failed to remove: %s", err.Error())
+
+ }
+
+ return "", nil, fmt.Errorf("id: %s: expired", email)
+
+ }
+
+ }
+
+ }
+
+ return "", nil, fmt.Errorf("id: %s: not found", email)
+}
+
+func RemoveSessionKeyFromSession(session_key string) error {
+
+ this_file_path := sessionPath + session_key + ".json"
+
+ err := os.Remove(this_file_path)
+
+ if err != nil {
+
+ return err
+
+ }
+
+ return nil
+}
+
+func MakeSessionForAdmin(session_key string, email string) error {
+
+ _, ss, _ := GetByIdFromSession(email)
+
+ if ss != nil {
+
+ return fmt.Errorf("valid session already exists for: %s", email)
+
+ }
+
+ new_ss := SessionStruct{}
+
+ t_now := time.Now()
+
+ t_str := t_now.Format("2006-01-02-15-04-05")
+
+ new_ss.Type = "admin"
+
+ new_ss.Id = email
+
+ new_ss.StartTime = t_str
+
+ new_ss.DurationSeconds = 0
+
+ jb, err := json.Marshal(new_ss)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to marshal new session admin: %s", err.Error())
+ }
+
+ this_file_path := sessionPath + session_key + ".json"
+
+ err = os.WriteFile(this_file_path, jb, 0644)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to write new session admin: %s", err.Error())
+ }
+
+ return nil
+}
+
+func MakeUser(id string, passphrase string, duration_seconds int) error {
+
+ var us UserStruct
+
+ this_file_path := userPath + id + ".json"
+
+ if _, err := os.Stat(this_file_path); err == nil {
+
+ return fmt.Errorf("id: %s: exists", err.Error())
+
+ }
+
+ us.Passphrase = passphrase
+
+ us.DurationSeconds = duration_seconds
+
+ jb, err := json.Marshal(us)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to make user: %s", err.Error())
+
+ }
+
+ err = os.WriteFile(this_file_path, jb, 0644)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to make user: %s", err.Error())
+ }
+
+ return nil
+}
+
+func RemoveUser(id string) error {
+
+ this_file_path := userPath + id + ".json"
+
+ key, ss, _ := GetByIdFromSession(id)
+
+ if ss == nil {
+
+ fmt.Println("remove user: existing session removed")
+
+ _ = RemoveSessionKeyFromSession(key)
+
+ }
+
+ err := os.Remove(this_file_path)
+
+ if err != nil {
+
+ return err
+
+ }
+
+ return nil
+}
+
+func MakeSessionForUser(session_key string, id string, duration_seconds int) error {
+
+ var ss SessionStruct
+
+ this_file_path := sessionPath + session_key + ".json"
+
+ t_now := time.Now()
+
+ t_str := t_now.Format("2006-01-02-15-04-05")
+
+ ss.Type = "user"
+
+ ss.Id = id
+
+ ss.StartTime = t_str
+
+ ss.DurationSeconds = duration_seconds
+
+ jb, err := json.Marshal(ss)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to make session for user: %s", err.Error())
+
+ }
+
+ err = os.WriteFile(this_file_path, jb, 0644)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to make session: %s", err.Error())
+
+ }
+
+ return nil
+}
+
+func UploadVideo(c *gin.Context, file *multipart.FileHeader, filename string, new_filename string, extension string) error {
+
+ ms := MediaStruct{}
+
+ this_file_path := mediaPath + new_filename + ".json"
+
+ this_video_path := videoPath + new_filename + "." + extension
+
+ ms.ISPublic = true
+ ms.Type = "video"
+ ms.PlainName = filename
+ ms.Extension = extension
+
+ jb, err := json.Marshal(ms)
if err != nil {
return fmt.Errorf("failed to upload: %s", err.Error())
}
+ err = os.WriteFile(this_file_path, jb, 0644)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to upload: %s", err.Error())
+ }
+
+ err = c.SaveUploadedFile(file, this_video_path)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to upload: %s", err.Error())
+ }
+
+ return nil
+}
+
+func DeleteVideo(media_key string) error {
+
+ var ms MediaStruct
+
+ this_file_path := mediaPath + media_key + ".json"
+
+ file_b, err := os.ReadFile(this_file_path)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to delete video: %s", err.Error())
+
+ }
+
+ err = json.Unmarshal(file_b, &ms)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to delete video: marshal: %s", err.Error())
+ }
+
+ this_video_path := videoPath + media_key + "." + ms.Extension
+
+ err = os.Remove(this_video_path)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to delete video: rmvid: %s", err.Error())
+ }
+
+ err = os.Remove(this_file_path)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to delete video: rmmd: %s", err.Error())
+ }
+
return nil
}
func DownloadVideo(c *gin.Context, watchId string) error {
- download_path := path.Join(videoPath, watchId)
+ ms, err := GetByMediaKeyFromMedia(watchId)
+
+ if ms == nil {
+
+ return fmt.Errorf("failed to download video: %s", err.Error())
+
+ }
+
+ if ms.Type != "video" {
+
+ return fmt.Errorf("failed to download video: %s: %s", "wrong type", ms.Type)
+ }
+
+ this_video_path := videoPath + watchId + "." + ms.Extension
- if _, err := os.Stat(download_path); err != nil {
+ if _, err := os.Stat(this_video_path); err != nil {
return err
}
- c.Header("Content-Type", "video/mp4")
+ c.Header("Content-Type", "video/"+ms.Extension)
- c.File(download_path)
+ c.File(this_video_path)
return nil
}
--- /dev/null
+package edition
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/gin-gonic/gin"
+ pkgauth "github.com/seantywork/sorrylinus-again/pkg/auth"
+ "github.com/seantywork/sorrylinus-again/pkg/com"
+ "github.com/seantywork/sorrylinus-again/pkg/dbquery"
+ pkgdbq "github.com/seantywork/sorrylinus-again/pkg/dbquery"
+ pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
+)
+
+var EXTENSION_ALLOWLIST []string
+
+func PostVideoUpload(c *gin.Context) {
+
+ _, my_type, _ := pkgauth.WhoAmI(c)
+
+ if my_type != "admin" {
+
+ fmt.Printf("video upload: not admin\n")
+
+ c.JSON(http.StatusForbidden, com.SERVER_RE{Status: "error", Reply: "you're not admin"})
+
+ return
+
+ }
+
+ file, _ := c.FormFile("file")
+
+ f_name := file.Filename
+
+ f_name_list := strings.Split(f_name, ".")
+
+ f_name_len := len(f_name_list)
+
+ if f_name_len < 1 {
+
+ fmt.Println("no extension specified")
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+ }
+
+ v_fname := pkgauth.SanitizePlainNameValue(f_name_list[0])
+
+ extension := f_name_list[f_name_len-1]
+
+ if !pkgutils.CheckIfSliceContains[string](EXTENSION_ALLOWLIST, extension) {
+
+ fmt.Println("extension not allowed")
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+
+ }
+
+ fmt.Printf("received: %s, size: %d\n", file.Filename, file.Size)
+
+ file_name, _ := pkgutils.GetRandomHex(32)
+
+ err := pkgdbq.UploadVideo(c, file, v_fname, file_name, extension)
+
+ if err != nil {
+
+ fmt.Println(err.Error())
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed to save"})
+
+ return
+
+ }
+
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "success", Reply: "uploaded"})
+
+}
+
+func PostVideoDelete(c *gin.Context) {
+
+ _, my_type, _ := pkgauth.WhoAmI(c)
+
+ if my_type != "admin" {
+
+ fmt.Printf("video delete: not admin\n")
+
+ c.JSON(http.StatusForbidden, com.SERVER_RE{Status: "error", Reply: "you're not admin"})
+
+ return
+
+ }
+
+ fmt.Println("delete video")
+
+ var req com.CLIENT_REQ
+
+ if err := c.BindJSON(&req); err != nil {
+
+ fmt.Printf("video delete: failed to bind: %s\n", err.Error())
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+ }
+
+ if !pkgauth.VerifyCodeNameValue(req.Data) {
+
+ fmt.Printf("video name verification failed: %s\n", req.Data)
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+
+ }
+
+ err := dbquery.DeleteVideo(req.Data)
+
+ if err != nil {
+
+ fmt.Printf("video delete: %s\n", err.Error())
+
+ c.JSON(http.StatusInternalServerError, com.SERVER_RE{Status: "error", Reply: "failed delete"})
+
+ return
+
+ }
+
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "success", Reply: "deleted"})
+
+}
+
+func GetVideoContentByID(c *gin.Context) {
+
+ watchId := c.Param("contentId")
+
+ if !pkgauth.VerifyCodeNameValue(watchId) {
+
+ fmt.Printf("download video: illegal: %s\n", watchId)
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+
+ }
+
+ err := pkgdbq.DownloadVideo(c, watchId)
+
+ if err != nil {
+
+ fmt.Printf("download video: %s\n", err.Error())
+
+ c.JSON(http.StatusBadRequest, com.SERVER_RE{Status: "error", Reply: "invalid format"})
+
+ return
+
+ }
+
+ fmt.Println("download success")
+
+}
package stream
import (
- "log"
- "net/http"
- "sync"
"time"
"github.com/gorilla/websocket"
"github.com/pion/webrtc/v4"
+ ch "github.com/seantywork/sorrylinus-again/pkg/com"
)
-var SIGNAL_ADDR string
-
-var SIGNAL_PORT string
-
-var SIGNAL_PORT_EXTERNAL string
-
-var USER_SIGNAL = make(map[string]*websocket.Conn)
-
-var UPGRADER = websocket.Upgrader{}
-
type SIGNAL_INFO struct {
Command string `json:"command"`
Status string `json:"status"`
Data string `json:"data"`
}
-var listLock sync.RWMutex
+var UPGRADER = websocket.Upgrader{}
+
var peerConnections = make([]peerConnectionState, 0)
var trackLocals = make(map[string]*webrtc.TrackLocalStaticRTP)
type peerConnectionState struct {
peerConnection *webrtc.PeerConnection
- websocket *threadSafeWriter
-}
-
-type threadSafeWriter struct {
- *websocket.Conn
- sync.Mutex
-}
-
-func (t *threadSafeWriter) WriteJSON(v interface{}) error {
- t.Lock()
- defer t.Unlock()
-
- return t.Conn.WriteJSON(v)
-}
-
-func AddSignalHandler(signalPath string, signalHandler func(w http.ResponseWriter, r *http.Request)) {
-
- http.HandleFunc(signalPath, signalHandler)
-
+ websocket *ch.ThreadSafeWriter
}
-func StartSignalHandler() {
-
- go func() {
-
- for range time.NewTicker(time.Second * 3).C {
- dispatchKeyFrame()
- }
-
- }()
-
- signal_addr := SIGNAL_ADDR + ":" + SIGNAL_PORT
-
- log.Fatal(http.ListenAndServe(signal_addr, nil))
+func SignalDispatcher() {
+ for range time.NewTicker(time.Second * 3).C {
+ dispatchKeyFrame()
+ }
}
"github.com/pion/ice/v3"
"github.com/pion/rtcp"
"github.com/pion/webrtc/v4"
+ "github.com/seantywork/sorrylinus-again/pkg/com"
)
-type CLIENT_REQ struct {
- Data string `json:"data"`
-}
-
-type SERVER_RE struct {
- Status string `json:"status"`
- Reply string `json:"reply"`
-}
-
var EXTERNAL_URL string
var INTERNAL_URL string
if err != nil {
- c.JSON(http.StatusOK, SERVER_RE{Status: "failed", Reply: "error"})
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "failed", Reply: "error"})
}
- c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: string(data_b)})
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "success", Reply: string(data_b)})
}
}
func addTrack(t *webrtc.TrackRemote) *webrtc.TrackLocalStaticRTP {
- listLock.Lock()
+ com.ListLock.Lock()
defer func() {
- listLock.Unlock()
+ com.ListLock.Unlock()
signalPeerConnections()
}()
}
func removeTrack(t *webrtc.TrackLocalStaticRTP) {
- listLock.Lock()
+ com.ListLock.Lock()
defer func() {
- listLock.Unlock()
+ com.ListLock.Unlock()
signalPeerConnections()
}()
"net"
"time"
- "github.com/OKESTRO-AIDevOps/nkia/pkg/utils"
"github.com/gin-gonic/gin"
"github.com/pion/webrtc/v4"
"github.com/pion/webrtc/v4/pkg/media"
"github.com/pkg/errors"
+ "github.com/seantywork/sorrylinus-again/pkg/com"
+ "github.com/seantywork/sorrylinus-again/pkg/utils"
flvtag "github.com/yutopp/go-flv/tag"
"github.com/yutopp/go-rtmp"
rtmpmsg "github.com/yutopp/go-rtmp/message"
panic(err)
}
- var req CLIENT_REQ
+ var req com.CLIENT_REQ
var offer webrtc.SessionDescription
}
<-gatherComplete
- streamingKey, err := utils.RandomHex(16)
+ streamingKey, err := utils.GetRandomHex(16)
if err != nil {
panic(err)
}
- var resp SERVER_RE
+ var resp com.SERVER_RE
resp.Status = "success"
resp.Reply = string(desc_b)
*/
- var resp SERVER_RE
+ var resp com.SERVER_RE
resp.Status = "success"
resp.Reply = ""
"github.com/gin-gonic/gin"
"github.com/pion/rtcp"
"github.com/pion/webrtc/v4"
+ "github.com/seantywork/sorrylinus-again/pkg/com"
pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
)
func GetPeersSignalAddress(c *gin.Context) {
- s_addr := EXTERNAL_URL + ":" + SIGNAL_PORT_EXTERNAL + PEERS_SIGNAL_PATH
+ s_addr := EXTERNAL_URL + ":" + com.CHANNEL_PORT_EXTERNAL + PEERS_SIGNAL_PATH
- c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: s_addr})
+ c.JSON(http.StatusOK, com.SERVER_RE{Status: "success", Reply: s_addr})
}
return
}
- c := &threadSafeWriter{unsafeConn, sync.Mutex{}}
+ c := &com.ThreadSafeWriter{unsafeConn, sync.Mutex{}}
// When this frame returns close the Websocket
defer c.Close() //nolint
}
// Add our new PeerConnection to global list
- listLock.Lock()
+ com.ListLock.Lock()
peerConnections = append(peerConnections, peerConnectionState{peerConnection, c})
- listLock.Unlock()
+ com.ListLock.Unlock()
// Trickle ICE. Emit server candidate to client
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
}
func dispatchKeyFrame() {
- listLock.Lock()
- defer listLock.Unlock()
+ com.ListLock.Lock()
+ defer com.ListLock.Unlock()
for i := range peerConnections {
for _, receiver := range peerConnections[i].peerConnection.GetReceivers() {
}
func signalPeerConnections() {
- listLock.Lock()
+ com.ListLock.Lock()
defer func() {
- listLock.Unlock()
+ com.ListLock.Unlock()
dispatchKeyFrame()
}()
+++ /dev/null
-package stream
-
-import (
- "fmt"
- "net/http"
- "strings"
-
- "github.com/gin-gonic/gin"
- pkgdbq "github.com/seantywork/sorrylinus-again/pkg/dbquery"
- pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
-)
-
-var EXTENSION_ALLOWLIST []string
-
-func PostVideoUpload(c *gin.Context) {
-
- file, _ := c.FormFile("file")
-
- f_name := file.Filename
-
- f_name_list := strings.Split(f_name, ".")
-
- f_name_len := len(f_name_list)
-
- if f_name_len < 1 {
-
- fmt.Println("no extension specified")
-
- c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid format"})
-
- return
- }
-
- extension := f_name_list[f_name_len-1]
-
- if !pkgutils.CheckIfSliceContains[string](f_name_list, extension) {
-
- fmt.Println("extension not allowed")
-
- c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid format"})
-
- return
-
- }
-
- fmt.Printf("received: %s, size: %d\n", file.Filename, file.Size)
-
- file_name, _ := pkgutils.GetRandomHex(32)
-
- err := pkgdbq.UploadVideo(c, file, f_name, file_name+"."+extension)
-
- if err != nil {
-
- fmt.Println(err.Error())
-
- c.JSON(http.StatusInternalServerError, SERVER_RE{Status: "error", Reply: "failed to save"})
-
- return
-
- }
-
- c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: "uploaded"})
-
-}
-
-func GetVideoContentByID(c *gin.Context) {
-
- watchId := c.Param("contentId")
-
- err := pkgdbq.DownloadVideo(c, watchId)
-
- if err != nil {
-
- fmt.Println("path doesn't exist")
-
- c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid format"})
-
- return
-
- }
-
- fmt.Println("download success")
-
-}
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
-
- <meta charset="utf-8">
- <title>peers</title>
- <!--<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>-->
- <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
-
- </head>
- <body>
- <h3> Local Video </h3>
- <video id="localVideo" width="160" height="120" autoplay muted></video> <br />
-
- <h3> Remote Video </h3>
- <div id="remoteVideos"></div> <br />
-
- <h3> Logs </h3>
- <div id="logs"></div>
-
-
- <button onclick="initPeers()">Join</button>
-
-
- </body>
-
- <script src="/public/js/peers.js"></script>
-</html>
\ No newline at end of file
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>webrtc client</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
+
+ <!--<link rel="stylesheet" href="/public/css/cctv_local.css">-->
+ <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
+
+ <script src="/public/js/cctv_local.js"></script>
+
+</head>
+<body>
+ Browser base64 Session Description<br />
+ <textarea id="localSessionDescription" readonly="true"></textarea> <br />
+
+ <button onclick="startCCTVOffer()"> Start offer </button><br />
+
+ Golang base64 Session Description<br />
+ <textarea id="remoteSessionDescription"> </textarea> <br/>
+ <button onclick="startSession()"> Start Session </button><br />
+
+ <br />
+
+ Video<br />
+ <div id="remoteVideos"></div> <br />
+
+ Logs<br />
+ <div id="div"></div>
+
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>webrtc client</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
+
+ <link rel="stylesheet" href="/public/css/peers_p2p.css">
+
+ <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
+
+</head>
+<body>
+
+ <label for="mid">meeting ID</label>
+ <input type="text" id="mid" name="mid"><br><br>
+ <label for="uid">user ID</label>
+ <input type="text" id="uid" name="uid"><br><br>
+ <button onclick="initPeers()" class="start-call">
+ Init the call!
+ </button>
+
+
+ <button onclick="startCall()" class="start-call">
+ Start the call!
+ </button>
+
+ <div id="peer-receive"></div>
+
+ <div id="peer-send">
+ <video autoplay id="senderVideo" width="500" height="500" controls muted preload="none"></video>
+ </div>
+
+
+</body>
+
+<script src="/public/js/peers.js"></script>
+
+</html>
\ No newline at end of file
+++ /dev/null
-<html>
- <head>
- <title> mypage </title>
- <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
- </head>
-
- <body>
- <h1> RTMP to WebRTC </h1>
- <div id="rtmpFeed"></div>
-
- <button onclick="initCCTV()"> start cctv </button>
-
- <form action="/video/upload" method="post" enctype="multipart/form-data">
- <label for="file">File</label>
- <input id="file" name="file" type="file" />
- <button>Upload</button>
- </form>
-
-
- </body>
-
- <script src="/public/js/cctv.js"></script>
- <script src="/public/js/video.js"></script>
-
-</html>
\ No newline at end of file
--- /dev/null
+<html>
+ <head>
+ <title> mypage </title>
+ <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
+ </head>
+
+ <body>
+ <h1> RTMP to WebRTC </h1>
+ <div id="rtmpFeed"></div>
+
+ <button onclick="initCCTV()"> start cctv </button>
+
+
+ </body>
+
+ <script src="/public/js/cctv.js"></script>
+
+
+</html>
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <title>mypage room</title>
+ <!--<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>-->
+ <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
+
+ </head>
+ <body>
+ <h3> Local Video </h3>
+ <video id="localVideo" width="160" height="120" autoplay muted></video> <br />
+
+ <h3> Remote Video </h3>
+ <div id="remoteVideos"></div> <br />
+
+ <h3> Logs </h3>
+ <div id="logs"></div>
+
+
+ <button onclick="initPeers()">Join</button>
+
+
+ </body>
+
+ <script src="/public/js/peers.js"></script>
+</html>
\ No newline at end of file
--- /dev/null
+<html>
+ <head>
+ <title> mypage video </title>
+ <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
+ </head>
+
+ <body>
+
+ <form action="/video/upload" method="post" enctype="multipart/form-data">
+ <label for="file">File</label>
+ <input id="file" name="file" type="file" />
+ <button>Upload</button>
+ </form>
+
+
+ </body>
+
+
+ <script src="/public/js/video.js"></script>
+
+</html>
\ No newline at end of file
+++ /dev/null
-<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <title>webrtc client</title>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="icon" type="image/x-icon" href="favicon.ico">
-
- <!--<link rel="stylesheet" href="/public/css/cctv_local.css">-->
- <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
-
- <script src="/public/js/cctv_local.js"></script>
-
-</head>
-<body>
- Browser base64 Session Description<br />
- <textarea id="localSessionDescription" readonly="true"></textarea> <br />
-
- <button onclick="startCCTVOffer()"> Start offer </button><br />
-
- Golang base64 Session Description<br />
- <textarea id="remoteSessionDescription"> </textarea> <br/>
- <button onclick="startSession()"> Start Session </button><br />
-
- <br />
-
- Video<br />
- <div id="remoteVideos"></div> <br />
-
- Logs<br />
- <div id="div"></div>
-
-</body>
-
-</html>
\ No newline at end of file
+++ /dev/null
-<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <title>webrtc client</title>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="icon" type="image/x-icon" href="favicon.ico">
-
- <link rel="stylesheet" href="/public/css/peers_p2p.css">
-
- <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
- <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
-
-</head>
-<body>
-
- <label for="mid">meeting ID</label>
- <input type="text" id="mid" name="mid"><br><br>
- <label for="uid">user ID</label>
- <input type="text" id="uid" name="uid"><br><br>
- <button onclick="initPeers()" class="start-call">
- Init the call!
- </button>
-
-
- <button onclick="startCall()" class="start-call">
- Start the call!
- </button>
-
- <div id="peer-receive"></div>
-
- <div id="peer-send">
- <video autoplay id="senderVideo" width="500" height="500" controls muted preload="none"></video>
- </div>
-
-
-</body>
-
-<script src="/public/js/peers.js"></script>
-
-</html>
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <title>peers</title>
+ <!--<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>-->
+ <!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
+
+ </head>
+ <body>
+ <h3> Local Video </h3>
+ <video id="localVideo" width="160" height="120" autoplay muted></video> <br />
+
+ <h3> Remote Video </h3>
+ <div id="remoteVideos"></div> <br />
+
+ <h3> Logs </h3>
+ <div id="logs"></div>
+
+
+ <button onclick="initPeers()">Join</button>
+
+
+ </body>
+
+ <script src="/public/js/peers.js"></script>
+</html>
\ No newline at end of file