From b14ce356a1598d9d69bd9b37497ca4f388d375af Mon Sep 17 00:00:00 2001 From: Har01d Date: Sun, 18 Jan 2026 11:46:56 +0800 Subject: [PATCH] quark share use tv account --- drivers/quark_uc_share/driver.go | 28 ++++ drivers/quark_uc_share/util.go | 115 +++++++++++++++- drivers/quark_uc_tv/driver.go | 3 + drivers/quark_uc_tv/extension.go | 212 +++++++++++++++++++++++++++++ drivers/quark_uc_tv/meta.go | 14 +- drivers/quark_uc_tv/types.go | 8 ++ drivers/quark_uc_tv/util.go | 9 +- internal/bootstrap/data/setting.go | 1 + internal/conf/const.go | 1 + 9 files changed, 383 insertions(+), 8 deletions(-) create mode 100644 drivers/quark_uc_tv/extension.go diff --git a/drivers/quark_uc_share/driver.go b/drivers/quark_uc_share/driver.go index 9de858e1..6e401b98 100755 --- a/drivers/quark_uc_share/driver.go +++ b/drivers/quark_uc_share/driver.go @@ -4,11 +4,14 @@ import ( "context" "errors" "fmt" + quark "github.com/OpenListTeam/OpenList/v4/drivers/quark_uc" + "github.com/OpenListTeam/OpenList/v4/drivers/quark_uc_tv" "github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/driver" "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/op" + "github.com/OpenListTeam/OpenList/v4/internal/setting" "github.com/OpenListTeam/OpenList/v4/internal/token" "github.com/OpenListTeam/OpenList/v4/pkg/utils" log "github.com/sirupsen/logrus" @@ -86,6 +89,31 @@ func (d *QuarkUCShare) Link(ctx context.Context, file model.Obj, args model.Link } func (d *QuarkUCShare) link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { + if setting.GetBool(conf.UssQuarkTv) { + var tvName string + if d.config.Name == "UCShare" { + tvName = "UCTV" + } else { + tvName = "QuarkTV" + } + storage := op.GetFirstDriver(tvName, idx) + if storage != nil { + uc := storage.(*quark_uc_tv.QuarkUCTV) + Cookie = uc.Cookie + log.Infof("[%v] 获取%s文件直链 %v %v %v", uc.ID, tvName, file.GetName(), file.GetID(), file.GetSize()) + newFile, err := d.saveTvFile(ctx, uc, file.GetID()) + if err != nil { + return nil, err + } + + link, err := d.getTvDownloadUrl(ctx, uc, newFile, args) + if link != nil { + link.URL = link.URL + "#proxy=0" + } + return link, err + } + } + name := d.getDriverName() storage := op.GetFirstDriver(name, idx) idx++ diff --git a/drivers/quark_uc_share/util.go b/drivers/quark_uc_share/util.go index ef9995ef..0fccf2c4 100755 --- a/drivers/quark_uc_share/util.go +++ b/drivers/quark_uc_share/util.go @@ -3,18 +3,20 @@ package quark_uc_share import ( "context" "errors" + "math/rand" + "net/http" + "strconv" + "strings" + "time" + quark "github.com/OpenListTeam/OpenList/v4/drivers/quark_uc" + "github.com/OpenListTeam/OpenList/v4/drivers/quark_uc_tv" "github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/internal/setting" "github.com/OpenListTeam/OpenList/v4/pkg/cookie" "github.com/go-resty/resty/v2" - "math/rand" - "net/http" - "strconv" - "strings" - "time" "github.com/OpenListTeam/OpenList/v4/drivers/base" log "github.com/sirupsen/logrus" @@ -194,6 +196,69 @@ func (d *QuarkUCShare) saveFile(quark *quark.QuarkOrUC, id string) (model.Obj, e return nil, errors.New("save file failed") } +func (d *QuarkUCShare) saveTvFile(ctx context.Context, quark *quark_uc_tv.QuarkUCTV, id string) (model.Obj, error) { + s := strings.Split(id, "-") + fileId := s[0] + fileTokenId := s[1] + pid := s[2] + for range 2 { + data := base.Json{ + "fid_list": []string{fileId}, + "fid_token_list": []string{fileTokenId}, + "exclude_fids": []string{}, + "to_pdir_fid": quark.TempDirId, + "pwd_id": d.ShareId, + "stoken": d.ShareToken, + "pdir_fid": "0", + "pdir_save_all": false, + "scene": "link", + } + query := map[string]string{ + "pr": d.conf.pr, + "fr": "pc", + "uc_param_str": "", + "__dt": strconv.Itoa(rand.Int()), + "__t": strconv.FormatInt(time.Now().Unix(), 10), + } + var resp SaveResp + res, err := d.request("/share/sharepage/save", http.MethodPost, func(req *resty.Request) { + req.SetBody(data).SetQueryParams(query) + }, &resp) + log.Debugf("saveFile: %v %+v response: %v, error: %v", id, data, string(res), err) + if err != nil { + if strings.Contains(err.Error(), "token校验异常") { + fileTokenId, err = d.getFileToken(pid, fileId) + if err != nil { + return nil, err + } + continue + } else { + log.Warnf("save file failed: %v", err) + return nil, err + } + } + if resp.Status != 200 { + return nil, errors.New(resp.Message) + } + taskId := resp.Data.TaskId + log.Debugf("save file task id: %v", taskId) + + newFileId, dirId, err := d.getSaveTaskResult(taskId) + if err != nil { + return nil, err + } + log.Debugf("new file id: %v dirId: %v", newFileId, dirId) + file, err := quark.GetTempFile(ctx, dirId, newFileId) + if err != nil { + log.Warnf("get temp file failed: %v", err) + return nil, err + } + log.Debugf("new file: %+v", file) + return file, nil + } + return nil, errors.New("save file failed") +} + func (d *QuarkUCShare) getSaveTaskResult(taskId string) (string, string, error) { time.Sleep(200 * time.Millisecond) for retry := 1; retry <= 60; { @@ -232,6 +297,11 @@ func (d *QuarkUCShare) getDownloadUrl(ctx context.Context, quark *quark.QuarkOrU return quark.Link(ctx, file, args) } +func (d *QuarkUCShare) getTvDownloadUrl(ctx context.Context, quark *quark_uc_tv.QuarkUCTV, file model.Obj, args model.LinkArgs) (*model.Link, error) { + go d.deleteDelayTv(ctx, quark, file.GetID()) + return quark.Link(ctx, file, args) +} + func (d *QuarkUCShare) deleteDelay(quark *quark.QuarkOrUC, fileId string) { delayTime := setting.GetInt(conf.DeleteDelayTime, 900) if delayTime == 0 { @@ -247,6 +317,21 @@ func (d *QuarkUCShare) deleteDelay(quark *quark.QuarkOrUC, fileId string) { d.deleteFile(quark, fileId) } +func (d *QuarkUCShare) deleteDelayTv(ctx context.Context, quark *quark_uc_tv.QuarkUCTV, fileId string) { + delayTime := setting.GetInt(conf.DeleteDelayTime, 900) + if delayTime == 0 { + return + } + if delayTime < 5 { + delayTime = 5 + } + + name := d.getDriverName() + log.Infof("[%v] Delete %s temp file %v after %v seconds.", quark.ID, name, fileId, delayTime) + time.Sleep(time.Duration(delayTime) * time.Second) + d.deleteFileTv(ctx, quark, fileId) +} + func (d *QuarkUCShare) deleteFile(quark *quark.QuarkOrUC, fileId string) { name := d.getDriverName() log.Infof("[%v] Delete %s temp file: %v", quark.ID, name, fileId) @@ -267,6 +352,26 @@ func (d *QuarkUCShare) deleteFile(quark *quark.QuarkOrUC, fileId string) { } } +func (d *QuarkUCShare) deleteFileTv(ctx context.Context, quark *quark_uc_tv.QuarkUCTV, fileId string) { + name := d.getDriverName() + log.Infof("[%v] Delete %s temp file: %v", quark.ID, name, fileId) + data := base.Json{ + "action_type": 1, + "exclude_fids": []string{}, + "filelist": []string{fileId}, + } + var resp PlayResp + res, err := quark.Request(ctx, "/file/delete", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }, &resp) + log.Debugf("[%v] Delete %s temp file: %v %v", quark.ID, name, fileId, string(res)) + if err != nil { + log.Warnf("[%v] Delete %s temp file failed: %v %v", quark.ID, name, fileId, err) + } else if resp.Status != 200 { + log.Warnf("[%v] Delete %s temp file failed: %v %v", quark.ID, name, fileId, resp.Message) + } +} + func (d *QuarkUCShare) getShareFiles(id string) ([]File, error) { log.Debugf("getShareFiles: %v", id) s := strings.Split(id, "-") diff --git a/drivers/quark_uc_tv/driver.go b/drivers/quark_uc_tv/driver.go index dc062d9f..ad2a864d 100644 --- a/drivers/quark_uc_tv/driver.go +++ b/drivers/quark_uc_tv/driver.go @@ -22,6 +22,8 @@ type QuarkUCTV struct { Addition config driver.Config conf Conf + + TempDirId string } func (d *QuarkUCTV) Config() driver.Config { @@ -84,6 +86,7 @@ func (d *QuarkUCTV) Init(ctx context.Context) error { if err != nil { return err } + d.getTempFolder(ctx) return nil } diff --git a/drivers/quark_uc_tv/extension.go b/drivers/quark_uc_tv/extension.go new file mode 100644 index 00000000..4b891aa6 --- /dev/null +++ b/drivers/quark_uc_tv/extension.go @@ -0,0 +1,212 @@ +package quark_uc_tv + +import ( + "context" + "errors" + "net/http" + "strconv" + "time" + + "github.com/OpenListTeam/OpenList/v4/drivers/base" + "github.com/OpenListTeam/OpenList/v4/internal/conf" + "github.com/OpenListTeam/OpenList/v4/internal/model" + "github.com/OpenListTeam/OpenList/v4/internal/op" + "github.com/OpenListTeam/OpenList/v4/internal/token" + "github.com/OpenListTeam/OpenList/v4/pkg/cookie" + "github.com/OpenListTeam/OpenList/v4/pkg/utils" + "github.com/go-resty/resty/v2" + log "github.com/sirupsen/logrus" +) + +func (d *QuarkUCTV) getTempFolder(ctx context.Context) { + files, err := d.GetFiles(ctx, "0") + if err != nil { + log.Warnf("get files error: %v", err) + } + + for _, file := range files { + if file.GetName() == conf.TempDirName { + d.TempDirId = file.GetID() + log.Infof("%v temp folder id: %v", d.config.Name, d.TempDirId) + d.cleanTempFolder(ctx) + return + } + } + + d.createTempFolder() +} + +func (d *QuarkUCTV) createTempFolder() { + data := base.Json{ + "dir_init_lock": false, + "dir_path": "", + "file_name": conf.TempDirName, + "pdir_fid": "0", + } + res, err := d.request2("/file", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }, nil) + fid := utils.Json.Get(res, "data", "fid").ToString() + if fid != "" { + d.TempDirId = fid + } + log.Infof("create temp folder: %v", string(res)) + if err != nil { + log.Warnf("create temp folder error: %v", err) + } +} + +func (d *QuarkUCTV) cleanTempFolder(ctx context.Context) { + if d.TempDirId == "0" { + return + } + + files, err := d.GetFiles(ctx, d.TempDirId) + if err != nil { + log.Warnf("get files error: %v", err) + } + + for _, file := range files { + go d.deleteFile(file.GetID()) + } +} + +func (d *QuarkUCTV) GetTempFile(ctx context.Context, dirId, id string) (model.Obj, error) { + var dir = &Files{ + Fid: dirId, + } + var args = model.ListArgs{} + for i := 0; i < 3; i++ { + files, err := d.List(ctx, dir, args) + if err != nil { + return nil, err + } + + for _, file := range files { + if file.GetID() == id { + return file, nil + } + } + time.Sleep(100 * time.Millisecond) + } + + return nil, errors.New("file not found") +} + +func (d *QuarkUCTV) deleteFile(fileId string) error { + data := base.Json{ + "action_type": 1, + "exclude_fids": []string{}, + "filelist": []string{fileId}, + } + res, err := d.request2("/file/delete", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }, nil) + log.Debugf("deleteFile: %v %v", fileId, string(res)) + if err != nil { + log.Warnf("Delete %v temp file failed: %v %v", d.Config().Name, fileId, err) + return err + } + return nil +} + +func (d *QuarkUCTV) GetFiles(ctx context.Context, parent string) ([]model.Obj, error) { + files := make([]model.Obj, 0) + pageIndex := int64(0) + pageSize := int64(100) + desc := "1" + orderBy := "3" + if d.OrderDirection == "asc" { + desc = "0" + } + if d.OrderBy == "file_name" { + orderBy = "1" + } + for { + var filesData FilesData + _, err := d.request(ctx, "/file", http.MethodGet, func(req *resty.Request) { + req.SetQueryParams(map[string]string{ + "method": "list", + "parent_fid": parent, + "order_by": orderBy, + "desc": desc, + "category": "", + "source": "", + "ex_source": "", + "list_all": "0", + "page_size": strconv.FormatInt(pageSize, 10), + "page_index": strconv.FormatInt(pageIndex, 10), + }) + }, &filesData) + if err != nil { + return nil, err + } + for i := range filesData.Data.Files { + files = append(files, &filesData.Data.Files[i]) + } + if pageIndex*pageSize >= filesData.Data.TotalCount { + break + } else { + pageIndex++ + } + } + return files, nil +} + +func (d *QuarkUCTV) request2(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { + u := d.conf.api + pathname + req := base.RestyClient.R() + req.SetHeaders(map[string]string{ + "Cookie": d.Cookie, + "Accept": "application/json, text/plain, */*", + "Referer": d.conf.referer, + }) + req.SetQueryParam("pr", d.conf.pr) + req.SetQueryParam("fr", "pc") + if d.config.Name == "UC" { + req.SetQueryParam("sys", "darwin") + req.SetQueryParam("ve", "1.8.6") + } + if callback != nil { + callback(req) + } + if resp != nil { + req.SetResult(resp) + } + var e Resp2 + req.SetError(&e) + res, err := req.Execute(method, u) + if err != nil { + return nil, err + } + __puus := cookie.GetCookie(res.Cookies(), "__puus") + if __puus != nil { + log.Debugf("update __puus: %v", __puus) + d.Cookie = cookie.SetStr(d.Cookie, "__puus", __puus.Value) + d.SaveCookie(d.Cookie) + } else { + c := res.Request.Header.Get("Cookie") + v1 := cookie.GetStr(d.Cookie, "__puus") + v2 := cookie.GetStr(c, "__puus") + if v2 != "" && v1 != v2 { + d.Cookie = cookie.SetStr(d.Cookie, "__puus", v2) + log.Debugf("update cookie: %v %v %v", d.Cookie, v1, v2) + d.SaveCookie(d.Cookie) + } + } + + if e.Status >= 400 || e.Code != 0 { + return nil, errors.New(e.Message) + } + return res.Body(), nil +} + +func (d *QuarkUCTV) SaveCookie(cookie string) { + var key = conf.QUARK + if d.config.Name == "UC" { + key = conf.UC + } + d.Cookie = cookie + op.MustSaveDriverStorage(d) + token.SaveAccountToken(key, d.Cookie, int(d.ID)) +} diff --git a/drivers/quark_uc_tv/meta.go b/drivers/quark_uc_tv/meta.go index a8af1a13..ad54b5c8 100644 --- a/drivers/quark_uc_tv/meta.go +++ b/drivers/quark_uc_tv/meta.go @@ -19,8 +19,9 @@ type Addition struct { // 视频文件链接获取方式 download(可获取源视频) or streaming(获取转码后的视频) VideoLinkMethod string `json:"link_method" required:"true" type:"select" options:"download,streaming" default:"download"` - Concurrency int `json:"concurrency" type:"number" default:"4"` - ChunkSize int `json:"chunk_size" type:"number" default:"256"` + Concurrency int `json:"concurrency" type:"number" default:"4"` + ChunkSize int `json:"chunk_size" type:"number" default:"256"` + Cookie string `json:"cookie" required:"true"` } type Conf struct { @@ -30,6 +31,9 @@ type Conf struct { appVer string channel string codeApi string + ua string + referer string + pr string } func init() { @@ -48,6 +52,9 @@ func init() { appVer: "1.8.2.2", channel: "GENERAL", codeApi: "http://api.extscreen.com/quarkdrive", + ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch", + referer: "https://pan.quark.cn", + pr: "ucpro", }, } }) @@ -66,6 +73,9 @@ func init() { appVer: "1.7.2.2", channel: "UCTVOFFICIALWEB", codeApi: "http://api.extscreen.com/ucdrive", + ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uc-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch", + referer: "https://drive.uc.cn", + pr: "UCBrowser", }, } }) diff --git a/drivers/quark_uc_tv/types.go b/drivers/quark_uc_tv/types.go index b8e04f18..ab1fc7d9 100644 --- a/drivers/quark_uc_tv/types.go +++ b/drivers/quark_uc_tv/types.go @@ -13,6 +13,14 @@ type Resp struct { ErrorInfo string `json:"error_info"` } +type Resp2 struct { + Status int `json:"status"` + Code int `json:"code"` + Message string `json:"message"` + //ReqId string `json:"req_id"` + //Timestamp int `json:"timestamp"` +} + type CommonRsp struct { Status int `json:"status"` ReqID string `json:"req_id"` diff --git a/drivers/quark_uc_tv/util.go b/drivers/quark_uc_tv/util.go index 3ff61127..9f37e932 100644 --- a/drivers/quark_uc_tv/util.go +++ b/drivers/quark_uc_tv/util.go @@ -7,11 +7,12 @@ import ( "encoding/hex" "errors" "fmt" - log "github.com/sirupsen/logrus" "net/http" "strconv" "time" + log "github.com/sirupsen/logrus" + "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/drivers/base" @@ -32,6 +33,10 @@ const ( ActivityRect = "{}" ) +func (d *QuarkUCTV) Request(ctx context.Context, pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { + return d.request(ctx, pathname, method, callback, resp) +} + func (d *QuarkUCTV) request(ctx context.Context, pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { u := d.conf.api + pathname tm, token, reqID := d.generateReqSign(method, pathname, d.conf.signKey) @@ -243,6 +248,7 @@ func (d *QuarkUCTV) getTranscodingLink(ctx context.Context, file model.Obj) (*mo } if url != "" { + log.Debugf("transcoding link url: %v", url) return &model.Link{ URL: url + fmt.Sprintf("#storageId=%d", d.ID), ContentLength: size, @@ -269,6 +275,7 @@ func (d *QuarkUCTV) getDownloadLink(ctx context.Context, file model.Obj) (*model return nil, err } + log.Debugf("download link: %v", fileLink) return &model.Link{ URL: fileLink.Data.DownloadURL + fmt.Sprintf("#storageId=%d", d.ID), Concurrency: d.Concurrency, diff --git a/internal/bootstrap/data/setting.go b/internal/bootstrap/data/setting.go index f05fb18a..1385c96f 100644 --- a/internal/bootstrap/data/setting.go +++ b/internal/bootstrap/data/setting.go @@ -243,6 +243,7 @@ func InitialSettings() []model.SettingItem { {Key: conf.ExternalPort, Value: "5344", Type: conf.TypeNumber, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: conf.AliTo115, Value: "false", Type: conf.TypeBool, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: conf.DriverRoundRobin, Value: "false", Type: conf.TypeBool, Group: model.SINGLE, Flag: model.PRIVATE}, + {Key: conf.UssQuarkTv, Value: "true", Type: conf.TypeBool, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: "ali_lazy_load", Value: "true", Type: conf.TypeBool, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: "open_token_url", Value: "https://api.xhofe.top/alist/ali_open/token", Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: "open_api_client_id", Value: "", Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE}, diff --git a/internal/conf/const.go b/internal/conf/const.go index 1ef56208..d7ffa09b 100644 --- a/internal/conf/const.go +++ b/internal/conf/const.go @@ -106,6 +106,7 @@ const ( AliAccountId = "ali_account_id" TempDirName = "alist-tvbox-temp" DriverRoundRobin = "driver_round_robin" + UssQuarkTv = "use_quark_tv" // SSO SSOClientId = "sso_client_id"