Feat: lazy init movies

pull/21/head
zijiren233 2 years ago
parent e2a83a68c0
commit 854e78c1f4

@ -13,13 +13,10 @@ func CreateMovie(movie *model.Movie) error {
return db.Create(movie).Error
}
func GetAllMoviesByRoomID(roomID uint) ([]*model.Movie, error) {
func GetAllMoviesByRoomID(roomID uint) []*model.Movie {
movies := []*model.Movie{}
err := db.Where("room_id = ?", roomID).Order("position ASC").Find(&movies).Error
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
return movies, errors.New("room not found")
}
return movies, err
db.Where("room_id = ?", roomID).Order("position ASC").Find(&movies)
return movies
}
func DeleteMovieByID(roomID, id uint) error {

@ -2,180 +2,166 @@ package op
import (
"errors"
"net/url"
"sync"
"time"
"github.com/bluele/gcache"
"github.com/synctv-org/synctv/internal/db"
"github.com/go-resty/resty/v2"
"github.com/google/uuid"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/model"
"github.com/zijiren233/gencontainer/dllist"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/livelib/av"
"github.com/zijiren233/livelib/container/flv"
rtmpProto "github.com/zijiren233/livelib/protocol/rtmp"
"github.com/zijiren233/livelib/protocol/rtmp/core"
rtmps "github.com/zijiren233/livelib/server"
)
var movieCache gcache.Cache
func GetAllMoviesByRoomID(roomID uint) (*dllist.Dllist[*model.Movie], error) {
i, err := movieCache.Get(roomID)
if err == nil {
return i.(*dllist.Dllist[*model.Movie]), nil
}
m, err := db.GetAllMoviesByRoomID(roomID)
if err != nil {
return nil, err
}
d := dllist.New[*model.Movie]()
for i := range m {
d.PushBack(m[i])
}
return d, movieCache.SetWithExpire(roomID, d, time.Hour)
type movie struct {
*model.Movie
lock sync.RWMutex
channel *rtmps.Channel
}
func GetMoviesByRoomIDWithPage(roomID uint, page, max int) ([]*model.Movie, error) {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return nil, err
}
start := (page - 1) * max
if start >= ms.Len() {
start = ms.Len()
}
end := start + max
if end > ms.Len() {
end = ms.Len()
}
var m []*model.Movie = make([]*model.Movie, 0, end-start)
idx := 0
for i := ms.Front(); i != nil; i = i.Next() {
if idx >= start && idx < end {
m = append(m, i.Value)
}
idx++
}
return m, nil
func (m *movie) Channel() (*rtmps.Channel, error) {
m.lock.Lock()
defer m.lock.Unlock()
return m.channel, m.init()
}
func GetMovieByID(roomID, id uint) (*model.Movie, error) {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return nil, err
}
for i := ms.Front(); i != nil; i = i.Next() {
if i.Value.ID == id {
return i.Value, nil
func (m *movie) init() (err error) {
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")
}
}
return nil, errors.New("movie not found")
}
func GetMoviesCountByRoomID(roomID uint) (int, error) {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return 0, err
}
return ms.Len(), nil
}
func DeleteMovieByID(roomID, id uint) error {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return err
}
for i := ms.Front(); i != nil; i = i.Next() {
if i.Value.ID == id {
ms.Remove(i)
return db.DeleteMovieByID(roomID, id)
if m.PullKey == "" {
m.PullKey = uuid.NewString()
}
}
return errors.New("movie not found")
}
func UpdateMovie(movie *model.Movie) error {
err := db.UpdateMovie(movie)
if err != nil {
return err
}
m, err := GetMovieByID(movie.RoomID, movie.ID)
if err != nil {
return err
}
*m = *movie
return nil
}
func SaveMovie(movie *model.Movie) error {
err := db.SaveMovie(movie)
if err != nil {
return err
}
m, err := GetMovieByID(movie.RoomID, movie.ID)
if err != nil {
return err
}
*m = *movie
return nil
}
func DeleteMoviesByRoomID(roomID uint) error {
movieCache.Remove(roomID)
return db.DeleteMoviesByRoomID(roomID)
}
func LoadAndDeleteMovieByID(roomID, id uint) (*model.Movie, error) {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return nil, err
}
for i := ms.Front(); i != nil; i = i.Next() {
if i.Value.ID == id {
ms.Remove(i)
return db.LoadAndDeleteMovieByID(roomID, id)
if m.channel == nil {
m.channel = rtmps.NewChannel()
m.channel.InitHlsPlayer()
}
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":
m.PullKey = uuid.NewMD5(uuid.NameSpaceURL, []byte(m.Url)).String()
if m.channel == nil {
m.channel = rtmps.NewChannel()
m.channel.InitHlsPlayer()
go func() {
for {
if m.channel.Closed() {
return
}
cli := core.NewConnClient()
if err = cli.Start(m.Url, av.PLAY); err != nil {
cli.Close()
time.Sleep(time.Second)
continue
}
if err := m.channel.PushStart(rtmpProto.NewReader(cli)); err != nil {
cli.Close()
time.Sleep(time.Second)
}
}
}()
}
case "http", "https":
if m.Type != "flv" {
return errors.New("only flv is supported")
}
m.PullKey = uuid.NewMD5(uuid.NameSpaceURL, []byte(m.Url)).String()
if m.channel == nil {
m.channel = rtmps.NewChannel()
m.channel.InitHlsPlayer()
go func() {
for {
if m.channel.Closed() {
return
}
r := resty.New().R()
for k, v := range m.Headers {
r.SetHeader(k, v)
}
// r.SetHeader("User-Agent", UserAgent)
resp, err := r.Get(m.Url)
if err != nil {
time.Sleep(time.Second)
continue
}
if err := m.channel.PushStart(flv.NewReader(resp.RawBody())); err != nil {
time.Sleep(time.Second)
}
resp.RawBody().Close()
}
}()
}
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")
}
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")
}
m.PullKey = uuid.NewMD5(uuid.NameSpaceURL, []byte(m.Url)).String()
case !m.Live && !m.Proxy, m.Live && !m.Proxy && !m.RtmpSource:
u, err := url.Parse(m.Url)
if err != nil {
return err
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("unsupported scheme")
}
m.PullKey = ""
default:
return errors.New("unknown error")
}
return nil, errors.New("movie not found")
return nil
}
// data race
func CreateMovie(movie *model.Movie) error {
ms, err := GetAllMoviesByRoomID(movie.RoomID)
if err != nil {
return err
}
err = db.CreateMovie(movie)
if err != nil {
return err
}
ms.PushBack(movie)
return nil
func (m *movie) Terminate() {
m.lock.Lock()
defer m.lock.Unlock()
m.terminate()
}
func GetMovieWithPullKey(roomID uint, pullKey string) (*model.Movie, error) {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return nil, err
func (m *movie) terminate() {
if m.channel != nil {
m.channel.Close()
m.channel = nil
}
for i := ms.Front(); i != nil; i = i.Next() {
if i.Value.PullKey == pullKey {
return i.Value, nil
}
}
return nil, errors.New("movie not found")
}
func SwapMoviePositions(roomID uint, movie1ID uint, movie2ID uint) error {
ms, err := GetAllMoviesByRoomID(roomID)
if err != nil {
return err
}
var m1, m2 *model.Movie
for i := ms.Front(); i != nil; i = i.Next() {
if i.Value.ID == movie1ID {
m1 = i.Value
}
if i.Value.ID == movie2ID {
m2 = i.Value
}
}
if m1 == nil || m2 == nil {
return errors.New("movie not found")
}
m1.Position, m2.Position = m2.Position, m1.Position
return db.SwapMoviePositions(roomID, movie1ID, movie2ID)
func (m *movie) Update(movie model.BaseMovieInfo) error {
m.lock.Lock()
defer m.lock.Unlock()
m.terminate()
m.Movie.BaseMovieInfo = movie
return m.init()
}

@ -0,0 +1,220 @@
package op
import (
"errors"
"sync"
"time"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/gencontainer/dllist"
rtmps "github.com/zijiren233/livelib/server"
)
type movies struct {
roomID uint
lock sync.RWMutex
list dllist.Dllist[*movie]
once sync.Once
}
func (m *movies) init() {
m.once.Do(func() {
for _, m2 := range db.GetAllMoviesByRoomID(m.roomID) {
m.list.PushBack(&movie{
Movie: m2,
})
}
})
}
func (m *movies) Len() int {
m.lock.RLock()
defer m.lock.RUnlock()
m.init()
return m.list.Len()
}
func (m *movies) Add(mo *model.Movie) error {
m.lock.Lock()
defer m.lock.Unlock()
m.init()
mo.Position = uint(time.Now().UnixMilli())
movie := &movie{
Movie: mo,
}
// You need to init to get the pullKey first, and then create it into the database.
err := movie.init()
if err != nil {
return err
}
err = db.CreateMovie(mo)
if err != nil {
movie.terminate()
return err
}
m.list.PushBack(movie)
return nil
}
func (m *movies) GetChannel(channelName string) (*rtmps.Channel, error) {
if channelName == "" {
return nil, errors.New("channel name is nil")
}
m.lock.RLock()
defer m.lock.RUnlock()
m.init()
for e := m.list.Front(); e != nil; e = e.Next() {
if e.Value.PullKey == channelName {
return e.Value.Channel()
}
}
return nil, errors.New("channel not found")
}
func (m *movies) Update(movieId uint, movie model.BaseMovieInfo) error {
m.lock.Lock()
defer m.lock.Unlock()
m.init()
for e := m.list.Front(); e != nil; e = e.Next() {
if e.Value.ID == movieId {
err := e.Value.Update(movie)
if err != nil {
return err
}
return db.SaveMovie(e.Value.Movie)
}
}
return nil
}
func (m *movies) Clear() error {
m.lock.Lock()
defer m.lock.Unlock()
err := db.DeleteMoviesByRoomID(m.roomID)
if err != nil {
return err
}
for e := m.list.Front(); e != nil; e = e.Next() {
e.Value.Terminate()
}
m.list.Clear()
return nil
}
func (m *movies) Close() error {
m.lock.Lock()
defer m.lock.Unlock()
for e := m.list.Front(); e != nil; e = e.Next() {
e.Value.Terminate()
}
m.list.Clear()
return nil
}
func (m *movies) DeleteMovieByID(id uint) error {
m.lock.Lock()
defer m.lock.Unlock()
m.init()
err := db.DeleteMovieByID(m.roomID, id)
if err != nil {
return err
}
for e := m.list.Front(); e != nil; e = e.Next() {
if e.Value.ID == id {
m.list.Remove(e).Terminate()
return nil
}
}
return errors.New("movie not found")
}
func (m *movies) GetMovieByID(id uint) (*movie, error) {
m.lock.RLock()
defer m.lock.RUnlock()
return m.getMovieByID(id)
}
func (m *movies) getMovieByID(id uint) (*movie, error) {
m.init()
for e := m.list.Front(); e != nil; e = e.Next() {
if e.Value.ID == id {
return e.Value, nil
}
}
return nil, errors.New("movie not found")
}
func (m *movies) getMovieElementByID(id uint) (*dllist.Element[*movie], error) {
m.init()
for e := m.list.Front(); e != nil; e = e.Next() {
if e.Value.ID == id {
return e, nil
}
}
return nil, errors.New("movie not found")
}
func (m *movies) GetMovieWithPullKey(pullKey string) (*movie, error) {
m.lock.RLock()
defer m.lock.RUnlock()
m.init()
for e := m.list.Front(); e != nil; e = e.Next() {
if e.Value.PullKey == pullKey {
return e.Value, nil
}
}
return nil, errors.New("movie not found")
}
func (m *movies) SwapMoviePositions(id1, id2 uint) error {
m.lock.Lock()
defer m.lock.Unlock()
m.init()
err := db.SwapMoviePositions(m.roomID, id1, id2)
if err != nil {
return err
}
movie1, err := m.getMovieElementByID(id1)
if err != nil {
return err
}
movie2, err := m.getMovieElementByID(id2)
if err != nil {
return err
}
movie1.Value.Position, movie2.Value.Position = movie2.Value.Position, movie1.Value.Position
m.list.Swap(movie1, movie2)
return nil
}
func (m *movies) GetMoviesWithPage(page, pageSize int) []*movie {
m.lock.RLock()
defer m.lock.RUnlock()
m.init()
start, end := utils.GetPageItemsRange(m.list.Len(), page, pageSize)
ms := make([]*movie, 0, end-start)
i := 0
for e := m.list.Front(); e != nil; e = e.Next() {
if i >= start && i < end {
ms = append(ms, e.Value)
} else if i >= end {
return ms
}
i++
}
return ms
}

@ -9,9 +9,5 @@ func Init(size int) error {
LRU().
Build()
movieCache = gcache.New(size).
LRU().
Build()
return nil
}

@ -3,23 +3,12 @@ package op
import (
"errors"
"hash/crc32"
"net/url"
"sync/atomic"
"time"
"github.com/go-resty/resty/v2"
"github.com/google/uuid"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/gencontainer/rwmap"
"github.com/zijiren233/livelib/av"
"github.com/zijiren233/livelib/container/flv"
rtmpProto "github.com/zijiren233/livelib/protocol/rtmp"
"github.com/zijiren233/livelib/protocol/rtmp/core"
rtmps "github.com/zijiren233/livelib/server"
"github.com/zijiren233/stream"
"golang.org/x/crypto/bcrypt"
@ -31,28 +20,13 @@ type Room struct {
current *current
initOnce utils.Once
hub *Hub
channles rwmap.RWMap[string, *rtmps.Channel]
movies movies
}
func (r *Room) LazyInit() (err error) {
func (r *Room) lazyInitHub() {
r.initOnce.Do(func() {
r.hub = newHub(r.ID)
var ms []*model.Movie
ms, err = r.GetAllMovies()
if err != nil {
log.Errorf("failed to get movies: %s", err.Error())
return
}
for _, m := range ms {
if err = r.initMovie(m); err != nil {
log.Errorf("lazy init room %d movie %d failed: %s", r.ID, m.ID, err.Error())
DeleteMovieByID(r.ID, m.ID)
}
}
})
return
}
func (r *Room) ClientNum() int64 {
@ -70,26 +44,13 @@ func (r *Room) Broadcast(data Message, conf ...BroadcastConf) error {
}
func (r *Room) GetChannel(channelName string) (*rtmps.Channel, error) {
err := r.LazyInit()
if err != nil {
return nil, err
}
c, ok := r.channles.Load(channelName)
if !ok {
return nil, errors.New("channel not found")
}
return c, nil
return r.movies.GetChannel(channelName)
}
func (r *Room) close() {
if r.initOnce.Done() {
r.hub.Close()
r.channles.Range(func(_ string, c *rtmps.Channel) bool {
c.Close()
return true
})
r.movies.Close()
}
}
@ -102,180 +63,12 @@ func (r *Room) CheckVersion(version uint32) bool {
}
func (r *Room) UpdateMovie(movieId uint, movie model.BaseMovieInfo) error {
err := r.LazyInit()
if err != nil {
return err
}
m, err := GetMovieByID(r.ID, movieId)
if err != nil {
return err
}
err = r.terminateMovie(m)
if err != nil {
return err
}
m.MovieInfo.BaseMovieInfo = movie
err = r.initMovie(m)
if err != nil {
return err
}
return SaveMovie(m)
}
func (r *Room) terminateMovie(movie *model.Movie) error {
switch {
case movie.Live && movie.RtmpSource, movie.Live && movie.Proxy:
c, loaded := r.channles.LoadAndDelete(movie.PullKey)
if loaded {
return c.Close()
}
}
return nil
}
func (r *Room) initMovie(movie *model.Movie) error {
switch {
case movie.RtmpSource && movie.Proxy:
return errors.New("rtmp source and proxy can't be true at the same time")
case movie.Live && movie.RtmpSource:
if !conf.Conf.Rtmp.Enable {
return errors.New("rtmp is not enabled")
}
if movie.PullKey == "" {
movie.PullKey = uuid.NewString()
}
c, loaded := r.channles.LoadOrStore(movie.PullKey, rtmps.NewChannel())
if loaded {
return errors.New("pull key already exists")
}
c.InitHlsPlayer()
case movie.Live && movie.Proxy:
if !conf.Conf.Proxy.LiveProxy {
return errors.New("live proxy is not enabled")
}
u, err := url.Parse(movie.Url)
if err != nil {
return err
}
if utils.IsLocalIP(u.Host) {
return errors.New("local ip is not allowed")
}
switch u.Scheme {
case "rtmp":
movie.PullKey = uuid.NewMD5(uuid.NameSpaceURL, []byte(movie.Url)).String()
c, loaded := r.channles.LoadOrStore(movie.PullKey, rtmps.NewChannel())
if loaded {
return errors.New("pull key already exists")
}
c.InitHlsPlayer()
go func() {
for {
if c.Closed() {
return
}
cli := core.NewConnClient()
if err = cli.Start(movie.Url, av.PLAY); err != nil {
cli.Close()
time.Sleep(time.Second)
continue
}
if err := c.PushStart(rtmpProto.NewReader(cli)); err != nil {
cli.Close()
time.Sleep(time.Second)
}
}
}()
case "http", "https":
if movie.Type != "flv" {
return errors.New("only flv is supported")
}
movie.PullKey = uuid.NewMD5(uuid.NameSpaceURL, []byte(movie.Url)).String()
c, loaded := r.channles.LoadOrStore(movie.PullKey, rtmps.NewChannel())
if loaded {
return errors.New("pull key already exists")
}
c.InitHlsPlayer()
go func() {
for {
if c.Closed() {
return
}
r := resty.New().R()
for k, v := range movie.Headers {
r.SetHeader(k, v)
}
// r.SetHeader("User-Agent", UserAgent)
resp, err := r.Get(movie.Url)
if err != nil {
time.Sleep(time.Second)
continue
}
if err := c.PushStart(flv.NewReader(resp.RawBody())); err != nil {
time.Sleep(time.Second)
}
resp.RawBody().Close()
}
}()
default:
return errors.New("unsupported scheme")
}
case !movie.Live && movie.RtmpSource:
return errors.New("rtmp source can't be true when movie is not live")
case !movie.Live && movie.Proxy:
if !conf.Conf.Proxy.MovieProxy {
return errors.New("movie proxy is not enabled")
}
u, err := url.Parse(movie.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")
}
movie.PullKey = uuid.NewMD5(uuid.NameSpaceURL, []byte(movie.Url)).String()
case !movie.Live && !movie.Proxy, movie.Live && !movie.Proxy && !movie.RtmpSource:
u, err := url.Parse(movie.Url)
if err != nil {
return err
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("unsupported scheme")
}
movie.PullKey = ""
default:
return errors.New("unknown error")
}
return nil
return r.movies.Update(movieId, movie)
}
func (r *Room) AddMovie(m model.Movie) error {
err := r.LazyInit()
if err != nil {
return err
}
m.Position = uint(time.Now().UnixMilli())
m.RoomID = r.ID
err = r.initMovie(&m)
if err != nil {
return err
}
err = CreateMovie(&m)
if err != nil {
r.terminateMovie(&m)
}
return err
return r.movies.Add(&m)
}
func (r *Room) HasPermission(user *model.User, permission model.Permission) bool {
@ -327,49 +120,20 @@ func (r *Room) DeleteUserPermission(userID uint) error {
return db.DeleteUserPermission(r.ID, userID)
}
func (r *Room) GetMoviesCount() (int, error) {
return GetMoviesCountByRoomID(r.ID)
}
func (r *Room) GetAllMovies() ([]*model.Movie, error) {
ms, err := GetAllMoviesByRoomID(r.ID)
if err != nil {
return nil, err
}
var m []*model.Movie = make([]*model.Movie, 0, ms.Len())
for i := ms.Front(); i != nil; i = i.Next() {
m = append(m, i.Value)
}
return m, nil
}
func (r *Room) GetMoviesByRoomIDWithPage(page, pageSize int) ([]*model.Movie, error) {
return GetMoviesByRoomIDWithPage(r.ID, page, pageSize)
}
func (r *Room) GetMovieByID(id uint) (*model.Movie, error) {
return GetMovieByID(r.ID, id)
func (r *Room) GetMoviesCount() int {
return r.movies.Len()
}
func (r *Room) DeleteMovieByID(id uint) error {
r.LazyInit()
m, err := LoadAndDeleteMovieByID(r.ID, id)
if err != nil {
return err
}
return r.terminateMovie(m)
return r.movies.DeleteMovieByID(id)
}
func (r *Room) ClearMovies() error {
r.LazyInit()
ms, err := db.LoadAndDeleteMoviesByRoomID(r.ID)
if err != nil {
return err
}
for _, m := range ms {
r.terminateMovie(m)
}
return nil
return r.movies.Clear()
}
func (r *Room) GetMovieByID(id uint) (*movie, error) {
return r.movies.GetMovieByID(id)
}
func (r *Room) Current() *Current {
@ -378,31 +142,33 @@ func (r *Room) Current() *Current {
}
func (r *Room) ChangeCurrentMovie(id uint) error {
r.LazyInit()
m, err := GetMovieByID(r.ID, id)
m, err := r.movies.GetMovieByID(id)
if err != nil {
return err
}
r.current.SetMovie(*m)
r.current.SetMovie(*m.Movie)
return nil
}
func (r *Room) SwapMoviePositions(id1, id2 uint) error {
r.LazyInit()
return SwapMoviePositions(r.ID, id1, id2)
return r.movies.SwapMoviePositions(id1, id2)
}
func (r *Room) GetMovieWithPullKey(pullKey string) (*movie, error) {
return r.movies.GetMovieWithPullKey(pullKey)
}
func (r *Room) GetMovieWithPullKey(pullKey string) (*model.Movie, error) {
return GetMovieWithPullKey(r.ID, pullKey)
func (r *Room) GetMoviesWithPage(page, pageSize int) []*movie {
return r.movies.GetMoviesWithPage(page, pageSize)
}
func (r *Room) RegClient(user *User, conn *websocket.Conn) (*Client, error) {
r.LazyInit()
r.lazyInitHub()
return r.hub.RegClient(newClient(user, r, conn))
}
func (r *Room) UnregisterClient(user *User) error {
r.LazyInit()
r.lazyInitHub()
return r.hub.UnRegClient(user)
}

@ -24,6 +24,9 @@ func InitRoom(room *model.Room) (*Room, error) {
Room: *room,
version: crc32.ChecksumIEEE(room.HashedPassword),
current: newCurrent(),
movies: movies{
roomID: room.ID,
},
}
r, loaded := roomCache.LoadOrStore(room.ID, r)
if loaded {
@ -32,14 +35,15 @@ func InitRoom(room *model.Room) (*Room, error) {
return r, nil
}
func LoadOrInitRoom(room *model.Room) (r *Room, loaded bool) {
r = &Room{
func LoadOrInitRoom(room *model.Room) (*Room, bool) {
return roomCache.LoadOrStore(room.ID, &Room{
Room: *room,
version: crc32.ChecksumIEEE(room.HashedPassword),
current: newCurrent(),
}
r, loaded = roomCache.LoadOrStore(room.ID, r)
return
movies: movies{
roomID: room.ID,
},
})
}
func DeleteRoom(room *Room) error {

@ -55,11 +55,7 @@ func MovieList(ctx *gin.Context) {
return
}
m, err := room.GetMoviesByRoomIDWithPage(int(page), int(max))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
m := room.GetMoviesWithPage(page, max)
mresp := make([]model.MoviesResp, len(m))
for i, v := range m {
@ -71,15 +67,9 @@ func MovieList(ctx *gin.Context) {
}
}
i, err := room.GetMoviesCount()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"current": room.Current(),
"total": i,
"total": room.GetMoviesCount(),
"movies": mresp,
}))
}
@ -103,11 +93,7 @@ func Movies(ctx *gin.Context) {
return
}
m, err := room.GetMoviesByRoomIDWithPage(int(page), int(max))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
m := room.GetMoviesWithPage(int(page), int(max))
mresp := make([]model.MoviesResp, len(m))
for i, v := range m {
@ -119,14 +105,8 @@ func Movies(ctx *gin.Context) {
}
}
i, err := room.GetMoviesCount()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"total": i,
"total": room.GetMoviesCount(),
"movies": mresp,
}))
}
@ -173,7 +153,6 @@ func NewPublishKey(ctx *gin.Context) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
movie, err := room.GetMovieByID(req.Id)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))

@ -35,19 +35,23 @@ func RandBytes(n int) []byte {
}
func GetPageItems[T any](items []T, page, pageSize int) []T {
start, end := GetPageItemsRange(len(items), page, pageSize)
return items[start:end]
}
func GetPageItemsRange(total, page, pageSize int) (start, end int) {
if pageSize <= 0 || page <= 0 {
return nil
return 0, 0
}
start := (page - 1) * pageSize
l := len(items)
if start > l {
start = l
start = (page - 1) * pageSize
if start > total {
start = total
}
end := page * pageSize
if end > l {
end = l
end = page * pageSize
if end > total {
end = total
}
return items[start:end]
return
}
func Index[T comparable](items []T, item T) int {

Loading…
Cancel
Save