Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/mega/driver.go
1986 views
1
package mega
2
3
import (
4
"context"
5
"errors"
6
"fmt"
7
"io"
8
"time"
9
10
"github.com/alist-org/alist/v3/pkg/http_range"
11
"github.com/pquerna/otp/totp"
12
"github.com/rclone/rclone/lib/readers"
13
14
"github.com/alist-org/alist/v3/internal/driver"
15
"github.com/alist-org/alist/v3/internal/errs"
16
"github.com/alist-org/alist/v3/internal/model"
17
"github.com/alist-org/alist/v3/pkg/utils"
18
log "github.com/sirupsen/logrus"
19
"github.com/t3rm1n4l/go-mega"
20
)
21
22
type Mega struct {
23
model.Storage
24
Addition
25
c *mega.Mega
26
}
27
28
func (d *Mega) Config() driver.Config {
29
return config
30
}
31
32
func (d *Mega) GetAddition() driver.Additional {
33
return &d.Addition
34
}
35
36
func (d *Mega) Init(ctx context.Context) error {
37
var twoFACode = d.TwoFACode
38
d.c = mega.New()
39
if d.TwoFASecret != "" {
40
code, err := totp.GenerateCode(d.TwoFASecret, time.Now())
41
if err != nil {
42
return fmt.Errorf("generate totp code failed: %w", err)
43
}
44
twoFACode = code
45
}
46
return d.c.MultiFactorLogin(d.Email, d.Password, twoFACode)
47
}
48
49
func (d *Mega) Drop(ctx context.Context) error {
50
return nil
51
}
52
53
func (d *Mega) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
54
if node, ok := dir.(*MegaNode); ok {
55
nodes, err := d.c.FS.GetChildren(node.n)
56
if err != nil {
57
return nil, err
58
}
59
fn := make(map[string]model.Obj)
60
for i := range nodes {
61
n := nodes[i]
62
if n.GetType() != mega.FILE && n.GetType() != mega.FOLDER {
63
continue
64
}
65
if _, ok := fn[n.GetName()]; !ok {
66
fn[n.GetName()] = &MegaNode{n}
67
} else if sameNameObj := fn[n.GetName()]; (&MegaNode{n}).ModTime().After(sameNameObj.ModTime()) {
68
fn[n.GetName()] = &MegaNode{n}
69
}
70
}
71
res := make([]model.Obj, 0)
72
for _, v := range fn {
73
res = append(res, v)
74
}
75
return res, nil
76
}
77
log.Errorf("can't convert: %+v", dir)
78
return nil, fmt.Errorf("unable to convert dir to mega n")
79
}
80
81
func (d *Mega) GetRoot(ctx context.Context) (model.Obj, error) {
82
n := d.c.FS.GetRoot()
83
log.Debugf("mega root: %+v", *n)
84
return &MegaNode{n}, nil
85
}
86
87
func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
88
if node, ok := file.(*MegaNode); ok {
89
90
//down, err := d.c.NewDownload(n.Node)
91
//if err != nil {
92
// return nil, fmt.Errorf("open download file failed: %w", err)
93
//}
94
95
size := file.GetSize()
96
resultRangeReader := func(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error) {
97
length := httpRange.Length
98
if httpRange.Length >= 0 && httpRange.Start+httpRange.Length >= size {
99
length = -1
100
}
101
var down *mega.Download
102
err := utils.Retry(3, time.Second, func() (err error) {
103
down, err = d.c.NewDownload(node.n)
104
return err
105
})
106
if err != nil {
107
return nil, fmt.Errorf("open download file failed: %w", err)
108
}
109
oo := &openObject{
110
ctx: ctx,
111
d: down,
112
skip: httpRange.Start,
113
}
114
115
return readers.NewLimitedReadCloser(oo, length), nil
116
}
117
resultRangeReadCloser := &model.RangeReadCloser{RangeReader: resultRangeReader}
118
resultLink := &model.Link{
119
RangeReadCloser: resultRangeReadCloser,
120
}
121
return resultLink, nil
122
}
123
return nil, fmt.Errorf("unable to convert dir to mega n")
124
}
125
126
func (d *Mega) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
127
if parentNode, ok := parentDir.(*MegaNode); ok {
128
_, err := d.c.CreateDir(dirName, parentNode.n)
129
return err
130
}
131
return fmt.Errorf("unable to convert dir to mega n")
132
}
133
134
func (d *Mega) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
135
if srcNode, ok := srcObj.(*MegaNode); ok {
136
if dstNode, ok := dstDir.(*MegaNode); ok {
137
return d.c.Move(srcNode.n, dstNode.n)
138
}
139
}
140
return fmt.Errorf("unable to convert dir to mega n")
141
}
142
143
func (d *Mega) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
144
if srcNode, ok := srcObj.(*MegaNode); ok {
145
return d.c.Rename(srcNode.n, newName)
146
}
147
return fmt.Errorf("unable to convert dir to mega n")
148
}
149
150
func (d *Mega) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
151
return errs.NotImplement
152
}
153
154
func (d *Mega) Remove(ctx context.Context, obj model.Obj) error {
155
if node, ok := obj.(*MegaNode); ok {
156
return d.c.Delete(node.n, false)
157
}
158
return fmt.Errorf("unable to convert dir to mega n")
159
}
160
161
func (d *Mega) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
162
if dstNode, ok := dstDir.(*MegaNode); ok {
163
u, err := d.c.NewUpload(dstNode.n, stream.GetName(), stream.GetSize())
164
if err != nil {
165
return err
166
}
167
168
reader := driver.NewLimitedUploadStream(ctx, stream)
169
for id := 0; id < u.Chunks(); id++ {
170
if utils.IsCanceled(ctx) {
171
return ctx.Err()
172
}
173
_, chkSize, err := u.ChunkLocation(id)
174
if err != nil {
175
return err
176
}
177
chunk := make([]byte, chkSize)
178
n, err := io.ReadFull(reader, chunk)
179
if err != nil && err != io.EOF {
180
return err
181
}
182
if n != len(chunk) {
183
return errors.New("chunk too short")
184
}
185
186
err = u.UploadChunk(id, chunk)
187
if err != nil {
188
return err
189
}
190
up(float64(id) * 100 / float64(u.Chunks()))
191
}
192
193
_, err = u.Finish()
194
return err
195
}
196
return fmt.Errorf("unable to convert dir to mega n")
197
}
198
199
//func (d *Mega) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
200
// return nil, errs.NotSupport
201
//}
202
203
var _ driver.Driver = (*Mega)(nil)
204
205