Compare commits

...

11 Commits

15 changed files with 166 additions and 63 deletions

View File

@ -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])

View File

@ -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 {

View File

@ -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
}

View File

@ -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 {

View File

@ -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"`
}
// 登录特征,用于判断是否重新登录

View File

@ -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"`

View File

@ -52,6 +52,7 @@ type Common struct {
PackageName string
UserAgent string
DownloadUserAgent string
UseVideoUrl bool
}
func (c *Common) SetCaptchaToken(captchaToken string) {

View File

@ -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 {

View File

@ -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":
{

View File

@ -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())
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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]]{},

View File

@ -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
}

View File

@ -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)