Compare commits
11 Commits
v3.0.0-rc.
...
v3.0.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
2185839236 | |||
24d58f278a | |||
f80be96cf9 | |||
6c89c6c8ae | |||
b74b55fa4a | |||
09564102e7 | |||
d436a6e676 | |||
bec3a327a7 | |||
d329df70f3 | |||
1af9f4061e | |||
0d012f85cb |
@ -234,7 +234,10 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
||||
buf := make([]byte, 8)
|
||||
r, _ := new(big.Int).SetString(utils.GetMD5Encode(d.AccessToken)[:16], 16)
|
||||
i := new(big.Int).SetInt64(file.GetSize())
|
||||
o := r.Mod(r, i)
|
||||
o := new(big.Int).SetInt64(0)
|
||||
if file.GetSize() > 0 {
|
||||
o = r.Mod(r, i)
|
||||
}
|
||||
n, _ := io.NewSectionReader(tempFile, o.Int64(), 8).Read(buf[:8])
|
||||
reqBody["proof_code"] = base64.StdEncoding.EncodeToString(buf[:n])
|
||||
|
||||
|
@ -130,13 +130,10 @@ func (d *S3) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
}
|
||||
|
||||
func (d *S3) Remove(ctx context.Context, obj model.Obj) error {
|
||||
key := getKey(obj.GetPath(), obj.IsDir())
|
||||
input := &s3.DeleteObjectInput{
|
||||
Bucket: &d.Bucket,
|
||||
Key: &key,
|
||||
if obj.IsDir() {
|
||||
return d.removeDir(ctx, obj.GetPath())
|
||||
}
|
||||
_, err := d.client.DeleteObject(input)
|
||||
return err
|
||||
return d.removeFile(obj.GetPath())
|
||||
}
|
||||
|
||||
func (d *S3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
|
@ -52,12 +52,11 @@ func getKey(path string, dir bool) string {
|
||||
return path
|
||||
}
|
||||
|
||||
var defaultPlaceholderName = ".placeholder"
|
||||
|
||||
// var defaultPlaceholderName = ".placeholder"
|
||||
func getPlaceholderName(placeholder string) string {
|
||||
if placeholder == "" {
|
||||
return defaultPlaceholderName
|
||||
}
|
||||
//if placeholder == "" {
|
||||
// return defaultPlaceholderName
|
||||
//}
|
||||
return placeholder
|
||||
}
|
||||
|
||||
@ -205,3 +204,33 @@ func (d *S3) copyDir(ctx context.Context, src string, dst string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *S3) removeDir(ctx context.Context, src string) error {
|
||||
objs, err := op.List(ctx, d, src, model.ListArgs{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, obj := range objs {
|
||||
cSrc := path.Join(src, obj.GetName())
|
||||
if obj.IsDir() {
|
||||
err = d.removeDir(ctx, cSrc)
|
||||
} else {
|
||||
err = d.removeFile(cSrc)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = d.removeFile(path.Join(src, getPlaceholderName(d.Placeholder)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *S3) removeFile(src string) error {
|
||||
key := getKey(src, true)
|
||||
input := &s3.DeleteObjectInput{
|
||||
Bucket: &d.Bucket,
|
||||
Key: &key,
|
||||
}
|
||||
_, err := d.client.DeleteObject(input)
|
||||
return err
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ func (x *ThunderExpert) Init(ctx context.Context, storage model.Storage) (err er
|
||||
PackageName: x.PackageName,
|
||||
UserAgent: x.UserAgent,
|
||||
DownloadUserAgent: x.DownloadUserAgent,
|
||||
UseVideoUrl: x.UseVideoUrl,
|
||||
},
|
||||
}
|
||||
|
||||
@ -209,6 +210,7 @@ func (x *ThunderExpert) Init(ctx context.Context, storage model.Storage) (err er
|
||||
}
|
||||
x.XunLeiCommon.UserAgent = x.UserAgent
|
||||
x.XunLeiCommon.DownloadUserAgent = x.DownloadUserAgent
|
||||
x.XunLeiCommon.UseVideoUrl = x.UseVideoUrl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -252,6 +254,15 @@ func (xc *XunLeiCommon) Link(ctx context.Context, file model.Obj, args model.Lin
|
||||
},
|
||||
}
|
||||
|
||||
if xc.UseVideoUrl {
|
||||
for _, media := range lFile.Medias {
|
||||
if media.Link.URL != "" {
|
||||
link.URL = media.Link.URL
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
strs := regexp.MustCompile(`e=([0-9]*)`).FindStringSubmatch(lFile.WebContentLink)
|
||||
if len(strs) == 2 {
|
||||
|
@ -41,6 +41,9 @@ type ExpertAddition struct {
|
||||
//不影响登录,影响下载速度
|
||||
UserAgent string `json:"user_agent" required:"true" default:"ANDROID-com.xunlei.downloadprovider/7.51.0.8196 netWorkType/4G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gdcf98eab238b) (JAVA 0)"`
|
||||
DownloadUserAgent string `json:"download_user_agent" required:"true" default:"Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)"`
|
||||
|
||||
//优先使用视频链接代替下载链接
|
||||
UseVideoUrl bool `json:"use_video_url"`
|
||||
}
|
||||
|
||||
// 登录特征,用于判断是否重新登录
|
||||
|
@ -77,6 +77,13 @@ type FileList struct {
|
||||
VersionOutdated bool `json:"version_outdated"`
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
URL string `json:"url"`
|
||||
Token string `json:"token"`
|
||||
Expire time.Time `json:"expire"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type Files struct {
|
||||
Kind string `json:"kind"`
|
||||
ID string `json:"id"`
|
||||
@ -95,20 +102,20 @@ type Files struct {
|
||||
ThumbnailLink string `json:"thumbnail_link"`
|
||||
//Md5Checksum string `json:"md5_checksum"`
|
||||
//Hash string `json:"hash"`
|
||||
//Links struct{} `json:"links"`
|
||||
Links map[string]Link `json:"links"`
|
||||
Phase string `json:"phase"`
|
||||
Audit struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Title string `json:"title"`
|
||||
} `json:"audit"`
|
||||
/* Medias []struct {
|
||||
Medias []struct {
|
||||
Category string `json:"category"`
|
||||
IconLink string `json:"icon_link"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
IsOrigin bool `json:"is_origin"`
|
||||
IsVisible bool `json:"is_visible"`
|
||||
//Link interface{} `json:"link"`
|
||||
Link Link `json:"link"`
|
||||
MediaID string `json:"media_id"`
|
||||
MediaName string `json:"media_name"`
|
||||
NeedMoreQuota bool `json:"need_more_quota"`
|
||||
@ -126,7 +133,7 @@ type Files struct {
|
||||
Width int `json:"width"`
|
||||
} `json:"video"`
|
||||
VipTypes []string `json:"vip_types"`
|
||||
} `json:"medias"` */
|
||||
} `json:"medias"`
|
||||
Trashed bool `json:"trashed"`
|
||||
DeleteTime string `json:"delete_time"`
|
||||
OriginalURL string `json:"original_url"`
|
||||
|
@ -52,6 +52,7 @@ type Common struct {
|
||||
PackageName string
|
||||
UserAgent string
|
||||
DownloadUserAgent string
|
||||
UseVideoUrl bool
|
||||
}
|
||||
|
||||
func (c *Common) SetCaptchaToken(captchaToken string) {
|
||||
|
@ -76,10 +76,15 @@ func (m *Monitor) Update() (bool, error) {
|
||||
}
|
||||
m.retried = 0
|
||||
if len(info.FollowedBy) != 0 {
|
||||
log.Debugf("followen by: %+v", info.FollowedBy)
|
||||
gid := info.FollowedBy[0]
|
||||
notify.Signals.Delete(m.tsk.ID)
|
||||
oldId := m.tsk.ID
|
||||
m.tsk.ID = gid
|
||||
DownTaskManager.RawTasks().Delete(oldId)
|
||||
DownTaskManager.RawTasks().Store(m.tsk.ID, m.tsk)
|
||||
notify.Signals.Store(gid, m.c)
|
||||
return false, nil
|
||||
}
|
||||
// update download status
|
||||
total, err := strconv.ParseUint(info.TotalLength, 10, 64)
|
||||
@ -120,6 +125,7 @@ func (m *Monitor) Complete() error {
|
||||
}
|
||||
// get files
|
||||
files, err := client.GetFiles(m.tsk.ID)
|
||||
log.Debugf("files len: %d", len(files))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get files of %s", m.tsk.ID)
|
||||
}
|
||||
@ -134,7 +140,8 @@ func (m *Monitor) Complete() error {
|
||||
log.Errorf("failed to remove aria2 temp dir: %+v", err.Error())
|
||||
}
|
||||
}()
|
||||
for _, file := range files {
|
||||
for i, _ := range files {
|
||||
file := files[i]
|
||||
TransferTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
||||
Name: fmt.Sprintf("transfer %s to [%s](%s)", file.Path, storage.GetStorage().MountPath, dstDirActualPath),
|
||||
Func: func(tsk *task.Task[uint64]) error {
|
||||
|
@ -53,9 +53,6 @@ func InitDB() {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s",
|
||||
database.User, database.Password, database.Host, database.Port, database.Name, database.SSLMode)
|
||||
dB, err = gorm.Open(mysql.Open(dsn), gormConfig)
|
||||
if err == nil {
|
||||
dB = dB.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4")
|
||||
}
|
||||
}
|
||||
case "postgres":
|
||||
{
|
||||
|
@ -3,6 +3,7 @@ package db
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@ -11,7 +12,12 @@ var db *gorm.DB
|
||||
|
||||
func Init(d *gorm.DB) {
|
||||
db = d
|
||||
err := db.AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem))
|
||||
var err error
|
||||
if conf.Conf.Database.Type == "mysql" {
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4").AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem))
|
||||
} else {
|
||||
err = db.AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem))
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("failed migrate database: %s", err.Error())
|
||||
}
|
||||
|
@ -16,37 +16,43 @@ import (
|
||||
func list(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error) {
|
||||
meta := ctx.Value("meta").(*model.Meta)
|
||||
user := ctx.Value("user").(*model.User)
|
||||
var objs []model.Obj
|
||||
storage, actualPath, err := op.GetStorageAndActualPath(path)
|
||||
virtualFiles := op.GetStorageVirtualFilesByPath(path)
|
||||
if err != nil {
|
||||
if len(virtualFiles) != 0 {
|
||||
return virtualFiles, nil
|
||||
}
|
||||
if len(virtualFiles) == 0 {
|
||||
return nil, errors.WithMessage(err, "failed get storage")
|
||||
}
|
||||
objs, err := op.List(ctx, storage, actualPath, model.ListArgs{
|
||||
} else {
|
||||
objs, err = op.List(ctx, storage, actualPath, model.ListArgs{
|
||||
ReqPath: path,
|
||||
}, refresh...)
|
||||
if err != nil {
|
||||
log.Errorf("%+v", err)
|
||||
if len(virtualFiles) != 0 {
|
||||
return virtualFiles, nil
|
||||
}
|
||||
if len(virtualFiles) == 0 {
|
||||
return nil, errors.WithMessage(err, "failed get objs")
|
||||
}
|
||||
}
|
||||
}
|
||||
if objs == nil {
|
||||
objs = virtualFiles
|
||||
} else {
|
||||
for _, storageFile := range virtualFiles {
|
||||
if !containsByName(objs, storageFile) {
|
||||
objs = append(objs, storageFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
if whetherHide(user, meta, path) {
|
||||
objs = hide(objs, meta)
|
||||
}
|
||||
// sort objs
|
||||
if storage != nil {
|
||||
if storage.Config().LocalSort {
|
||||
model.SortFiles(objs, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
|
||||
}
|
||||
model.ExtractFolder(objs, storage.GetStorage().ExtractFolder)
|
||||
}
|
||||
return objs, nil
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
}
|
||||
key := Key(storage, path)
|
||||
if len(refresh) == 0 || !refresh[0] {
|
||||
if files, ok := listCache.Get(key); ok {
|
||||
if files, ok := listCache.Get(key); ok && len(files) > 0 {
|
||||
return files, nil
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,10 @@ func (tm *Manager[K]) ClearDone() {
|
||||
tm.RemoveByStates(SUCCEEDED, CANCELED, ERRORED)
|
||||
}
|
||||
|
||||
func (tm *Manager[K]) RawTasks() *generic_sync.MapOf[K, *Task[K]] {
|
||||
return &tm.tasks
|
||||
}
|
||||
|
||||
func NewTaskManager[K comparable](maxWorker int, updateID ...func(*K)) *Manager[K] {
|
||||
tm := &Manager[K]{
|
||||
tasks: generic_sync.MapOf[K, *Task[K]]{},
|
||||
|
@ -3,7 +3,9 @@ package utils
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetSHA1Encode(data string) string {
|
||||
@ -17,3 +19,20 @@ func GetMD5Encode(data string) string {
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
var DEC = map[string]string{
|
||||
"-": "+",
|
||||
"_": "/",
|
||||
".": "=",
|
||||
}
|
||||
|
||||
func SafeAtob(data string) (string, error) {
|
||||
for k, v := range DEC {
|
||||
data = strings.ReplaceAll(data, k, v)
|
||||
}
|
||||
bytes, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), err
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Favicon(c *gin.Context) {
|
||||
@ -18,20 +18,33 @@ func Favicon(c *gin.Context) {
|
||||
|
||||
func Plist(c *gin.Context) {
|
||||
link := c.Param("link")
|
||||
u, err := url.PathUnescape(link)
|
||||
u, err := utils.SafeAtob(link)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
uUrl, err := url.Parse(u)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
name := c.Param("name")
|
||||
log.Debug("name", name)
|
||||
u = uUrl.String()
|
||||
name = strings.TrimSuffix(name, ".plist")
|
||||
fullName := c.Param("name")
|
||||
Url := uUrl.String()
|
||||
fullName = strings.TrimSuffix(fullName, ".plist")
|
||||
fullName, err = utils.SafeAtob(fullName)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
name := fullName
|
||||
identifier := fmt.Sprintf("ci.nn.%s", url.PathEscape(fullName))
|
||||
sep := "@"
|
||||
if strings.Contains(fullName, sep) {
|
||||
ss := strings.Split(fullName, sep)
|
||||
name = strings.Join(ss[:len(ss)-1], sep)
|
||||
identifier = ss[len(ss)-1]
|
||||
}
|
||||
|
||||
name = strings.ReplaceAll(name, "<", "[")
|
||||
name = strings.ReplaceAll(name, ">", "]")
|
||||
plist := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
@ -46,13 +59,13 @@ func Plist(c *gin.Context) {
|
||||
<key>kind</key>
|
||||
<string>software-package</string>
|
||||
<key>url</key>
|
||||
<string>%s</string>
|
||||
<string><![CDATA[%s]]></string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>metadata</key>
|
||||
<dict>
|
||||
<key>bundle-identifier</key>
|
||||
<string>ci.nn.%s</string>
|
||||
<string>%s</string>
|
||||
<key>bundle-version</key>
|
||||
<string>4.4</string>
|
||||
<key>kind</key>
|
||||
@ -63,7 +76,7 @@ func Plist(c *gin.Context) {
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>`, u, url.PathEscape(name), name)
|
||||
</plist>`, Url, identifier, name)
|
||||
c.Header("Content-Type", "application/xml;charset=utf-8")
|
||||
c.Status(200)
|
||||
_, _ = c.Writer.WriteString(plist)
|
||||
|
Reference in New Issue
Block a user