@ -12,6 +12,7 @@ import (
"math/rand"
"net/http"
"net/url"
"path"
"path/filepath"
"strconv"
"strings"
@ -21,12 +22,16 @@ import (
"github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/cache"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/db"
dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/rtmp"
"github.com/synctv-org/synctv/internal/settings"
"github.com/synctv-org/synctv/internal/vendor"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
"github.com/synctv-org/vendors/api/alist"
"github.com/synctv-org/vendors/api/emby"
uhc "github.com/zijiren233/go-uhc"
"github.com/zijiren233/livelib/protocol/hls"
"github.com/zijiren233/livelib/protocol/httpflv"
@ -87,59 +92,65 @@ func GetPageItems[T any](ctx *gin.Context, items []T) ([]T, error) {
func genMovieInfo (
ctx context . Context ,
user * op . User ,
room * op . Room ,
opMovie * op . Movie ,
userAgent string ,
userAgent ,
userToken string ,
) ( * model . Movie Resp , error ) {
) ( * model . Movie , error ) {
if opMovie == nil || opMovie . ID == "" {
return & model . MovieResp { } , nil
return & model . Movie { } , nil
}
if opMovie . IsFolder {
if ! opMovie . IsDynamicFolder ( ) {
return nil , errors . New ( "movie is static folder, can't get movie info" )
}
}
var movie = * opMovie . Movie
if movie . Base . VendorInfo . Vendor != "" {
if movie . Movie Base. VendorInfo . Vendor != "" {
vendorMovie , err := genVendorMovie ( ctx , user , opMovie , userAgent , userToken )
if err != nil {
return nil , err
}
movie = * vendorMovie
} else if movie . Base. RtmpSource || movie . Base. Live && movie . Base. Proxy {
switch movie . Base. Type {
} else if movie . Movie Base. RtmpSource || movie . Movie Base. Live && movie . Movie Base. Proxy {
switch movie . Movie Base. Type {
case "m3u8" :
movie . Base. Url = fmt . Sprintf ( "/api/movie/live/hls/list/%s.m3u8?token=%s" , movie . ID , userToken )
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/live/hls/list/%s.m3u8?token=%s" , movie . ID , userToken )
case "flv" :
movie . Base. Url = fmt . Sprintf ( "/api/movie/live/flv/%s.flv?token=%s" , movie . ID , userToken )
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/live/flv/%s.flv?token=%s" , movie . ID , userToken )
default :
return nil , errors . New ( "not support live movie type" )
}
movie . Base. Headers = nil
} else if movie . Base. Proxy {
movie . Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Base. Headers = nil
movie . Movie Base. Headers = nil
} else if movie . Movie Base. Proxy {
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Movie Base. Headers = nil
}
if movie . Base. Type == "" && movie . Base. Url != "" {
movie . Base. Type = utils . GetUrlExtension ( movie . Base. Url )
if movie . Movie Base. Type == "" && movie . Movie Base. Url != "" {
movie . Movie Base. Type = utils . GetUrlExtension ( movie . Movie Base. Url )
}
resp := & model . Movie Resp {
resp := & model . Movie {
Id : movie . ID ,
CreatedAt : movie . CreatedAt . UnixMilli ( ) ,
Base : movie . Base,
Base : movie . Movie Base,
Creator : op . GetUserName ( movie . CreatorID ) ,
CreatorId : movie . CreatorID ,
SubPath : opMovie . SubPath ( ) ,
}
return resp , nil
}
func genCurrentRespWithCurrent ( ctx context . Context , user * op . User , room * op . Room , current * op . Current , userAgent string , userToken string ) ( * model . CurrentMovieResp , error ) {
if current . MovieID == "" {
func genCurrentRespWithCurrent ( ctx context . Context , user * op . User , room * op . Room , userAgent string , userToken string ) ( * model . CurrentMovieResp , error ) {
current := room . Current ( )
if current . Movie . ID == "" {
return & model . CurrentMovieResp {
Movie : & model . Movie Resp { } ,
Movie : & model . Movie { } ,
} , nil
}
opMovie , err := room . GetMovieByID ( current . Movie ID)
opMovie , err := room . GetMovieByID ( current . Movie . ID)
if err != nil {
return nil , fmt . Errorf ( "get current movie error: %w" , err )
}
mr , err := genMovieInfo ( ctx , user , room, opMovie, userAgent , userToken )
mr , err := genMovieInfo ( ctx , user , opMovie, userAgent , userToken )
if err != nil {
return nil , fmt . Errorf ( "gen current movie info error: %w" , err )
}
@ -156,7 +167,7 @@ func CurrentMovie(ctx *gin.Context) {
user := ctx . MustGet ( "user" ) . ( * op . UserEntry ) . Value ( )
log := ctx . MustGet ( "log" ) . ( * logrus . Entry )
currentResp , err := genCurrentRespWithCurrent ( ctx , user , room , room. Current ( ) , ctx. GetHeader ( "User-Agent" ) , ctx . MustGet ( "token" ) . ( string ) )
currentResp , err := genCurrentRespWithCurrent ( ctx , user , room , ctx. GetHeader ( "User-Agent" ) , ctx . MustGet ( "token" ) . ( string ) )
if err != nil {
log . Errorf ( "gen current resp error: %v" , err )
ctx . AbortWithStatusJSON ( http . StatusInternalServerError , model . NewApiErrorResp ( err ) )
@ -171,6 +182,17 @@ func Movies(ctx *gin.Context) {
user := ctx . MustGet ( "user" ) . ( * op . UserEntry ) . Value ( )
log := ctx . MustGet ( "log" ) . ( * logrus . Entry )
if ! user . HasRoomPermission ( room , dbModel . PermissionGetMovieList ) {
ctx . AbortWithStatusJSON ( http . StatusForbidden , model . NewApiErrorResp ( dbModel . ErrNoPermission ) )
return
}
id := ctx . Query ( "id" )
if len ( id ) != 0 && len ( id ) != 32 {
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "id length must be 0 or 32" ) )
return
}
page , max , err := utils . GetPageAndMax ( ctx )
if err != nil {
log . Errorf ( "get page and max error: %v" , err )
@ -178,26 +200,223 @@ func Movies(ctx *gin.Context) {
return
}
m , total := user . GetRoomMoviesWithPage ( room , int ( page ) , int ( max ) )
if id != "" {
mv , err := room . GetMovieByID ( id )
if err != nil {
log . Errorf ( "get room movie by id error: %v" , err )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorResp ( err ) )
return
}
if ! mv . IsFolder {
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "parent id is not folder" ) )
return
}
if mv . IsDynamicFolder ( ) {
resp , err := listVendorDynamicMovie ( ctx , user , room , mv . Movie , ctx . Query ( "subPath" ) , page , max )
if err != nil {
log . Errorf ( "vendor dynamic movie list error: %v" , err )
ctx . AbortWithStatusJSON ( http . StatusInternalServerError , model . NewApiErrorResp ( err ) )
return
}
ctx . JSON ( http . StatusOK , model . NewApiDataResp ( resp ) )
return
}
}
m , total , err := user . GetRoomMoviesWithPage ( room , page , max , id )
if err != nil {
log . Errorf ( "get room movies with page error: %v" , err )
ctx . AbortWithStatusJSON ( http . StatusInternalServerError , model . NewApiErrorResp ( err ) )
return
}
paths , err := getParentMoviePath ( room , id )
if err != nil {
log . Errorf ( "get parent movie path error: %v" , err )
ctx . AbortWithStatusJSON ( http . StatusInternalServerError , model . NewApiErrorResp ( err ) )
return
}
resp := & model . MoviesResp {
Total : total ,
Movies : make ( [ ] * model . Movie , len ( m ) ) ,
Paths : paths ,
}
mresp := make ( [ ] * model . MovieResp , len ( m ) )
for i , v := range m {
mresp [ i ] = & model . MovieResp {
Id : v . Movie . ID ,
Base : v . Movie . Base ,
Creator : op . GetUserName ( v . Movie . CreatorID ) ,
resp . Movies [ i ] = & model . Movie {
Id : v . ID ,
CreatedAt : v . CreatedAt . UnixMilli ( ) ,
Base : v . MovieBase ,
Creator : op . GetUserName ( v . CreatorID ) ,
CreatorId : v . CreatorID ,
}
// hide url and headers when proxy
if user . ID != v . Movie . CreatorID && v . Movie . Base . Proxy {
mresp [ i ] . Base . Url = ""
mresp [ i ] . Base . Headers = nil
if user . ID != v . CreatorID && v . Movie Base. Proxy {
resp. Movies [ i ] . Base . Url = ""
resp. Movies [ i ] . Base . Headers = nil
}
}
ctx . JSON ( http . StatusOK , model . NewApiDataResp ( gin . H {
"total" : total ,
"movies" : mresp ,
} ) )
ctx . JSON ( http . StatusOK , model . NewApiDataResp ( resp ) )
}
func getParentMoviePath ( room * op . Room , id string ) ( [ ] * model . MoviePath , error ) {
paths := [ ] * model . MoviePath {
{
Name : "Home" ,
ID : "" ,
} ,
}
if id == "" {
return paths , nil
}
for id != "" {
p , err := room . GetMovieByID ( id )
if err != nil {
return nil , fmt . Errorf ( "get movie by id error: %w" , err )
}
paths = append ( paths , & model . MoviePath {
Name : p . MovieBase . Name ,
ID : p . ID ,
} )
id = p . ParentID . String ( )
}
return paths , nil
}
func listVendorDynamicMovie ( ctx context . Context , reqUser * op . User , room * op . Room , movie * dbModel . Movie , subPath string , page , max int ) ( * model . MoviesResp , error ) {
if reqUser . ID != movie . CreatorID {
return nil , fmt . Errorf ( "list vendor dynamic folder error: %w" , dbModel . ErrNoPermission )
}
// creatorE, err := op.LoadOrInitUserByID(movie.CreatorID)
// if err != nil {
// return nil, err
// }
user := reqUser
paths , err := getParentMoviePath ( room , movie . ID )
if err != nil {
return nil , fmt . Errorf ( "get parent movie path error: %w" , err )
}
resp := & model . MoviesResp {
Paths : paths ,
Dynamic : true ,
}
switch movie . MovieBase . VendorInfo . Vendor {
case dbModel . VendorAlist :
serverID , truePath , err := movie . VendorInfo . Alist . ServerIDAndFilePath ( )
if err != nil {
return nil , fmt . Errorf ( "load alist server id error: %w" , err )
}
newPath := path . Join ( truePath , subPath )
// check new path is in parent path
if ! strings . HasPrefix ( newPath , truePath ) {
return nil , fmt . Errorf ( "sub path is not in parent path" )
}
truePath = newPath
aucd , err := user . AlistCache ( ) . LoadOrStore ( ctx , serverID )
if err != nil {
if errors . Is ( err , db . ErrNotFound ( "vendor" ) ) {
return nil , errors . New ( "alist server not found" )
}
return nil , err
}
var cli = vendor . LoadAlistClient ( movie . VendorInfo . Backend )
data , err := cli . FsList ( ctx , & alist . FsListReq {
Token : aucd . Token ,
Password : movie . VendorInfo . Alist . Password ,
Path : truePath ,
Host : aucd . Host ,
Refresh : false ,
Page : uint64 ( page ) ,
PerPage : uint64 ( max ) ,
} )
if err != nil {
return nil , err
}
resp . Total = int64 ( data . Total )
resp . Movies = make ( [ ] * model . Movie , len ( data . Content ) )
for i , flr := range data . Content {
resp . Movies [ i ] = & model . Movie {
Id : movie . ID ,
CreatedAt : movie . CreatedAt . UnixMilli ( ) ,
Creator : op . GetUserName ( movie . CreatorID ) ,
CreatorId : movie . CreatorID ,
SubPath : fmt . Sprintf ( "/%s" , strings . Trim ( fmt . Sprintf ( "%s/%s" , subPath , flr . Name ) , "/" ) ) ,
Base : dbModel . MovieBase {
Name : flr . Name ,
IsFolder : flr . IsDir ,
ParentID : dbModel . EmptyNullString ( movie . ID ) ,
VendorInfo : dbModel . VendorInfo {
Vendor : dbModel . VendorAlist ,
Backend : movie . VendorInfo . Backend ,
Alist : & dbModel . AlistStreamingInfo {
Path : dbModel . FormatAlistPath ( serverID , fmt . Sprintf ( "/%s" , strings . Trim ( fmt . Sprintf ( "%s/%s" , truePath , flr . Name ) , "/" ) ) ) ,
} ,
} ,
} ,
}
}
resp . Paths = model . GenDefaultSubPaths ( subPath , true , resp . Paths ... )
case dbModel . VendorEmby :
serverID , truePath , err := movie . VendorInfo . Emby . ServerIDAndFilePath ( )
if err != nil {
return nil , fmt . Errorf ( "load emby server id error: %w" , err )
}
if subPath != "" {
truePath = subPath
}
aucd , err := user . EmbyCache ( ) . LoadOrStore ( ctx , serverID )
if err != nil {
if errors . Is ( err , db . ErrNotFound ( "vendor" ) ) {
return nil , errors . New ( "emby server not found" )
}
return nil , err
}
var cli = vendor . LoadEmbyClient ( movie . VendorInfo . Backend )
data , err := cli . FsList ( ctx , & emby . FsListReq {
Host : aucd . Host ,
Path : truePath ,
Token : aucd . ApiKey ,
UserId : aucd . UserID ,
Limit : uint64 ( max ) ,
StartIndex : uint64 ( ( page - 1 ) * max ) ,
} )
if err != nil {
return nil , fmt . Errorf ( "emby fs list error: %w" , err )
}
resp . Total = int64 ( data . Total )
resp . Movies = make ( [ ] * model . Movie , len ( data . Items ) )
for i , flr := range data . Items {
resp . Movies [ i ] = & model . Movie {
Id : movie . ID ,
CreatedAt : movie . CreatedAt . UnixMilli ( ) ,
Creator : op . GetUserName ( movie . CreatorID ) ,
CreatorId : movie . CreatorID ,
SubPath : flr . Id ,
Base : dbModel . MovieBase {
Name : flr . Name ,
IsFolder : flr . IsFolder ,
ParentID : dbModel . EmptyNullString ( movie . ID ) ,
VendorInfo : dbModel . VendorInfo {
Vendor : dbModel . VendorEmby ,
Backend : movie . VendorInfo . Backend ,
Emby : & dbModel . EmbyStreamingInfo {
Path : dbModel . FormatEmbyPath ( serverID , flr . Id ) ,
} ,
} ,
} ,
}
}
default :
return nil , fmt . Errorf ( "%v vendor not implement list dynamic movie" , movie . MovieBase . VendorInfo . Vendor )
}
return resp , nil
}
func PushMovie ( ctx * gin . Context ) {
@ -212,7 +431,7 @@ func PushMovie(ctx *gin.Context) {
return
}
m , err := user . AddRoomMovie ( room , ( * dbModel . Base Movie) ( & req ) )
m , err := user . AddRoomMovie ( room , ( * dbModel . MovieBas e) ( & req ) )
if err != nil {
log . Errorf ( "push movie error: %v" , err )
if errors . Is ( err , dbModel . ErrNoPermission ) {
@ -242,11 +461,10 @@ func PushMovies(ctx *gin.Context) {
return
}
var ms [ ] * dbModel . Base Movie = make ( [ ] * dbModel . Base Movie, len ( req ) )
var ms [ ] * dbModel . MovieBas e = make ( [ ] * dbModel . MovieBas e, len ( req ) )
for i , v := range req {
m := ( * dbModel . BaseMovie ) ( v )
ms [ i ] = m
ms [ i ] = ( * dbModel . MovieBase ) ( v )
}
m , err := user . AddRoomMovies ( room , ms )
@ -304,7 +522,7 @@ func NewPublishKey(ctx *gin.Context) {
return
}
if ! movie . Movie . Base. RtmpSource {
if ! movie . Movie . Movie Base. RtmpSource {
log . Errorf ( "new publish key error: %v" , "only rtmp source movie can get publish key" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "only live movie can get publish key" ) )
return
@ -344,7 +562,7 @@ func EditMovie(ctx *gin.Context) {
return
}
if err := user . UpdateRoomMovie ( room , req . Id , ( * dbModel . Base Movie) ( & req . PushMovieReq ) ) ; err != nil {
if err := user . UpdateRoomMovie ( room , req . Id , ( * dbModel . MovieBas e) ( & req . PushMovieReq ) ) ; err != nil {
log . Errorf ( "edit movie error: %v" , err )
if errors . Is ( err , dbModel . ErrNoPermission ) {
ctx . AbortWithStatusJSON (
@ -397,7 +615,13 @@ func ClearMovies(ctx *gin.Context) {
room := ctx . MustGet ( "room" ) . ( * op . RoomEntry ) . Value ( )
user := ctx . MustGet ( "user" ) . ( * op . UserEntry ) . Value ( )
if err := user . ClearRoomMovies ( room ) ; err != nil {
var req model . ClearMoviesReq
if err := model . Decode ( ctx , & req ) ; err != nil {
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorResp ( err ) )
return
}
if err := user . ClearRoomMoviesByParentID ( room , req . ParentId ) ; err != nil {
if errors . Is ( err , dbModel . ErrNoPermission ) {
ctx . AbortWithStatusJSON (
http . StatusForbidden ,
@ -437,14 +661,14 @@ func ChangeCurrentMovie(ctx *gin.Context) {
user := ctx . MustGet ( "user" ) . ( * op . UserEntry ) . Value ( )
log := ctx . MustGet ( "log" ) . ( * logrus . Entry )
req := model . IdCanEmpty Req{ }
req := model . SetRoomCurrentMovie Req{ }
err := model . Decode ( ctx , & req )
if err != nil {
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorResp ( err ) )
return
}
err = user . SetRoomCurrentMovie ( room , req . Id , req . Id != "" )
err = user . SetRoomCurrentMovie ( room , req . Id , req . SubPath, true )
if err != nil {
log . Errorf ( "change current movie error: %v" , err )
if errors . Is ( err , dbModel . ErrNoPermission ) {
@ -492,22 +716,22 @@ func ProxyMovie(ctx *gin.Context) {
return
}
if m . Movie . Base. VendorInfo . Vendor != "" {
if m . Movie . Movie Base. VendorInfo . Vendor != "" {
proxyVendorMovie ( ctx , m )
return
}
if ! m . Movie . Base. Proxy || m . Movie . Base. Live || m . Movie . Base. RtmpSource {
if ! m . Movie . Movie Base. Proxy || m . Movie . Movie Base. Live || m . Movie . Movie Base. RtmpSource {
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "not support movie proxy" ) )
return
}
switch m . Movie . Base. Type {
switch m . Movie . Movie Base. Type {
case "mpd" :
// TODO: cache mpd file
fallthrough
default :
err = proxyURL ( ctx , m . Movie . Base. Url , m . Movie . Base. Headers )
err = proxyURL ( ctx , m . Movie . Movie Base. Url , m . Movie . Movie Base. Headers )
if err != nil {
log . Errorf ( "proxy movie error: %v" , err )
return
@ -614,11 +838,11 @@ func JoinLive(ctx *gin.Context) {
ctx . AbortWithStatusJSON ( http . StatusNotFound , model . NewApiErrorResp ( err ) )
return
}
if m . Movie . Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
if m . Movie . Movie Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
log . Error ( "join live error: rtmp is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "rtmp is not enabled" ) )
return
} else if m . Movie . Base. Live && ! settings . LiveProxy . Get ( ) {
} else if m . Movie . Movie Base. Live && ! settings . LiveProxy . Get ( ) {
log . Error ( "join live error: live proxy is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "live proxy is not enabled" ) )
return
@ -632,7 +856,7 @@ func JoinLive(ctx *gin.Context) {
joinType := ctx . DefaultQuery ( "type" , "auto" )
if joinType == "auto" {
joinType = m . Movie . Base. Type
joinType = m . Movie . Movie Base. Type
}
switch joinType {
case "flv" :
@ -678,11 +902,11 @@ func JoinFlvLive(ctx *gin.Context) {
ctx . AbortWithStatusJSON ( http . StatusNotFound , model . NewApiErrorResp ( err ) )
return
}
if m . Movie . Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
if m . Movie . Movie Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
log . Error ( "join flv live error: rtmp is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "rtmp is not enabled" ) )
return
} else if m . Movie . Base. Live && ! settings . LiveProxy . Get ( ) {
} else if m . Movie . Movie Base. Live && ! settings . LiveProxy . Get ( ) {
log . Error ( "join flv live error: live proxy is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "live proxy is not enabled" ) )
return
@ -718,11 +942,11 @@ func JoinHlsLive(ctx *gin.Context) {
ctx . AbortWithStatusJSON ( http . StatusNotFound , model . NewApiErrorResp ( err ) )
return
}
if m . Movie . Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
if m . Movie . Movie Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
log . Error ( "join hls live error: rtmp is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "rtmp is not enabled" ) )
return
} else if m . Movie . Base. Live && ! settings . LiveProxy . Get ( ) {
} else if m . Movie . Movie Base. Live && ! settings . LiveProxy . Get ( ) {
log . Error ( "join hls live error: live proxy is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "live proxy is not enabled" ) )
return
@ -768,11 +992,11 @@ func ServeHlsLive(ctx *gin.Context) {
ctx . AbortWithStatusJSON ( http . StatusNotFound , model . NewApiErrorResp ( err ) )
return
}
if m . Movie . Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
if m . Movie . Movie Base. RtmpSource && ! conf . Conf . Server . Rtmp . Enable {
log . Error ( "serve hls live error: rtmp is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "rtmp is not enabled" ) )
return
} else if m . Movie . Base. Live && ! settings . LiveProxy . Get ( ) {
} else if m . Movie . Movie Base. Live && ! settings . LiveProxy . Get ( ) {
log . Error ( "serve hls live error: live proxy is not enabled" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "live proxy is not enabled" ) )
return
@ -833,9 +1057,9 @@ func ServeHlsLive(ctx *gin.Context) {
func proxyVendorMovie ( ctx * gin . Context , movie * op . Movie ) {
log := ctx . MustGet ( "log" ) . ( * logrus . Entry )
switch movie . Movie . Base. VendorInfo . Vendor {
switch movie . Movie . Movie Base. VendorInfo . Vendor {
case dbModel . VendorBilibili :
if movie . Base. Live {
if movie . Movie Base. Live {
data , err := movie . BilibiliCache ( ) . Live . Get ( ctx )
if err != nil {
log . Errorf ( "proxy vendor movie error: %v" , err )
@ -853,7 +1077,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) {
t := ctx . Query ( "t" )
switch t {
case "" , "hevc" :
if ! movie . Movie . Base. Proxy {
if ! movie . Movie . Movie Base. Proxy {
log . Errorf ( "proxy vendor movie error: %v" , "not support movie proxy" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "not support movie proxy" ) )
return
@ -901,7 +1125,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) {
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "stream id out of range" ) )
return
}
headers := maps . Clone ( movie . Movie . Base. Headers )
headers := maps . Clone ( movie . Movie . Movie Base. Headers )
if headers == nil {
headers = map [ string ] string {
"Referer" : "https://www.bilibili.com" ,
@ -1006,7 +1230,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) {
case cache . AlistProvider115 :
fallthrough
default :
if ! movie . Movie . Base. Proxy {
if ! movie . Movie . Movie Base. Proxy {
log . Errorf ( "proxy vendor movie error: %v" , "not support movie proxy" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "not support movie proxy" ) )
return
@ -1024,7 +1248,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) {
t := ctx . Query ( "t" )
switch t {
case "" :
if ! movie . Movie . Base. Proxy {
if ! movie . Movie . Movie Base. Proxy {
log . Errorf ( "proxy vendor movie error: %v" , "not support movie proxy" )
ctx . AbortWithStatusJSON ( http . StatusBadRequest , model . NewApiErrorStringResp ( "not support movie proxy" ) )
return
@ -1126,20 +1350,24 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) {
}
// user is the api requester
func genVendorMovie ( ctx context . Context , user * op . User , opMovie * op . Movie , userAgent string , userToken string ) ( * dbModel . Movie , error ) {
func genVendorMovie ( ctx context . Context , user * op . User , opMovie * op . Movie , userAgent , userToken string ) ( * dbModel . Movie , error ) {
movie := * opMovie . Movie
var err error
switch movie . Base. VendorInfo . Vendor {
switch movie . Movie Base. VendorInfo . Vendor {
case dbModel . VendorBilibili :
if movie . IsFolder {
return nil , fmt . Errorf ( "bilibili folder not support" )
}
bmc := opMovie . BilibiliCache ( )
if movie . Base . Live {
movie . Base . Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Base . Type = "m3u8"
if movie . Movie Base. Live {
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Movie Base. Type = "m3u8"
return & movie , nil
} else {
if ! movie . Base. Proxy {
if ! movie . Movie Base. Proxy {
var s string
if movie . Base. VendorInfo . Bilibili . Shared {
if movie . Movie Base. VendorInfo . Bilibili . Shared {
var u * op . UserEntry
u , err = op . LoadOrInitUserByID ( movie . CreatorID )
if err != nil {
@ -1153,11 +1381,11 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
return nil , err
}
movie . Base. Url = s
movie . Movie Base. Url = s
} else {
movie . Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Base. Type = "mpd"
movie . Base. MoreSource = map [ string ] string {
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Movie Base. Type = "mpd"
movie . Movie Base. MoreSource = map [ string ] string {
"hevc" : fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s&t=hevc" , movie . RoomID , movie . ID , userToken ) ,
}
}
@ -1166,10 +1394,10 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
return nil , err
}
for k := range srt {
if movie . Base. Subtitles == nil {
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( srt ) )
if movie . Movie Base. Subtitles == nil {
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( srt ) )
}
movie . Base. Subtitles [ k ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ k ] = & dbModel . Subtitle {
URL : fmt . Sprintf ( "/api/movie/proxy/%s/%s?t=subtitle&n=%s&token=%s" , movie . RoomID , movie . ID , k , userToken ) ,
Type : "srt" ,
}
@ -1192,10 +1420,10 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
}
for _ , subt := range data . Subtitles {
if movie . Base. Subtitles == nil {
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Subtitles ) )
if movie . Movie Base. Subtitles == nil {
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Subtitles ) )
}
movie . Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
URL : subt . URL ,
Type : subt . Type ,
}
@ -1203,23 +1431,23 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
switch data . Provider {
case cache . AlistProviderAli :
movie . Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Base. Type = "m3u8"
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Movie Base. Type = "m3u8"
for i , subt := range data . Subtitles {
if movie . Base. Subtitles == nil {
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Subtitles ) )
if movie . Movie Base. Subtitles == nil {
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Subtitles ) )
}
movie . Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
URL : fmt . Sprintf ( "/api/movie/proxy/%s/%s?t=subtitle&id=%d&token=%s" , movie . RoomID , movie . ID , i , userToken ) ,
Type : subt . Type ,
}
}
case cache . AlistProvider115 :
if movie . Base. Proxy {
movie . Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Base. Type = utils . GetUrlExtension ( data . URL )
if movie . Movie Base. Proxy {
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Movie Base. Type = utils . GetUrlExtension ( data . URL )
// TODO: proxy subtitle
} else {
@ -1230,10 +1458,10 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
if err != nil {
return nil , fmt . Errorf ( "refresh 115 movie cache error: %w" , err )
}
movie . Base. Url = data . URL
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Subtitles ) )
movie . Movie Base. Url = data . URL
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Subtitles ) )
for _ , subt := range data . Subtitles {
movie . Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
URL : subt . URL ,
Type : subt . Type ,
}
@ -1241,15 +1469,15 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
}
default :
if ! movie . Base. Proxy {
movie . Base. Url = data . URL
if ! movie . Movie Base. Proxy {
movie . Movie Base. Url = data . URL
} else {
movie . Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Base. Type = utils . GetUrlExtension ( data . URL )
movie . Movie Base. Url = fmt . Sprintf ( "/api/movie/proxy/%s/%s?token=%s" , movie . RoomID , movie . ID , userToken )
movie . Movie Base. Type = utils . GetUrlExtension ( data . URL )
}
}
movie . Base. VendorInfo . Alist . Password = ""
movie . Movie Base. VendorInfo . Alist . Password = ""
return & movie , nil
case dbModel . VendorEmby :
@ -1262,31 +1490,31 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
return nil , err
}
if ! movie . Base. Proxy {
if ! movie . Movie Base. Proxy {
if len ( data . Sources ) == 0 {
return nil , errors . New ( "no source" )
}
movie . Base. Url = data . Sources [ 0 ] . URL
movie . Movie Base. Url = data . Sources [ 0 ] . URL
for _ , s := range data . Sources [ 0 ] . Subtitles {
if movie . Base. Subtitles == nil {
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Sources [ 0 ] . Subtitles ) )
if movie . Movie Base. Subtitles == nil {
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( data . Sources [ 0 ] . Subtitles ) )
}
movie . Base. Subtitles [ s . Name ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ s . Name ] = & dbModel . Subtitle {
URL : s . URL ,
Type : s . Type ,
}
}
for _ , s := range data . Sources [ 1 : ] {
if movie . Base. MoreSource == nil {
movie . Base. MoreSource = make ( map [ string ] string , len ( data . Sources ) - 1 )
if movie . Movie Base. MoreSource == nil {
movie . Movie Base. MoreSource = make ( map [ string ] string , len ( data . Sources ) - 1 )
}
movie . Base. MoreSource [ s . Name ] = s . URL
movie . Movie Base. MoreSource [ s . Name ] = s . URL
for _ , subt := range s . Subtitles {
if movie . Base. Subtitles == nil {
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( s . Subtitles ) )
if movie . Movie Base. Subtitles == nil {
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( s . Subtitles ) )
}
movie . Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ subt . Name ] = & dbModel . Subtitle {
URL : subt . URL ,
Type : subt . Type ,
}
@ -1298,7 +1526,7 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
if si != len ( data . Sources ) - 1 {
continue
}
if movie . Base. Url == "" {
if movie . Movie Base. Url == "" {
return nil , errors . New ( "no source" )
}
}
@ -1314,15 +1542,15 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
Path : rawPath ,
RawQuery : rawQuery . Encode ( ) ,
}
movie . Base. Url = u . String ( )
movie . Base. Type = utils . GetUrlExtension ( es . URL )
movie . Movie Base. Url = u . String ( )
movie . Movie Base. Type = utils . GetUrlExtension ( es . URL )
if len ( es . Subtitles ) == 0 {
continue
}
for sbi , s := range es . Subtitles {
if movie . Base. Subtitles == nil {
movie . Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( es . Subtitles ) )
if movie . Movie Base. Subtitles == nil {
movie . Movie Base. Subtitles = make ( map [ string ] * dbModel . Subtitle , len ( es . Subtitles ) )
}
rawQuery := url . Values { }
rawQuery . Set ( "t" , "subtitle" )
@ -1333,7 +1561,7 @@ func genVendorMovie(ctx context.Context, user *op.User, opMovie *op.Movie, userA
Path : rawPath ,
RawQuery : rawQuery . Encode ( ) ,
}
movie . Base. Subtitles [ s . Name ] = & dbModel . Subtitle {
movie . Movie Base. Subtitles [ s . Name ] = & dbModel . Subtitle {
URL : u . String ( ) ,
Type : s . Type ,
}