mirror of https://github.com/synctv-org/synctv
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
348 lines
6.9 KiB
Go
348 lines
6.9 KiB
Go
package room
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/zijiren233/gencontainer/dllist"
|
|
rtmps "github.com/zijiren233/livelib/server"
|
|
)
|
|
|
|
type FormatErrMovieNotFound uint64
|
|
|
|
func (e FormatErrMovieNotFound) Error() string {
|
|
return fmt.Sprintf("movie id: %v not found", uint64(e))
|
|
}
|
|
|
|
type FormatErrMovieAlreadyExist uint64
|
|
|
|
func (e FormatErrMovieAlreadyExist) Error() string {
|
|
return fmt.Sprintf("movie id: %v already exist", uint64(e))
|
|
}
|
|
|
|
type movies struct {
|
|
l *dllist.Dllist[*Movie]
|
|
lock *sync.RWMutex
|
|
}
|
|
|
|
// Url will be `PullKey` when Live and Proxy are true
|
|
type Movie struct {
|
|
BaseMovie
|
|
PullKey string `json:"pullKey"`
|
|
CreateAt int64 `json:"createAt"`
|
|
LastEditAt int64 `json:"lastEditAt"`
|
|
|
|
id uint64
|
|
channel *rtmps.Channel
|
|
creator *User
|
|
}
|
|
|
|
type BaseMovie struct {
|
|
Url string `json:"url"`
|
|
Name string `json:"name"`
|
|
Live bool `json:"live"`
|
|
Proxy bool `json:"proxy"`
|
|
RtmpSource bool `json:"rtmpSource"`
|
|
Type string `json:"type"`
|
|
Headers map[string][]string `json:"headers"`
|
|
}
|
|
|
|
type MovieConf func(m *Movie)
|
|
|
|
func WithPullKey(PullKey string) MovieConf {
|
|
return func(m *Movie) {
|
|
m.PullKey = PullKey
|
|
}
|
|
}
|
|
|
|
func WithChannel(channel *rtmps.Channel) MovieConf {
|
|
return func(m *Movie) {
|
|
m.channel = channel
|
|
}
|
|
}
|
|
|
|
func WithCreator(creator *User) MovieConf {
|
|
return func(m *Movie) {
|
|
m.creator = creator
|
|
}
|
|
}
|
|
|
|
func NewMovie(id uint64, url, name, type_ string, live, proxy, rtmpSource bool, headers map[string][]string, conf ...MovieConf) (*Movie, error) {
|
|
return NewMovieWithBaseMovie(id, BaseMovie{
|
|
Url: url,
|
|
Name: name,
|
|
Live: live,
|
|
Proxy: proxy,
|
|
RtmpSource: rtmpSource,
|
|
Type: type_,
|
|
Headers: headers,
|
|
})
|
|
}
|
|
|
|
func NewMovieWithBaseMovie(id uint64, baseMovie BaseMovie, conf ...MovieConf) (*Movie, error) {
|
|
now := time.Now().UnixMicro()
|
|
m := &Movie{
|
|
id: id,
|
|
BaseMovie: baseMovie,
|
|
CreateAt: now,
|
|
LastEditAt: now,
|
|
}
|
|
m.Init(conf...)
|
|
return m, m.Check()
|
|
}
|
|
|
|
func (m *Movie) Check() error {
|
|
_, err := url.Parse(m.Url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *Movie) Id() uint64 {
|
|
return m.id
|
|
}
|
|
|
|
func (m *Movie) Init(conf ...MovieConf) {
|
|
for _, c := range conf {
|
|
c(m)
|
|
}
|
|
}
|
|
|
|
func (m *Movie) Creator() *User {
|
|
return m.creator
|
|
}
|
|
|
|
func (m *Movie) SetCreator(creator *User) {
|
|
m.creator = creator
|
|
}
|
|
|
|
func (m *Movie) Channel() *rtmps.Channel {
|
|
return m.channel
|
|
}
|
|
|
|
func (m *Movie) SetChannel(channel *rtmps.Channel) {
|
|
m.channel = channel
|
|
}
|
|
|
|
func newMovies() *movies {
|
|
return &movies{l: dllist.New[*Movie](), lock: &sync.RWMutex{}}
|
|
}
|
|
|
|
func (m *movies) Range(f func(e *dllist.Element[*Movie]) bool) (interrupt bool) {
|
|
m.lock.RLock()
|
|
defer m.lock.RUnlock()
|
|
|
|
interrupt = false
|
|
for e := m.l.Front(); !interrupt && e != nil; e = e.Next() {
|
|
interrupt = !f(e)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (m *movies) range_(f func(e *dllist.Element[*Movie]) bool) (interrupt bool) {
|
|
interrupt = false
|
|
for e := m.l.Front(); !interrupt && e != nil; e = e.Next() {
|
|
interrupt = !f(e)
|
|
}
|
|
return
|
|
}
|
|
|
|
type MovieInfo struct {
|
|
Id uint64 `json:"id"`
|
|
Url string `json:"url"`
|
|
Name string `json:"name"`
|
|
Live bool `json:"live"`
|
|
Proxy bool `json:"proxy"`
|
|
RtmpSource bool `json:"rtmpSource"`
|
|
Type string `json:"type"`
|
|
Headers map[string][]string `json:"headers"`
|
|
PullKey string `json:"pullKey"`
|
|
CreateAt int64 `json:"createAt"`
|
|
LastEditAt int64 `json:"lastEditAt"`
|
|
Creator string `json:"creator"`
|
|
}
|
|
|
|
func (m *movies) MovieList() (movies []MovieInfo) {
|
|
m.lock.RLock()
|
|
defer m.lock.RUnlock()
|
|
|
|
movies = make([]MovieInfo, 0, m.l.Len())
|
|
m.range_(func(e *dllist.Element[*Movie]) bool {
|
|
movies = append(movies, MovieInfo{
|
|
Id: e.Value.id,
|
|
Url: e.Value.Url,
|
|
Name: e.Value.Name,
|
|
Live: e.Value.Live,
|
|
Proxy: e.Value.Proxy,
|
|
RtmpSource: e.Value.RtmpSource,
|
|
Type: e.Value.Type,
|
|
Headers: e.Value.Headers,
|
|
CreateAt: e.Value.CreateAt,
|
|
LastEditAt: e.Value.LastEditAt,
|
|
Creator: e.Value.Creator().Name(),
|
|
})
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
func (m *movies) Clear() error {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
m.l.Clear()
|
|
return nil
|
|
}
|
|
|
|
func (m *movies) GetAndClear() (movies []*Movie) {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
movies = make([]*Movie, 0, m.l.Len())
|
|
m.range_(func(e *dllist.Element[*Movie]) bool {
|
|
movies = append(movies, e.Value)
|
|
return true
|
|
})
|
|
m.l.Clear()
|
|
return
|
|
}
|
|
|
|
func (m *movies) HasMovie(id uint64) bool {
|
|
m.lock.RLock()
|
|
defer m.lock.RUnlock()
|
|
|
|
return m.hasMovie(id)
|
|
}
|
|
|
|
func (m *movies) hasMovie(id uint64) bool {
|
|
return m.range_(func(e *dllist.Element[*Movie]) bool {
|
|
return e.Value.id != id
|
|
})
|
|
}
|
|
|
|
func (m *movies) GetMovie(id uint64) (movie *Movie, err error) {
|
|
m.lock.RLock()
|
|
defer m.lock.RUnlock()
|
|
|
|
e, err := m.getMovie(id)
|
|
if err != nil {
|
|
return nil, FormatErrMovieNotFound(id)
|
|
}
|
|
return e.Value, nil
|
|
}
|
|
|
|
func (m *movies) GetMovieWithPullKey(pullKey string) (movie *Movie, err error) {
|
|
if pullKey == "" {
|
|
return nil, fmt.Errorf("pullKey is empty")
|
|
}
|
|
|
|
m.lock.RLock()
|
|
defer m.lock.RUnlock()
|
|
|
|
m.range_(func(e *dllist.Element[*Movie]) bool {
|
|
if e.Value.PullKey == pullKey {
|
|
movie = e.Value
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
if movie == nil {
|
|
return nil, fmt.Errorf("pullKey: %v not found", pullKey)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (m *movies) GetAndDelMovie(id ...uint64) (movies []*Movie, err error) {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
es := make([]*dllist.Element[*Movie], 0, len(id))
|
|
|
|
for _, v := range id {
|
|
e, err := m.getMovie(v)
|
|
if err != nil {
|
|
return nil, FormatErrMovieNotFound(v)
|
|
}
|
|
es = append(es, e)
|
|
}
|
|
|
|
movies = make([]*Movie, 0, len(es))
|
|
for _, e := range es {
|
|
movies = append(movies, e.Value)
|
|
e.Remove()
|
|
}
|
|
return movies, nil
|
|
}
|
|
|
|
func (m *movies) getMovie(id uint64) (element *dllist.Element[*Movie], err error) {
|
|
err = FormatErrMovieNotFound(id)
|
|
m.range_(func(e *dllist.Element[*Movie]) bool {
|
|
if e.Value.id == id {
|
|
element = e
|
|
err = nil
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
func (m *movies) PushBackMovie(movie *Movie) error {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
if m.hasMovie(movie.id) {
|
|
return FormatErrMovieAlreadyExist(movie.id)
|
|
}
|
|
|
|
m.l.PushBack(movie)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *movies) PushFrontMovie(movie *Movie) error {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
if m.hasMovie(movie.id) {
|
|
return FormatErrMovieAlreadyExist(movie.id)
|
|
}
|
|
|
|
m.l.PushFront(movie)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *movies) DelMovie(id uint64) error {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
e, err := m.getMovie(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.Remove()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *movies) SwapMovie(id1, id2 uint64) error {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
e1, err := m.getMovie(id1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e2, err := m.getMovie(id2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.l.Swap(e1, e2)
|
|
return nil
|
|
}
|