Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/mediafire/driver.go
1986 views
1
package mediafire
2
3
/*
4
Package mediafire
5
Author: Da3zKi7<[email protected]>
6
Date: 2025-09-11
7
8
D@' 3z K!7 - The King Of Cracking
9
*/
10
11
import (
12
"context"
13
"fmt"
14
"math/rand"
15
"net/http"
16
"os"
17
"time"
18
19
"github.com/alist-org/alist/v3/drivers/base"
20
"github.com/alist-org/alist/v3/internal/driver"
21
"github.com/alist-org/alist/v3/internal/errs"
22
"github.com/alist-org/alist/v3/internal/model"
23
"github.com/alist-org/alist/v3/pkg/cron"
24
"github.com/alist-org/alist/v3/pkg/utils"
25
)
26
27
type Mediafire struct {
28
model.Storage
29
Addition
30
cron *cron.Cron
31
32
actionToken string
33
34
appBase string
35
apiBase string
36
hostBase string
37
maxRetries int
38
39
secChUa string
40
secChUaPlatform string
41
userAgent string
42
}
43
44
func (d *Mediafire) Config() driver.Config {
45
return config
46
}
47
48
func (d *Mediafire) GetAddition() driver.Additional {
49
return &d.Addition
50
}
51
52
func (d *Mediafire) Init(ctx context.Context) error {
53
if d.SessionToken == "" {
54
return fmt.Errorf("Init :: [MediaFire] {critical} missing sessionToken")
55
}
56
57
if d.Cookie == "" {
58
return fmt.Errorf("Init :: [MediaFire] {critical} missing Cookie")
59
}
60
61
if _, err := d.getSessionToken(ctx); err != nil {
62
63
d.renewToken(ctx)
64
65
num := rand.Intn(4) + 6
66
67
d.cron = cron.NewCron(time.Minute * time.Duration(num))
68
d.cron.Do(func() {
69
d.renewToken(ctx)
70
})
71
72
}
73
74
return nil
75
}
76
77
func (d *Mediafire) Drop(ctx context.Context) error {
78
return nil
79
}
80
81
func (d *Mediafire) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
82
files, err := d.getFiles(ctx, dir.GetID())
83
if err != nil {
84
return nil, err
85
}
86
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
87
return d.fileToObj(src), nil
88
})
89
}
90
91
func (d *Mediafire) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
92
93
downloadUrl, err := d.getDirectDownloadLink(ctx, file.GetID())
94
if err != nil {
95
return nil, err
96
}
97
98
res, err := base.NoRedirectClient.R().SetDoNotParseResponse(true).SetContext(ctx).Get(downloadUrl)
99
if err != nil {
100
return nil, err
101
}
102
defer func() {
103
_ = res.RawBody().Close()
104
}()
105
106
if res.StatusCode() == 302 {
107
downloadUrl = res.Header().Get("location")
108
}
109
110
return &model.Link{
111
URL: downloadUrl,
112
Header: http.Header{
113
"Origin": []string{d.appBase},
114
"Referer": []string{d.appBase + "/"},
115
"sec-ch-ua": []string{d.secChUa},
116
"sec-ch-ua-platform": []string{d.secChUaPlatform},
117
"User-Agent": []string{d.userAgent},
118
//"User-Agent": []string{base.UserAgent},
119
},
120
}, nil
121
}
122
123
func (d *Mediafire) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
124
data := map[string]string{
125
"session_token": d.SessionToken,
126
"response_format": "json",
127
"parent_key": parentDir.GetID(),
128
"foldername": dirName,
129
}
130
131
var resp MediafireFolderCreateResponse
132
_, err := d.postForm("/folder/create.php", data, &resp)
133
if err != nil {
134
return nil, err
135
}
136
137
if resp.Response.Result != "Success" {
138
return nil, fmt.Errorf("MediaFire API error: %s", resp.Response.Result)
139
}
140
141
created, _ := time.Parse("2006-01-02T15:04:05Z", resp.Response.CreatedUTC)
142
143
return &model.ObjThumb{
144
Object: model.Object{
145
ID: resp.Response.FolderKey,
146
Name: resp.Response.Name,
147
Size: 0,
148
Modified: created,
149
Ctime: created,
150
IsFolder: true,
151
},
152
Thumbnail: model.Thumbnail{},
153
}, nil
154
}
155
156
func (d *Mediafire) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
157
var data map[string]string
158
var endpoint string
159
160
if srcObj.IsDir() {
161
162
endpoint = "/folder/move.php"
163
data = map[string]string{
164
"session_token": d.SessionToken,
165
"response_format": "json",
166
"folder_key_src": srcObj.GetID(),
167
"folder_key_dst": dstDir.GetID(),
168
}
169
} else {
170
171
endpoint = "/file/move.php"
172
data = map[string]string{
173
"session_token": d.SessionToken,
174
"response_format": "json",
175
"quick_key": srcObj.GetID(),
176
"folder_key": dstDir.GetID(),
177
}
178
}
179
180
var resp MediafireMoveResponse
181
_, err := d.postForm(endpoint, data, &resp)
182
if err != nil {
183
return nil, err
184
}
185
186
if resp.Response.Result != "Success" {
187
return nil, fmt.Errorf("MediaFire API error: %s", resp.Response.Result)
188
}
189
190
return srcObj, nil
191
}
192
193
func (d *Mediafire) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
194
var data map[string]string
195
var endpoint string
196
197
if srcObj.IsDir() {
198
199
endpoint = "/folder/update.php"
200
data = map[string]string{
201
"session_token": d.SessionToken,
202
"response_format": "json",
203
"folder_key": srcObj.GetID(),
204
"foldername": newName,
205
}
206
} else {
207
208
endpoint = "/file/update.php"
209
data = map[string]string{
210
"session_token": d.SessionToken,
211
"response_format": "json",
212
"quick_key": srcObj.GetID(),
213
"filename": newName,
214
}
215
}
216
217
var resp MediafireRenameResponse
218
_, err := d.postForm(endpoint, data, &resp)
219
if err != nil {
220
return nil, err
221
}
222
223
if resp.Response.Result != "Success" {
224
return nil, fmt.Errorf("MediaFire API error: %s", resp.Response.Result)
225
}
226
227
return &model.ObjThumb{
228
Object: model.Object{
229
ID: srcObj.GetID(),
230
Name: newName,
231
Size: srcObj.GetSize(),
232
Modified: srcObj.ModTime(),
233
Ctime: srcObj.CreateTime(),
234
IsFolder: srcObj.IsDir(),
235
},
236
Thumbnail: model.Thumbnail{},
237
}, nil
238
}
239
240
func (d *Mediafire) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
241
var data map[string]string
242
var endpoint string
243
244
if srcObj.IsDir() {
245
246
endpoint = "/folder/copy.php"
247
data = map[string]string{
248
"session_token": d.SessionToken,
249
"response_format": "json",
250
"folder_key_src": srcObj.GetID(),
251
"folder_key_dst": dstDir.GetID(),
252
}
253
} else {
254
255
endpoint = "/file/copy.php"
256
data = map[string]string{
257
"session_token": d.SessionToken,
258
"response_format": "json",
259
"quick_key": srcObj.GetID(),
260
"folder_key": dstDir.GetID(),
261
}
262
}
263
264
var resp MediafireCopyResponse
265
_, err := d.postForm(endpoint, data, &resp)
266
if err != nil {
267
return nil, err
268
}
269
270
if resp.Response.Result != "Success" {
271
return nil, fmt.Errorf("MediaFire API error: %s", resp.Response.Result)
272
}
273
274
var newID string
275
if srcObj.IsDir() {
276
if len(resp.Response.NewFolderKeys) > 0 {
277
newID = resp.Response.NewFolderKeys[0]
278
}
279
} else {
280
if len(resp.Response.NewQuickKeys) > 0 {
281
newID = resp.Response.NewQuickKeys[0]
282
}
283
}
284
285
return &model.ObjThumb{
286
Object: model.Object{
287
ID: newID,
288
Name: srcObj.GetName(),
289
Size: srcObj.GetSize(),
290
Modified: srcObj.ModTime(),
291
Ctime: srcObj.CreateTime(),
292
IsFolder: srcObj.IsDir(),
293
},
294
Thumbnail: model.Thumbnail{},
295
}, nil
296
}
297
298
func (d *Mediafire) Remove(ctx context.Context, obj model.Obj) error {
299
var data map[string]string
300
var endpoint string
301
302
if obj.IsDir() {
303
304
endpoint = "/folder/delete.php"
305
data = map[string]string{
306
"session_token": d.SessionToken,
307
"response_format": "json",
308
"folder_key": obj.GetID(),
309
}
310
} else {
311
312
endpoint = "/file/delete.php"
313
data = map[string]string{
314
"session_token": d.SessionToken,
315
"response_format": "json",
316
"quick_key": obj.GetID(),
317
}
318
}
319
320
var resp MediafireRemoveResponse
321
_, err := d.postForm(endpoint, data, &resp)
322
if err != nil {
323
return err
324
}
325
326
if resp.Response.Result != "Success" {
327
return fmt.Errorf("MediaFire API error: %s", resp.Response.Result)
328
}
329
330
return nil
331
}
332
333
func (d *Mediafire) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
334
_, err := d.PutResult(ctx, dstDir, file, up)
335
return err
336
}
337
338
func (d *Mediafire) PutResult(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
339
340
tempFile, err := file.CacheFullInTempFile()
341
if err != nil {
342
return nil, err
343
}
344
defer tempFile.Close()
345
346
osFile, ok := tempFile.(*os.File)
347
if !ok {
348
return nil, fmt.Errorf("expected *os.File, got %T", tempFile)
349
}
350
351
fileHash, err := d.calculateSHA256(osFile)
352
if err != nil {
353
return nil, err
354
}
355
356
checkResp, err := d.uploadCheck(ctx, file.GetName(), file.GetSize(), fileHash, dstDir.GetID())
357
if err != nil {
358
return nil, err
359
}
360
361
if checkResp.Response.ResumableUpload.AllUnitsReady == "yes" {
362
up(100.0)
363
}
364
365
if checkResp.Response.HashExists == "yes" && checkResp.Response.InAccount == "yes" {
366
up(100.0)
367
existingFile, err := d.getExistingFileInfo(ctx, fileHash, file.GetName(), dstDir.GetID())
368
if err == nil {
369
return existingFile, nil
370
}
371
}
372
373
var pollKey string
374
375
if checkResp.Response.ResumableUpload.AllUnitsReady != "yes" {
376
377
var err error
378
379
pollKey, err = d.uploadUnits(ctx, osFile, checkResp, file.GetName(), fileHash, dstDir.GetID(), up)
380
if err != nil {
381
return nil, err
382
}
383
} else {
384
385
pollKey = checkResp.Response.ResumableUpload.UploadKey
386
}
387
388
//fmt.Printf("pollKey: %+v\n", pollKey)
389
390
pollResp, err := d.pollUpload(ctx, pollKey)
391
if err != nil {
392
return nil, err
393
}
394
395
quickKey := pollResp.Response.Doupload.QuickKey
396
397
return &model.ObjThumb{
398
Object: model.Object{
399
ID: quickKey,
400
Name: file.GetName(),
401
Size: file.GetSize(),
402
},
403
Thumbnail: model.Thumbnail{},
404
}, nil
405
}
406
407
func (d *Mediafire) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
408
// TODO get archive file meta-info, return errs.NotImplement to use an internal archive tool, optional
409
return nil, errs.NotImplement
410
}
411
412
func (d *Mediafire) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
413
// TODO list args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
414
return nil, errs.NotImplement
415
}
416
417
func (d *Mediafire) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
418
// TODO return link of file args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
419
return nil, errs.NotImplement
420
}
421
422
func (d *Mediafire) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) ([]model.Obj, error) {
423
// TODO extract args.InnerPath path in the archive srcObj to the dstDir location, optional
424
// a folder with the same name as the archive file needs to be created to store the extracted results if args.PutIntoNewDir
425
// return errs.NotImplement to use an internal archive tool
426
return nil, errs.NotImplement
427
}
428
429
//func (d *Mediafire) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
430
// return nil, errs.NotSupport
431
//}
432
433
var _ driver.Driver = (*Mediafire)(nil)
434
435