From 846d81e52caca62f44347b6ba2ab355f304a6f77 Mon Sep 17 00:00:00 2001 From: zijiren233 Date: Tue, 19 Dec 2023 21:07:27 +0800 Subject: [PATCH] Feat: alist parse support --- internal/cache/alist.go | 85 +++++++++++++------- internal/db/vendorRecord.go | 5 +- server/handlers/movie.go | 7 +- server/handlers/vendors/vendorAlist/login.go | 65 +++++---------- server/handlers/vendors/vendorAlist/parse.go | 38 ++++++++- server/model/vendor.go | 17 ++++ 6 files changed, 131 insertions(+), 86 deletions(-) diff --git a/internal/cache/alist.go b/internal/cache/alist.go index e3bf405..0042668 100644 --- a/internal/cache/alist.go +++ b/internal/cache/alist.go @@ -33,37 +33,40 @@ func AlistAuthorizationCacheWithUserIDInitFunc(userID string) func(ctx context.C if err != nil { return nil, err } - return AlistAuthorizationCacheWithConfigInitFunc(v)(ctx) + return AlistAuthorizationCacheWithConfigInitFunc(ctx, v) } } -func AlistAuthorizationCacheWithConfigInitFunc(v *model.AlistVendor) func(ctx context.Context, args ...struct{}) (*AlistUserCacheData, error) { - return func(ctx context.Context, args ...struct{}) (*AlistUserCacheData, error) { - cli := vendor.LoadAlistClient(v.Backend) - if v.Username == "" { - _, err := cli.Me(ctx, &alist.MeReq{ - Host: v.Host, - }) - return &AlistUserCacheData{ - Host: v.Host, - Backend: v.Backend, - }, err - } else { - resp, err := cli.Login(ctx, &alist.LoginReq{ - Host: v.Host, - Username: v.UserID, - Password: string(v.HashedPassword), - Hashed: true, - }) - if err != nil { - return nil, err - } - return &AlistUserCacheData{ - Host: v.Host, - Token: resp.Token, - Backend: v.Backend, - }, nil +func AlistAuthorizationCacheWithConfigInitFunc(ctx context.Context, v *model.AlistVendor) (*AlistUserCacheData, error) { + cli := vendor.LoadAlistClient(v.Backend) + + if v.Username == "" { + _, err := cli.Me(ctx, &alist.MeReq{ + Host: v.Host, + }) + if err != nil { + return nil, err + } + return &AlistUserCacheData{ + Host: v.Host, + Backend: v.Backend, + }, nil + } else { + resp, err := cli.Login(ctx, &alist.LoginReq{ + Host: v.Host, + Username: v.Username, + Password: string(v.HashedPassword), + Hashed: true, + }) + if err != nil { + return nil, err } + + return &AlistUserCacheData{ + Host: v.Host, + Token: resp.Token, + Backend: v.Backend, + }, nil } } @@ -74,7 +77,10 @@ func NewAlistMovieCache(movie *model.Movie) *AlistMovieCache { } type AlistMovieCacheData struct { - URL string + URLs []struct { + URL string + Name string + } } func NewAlistMovieCacheInitFunc(movie *model.Movie) func(ctx context.Context, args ...*AlistUserCache) (*AlistMovieCacheData, error) { @@ -105,7 +111,14 @@ func NewAlistMovieCacheInitFunc(movie *model.Movie) func(ctx context.Context, ar } cache := &AlistMovieCacheData{ - URL: fg.RawUrl, + URLs: []struct { + URL string + Name string + }{ + { + URL: fg.RawUrl, + }, + }, } if fg.Provider == "AliyundriveOpen" { fo, err := cli.FsOther(ctx, &alist.FsOtherReq{ @@ -118,7 +131,19 @@ func NewAlistMovieCacheInitFunc(movie *model.Movie) func(ctx context.Context, ar if err != nil { return nil, err } - cache.URL = fo.VideoPreviewPlayInfo.LiveTranscodingTaskList[len(fo.VideoPreviewPlayInfo.LiveTranscodingTaskList)-1].Url + cache.URLs = make([]struct { + URL string + Name string + }, len(fo.VideoPreviewPlayInfo.LiveTranscodingTaskList)) + for i, v := range fo.VideoPreviewPlayInfo.LiveTranscodingTaskList { + cache.URLs[i] = struct { + URL string + Name string + }{ + URL: v.Url, + Name: v.TemplateId, + } + } } return cache, nil } diff --git a/internal/db/vendorRecord.go b/internal/db/vendorRecord.go index 2dce3ca..a5820d3 100644 --- a/internal/db/vendorRecord.go +++ b/internal/db/vendorRecord.go @@ -5,12 +5,11 @@ import ( "github.com/synctv-org/synctv/internal/model" "gorm.io/gorm" - "gorm.io/gorm/clause" ) func GetBilibiliVendor(userID string) (*model.BilibiliVendor, error) { var vendor model.BilibiliVendor - err := db.Where("user_id = ?", userID).Preload(clause.Associations).First(&vendor).Error + err := db.Where("user_id = ?", userID).First(&vendor).Error return &vendor, HandleNotFound(err, "vendor") } @@ -33,7 +32,7 @@ func DeleteBilibiliVendor(userID string) error { func GetAlistVendor(userID string) (*model.AlistVendor, error) { var vendor model.AlistVendor - err := db.Where("user_id = ?", userID).Preload(clause.Associations).First(&vendor).Error + err := db.Where("user_id = ?", userID).First(&vendor).Error return &vendor, HandleNotFound(err, "vendor") } diff --git a/server/handlers/movie.go b/server/handlers/movie.go index e18c27b..5ca3684 100644 --- a/server/handlers/movie.go +++ b/server/handlers/movie.go @@ -781,12 +781,11 @@ func parse2VendorMovie(ctx context.Context, user *op.User, room *op.Room, movie return nil case dbModel.VendorAlist: - opM, err := room.GetMovieByID(movie.ID) + u, err := op.LoadOrInitUserByID(movie.CreatorID) if err != nil { return err } - - u, err := op.LoadOrInitUserByID(movie.CreatorID) + opM, err := room.GetMovieByID(movie.ID) if err != nil { return err } @@ -795,7 +794,7 @@ func parse2VendorMovie(ctx context.Context, user *op.User, room *op.Room, movie return err } - movie.Base.Url = data.URL + movie.Base.Url = data.URLs[len(data.URLs)-1].URL movie.Base.VendorInfo.Alist = nil return nil diff --git a/server/handlers/vendors/vendorAlist/login.go b/server/handlers/vendors/vendorAlist/login.go index c208d55..4f1dd01 100644 --- a/server/handlers/vendors/vendorAlist/login.go +++ b/server/handlers/vendors/vendorAlist/login.go @@ -13,9 +13,7 @@ import ( "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/vendor" "github.com/synctv-org/synctv/server/model" - "github.com/synctv-org/vendors/api/alist" ) type LoginReq struct { @@ -55,52 +53,27 @@ func Login(ctx *gin.Context) { } backend := ctx.Query("backend") - cli := vendor.LoadAlistClient(backend) - - if req.Username == "" { - _, err := cli.Me(ctx, &alist.MeReq{ - Host: req.Host, - }) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) - return - } - _, err = user.AlistCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.AlistUserCacheData, error) { - return &cache.AlistUserCacheData{ - Host: req.Host, - Backend: backend, - }, nil - }) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) - return - } - } else { - resp, err := cli.Login(ctx, &alist.LoginReq{ - Host: req.Host, - Username: req.Username, - Password: req.HashedPassword, - Hashed: true, - }) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) - return - } - - _, err = user.AlistCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.AlistUserCacheData, error) { - return &cache.AlistUserCacheData{ - Host: req.Host, - Token: resp.Token, - Backend: backend, - }, nil - }) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) - return - } + + data, err := cache.AlistAuthorizationCacheWithConfigInitFunc(ctx, &dbModel.AlistVendor{ + Host: req.Host, + Username: req.Username, + HashedPassword: []byte(req.HashedPassword), + Backend: backend, + }) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return + } + + _, err = user.AlistCache().Data().Refresh(ctx, func(ctx context.Context, args ...struct{}) (*cache.AlistUserCacheData, error) { + return data, nil + }) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) + return } - _, err := db.CreateOrSaveAlistVendor(user.ID, &dbModel.AlistVendor{ + _, err = db.CreateOrSaveAlistVendor(user.ID, &dbModel.AlistVendor{ Backend: backend, Host: req.Host, Username: req.Username, diff --git a/server/handlers/vendors/vendorAlist/parse.go b/server/handlers/vendors/vendorAlist/parse.go index e3a005a..1269f19 100644 --- a/server/handlers/vendors/vendorAlist/parse.go +++ b/server/handlers/vendors/vendorAlist/parse.go @@ -2,7 +2,10 @@ package vendorAlist import ( "errors" + "fmt" "net/http" + "path/filepath" + "strings" "github.com/gin-gonic/gin" json "github.com/json-iterator/go" @@ -39,18 +42,24 @@ func List(ctx *gin.Context) { return } + req.Path = strings.TrimRight(req.Path, "/") + if !strings.HasPrefix(req.Path, "/") { + req.Path = "/" + req.Path + } + aucd, err := user.AlistCache().Get(ctx) if err != nil { if errors.Is(err, db.ErrNotFound("vendor")) { ctx.JSON(http.StatusBadRequest, model.NewApiErrorStringResp("alist not login")) return } + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } var cli = vendor.LoadAlistClient(ctx.Query("backend")) - resp, err := cli.FsList(ctx, &alist.FsListReq{ + data, err := cli.FsList(ctx, &alist.FsListReq{ Token: aucd.Token, Password: req.Password, Path: req.Path, @@ -58,9 +67,32 @@ func List(ctx *gin.Context) { Refresh: req.Refresh, }) if err != nil { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } - ctx.JSON(http.StatusOK, model.NewApiDataResp(resp)) + var resp model.VendorFSListResp + resp.Total = data.Total + if req.Path == "/" { + req.Path = "" + } + for i, v := range strings.Split(req.Path, string(filepath.Separator)) { + var p = v + if i != 0 { + p = fmt.Sprintf("%s/%s", resp.Paths[i-1].Path, v) + } + resp.Paths = append(resp.Paths, &model.Path{ + Name: v, + Path: p, + }) + } + for _, flr := range data.Content { + resp.Items = append(resp.Items, &model.Item{ + Name: flr.Name, + Path: fmt.Sprintf("%s/%s", req.Path, flr.Name), + IsDir: flr.IsDir, + }) + } + + ctx.JSON(http.StatusOK, model.NewApiDataResp(&resp)) } diff --git a/server/model/vendor.go b/server/model/vendor.go index 1aece34..4cb807c 100644 --- a/server/model/vendor.go +++ b/server/model/vendor.go @@ -4,3 +4,20 @@ type VendorMeResp[T any] struct { IsLogin bool `json:"isLogin"` Info T `json:"info,omitempty"` } + +type VendorFSListResp struct { + Paths []*Path `json:"paths"` + Items []*Item `json:"items"` + Total uint64 `json:"total"` +} + +type Item struct { + Name string `json:"name"` + Path string `json:"path"` + IsDir bool `json:"isDir"` +} + +type Path struct { + Name string `json:"name"` + Path string `json:"path"` +}