Feat: push movies

pull/31/head
zijiren233 1 year ago
parent bf6f5418bf
commit 16acae8bf5

@ -1,9 +1,14 @@
package model
import (
"errors"
"fmt"
"net/url"
"time"
"github.com/google/uuid"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/utils"
"gorm.io/gorm"
)
@ -24,6 +29,10 @@ func (m *Movie) BeforeCreate(tx *gorm.DB) error {
return nil
}
func (m *Movie) Validate() error {
return m.Base.Validate()
}
type BaseMovie struct {
Url string `json:"url"`
Name string `gorm:"not null" json:"name"`
@ -35,6 +44,105 @@ type BaseMovie struct {
VendorInfo `gorm:"embedded;embeddedPrefix:vendor_info_" json:"vendorInfo,omitempty"`
}
func (m *BaseMovie) Validate() error {
if m.VendorInfo.Vendor != "" {
err := m.validateVendorMovie()
if err != nil {
return err
}
}
switch {
case m.RtmpSource && m.Proxy:
return errors.New("rtmp source and proxy can't be true at the same time")
case m.Live && m.RtmpSource:
if !conf.Conf.Rtmp.Enable {
return errors.New("rtmp is not enabled")
}
case m.Live && m.Proxy:
if !conf.Conf.Proxy.LiveProxy {
return errors.New("live proxy is not enabled")
}
u, err := url.Parse(m.Url)
if err != nil {
return err
}
if utils.IsLocalIP(u.Host) {
return errors.New("local ip is not allowed")
}
switch u.Scheme {
case "rtmp":
case "http", "https":
default:
return errors.New("unsupported scheme")
}
case !m.Live && m.RtmpSource:
return errors.New("rtmp source can't be true when movie is not live")
case !m.Live && m.Proxy:
if !conf.Conf.Proxy.MovieProxy {
return errors.New("movie proxy is not enabled")
}
if m.VendorInfo.Vendor != "" {
return nil
}
u, err := url.Parse(m.Url)
if err != nil {
return err
}
if utils.IsLocalIP(u.Host) {
return errors.New("local ip is not allowed")
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("unsupported scheme")
}
case !m.Live && !m.Proxy, m.Live && !m.Proxy && !m.RtmpSource:
if m.VendorInfo.Vendor == "" {
u, err := url.Parse(m.Url)
if err != nil {
return err
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("unsupported scheme")
}
}
default:
return errors.New("unknown error")
}
return nil
}
func (m *BaseMovie) validateVendorMovie() error {
switch m.VendorInfo.Vendor {
case StreamingVendorBilibili:
info := m.VendorInfo.Bilibili
if info.Bvid == "" && info.Epid == 0 {
return fmt.Errorf("bvid and epid are empty")
}
if info.Bvid != "" && info.Epid != 0 {
return fmt.Errorf("bvid and epid can't be set at the same time")
}
if info.Bvid != "" && info.Cid == 0 {
return fmt.Errorf("cid is empty")
}
if m.Headers == nil {
m.Headers = map[string]string{
"Referer": "https://www.bilibili.com",
"User-Agent": utils.UA,
}
} else {
m.Headers["Referer"] = "https://www.bilibili.com"
m.Headers["User-Agent"] = utils.UA
}
default:
return fmt.Errorf("vendor not support")
}
return nil
}
type VendorInfo struct {
Vendor StreamingVendor `json:"vendor"`
Shared bool `gorm:"not null;default:false" json:"shared"`

@ -2,15 +2,12 @@ package op
import (
"errors"
"fmt"
"net/url"
"sync"
"time"
"github.com/go-resty/resty/v2"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/livelib/av"
"github.com/zijiren233/livelib/container/flv"
rtmpProto "github.com/zijiren233/livelib/protocol/rtmp"
@ -31,32 +28,20 @@ func (m *movie) Channel() (*rtmps.Channel, error) {
}
func (m *movie) init() (err error) {
err = m.validateVendorMovie()
if err != nil {
if err = m.Movie.Validate(); err != nil {
return
}
switch {
case m.Base.RtmpSource && m.Base.Proxy:
return errors.New("rtmp source and proxy can't be true at the same time")
case m.Base.Live && m.Base.RtmpSource:
if !conf.Conf.Rtmp.Enable {
return errors.New("rtmp is not enabled")
}
if m.channel == nil {
m.channel = rtmps.NewChannel()
m.channel.InitHlsPlayer()
}
case m.Base.Live && m.Base.Proxy:
if !conf.Conf.Proxy.LiveProxy {
return errors.New("live proxy is not enabled")
}
u, err := url.Parse(m.Base.Url)
if err != nil {
return err
}
if utils.IsLocalIP(u.Host) {
return errors.New("local ip is not allowed")
}
switch u.Scheme {
case "rtmp":
if m.channel == nil {
@ -81,9 +66,6 @@ func (m *movie) init() (err error) {
}()
}
case "http", "https":
if m.Base.Type != "flv" {
return errors.New("only flv is supported")
}
if m.channel == nil {
m.channel = rtmps.NewChannel()
m.channel.InitHlsPlayer()
@ -112,77 +94,10 @@ func (m *movie) init() (err error) {
default:
return errors.New("unsupported scheme")
}
case !m.Base.Live && m.Base.RtmpSource:
return errors.New("rtmp source can't be true when movie is not live")
case !m.Base.Live && m.Base.Proxy:
if !conf.Conf.Proxy.MovieProxy {
return errors.New("movie proxy is not enabled")
}
if m.Base.VendorInfo.Vendor != "" {
return nil
}
u, err := url.Parse(m.Base.Url)
if err != nil {
return err
}
if utils.IsLocalIP(u.Host) {
return errors.New("local ip is not allowed")
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("unsupported scheme")
}
case !m.Base.Live && !m.Base.Proxy, m.Base.Live && !m.Base.Proxy && !m.Base.RtmpSource:
if m.Base.VendorInfo.Vendor == "" {
u, err := url.Parse(m.Base.Url)
if err != nil {
return err
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("unsupported scheme")
}
}
default:
return errors.New("unknown error")
}
return nil
}
func (m *movie) validateVendorMovie() error {
if m.Base.VendorInfo.Vendor == "" {
return nil
}
switch m.Base.VendorInfo.Vendor {
case model.StreamingVendorBilibili:
info := m.Base.VendorInfo.Bilibili
if info.Bvid == "" && info.Epid == 0 {
return fmt.Errorf("bvid and epid are empty")
}
if info.Bvid != "" && info.Epid != 0 {
return fmt.Errorf("bvid and epid can't be set at the same time")
}
if info.Bvid != "" && info.Cid == 0 {
return fmt.Errorf("cid is empty")
}
if m.Base.Headers == nil {
m.Base.Headers = map[string]string{
"Referer": "https://www.bilibili.com",
"User-Agent": utils.UA,
}
} else {
m.Base.Headers["Referer"] = "https://www.bilibili.com"
m.Base.Headers["User-Agent"] = utils.UA
}
default:
return fmt.Errorf("vendor not support")
}
return nil
}
func (m *movie) Terminate() {
m.lock.Lock()
defer m.lock.Unlock()

@ -73,9 +73,9 @@ func (r *Room) UpdateMovie(movieId string, movie model.BaseMovie) error {
return r.movies.Update(movieId, movie)
}
func (r *Room) AddMovie(m model.Movie) error {
func (r *Room) AddMovie(m *model.Movie) error {
m.RoomID = r.ID
return r.movies.Add(&m)
return r.movies.Add(m)
}
func (r *Room) HasPermission(userID string, permission model.Permission) bool {

@ -24,13 +24,21 @@ func (u *User) CreateRoom(name, password string, conf ...db.CreateRoomConfig) (*
return db.CreateRoom(name, password, append(conf, db.WithCreator(&u.User))...)
}
func (u *User) NewMovie(movie model.BaseMovie) model.Movie {
return model.Movie{
Base: movie,
func (u *User) NewMovie(movie *model.BaseMovie) *model.Movie {
return &model.Movie{
Base: *movie,
CreatorID: u.ID,
}
}
func (u *User) NewMovies(movies []*model.BaseMovie) []*model.Movie {
var ms = make([]*model.Movie, 0, len(movies))
for i, m := range movies {
ms[i] = u.NewMovie(m)
}
return ms
}
func (u *User) IsRoot() bool {
return u.Role == model.RoleRoot
}

@ -107,6 +107,8 @@ func Init(e *gin.Engine) {
needAuthMovie.POST("/push", PushMovie)
needAuthMovie.POST("/pushs", PushMovies)
needAuthMovie.POST("/edit", EditMovie)
needAuthMovie.POST("/swap", SwapMovie)

@ -166,7 +166,7 @@ func PushMovie(ctx *gin.Context) {
return
}
mi := user.NewMovie(dbModel.BaseMovie(req))
mi := user.NewMovie((*dbModel.BaseMovie)(&req))
err := room.AddMovie(mi)
if err != nil {
@ -187,6 +187,49 @@ func PushMovie(ctx *gin.Context) {
ctx.Status(http.StatusNoContent)
}
func PushMovies(ctx *gin.Context) {
room := ctx.MustGet("room").(*op.Room)
user := ctx.MustGet("user").(*op.User)
req := model.PushMoviesReq{}
if err := model.Decode(ctx, &req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
var ms []*dbModel.Movie = make([]*dbModel.Movie, len(req))
for i, v := range req {
m := (*dbModel.BaseMovie)(v)
err := m.Validate()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ms[i] = user.NewMovie(m)
}
for _, m := range ms {
err := room.AddMovie(m)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.Status(http.StatusNoContent)
}
func NewPublishKey(ctx *gin.Context) {
room := ctx.MustGet("room").(*op.Room)
user := ctx.MustGet("user").(*op.User)

@ -44,6 +44,21 @@ func (p *PushMovieReq) Validate() error {
return nil
}
type PushMoviesReq []*PushMovieReq
func (p *PushMoviesReq) Decode(ctx *gin.Context) error {
return json.NewDecoder(ctx.Request.Body).Decode(p)
}
func (p *PushMoviesReq) Validate() error {
for _, v := range *p {
if err := v.Validate(); err != nil {
return err
}
}
return nil
}
type IdReq struct {
Id string `json:"id"`
}

Loading…
Cancel
Save