Skip to content

Commit

Permalink
Merge pull request #10 from daystram/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
daystram authored Feb 5, 2021
2 parents 617fe7b + 4378007 commit f786f0b
Show file tree
Hide file tree
Showing 52 changed files with 672 additions and 986 deletions.
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,23 @@ For ease of deployment, the following `docker-compose.yml` file can be used to o
```yaml
version: "3"
services:
cast-fe:
image: daystram/cast:fe
cast-be:
image: daystram/cast:be
ports:
- "80:80"
- "8080:8080"
- "1935:1935"
env_file:
- /path_to_env_file/.env
restart: unless-stopped
cast-is: # no attached GPU
image: daystram/cast:is
env_file:
- /path_to_env_file/.env
restart: unless-stopped
cast-be:
image: daystram/cast:be
cast-fe:
image: daystram/cast:fe
ports:
- "8080:8080"
env_file:
- /path_to_env_file/.env
- "80:80"
restart: unless-stopped
mongodb:
image: mongo:4.4-bionic
Expand Down Expand Up @@ -140,5 +141,14 @@ services:

This image is built on top of [NVIDIA's CUDA images](https://hub.docker.com/r/nvidia/cuda/) to enable FFmpeg harware acceleration on supported hosts. MP4Box is built from source, as seen on the [Dockerfile](https://github.com/daystram/cast/blob/master/cast-is/ingest-base.Dockerfile).

### MongoDB Indexes
For features to work properly, some indexes needs to be created in the MongoDB instance. Use the following command in `mongo` CLI to create indexes for `video` collection:

```
use MONGODB_NAME;
db.video.createIndex({title: "text", description: "text"}, {collation: {locale: "simple"}});
db.video.createIndex({hash: "hashed"});
```

## License
This project is licensed under the [MIT License](https://github.com/daystram/cast/blob/master/LICENSE).
4 changes: 2 additions & 2 deletions cast-be/controller/middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func AuthenticateAccessToken(ctx *context.Context) {
var accessToken string
if accessToken = strings.TrimPrefix(ctx.Input.Header("Authorization"), "Bearer "); accessToken == "" {
if accessToken = ctx.Input.Query("access_token"); accessToken == "" {
ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
ctx.ResponseWriter.WriteHeader(http.StatusUnauthorized)
return
}
}
Expand All @@ -29,7 +29,7 @@ func AuthenticateAccessToken(ctx *context.Context) {
if info, err = verifyAccessToken(accessToken); err != nil || !info.Active {
log.Printf("[AuthFilter] Invalid access_token. %+v\n", err)
errMessage, _ := json.Marshal(map[string]interface{}{"message": "invalid access_token"})
ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
ctx.ResponseWriter.WriteHeader(http.StatusUnauthorized)
_, _ = ctx.ResponseWriter.Write(errMessage)
return
}
Expand Down
36 changes: 15 additions & 21 deletions cast-be/controller/v1/video.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,36 +129,25 @@ func (c *VideoControllerAuth) EditVideo(_ string) datatransfers.Response {
fmt.Printf("[VideoControllerAuth::EditVideo] failed parsing video details. %+v\n", err)
return datatransfers.Response{Error: "Failed parsing video detail", Code: http.StatusInternalServerError}
}
if matchVideo, err := c.Handler.VideoDetails(video.Hash); err != nil {
log.Printf("[VideoControllerAuth::EditVideo] failed retrieving video. %+v\n", err)
return datatransfers.Response{Error: "Failed editing video", Code: http.StatusInternalServerError}
} else if matchVideo.Title != video.Title {
if err = c.Handler.CheckUniqueVideoTitle(video.Title); err != nil {
log.Printf("[VideoControllerAuth::EditVideo] title already used. %+v\n", err)
return datatransfers.Response{Error: "Title already used", Code: http.StatusConflict}
}
}
err = c.Handler.UpdateVideo(datatransfers.VideoEdit{
Hash: video.Hash,
Title: video.Title,
Description: video.Description,
Tags: strings.Split(video.Tags, ","),
}, c.userID)
}, c.Controller, c.userID)
if err != nil {
fmt.Printf("[VideoControllerAuth::EditVideo] failed editing video. %+v\n", err)
return datatransfers.Response{Error: "Failed editing video", Code: http.StatusInternalServerError}
}
if _, _, err = c.GetFile("thumbnail"); err != nil {
if err == http.ErrMissingFile {
return datatransfers.Response{Code: http.StatusOK}
} else {
fmt.Printf("[VideoControllerAuth::EditVideo] failed retrieving profile image. %+v\n", err)
return datatransfers.Response{Error: "Failed retrieving profile image", Code: http.StatusInternalServerError}
}
}
// TODO: use S3
//// New thumbnail uploaded
//err = c.SaveToFile("thumbnail", fmt.Sprintf("%s/thumbnail/%s.ori", config.AppConfig.UploadsDirectory, video.Hash))
//if err != nil {
// fmt.Printf("[VideoControllerAuth::EditVideo] failed saving thumbnail. %+v\n", err)
// return datatransfers.Response{Error: "Failed saving thumbnail", Code: http.StatusInternalServerError}
//}
//err = c.Handler.NormalizeThumbnail(video.Hash)
//if err != nil {
// fmt.Printf("[VideoControllerAuth::EditVideo] failed normalizing thumbnail. %+v\n", err)
// return datatransfers.Response{Error: "Failed normalizing thumbnail", Code: http.StatusInternalServerError}
//}
return datatransfers.Response{Code: http.StatusOK}
}

Expand Down Expand Up @@ -191,6 +180,11 @@ func (c *VideoControllerAuth) UploadVideo(_ string) datatransfers.Response {
fmt.Printf("[VideoControllerAuth::UploadVideo] failed parsing video details. %+v\n", err)
return datatransfers.Response{Error: "Failed parsing video detail", Code: http.StatusInternalServerError}
}
err = c.Handler.CheckUniqueVideoTitle(upload.Title)
if err != nil {
log.Printf("[VideoControllerAuth::UploadVideo] title already used. %+v\n", err)
return datatransfers.Response{Error: "Title already used", Code: http.StatusConflict}
}
var videoID primitive.ObjectID
videoID, err = c.Handler.CreateVOD(datatransfers.VideoUpload{
Title: upload.Title,
Expand Down
1 change: 1 addition & 0 deletions cast-be/datatransfers/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ChatOutgoing struct {
// WS notification message
type NotificationOutgoing struct {
Message string `json:"message"`
Name string `json:"name"`
Username string `json:"username"`
Hash string `json:"hash"`
CreatedAt time.Time `json:"created_at"`
Expand Down
3 changes: 3 additions & 0 deletions cast-be/datatransfers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import (
type User struct {
ID string `json:"id" bson:"_id"`
Username string `json:"username" bson:"username"`
Name string `json:"name" bson:"name"`
CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at"`
}

type UserDetail struct {
ID string `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
Subscribers int `json:"subscribers" bson:"subscribers"`
Views int `json:"views" bson:"views"`
Uploads int `json:"uploads" bson:"uploads"`
Expand All @@ -21,6 +23,7 @@ type UserDetail struct {
type UserItem struct {
ID string `json:"-" bson:"_id"`
Username string `json:"username" bson:"username"`
Name string `json:"name" bson:"name"`
Subscribers int `json:"subscribers" bson:"subscribers"`
}

Expand Down
1 change: 1 addition & 0 deletions cast-be/handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func parseIDToken(idToken string) (user datatransfers.User, err error) {
user = datatransfers.User{
ID: claims["sub"].(string),
Username: claims["preferred_username"].(string),
Name: fmt.Sprintf("%s %s", claims["given_name"], claims["family_name"]),
CreatedAt: time.Now(),
}
return
Expand Down
2 changes: 1 addition & 1 deletion cast-be/handlers/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ type Handler interface {
VideoDetails(hash string) (video data.Video, err error)
CreateVOD(upload data.VideoUpload, controller beego.Controller, userID string) (ID primitive.ObjectID, err error)
DeleteVideo(ID primitive.ObjectID, userID string) (err error)
UpdateVideo(video data.VideoEdit, userID string) (err error)
UpdateVideo(video data.VideoEdit, controller beego.Controller, userID string) (err error)
CheckUniqueVideoTitle(title string) (err error)
LikeVideo(userID string, hash string, like bool) (err error)
Subscribe(userID string, username string, subscribe bool) (err error)
Expand Down
8 changes: 5 additions & 3 deletions cast-be/handlers/rtmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"errors"
"fmt"
"io"
"log"
"net/http"
"path"
"sync"
"time"

"github.com/nareix/joy4/av/avutil"
"github.com/nareix/joy4/av/pubsub"
"github.com/nareix/joy4/format/flv"
Expand Down Expand Up @@ -64,7 +65,8 @@ func (m *module) CreateRTMPUpLink() {
}
fmt.Printf("[RTMPUpLink] UpLink for %s connected\n", username)
m.BroadcastNotificationSubscriber(video.Author.ID, datatransfers.NotificationOutgoing{
Message: fmt.Sprintf("%s just went live! Watch now!", video.Author.Username),
Message: fmt.Sprintf("%s just went live! Watch now!", video.Author.Name),
Name: video.Author.Name,
Username: video.Author.Username,
Hash: video.Hash,
CreatedAt: time.Now(),
Expand All @@ -86,7 +88,7 @@ func (m *module) CreateRTMPUpLink() {
}
m.live.uplink.Addr = fmt.Sprintf(":%d", config.AppConfig.RTMPPort)
go m.live.uplink.ListenAndServe()
fmt.Printf("[CreateRTMPUpLink] RTMP UpLink Window opened at port %d\n", config.AppConfig.RTMPPort)
log.Printf("[CreateRTMPUpLink] RTMP UpLink Window opened at port %d\n", config.AppConfig.RTMPPort)
}

func (m *module) ControlUpLinkWindow(userID string, open bool) (err error) {
Expand Down
4 changes: 3 additions & 1 deletion cast-be/handlers/transcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ func (m *module) TranscodeListenerWorker() {
if resolution >= 1 {
m.PushNotification(video.Author.ID, datatransfers.NotificationOutgoing{
Message: fmt.Sprintf("%s is now ready in %s!", video.Title, constants.VideoResolutions[resolution]),
Name: video.Author.Name,
Username: video.Author.Username,
Hash: video.Hash,
CreatedAt: time.Now(),
})
}
if resolution == 1 {
m.BroadcastNotificationSubscriber(video.Author.ID, datatransfers.NotificationOutgoing{
Message: fmt.Sprintf("%s just uploaded %s! Watch now!", video.Author.Username, video.Title),
Message: fmt.Sprintf("%s just uploaded %s! Watch now!", video.Author.Name, video.Title),
Name: video.Author.Name,
Username: video.Author.Username,
Hash: video.Hash,
CreatedAt: time.Now(),
Expand Down
1 change: 1 addition & 0 deletions cast-be/handlers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func (m *module) UserDetails(userID string) (detail data.UserDetail, err error)
detail = data.UserDetail{
ID: user.ID,
Username: user.Username,
Name: user.Name,
Subscribers: subscriberCount,
Views: views,
Uploads: len(videos),
Expand Down
33 changes: 31 additions & 2 deletions cast-be/handlers/video.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"errors"
"fmt"
"mime/multipart"
"net/http"
"time"

"github.com/astaxie/beego"
Expand Down Expand Up @@ -66,13 +68,18 @@ func (m *module) SearchVideo(query string, _ []string, count, offset int) (video
}

func (m *module) VideoDetails(hash string) (video data.Video, err error) {
var author data.UserDetail
var comments []data.Comment
if video, err = m.db.videoOrm.GetOneByHash(hash); err != nil {
return data.Video{}, errors.New(fmt.Sprintf("[VideoDetails] video with hash %s not found. %+v", hash, err))
}
if author, err = m.UserDetails(video.Author.ID); err != nil {
return data.Video{}, errors.New(fmt.Sprintf("[VideoDetails] failed video author. %+v", err))
}
if comments, err = m.db.commentOrm.GetAllByHash(hash); err != nil {
return data.Video{}, errors.New(fmt.Sprintf("[VideoDetails] failed getting comment list for %s. %+v", hash, err))
}
video.Author.Subscribers = author.Subscribers
video.Views++
video.Comments = comments
if video.Type == constants.VideoTypeVOD {
Expand Down Expand Up @@ -165,7 +172,7 @@ func (m *module) DeleteVideo(ID primitive.ObjectID, userID string) (err error) {
return m.db.videoOrm.DeleteOneByID(ID)
}

func (m *module) UpdateVideo(video data.VideoEdit, userID string) (err error) {
func (m *module) UpdateVideo(video data.VideoEdit, controller beego.Controller, userID string) (err error) {
if err = m.db.videoOrm.EditVideo(data.VideoInsert{
Hash: video.Hash,
Title: video.Title,
Expand All @@ -175,6 +182,26 @@ func (m *module) UpdateVideo(video data.VideoEdit, userID string) (err error) {
}); err != nil {
return errors.New(fmt.Sprintf("[UpdateVideo] error updating video. %+v", err))
}
// Retrieve thumbnail
var thumbnail multipart.File
if thumbnail, _, err = controller.GetFile("thumbnail"); err!= nil {
if err == http.ErrMissingFile {
return nil
} else {
return fmt.Errorf("[UpdateVideo] Failed retrieving thumbnail image. %+v\n", err)
}
}
var result bytes.Buffer
if result, err = util.NormalizeImage(thumbnail, constants.ThumbnailWidth, constants.ThumbnailHeight); err != nil {
return fmt.Errorf("[UpdateVideo] Failed normalizing thumbnail image. %+v", err)
}
if _, err = m.s3.PutObject(&s3.PutObjectInput{
Bucket: aws.String(config.AppConfig.S3Bucket),
Key: aws.String(fmt.Sprintf("%s/%s.jpg", constants.ThumbnailRootDir, video.Hash)),
Body: bytes.NewReader(result.Bytes()),
}); err != nil {
return fmt.Errorf("[UpdateVideo] Failed saving thumbnail image. %+v", err)
}
return
}

Expand Down Expand Up @@ -225,7 +252,8 @@ func (m *module) Subscribe(userID string, username string, subscribe bool) (err
CreatedAt: time.Now(),
})
m.PushNotification(author.ID, data.NotificationOutgoing{
Message: fmt.Sprintf("%s just subscribed!", user.Username),
Message: fmt.Sprintf("%s just subscribed!", user.Name),
Name: user.Name,
Username: user.Username,
CreatedAt: time.Now(),
})
Expand Down Expand Up @@ -270,6 +298,7 @@ func (m *module) CommentVideo(userID string, hash, content string) (comment data
Content: content,
Author: data.UserItem{
Username: user.Username,
Name: user.Name,
},
CreatedAt: now,
}, nil
Expand Down
3 changes: 1 addition & 2 deletions cast-be/routers/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package routers

import (
"context"
"fmt"
"log"

"github.com/astaxie/beego"
Expand Down Expand Up @@ -80,7 +79,7 @@ func init() {

// Init Transcoder Listener
go h.TranscodeListenerWorker()
fmt.Printf("[Initialization] Initialization complete!\n")
log.Printf("[Initialization] Initialization complete!\n")

apiV1 := beego.NewNamespace("api/v1",
beego.NSNamespace("/ping",
Expand Down
2 changes: 1 addition & 1 deletion cast-fe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@daystram/ratify-client": "^1.3.0",
"@daystram/ratify-client": "^1.4.0",
"axios": ">=0.21.1",
"bootstrap": "^4.4.1",
"bs-custom-file-input": "^1.3.4",
Expand Down
2 changes: 1 addition & 1 deletion cast-fe/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/>
<link href="https://fonts.googleapis.com/css2?family=Comfortaa:wght@300;400;500;600;700&family=Open+Sans:ital,wght@0,300;0,400;1,300;1,600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script src="https://kit.fontawesome.com/a07fa447ca.js" crossorigin="anonymous"></script>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" />
<title>cast</title>
Expand Down
Loading

0 comments on commit f786f0b

Please sign in to comment.