Skip to content

Commit 62ed169

Browse files
authored
feat: add support for quark tv driver and uc tv driver (#6959)
1 parent 979d0cf commit 62ed169

File tree

5 files changed

+555
-0
lines changed

5 files changed

+555
-0
lines changed

drivers/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
_ "github.com/alist-org/alist/v3/drivers/pikpak"
4242
_ "github.com/alist-org/alist/v3/drivers/pikpak_share"
4343
_ "github.com/alist-org/alist/v3/drivers/quark_uc"
44+
_ "github.com/alist-org/alist/v3/drivers/quark_uc_tv"
4445
_ "github.com/alist-org/alist/v3/drivers/quqi"
4546
_ "github.com/alist-org/alist/v3/drivers/s3"
4647
_ "github.com/alist-org/alist/v3/drivers/seafile"

drivers/quark_uc_tv/driver.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package quark_uc_tv
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/alist-org/alist/v3/internal/op"
7+
"github.com/alist-org/alist/v3/pkg/utils"
8+
"github.com/go-resty/resty/v2"
9+
"strconv"
10+
"time"
11+
12+
"github.com/alist-org/alist/v3/internal/driver"
13+
"github.com/alist-org/alist/v3/internal/errs"
14+
"github.com/alist-org/alist/v3/internal/model"
15+
)
16+
17+
type QuarkUCTV struct {
18+
*QuarkUCTVCommon
19+
model.Storage
20+
Addition
21+
config driver.Config
22+
conf Conf
23+
}
24+
25+
func (d *QuarkUCTV) Config() driver.Config {
26+
return d.config
27+
}
28+
29+
func (d *QuarkUCTV) GetAddition() driver.Additional {
30+
return &d.Addition
31+
}
32+
33+
func (d *QuarkUCTV) Init(ctx context.Context) error {
34+
35+
if d.Addition.DeviceID == "" {
36+
d.Addition.DeviceID = utils.GetMD5EncodeStr(time.Now().String())
37+
}
38+
op.MustSaveDriverStorage(d)
39+
40+
if d.QuarkUCTVCommon == nil {
41+
d.QuarkUCTVCommon = &QuarkUCTVCommon{
42+
AccessToken: "",
43+
}
44+
}
45+
ctx1, cancelFunc := context.WithTimeout(ctx, 5*time.Second)
46+
defer cancelFunc()
47+
if d.Addition.RefreshToken == "" {
48+
if d.Addition.QueryToken == "" {
49+
qrData, err := d.getLoginCode(ctx1)
50+
if err != nil {
51+
return err
52+
}
53+
// 展示二维码
54+
qrTemplate := `<body>
55+
<img src="data:image/jpeg;base64,%s"/>
56+
</body>`
57+
qrPage := fmt.Sprintf(qrTemplate, qrData)
58+
return fmt.Errorf("need verify: \n%s", qrPage)
59+
} else {
60+
// 通过query token获取code -> refresh token
61+
code, err := d.getCode(ctx1)
62+
if err != nil {
63+
return err
64+
}
65+
// 通过code获取refresh token
66+
err = d.getRefreshTokenByTV(ctx1, code, false)
67+
if err != nil {
68+
return err
69+
}
70+
}
71+
}
72+
// 通过refresh token获取access token
73+
if d.QuarkUCTVCommon.AccessToken == "" {
74+
err := d.getRefreshTokenByTV(ctx1, d.Addition.RefreshToken, true)
75+
if err != nil {
76+
return err
77+
}
78+
}
79+
80+
// 验证 access token 是否有效
81+
_, err := d.isLogin(ctx1)
82+
if err != nil {
83+
return err
84+
}
85+
return nil
86+
}
87+
88+
func (d *QuarkUCTV) Drop(ctx context.Context) error {
89+
return nil
90+
}
91+
92+
func (d *QuarkUCTV) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
93+
files := make([]model.Obj, 0)
94+
pageIndex := int64(0)
95+
pageSize := int64(100)
96+
for {
97+
var filesData FilesData
98+
_, err := d.request(ctx, "/file", "GET", func(req *resty.Request) {
99+
req.SetQueryParams(map[string]string{
100+
"method": "list",
101+
"parent_fid": dir.GetID(),
102+
"order_by": "3",
103+
"desc": "1",
104+
"category": "",
105+
"source": "",
106+
"ex_source": "",
107+
"list_all": "0",
108+
"page_size": strconv.FormatInt(pageSize, 10),
109+
"page_index": strconv.FormatInt(pageIndex, 10),
110+
})
111+
}, &filesData)
112+
if err != nil {
113+
return nil, err
114+
}
115+
for i := range filesData.Data.Files {
116+
files = append(files, &filesData.Data.Files[i])
117+
}
118+
if pageIndex*pageSize >= filesData.Data.TotalCount {
119+
break
120+
} else {
121+
pageIndex++
122+
}
123+
}
124+
return files, nil
125+
}
126+
127+
func (d *QuarkUCTV) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
128+
files := &model.Link{}
129+
var fileLink FileLink
130+
_, err := d.request(ctx, "/file", "GET", func(req *resty.Request) {
131+
req.SetQueryParams(map[string]string{
132+
"method": "download",
133+
"group_by": "source",
134+
"fid": file.GetID(),
135+
"resolution": "low,normal,high,super,2k,4k",
136+
"support": "dolby_vision",
137+
})
138+
}, &fileLink)
139+
if err != nil {
140+
return nil, err
141+
}
142+
files.URL = fileLink.Data.DownloadURL
143+
return files, nil
144+
}
145+
146+
func (d *QuarkUCTV) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
147+
return nil, errs.NotImplement
148+
}
149+
150+
func (d *QuarkUCTV) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
151+
return nil, errs.NotImplement
152+
}
153+
154+
func (d *QuarkUCTV) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
155+
return nil, errs.NotImplement
156+
}
157+
158+
func (d *QuarkUCTV) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
159+
return nil, errs.NotImplement
160+
}
161+
162+
func (d *QuarkUCTV) Remove(ctx context.Context, obj model.Obj) error {
163+
return errs.NotImplement
164+
}
165+
166+
func (d *QuarkUCTV) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
167+
return nil, errs.NotImplement
168+
}
169+
170+
type QuarkUCTVCommon struct {
171+
AccessToken string
172+
}
173+
174+
var _ driver.Driver = (*QuarkUCTV)(nil)

drivers/quark_uc_tv/meta.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package quark_uc_tv
2+
3+
import (
4+
"github.com/alist-org/alist/v3/internal/driver"
5+
"github.com/alist-org/alist/v3/internal/op"
6+
)
7+
8+
type Addition struct {
9+
// Usually one of two
10+
driver.RootID
11+
// define other
12+
RefreshToken string `json:"refresh_token" required:"false" default:""`
13+
// 必要且影响登录,由签名决定
14+
DeviceID string `json:"device_id" required:"false" default:""`
15+
// 登陆所用的数据 无需手动填写
16+
QueryToken string `json:"query_token" required:"false" default:"" help:"don't edit'"`
17+
}
18+
19+
type Conf struct {
20+
api string
21+
clientID string
22+
signKey string
23+
appVer string
24+
channel string
25+
codeApi string
26+
}
27+
28+
func init() {
29+
op.RegisterDriver(func() driver.Driver {
30+
return &QuarkUCTV{
31+
config: driver.Config{
32+
Name: "QuarkTV",
33+
OnlyLocal: false,
34+
DefaultRoot: "0",
35+
NoOverwriteUpload: true,
36+
NoUpload: true,
37+
},
38+
conf: Conf{
39+
api: "https://open-api-drive.quark.cn",
40+
clientID: "d3194e61504e493eb6222857bccfed94",
41+
signKey: "kw2dvtd7p4t3pjl2d9ed9yc8yej8kw2d",
42+
appVer: "1.5.6",
43+
channel: "CP",
44+
codeApi: "http://api.extscreen.com/quarkdrive",
45+
},
46+
}
47+
})
48+
op.RegisterDriver(func() driver.Driver {
49+
return &QuarkUCTV{
50+
config: driver.Config{
51+
Name: "UCTV",
52+
OnlyLocal: false,
53+
DefaultRoot: "0",
54+
NoOverwriteUpload: true,
55+
NoUpload: true,
56+
},
57+
conf: Conf{
58+
api: "https://open-api-drive.uc.cn",
59+
clientID: "5acf882d27b74502b7040b0c65519aa7",
60+
signKey: "l3srvtd7p42l0d0x1u8d7yc8ye9kki4d",
61+
appVer: "1.6.5",
62+
channel: "UCTVOFFICIALWEB",
63+
codeApi: "http://api.extscreen.com/ucdrive",
64+
},
65+
}
66+
})
67+
}

drivers/quark_uc_tv/types.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package quark_uc_tv
2+
3+
import (
4+
"github.com/alist-org/alist/v3/internal/model"
5+
"github.com/alist-org/alist/v3/pkg/utils"
6+
"time"
7+
)
8+
9+
type Resp struct {
10+
CommonRsp
11+
Errno int `json:"errno"`
12+
ErrorInfo string `json:"error_info"`
13+
}
14+
15+
type CommonRsp struct {
16+
Status int `json:"status"`
17+
ReqID string `json:"req_id"`
18+
}
19+
20+
type RefreshTokenAuthResp struct {
21+
Code int `json:"code"`
22+
Message string `json:"message"`
23+
Data struct {
24+
Status int `json:"status"`
25+
Errno int `json:"errno"`
26+
ErrorInfo string `json:"error_info"`
27+
ReqID string `json:"req_id"`
28+
AccessToken string `json:"access_token"`
29+
RefreshToken string `json:"refresh_token"`
30+
ExpiresIn int `json:"expires_in"`
31+
Scope string `json:"scope"`
32+
} `json:"data"`
33+
}
34+
type Files struct {
35+
Fid string `json:"fid"`
36+
ParentFid string `json:"parent_fid"`
37+
Category int `json:"category"`
38+
Filename string `json:"filename"`
39+
Size int64 `json:"size"`
40+
FileType string `json:"file_type"`
41+
SubItems int `json:"sub_items,omitempty"`
42+
Isdir int `json:"isdir"`
43+
Duration int `json:"duration"`
44+
CreatedAt int64 `json:"created_at"`
45+
UpdatedAt int64 `json:"updated_at"`
46+
IsBackup int `json:"is_backup"`
47+
ThumbnailURL string `json:"thumbnail_url,omitempty"`
48+
}
49+
50+
func (f *Files) GetSize() int64 {
51+
return f.Size
52+
}
53+
54+
func (f *Files) GetName() string {
55+
return f.Filename
56+
}
57+
58+
func (f *Files) ModTime() time.Time {
59+
//return time.Unix(f.UpdatedAt, 0)
60+
return time.Unix(0, f.UpdatedAt*int64(time.Millisecond))
61+
}
62+
63+
func (f *Files) CreateTime() time.Time {
64+
//return time.Unix(f.CreatedAt, 0)
65+
return time.Unix(0, f.CreatedAt*int64(time.Millisecond))
66+
}
67+
68+
func (f *Files) IsDir() bool {
69+
return f.Isdir == 1
70+
}
71+
72+
func (f *Files) GetHash() utils.HashInfo {
73+
return utils.HashInfo{}
74+
}
75+
76+
func (f *Files) GetID() string {
77+
return f.Fid
78+
}
79+
80+
func (f *Files) GetPath() string {
81+
return ""
82+
}
83+
84+
var _ model.Obj = (*Files)(nil)
85+
86+
type FilesData struct {
87+
CommonRsp
88+
Data struct {
89+
TotalCount int64 `json:"total_count"`
90+
Files []Files `json:"files"`
91+
} `json:"data"`
92+
}
93+
94+
type FileLink struct {
95+
CommonRsp
96+
Data struct {
97+
Fid string `json:"fid"`
98+
FileName string `json:"file_name"`
99+
Size int64 `json:"size"`
100+
DownloadURL string `json:"download_url"`
101+
} `json:"data"`
102+
}

0 commit comments

Comments
 (0)