mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-11-25 11:29:29 +08:00
Compare commits
8 Commits
renovate/g
...
v4.1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0857478516 | ||
|
|
66d9809057 | ||
|
|
db8a7e8caf | ||
|
|
8f18e34da0 | ||
|
|
525f26dc23 | ||
|
|
a0fcfa3ed2 | ||
|
|
15f276537c | ||
|
|
623a12050e |
@@ -2,6 +2,7 @@ package flags
|
||||
|
||||
var (
|
||||
DataDir string
|
||||
ConfigPath string
|
||||
Debug bool
|
||||
NoPrefix bool
|
||||
Dev bool
|
||||
|
||||
@@ -27,7 +27,8 @@ func Execute() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "data folder")
|
||||
RootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "data directory (relative paths are resolved against the current working directory)")
|
||||
RootCmd.PersistentFlags().StringVar(&flags.ConfigPath, "config", "", "path to config.json (relative to current working directory; defaults to [data directory]/config.json, where [data directory] is set by --data)")
|
||||
RootCmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "start with debug mode")
|
||||
RootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
|
||||
RootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
|
||||
|
||||
@@ -17,7 +17,7 @@ type Addition struct {
|
||||
var config = driver.Config{
|
||||
Name: "115 Cloud",
|
||||
DefaultRoot: "0",
|
||||
LinkCacheType: 2,
|
||||
LinkCacheMode: driver.LinkCacheUA,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -19,7 +19,7 @@ type Addition struct {
|
||||
var config = driver.Config{
|
||||
Name: "115 Open",
|
||||
DefaultRoot: "0",
|
||||
LinkCacheType: 2,
|
||||
LinkCacheMode: driver.LinkCacheUA,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -41,7 +41,9 @@ func (d *Pan123) GetAddition() driver.Additional {
|
||||
}
|
||||
|
||||
func (d *Pan123) Init(ctx context.Context) error {
|
||||
_, err := d.Request(UserInfo, http.MethodGet, nil, nil)
|
||||
_, err := d.Request(UserInfo, http.MethodGet, func(req *resty.Request) {
|
||||
req.SetHeader("platform", "web")
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ type Addition struct {
|
||||
//OrderBy string `json:"order_by" type:"select" options:"file_id,file_name,size,update_at" default:"file_name"`
|
||||
//OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||
AccessToken string
|
||||
UploadThread int `json:"UploadThread" type:"number" default:"3" help:"the threads of upload"`
|
||||
UploadThread int `json:"UploadThread" type:"number" default:"3" help:"the threads of upload"`
|
||||
Platform string `json:"platform" type:"string" default:"web" help:"the platform header value, sent with API requests"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
||||
@@ -203,7 +203,7 @@ do:
|
||||
"referer": "https://www.123pan.com/",
|
||||
"authorization": "Bearer " + d.AccessToken,
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) openlist-client",
|
||||
"platform": "web",
|
||||
"platform": d.Platform,
|
||||
"app-version": "3",
|
||||
//"user-agent": base.UserAgent,
|
||||
})
|
||||
|
||||
@@ -524,4 +524,25 @@ func (d *Alias) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Alias) ResolveLinkCacheMode(path string) driver.LinkCacheMode {
|
||||
root, sub := d.getRootAndPath(path)
|
||||
dsts, ok := d.pathMap[root]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
for _, dst := range dsts {
|
||||
storage, actualPath, err := op.GetStorageAndActualPath(stdpath.Join(dst, sub))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
mode := storage.Config().LinkCacheMode
|
||||
if mode == -1 {
|
||||
return storage.(driver.LinkCacheModeResolver).ResolveLinkCacheMode(actualPath)
|
||||
} else {
|
||||
return mode
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*Alias)(nil)
|
||||
|
||||
@@ -26,6 +26,7 @@ var config = driver.Config{
|
||||
NoUpload: false,
|
||||
DefaultRoot: "/",
|
||||
ProxyRangeOption: true,
|
||||
LinkCacheMode: driver.LinkCacheAuto,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -299,10 +299,7 @@ func (d *AliyundriveOpen) GetDetails(ctx context.Context) (*model.StorageDetails
|
||||
total := utils.Json.Get(res, "personal_space_info", "total_size").ToUint64()
|
||||
used := utils.Json.Get(res, "personal_space_info", "used_size").ToUint64()
|
||||
return &model.StorageDetails{
|
||||
DiskUsage: model.DiskUsage{
|
||||
TotalSpace: total,
|
||||
FreeSpace: total - used,
|
||||
},
|
||||
DiskUsage: driver.DiskUsageFromUsedAndTotal(used, total),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ type Addition struct {
|
||||
var config = driver.Config{
|
||||
Name: "BaiduPhoto",
|
||||
LocalSort: true,
|
||||
LinkCacheType: 2,
|
||||
LinkCacheMode: driver.LinkCacheUA,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -19,7 +19,7 @@ var config = driver.Config{
|
||||
Name: "FebBox",
|
||||
NoUpload: true,
|
||||
DefaultRoot: "0",
|
||||
LinkCacheType: 1,
|
||||
LinkCacheMode: driver.LinkCacheIP,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -26,11 +26,6 @@ type OpenList struct {
|
||||
}
|
||||
|
||||
func (d *OpenList) Config() driver.Config {
|
||||
if d.PassUAToUpsteam {
|
||||
c := config
|
||||
c.LinkCacheType = 2 // add User-Agent to cache key
|
||||
return c
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -115,19 +110,29 @@ func (d *OpenList) List(ctx context.Context, dir model.Obj, args model.ListArgs)
|
||||
|
||||
func (d *OpenList) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
var resp common.Resp[FsGetResp]
|
||||
headers := map[string]string{
|
||||
"User-Agent": base.UserAgent,
|
||||
}
|
||||
// if PassUAToUpsteam is true, then pass the user-agent to the upstream
|
||||
userAgent := base.UserAgent
|
||||
if d.PassUAToUpsteam {
|
||||
userAgent = args.Header.Get("user-agent")
|
||||
if userAgent == "" {
|
||||
userAgent = base.UserAgent
|
||||
userAgent := args.Header.Get("user-agent")
|
||||
if userAgent != "" {
|
||||
headers["User-Agent"] = base.UserAgent
|
||||
}
|
||||
}
|
||||
// if PassIPToUpsteam is true, then pass the ip address to the upstream
|
||||
if d.PassIPToUpsteam {
|
||||
ip := args.IP
|
||||
if ip != "" {
|
||||
headers["X-Forwarded-For"] = ip
|
||||
headers["X-Real-Ip"] = ip
|
||||
}
|
||||
}
|
||||
_, _, err := d.request("/fs/get", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetResult(&resp).SetBody(FsGetReq{
|
||||
Path: file.GetPath(),
|
||||
Password: d.MetaPassword,
|
||||
}).SetHeader("user-agent", userAgent)
|
||||
}).SetHeaders(headers)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -360,8 +365,15 @@ func (d *OpenList) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.O
|
||||
return err
|
||||
}
|
||||
|
||||
//func (d *OpenList) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||
// return nil, errs.NotSupport
|
||||
//}
|
||||
func (d *OpenList) ResolveLinkCacheMode(_ string) driver.LinkCacheMode {
|
||||
var mode driver.LinkCacheMode
|
||||
if d.PassIPToUpsteam {
|
||||
mode |= driver.LinkCacheIP
|
||||
}
|
||||
if d.PassUAToUpsteam {
|
||||
mode |= driver.LinkCacheUA
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*OpenList)(nil)
|
||||
|
||||
@@ -12,6 +12,7 @@ type Addition struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
PassIPToUpsteam bool `json:"pass_ip_to_upsteam" default:"true"`
|
||||
PassUAToUpsteam bool `json:"pass_ua_to_upsteam" default:"true"`
|
||||
ForwardArchiveReq bool `json:"forward_archive_requests" default:"true"`
|
||||
}
|
||||
@@ -22,6 +23,7 @@ var config = driver.Config{
|
||||
DefaultRoot: "/",
|
||||
CheckStatus: true,
|
||||
ProxyRangeOption: true,
|
||||
LinkCacheMode: driver.LinkCacheAuto,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Strm struct {
|
||||
@@ -40,6 +41,9 @@ func (d *Strm) Init(ctx context.Context) error {
|
||||
if d.Paths == "" {
|
||||
return errors.New("paths is required")
|
||||
}
|
||||
if d.SaveStrmToLocal && len(d.SaveStrmLocalPath) <= 0 {
|
||||
return errors.New("SaveStrmLocalPath is required")
|
||||
}
|
||||
d.pathMap = make(map[string][]string)
|
||||
for _, path := range strings.Split(d.Paths, "\n") {
|
||||
path = strings.TrimSpace(path)
|
||||
@@ -48,6 +52,11 @@ func (d *Strm) Init(ctx context.Context) error {
|
||||
}
|
||||
k, v := getPair(path)
|
||||
d.pathMap[k] = append(d.pathMap[k], v)
|
||||
err := InsertStrm(utils.FixAndCleanPath(strings.TrimSpace(path)), d)
|
||||
if err != nil {
|
||||
log.Errorf("insert strmTrie error: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(d.pathMap) == 1 {
|
||||
for k := range d.pathMap {
|
||||
@@ -87,6 +96,9 @@ func (d *Strm) Drop(ctx context.Context) error {
|
||||
d.pathMap = nil
|
||||
d.downloadSuffix = nil
|
||||
d.supportSuffix = nil
|
||||
for _, path := range strings.Split(d.Paths, "\n") {
|
||||
RemoveStrm(utils.FixAndCleanPath(strings.TrimSpace(path)), d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
164
drivers/strm/hook.go
Normal file
164
drivers/strm/hook.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package strm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
stdpath "path"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tchap/go-patricia/v2/patricia"
|
||||
)
|
||||
|
||||
var strmTrie = patricia.NewTrie()
|
||||
|
||||
func UpdateLocalStrm(ctx context.Context, path string, objs []model.Obj) {
|
||||
path = utils.FixAndCleanPath(path)
|
||||
updateLocal := func(driver *Strm, basePath string, objs []model.Obj) {
|
||||
relParent := strings.TrimPrefix(basePath, driver.MountPath)
|
||||
localParentPath := stdpath.Join(driver.SaveStrmLocalPath, relParent)
|
||||
for _, obj := range objs {
|
||||
localPath := stdpath.Join(localParentPath, obj.GetName())
|
||||
generateStrm(ctx, driver, obj, localPath)
|
||||
}
|
||||
deleteExtraFiles(localParentPath, objs)
|
||||
}
|
||||
|
||||
_ = strmTrie.VisitPrefixes(patricia.Prefix(path), func(needPathPrefix patricia.Prefix, item patricia.Item) error {
|
||||
strmDrivers := item.([]*Strm)
|
||||
needPath := string(needPathPrefix)
|
||||
restPath := strings.TrimPrefix(path, needPath)
|
||||
if len(restPath) > 0 && restPath[0] != '/' {
|
||||
return nil
|
||||
}
|
||||
for _, strmDriver := range strmDrivers {
|
||||
strmObjs, _ := utils.SliceConvert(objs, func(obj model.Obj) (model.Obj, error) {
|
||||
ret := strmDriver.convert2strmObj(ctx, path, obj)
|
||||
return &ret, nil
|
||||
})
|
||||
updateLocal(strmDriver, stdpath.Join(stdpath.Base(needPath), restPath), strmObjs)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func InsertStrm(dstPath string, d *Strm) error {
|
||||
prefix := patricia.Prefix(strings.TrimRight(dstPath, "/"))
|
||||
existing := strmTrie.Get(prefix)
|
||||
|
||||
if existing == nil {
|
||||
if !strmTrie.Insert(prefix, []*Strm{d}) {
|
||||
return errors.New("failed to insert strm")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if lst, ok := existing.([]*Strm); ok {
|
||||
strmTrie.Set(prefix, append(lst, d))
|
||||
} else {
|
||||
return errors.New("invalid trie item type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RemoveStrm(dstPath string, d *Strm) {
|
||||
prefix := patricia.Prefix(strings.TrimRight(dstPath, "/"))
|
||||
existing := strmTrie.Get(prefix)
|
||||
if existing == nil {
|
||||
return
|
||||
}
|
||||
lst, ok := existing.([]*Strm)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if len(lst) == 1 && lst[0] == d {
|
||||
strmTrie.Delete(prefix)
|
||||
return
|
||||
}
|
||||
|
||||
for i, di := range lst {
|
||||
if di == d {
|
||||
newList := append(lst[:i], lst[i+1:]...)
|
||||
strmTrie.Set(prefix, newList)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateStrm(ctx context.Context, driver *Strm, obj model.Obj, localPath string) {
|
||||
link, err := driver.Link(ctx, obj, model.LinkArgs{})
|
||||
if err != nil {
|
||||
log.Warnf("failed to generate strm of obj %s: failed to link: %v", localPath, err)
|
||||
return
|
||||
}
|
||||
seekableStream, err := stream.NewSeekableStream(&stream.FileStream{
|
||||
Obj: obj,
|
||||
Ctx: ctx,
|
||||
}, link)
|
||||
if err != nil {
|
||||
_ = link.Close()
|
||||
log.Warnf("failed to generate strm of obj %s: failed to get seekable stream: %v", localPath, err)
|
||||
return
|
||||
}
|
||||
defer seekableStream.Close()
|
||||
file, err := utils.CreateNestedFile(localPath)
|
||||
if err != nil {
|
||||
log.Warnf("failed to generate strm of obj %s: failed to create local file: %v", localPath, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err := io.Copy(file, seekableStream); err != nil {
|
||||
log.Warnf("failed to generate strm of obj %s: copy failed: %v", localPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteExtraFiles(localPath string, objs []model.Obj) {
|
||||
localFiles, err := getLocalFiles(localPath)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read local files from %s: %v", localPath, err)
|
||||
return
|
||||
}
|
||||
|
||||
objsSet := make(map[string]struct{})
|
||||
for _, obj := range objs {
|
||||
if obj.IsDir() {
|
||||
continue
|
||||
}
|
||||
objsSet[stdpath.Join(localPath, obj.GetName())] = struct{}{}
|
||||
}
|
||||
|
||||
for _, localFile := range localFiles {
|
||||
if _, exists := objsSet[localFile]; !exists {
|
||||
err := os.Remove(localFile)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to delete file: %s, error: %v\n", localFile, err)
|
||||
} else {
|
||||
log.Infof("Deleted file %s", localFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalFiles(localPath string) ([]string, error) {
|
||||
var files []string
|
||||
entries, err := os.ReadDir(localPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
files = append(files, stdpath.Join(localPath, entry.Name()))
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterObjsUpdateHook(UpdateLocalStrm)
|
||||
}
|
||||
@@ -11,7 +11,9 @@ type Addition struct {
|
||||
FilterFileTypes string `json:"filterFileTypes" type:"text" default:"strm" required:"false" help:"Supports suffix name of strm file"`
|
||||
DownloadFileTypes string `json:"downloadFileTypes" type:"text" default:"ass" required:"false" help:"Files need to download with strm (usally subtitles)"`
|
||||
EncodePath bool `json:"encodePath" default:"true" required:"true" help:"encode the path in the strm file"`
|
||||
LocalModel bool `json:"localModel" default:"false" help:"enable local mode"`
|
||||
WithoutUrl bool `json:"withoutUrl" default:"false" help:"strm file content without URL prefix"`
|
||||
SaveStrmToLocal bool `json:"SaveStrmToLocal" default:"false" help:"save strm file locally"`
|
||||
SaveStrmLocalPath string `json:"SaveStrmLocalPath" type:"text" help:"save strm file local path"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
||||
@@ -61,36 +61,12 @@ func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]
|
||||
|
||||
var validObjs []model.Obj
|
||||
for _, obj := range objs {
|
||||
id, name, path := "", obj.GetName(), ""
|
||||
size := int64(0)
|
||||
if !obj.IsDir() {
|
||||
path = stdpath.Join(reqPath, obj.GetName())
|
||||
ext := strings.ToLower(utils.Ext(name))
|
||||
if _, ok := d.supportSuffix[ext]; ok {
|
||||
id = "strm"
|
||||
name = strings.TrimSuffix(name, ext) + "strm"
|
||||
size = int64(len(d.getLink(ctx, path)))
|
||||
} else if _, ok := d.downloadSuffix[ext]; ok {
|
||||
size = obj.GetSize()
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
objRes := model.Object{
|
||||
ID: id,
|
||||
Path: path,
|
||||
Name: name,
|
||||
Size: size,
|
||||
Modified: obj.ModTime(),
|
||||
IsFolder: obj.IsDir(),
|
||||
}
|
||||
|
||||
objRes := d.convert2strmObj(ctx, reqPath, obj)
|
||||
thumb, ok := model.GetThumb(obj)
|
||||
if !ok {
|
||||
validObjs = append(validObjs, &objRes)
|
||||
continue
|
||||
}
|
||||
|
||||
validObjs = append(validObjs, &model.ObjThumb{
|
||||
Object: objRes,
|
||||
Thumbnail: model.Thumbnail{
|
||||
@@ -101,6 +77,32 @@ func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]
|
||||
return validObjs, nil
|
||||
}
|
||||
|
||||
func (d *Strm) convert2strmObj(ctx context.Context, reqPath string, obj model.Obj) model.Object {
|
||||
id, name, path := "", obj.GetName(), ""
|
||||
size := int64(0)
|
||||
if !obj.IsDir() {
|
||||
path = stdpath.Join(reqPath, obj.GetName())
|
||||
ext := strings.ToLower(utils.Ext(name))
|
||||
if _, ok := d.supportSuffix[ext]; ok {
|
||||
id = "strm"
|
||||
name = strings.TrimSuffix(name, ext) + "strm"
|
||||
size = int64(len(d.getLink(ctx, path)))
|
||||
} else if _, ok := d.downloadSuffix[ext]; ok {
|
||||
size = obj.GetSize()
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
return model.Object{
|
||||
ID: id,
|
||||
Path: path,
|
||||
Name: name,
|
||||
Size: size,
|
||||
Modified: obj.ModTime(),
|
||||
IsFolder: obj.IsDir(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Strm) getLink(ctx context.Context, path string) string {
|
||||
finalPath := path
|
||||
if d.EncodePath {
|
||||
@@ -110,7 +112,7 @@ func (d *Strm) getLink(ctx context.Context, path string) string {
|
||||
signPath := sign.Sign(path)
|
||||
finalPath = fmt.Sprintf("%s?sign=%s", finalPath, signPath)
|
||||
}
|
||||
if d.LocalModel {
|
||||
if d.WithoutUrl {
|
||||
return finalPath
|
||||
}
|
||||
apiUrl := d.SiteUrl
|
||||
@@ -119,7 +121,9 @@ func (d *Strm) getLink(ctx context.Context, path string) string {
|
||||
} else {
|
||||
apiUrl = common.GetApiUrl(ctx)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(finalPath, "/") {
|
||||
finalPath = "/" + finalPath
|
||||
}
|
||||
return fmt.Sprintf("%s/d%s",
|
||||
apiUrl,
|
||||
finalPath)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -68,6 +69,7 @@ func (x *Thunder) Init(ctx context.Context) (err error) {
|
||||
PackageName: "com.xunlei.downloadprovider",
|
||||
UserAgent: "ANDROID-com.xunlei.downloadprovider/8.31.0.9726 netWorkType/5G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/512000 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)",
|
||||
DownloadUserAgent: "Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)",
|
||||
Space: x.Space,
|
||||
refreshCTokenCk: func(token string) {
|
||||
x.CaptchaToken = token
|
||||
op.MustSaveDriverStorage(x)
|
||||
@@ -167,6 +169,7 @@ func (x *ThunderExpert) Init(ctx context.Context) (err error) {
|
||||
UserAgent: x.UserAgent,
|
||||
DownloadUserAgent: x.DownloadUserAgent,
|
||||
UseVideoUrl: x.UseVideoUrl,
|
||||
Space: x.Space,
|
||||
|
||||
refreshCTokenCk: func(token string) {
|
||||
x.CaptchaToken = token
|
||||
@@ -281,7 +284,7 @@ func (xc *XunLeiCommon) Link(ctx context.Context, file model.Obj, args model.Lin
|
||||
_, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodGet, func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetPathParam("fileID", file.GetID())
|
||||
//r.SetQueryParam("space", "")
|
||||
r.SetQueryParam("space", xc.Space)
|
||||
}, &lFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -322,6 +325,7 @@ func (xc *XunLeiCommon) MakeDir(ctx context.Context, parentDir model.Obj, dirNam
|
||||
"kind": FOLDER,
|
||||
"name": dirName,
|
||||
"parent_id": parentDir.GetID(),
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
@@ -331,8 +335,9 @@ func (xc *XunLeiCommon) Move(ctx context.Context, srcObj, dstDir model.Obj) erro
|
||||
_, err := xc.Request(FILE_API_URL+":batchMove", http.MethodPost, func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetBody(&base.Json{
|
||||
"to": base.Json{"parent_id": dstDir.GetID()},
|
||||
"ids": []string{srcObj.GetID()},
|
||||
"to": base.Json{"parent_id": dstDir.GetID()},
|
||||
"ids": []string{srcObj.GetID()},
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
@@ -342,7 +347,10 @@ func (xc *XunLeiCommon) Rename(ctx context.Context, srcObj model.Obj, newName st
|
||||
_, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodPatch, func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetPathParam("fileID", srcObj.GetID())
|
||||
r.SetBody(&base.Json{"name": newName})
|
||||
r.SetBody(&base.Json{
|
||||
"name": newName,
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
@@ -351,8 +359,9 @@ func (xc *XunLeiCommon) Copy(ctx context.Context, srcObj, dstDir model.Obj) erro
|
||||
_, err := xc.Request(FILE_API_URL+":batchCopy", http.MethodPost, func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetBody(&base.Json{
|
||||
"to": base.Json{"parent_id": dstDir.GetID()},
|
||||
"ids": []string{srcObj.GetID()},
|
||||
"to": base.Json{"parent_id": dstDir.GetID()},
|
||||
"ids": []string{srcObj.GetID()},
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
@@ -362,6 +371,7 @@ func (xc *XunLeiCommon) Remove(ctx context.Context, obj model.Obj) error {
|
||||
_, err := xc.Request(FILE_API_URL+"/{fileID}/trash", http.MethodPatch, func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetPathParam("fileID", obj.GetID())
|
||||
r.SetQueryParam("space", xc.Space)
|
||||
r.SetBody("{}")
|
||||
}, nil)
|
||||
return err
|
||||
@@ -387,6 +397,7 @@ func (xc *XunLeiCommon) Put(ctx context.Context, dstDir model.Obj, file model.Fi
|
||||
"size": file.GetSize(),
|
||||
"hash": gcid,
|
||||
"upload_type": UPLOAD_TYPE_RESUMABLE,
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
@@ -430,7 +441,7 @@ func (xc *XunLeiCommon) getFiles(ctx context.Context, folderId string) ([]model.
|
||||
_, err := xc.Request(FILE_API_URL, http.MethodGet, func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetQueryParams(map[string]string{
|
||||
"space": "",
|
||||
"space": xc.Space,
|
||||
"__type": "drive",
|
||||
"refresh": "true",
|
||||
"__sync": "true",
|
||||
@@ -440,6 +451,17 @@ func (xc *XunLeiCommon) getFiles(ctx context.Context, folderId string) ([]model.
|
||||
"limit": "100",
|
||||
"filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`,
|
||||
})
|
||||
// 获取硬盘挂载目录等
|
||||
if xc.Space != "" {
|
||||
r.SetQueryParamsFromValues(url.Values{
|
||||
"with": []string{
|
||||
"withCategoryDiskMountPath",
|
||||
"withCategoryDriveCachePath",
|
||||
"withCategoryHistoryDownloadPath",
|
||||
"withReadOnlyFS",
|
||||
},
|
||||
})
|
||||
}
|
||||
}, &fileList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -576,6 +598,7 @@ func (xc *XunLeiCommon) OfflineDownload(ctx context.Context, fileUrl string, par
|
||||
"name": fileName,
|
||||
"parent_id": parentDir.GetID(),
|
||||
"upload_type": UPLOAD_TYPE_URL,
|
||||
"space": xc.Space,
|
||||
"url": base.Json{
|
||||
"url": fileUrl,
|
||||
},
|
||||
@@ -602,6 +625,7 @@ func (xc *XunLeiCommon) OfflineList(ctx context.Context, nextPageToken string) (
|
||||
"type": "offline",
|
||||
"limit": "10000",
|
||||
"page_token": nextPageToken,
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, &resp)
|
||||
|
||||
@@ -618,6 +642,7 @@ func (xc *XunLeiCommon) DeleteOfflineTasks(ctx context.Context, taskIDs []string
|
||||
SetQueryParams(map[string]string{
|
||||
"task_ids": strings.Join(taskIDs, ","),
|
||||
"delete_files": strconv.FormatBool(deleteFiles),
|
||||
"space": xc.Space,
|
||||
})
|
||||
}, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -46,6 +46,8 @@ type ExpertAddition struct {
|
||||
|
||||
//优先使用视频链接代替下载链接
|
||||
UseVideoUrl bool `json:"use_video_url"`
|
||||
|
||||
Space string `json:"space" default:"" help:"device id for remote device"`
|
||||
}
|
||||
|
||||
// 登录特征,用于判断是否重新登录
|
||||
@@ -80,6 +82,8 @@ type Addition struct {
|
||||
CreditKey string `json:"credit_key" help:"credit key,used for login"`
|
||||
// 登录设备ID
|
||||
DeviceID string `json:"device_id" default:""`
|
||||
|
||||
Space string `json:"space" default:"" help:"device id for remote device"`
|
||||
}
|
||||
|
||||
// 登录特征,用于判断是否重新登录
|
||||
@@ -90,7 +94,6 @@ func (i *Addition) GetIdentity() string {
|
||||
var config = driver.Config{
|
||||
Name: "Thunder",
|
||||
LocalSort: true,
|
||||
OnlyProxy: true,
|
||||
}
|
||||
|
||||
var configExpert = driver.Config{
|
||||
|
||||
@@ -68,6 +68,7 @@ type Common struct {
|
||||
UserAgent string
|
||||
DownloadUserAgent string
|
||||
UseVideoUrl bool
|
||||
Space string
|
||||
|
||||
// 验证码token刷新成功回调
|
||||
refreshCTokenCk func(token string)
|
||||
|
||||
@@ -39,7 +39,21 @@ func InitConfig() {
|
||||
if !filepath.IsAbs(dataDir) {
|
||||
flags.DataDir = filepath.Join(pwd, flags.DataDir)
|
||||
}
|
||||
configPath := filepath.Join(flags.DataDir, "config.json")
|
||||
// Determine config file path: use flags.ConfigPath if provided, otherwise default to <dataDir>/config.json
|
||||
configPath := flags.ConfigPath
|
||||
if configPath == "" {
|
||||
configPath = filepath.Join(flags.DataDir, "config.json")
|
||||
} else {
|
||||
// if relative, resolve relative to working directory
|
||||
if !filepath.IsAbs(configPath) {
|
||||
if absPath, err := filepath.Abs(configPath); err == nil {
|
||||
configPath = absPath
|
||||
} else {
|
||||
configPath = filepath.Join(pwd, configPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
configPath = filepath.Clean(configPath)
|
||||
log.Infof("reading config file: %s", configPath)
|
||||
if !utils.Exists(configPath) {
|
||||
log.Infof("config file not exists, creating default config file")
|
||||
|
||||
17
internal/cache/typed_cache.go
vendored
17
internal/cache/typed_cache.go
vendored
@@ -43,23 +43,14 @@ func (c *TypedCache[T]) SetTypeWithExpirable(key, typeKey string, value T, exp E
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer to use typeKeys for lookup; if none match, use fallbackTypeKey for lookup
|
||||
func (c *TypedCache[T]) GetType(key, fallbackTypeKey string, typeKeys ...string) (T, bool) {
|
||||
func (c *TypedCache[T]) GetType(key, typeKey string) (T, bool) {
|
||||
c.mu.RLock()
|
||||
cache, exists := c.entries[key]
|
||||
if !exists {
|
||||
c.mu.RUnlock()
|
||||
return *new(T), false
|
||||
}
|
||||
entry, exists := cache[fallbackTypeKey]
|
||||
if len(typeKeys) > 0 {
|
||||
for _, tk := range typeKeys {
|
||||
if entry, exists = cache[tk]; exists {
|
||||
fallbackTypeKey = tk
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
entry, exists := cache[typeKey]
|
||||
if !exists {
|
||||
c.mu.RUnlock()
|
||||
return *new(T), false
|
||||
@@ -72,8 +63,8 @@ func (c *TypedCache[T]) GetType(key, fallbackTypeKey string, typeKeys ...string)
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
if cache[fallbackTypeKey] == entry {
|
||||
delete(cache, fallbackTypeKey)
|
||||
if cache[typeKey] == entry {
|
||||
delete(cache, typeKey)
|
||||
if len(cache) == 0 {
|
||||
delete(c.entries, key)
|
||||
}
|
||||
|
||||
@@ -60,3 +60,7 @@ func DeleteSharingById(id string) error {
|
||||
s := model.SharingDB{ID: id}
|
||||
return errors.WithStack(db.Where(s).Delete(&s).Error)
|
||||
}
|
||||
|
||||
func DeleteSharingsByCreatorId(creatorId uint) error {
|
||||
return errors.WithStack(db.Where("creator_id = ?", creatorId).Delete(&model.SharingDB{}).Error)
|
||||
}
|
||||
|
||||
@@ -17,11 +17,23 @@ type Config struct {
|
||||
ProxyRangeOption bool `json:"-"`
|
||||
// if the driver returns Link without URL, this should be set to true
|
||||
NoLinkURL bool `json:"-"`
|
||||
// LinkCacheType=1 add IP to cache key
|
||||
//
|
||||
// LinkCacheType=2 add UserAgent to cache key
|
||||
LinkCacheType uint8 `json:"-"`
|
||||
// Link cache behaviour:
|
||||
// - LinkCacheAuto: let driver decide per-path (implement driver.LinkCacheModeResolver)
|
||||
// - LinkCacheNone: no extra info added to cache key (default)
|
||||
// - flags (OR-able) can add more attributes to cache key (IP, UA, ...)
|
||||
LinkCacheMode `json:"-"`
|
||||
}
|
||||
type LinkCacheMode int8
|
||||
|
||||
const (
|
||||
LinkCacheAuto LinkCacheMode = -1 // Let the driver decide per-path (use driver.LinkCacheModeResolver)
|
||||
LinkCacheNone LinkCacheMode = 0 // No extra info added to cache key (default)
|
||||
)
|
||||
|
||||
const (
|
||||
LinkCacheIP LinkCacheMode = 1 << iota // include client IP in cache key
|
||||
LinkCacheUA // include User-Agent in cache key
|
||||
)
|
||||
|
||||
func (c Config) MustProxy() bool {
|
||||
return c.OnlyProxy || c.NoLinkURL
|
||||
|
||||
@@ -213,3 +213,8 @@ type WithDetails interface {
|
||||
type Reference interface {
|
||||
InitReference(storage Driver) error
|
||||
}
|
||||
|
||||
type LinkCacheModeResolver interface {
|
||||
// ResolveLinkCacheMode returns the LinkCacheMode for the given path.
|
||||
ResolveLinkCacheMode(path string) LinkCacheMode
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (s *Sharing) Valid() bool {
|
||||
if len(s.Files) == 0 {
|
||||
return false
|
||||
}
|
||||
if !s.Creator.CanShare() {
|
||||
if s.Creator == nil || !s.Creator.CanShare() {
|
||||
return false
|
||||
}
|
||||
if s.Expires != nil && !s.Expires.IsZero() && s.Expires.Before(time.Now()) {
|
||||
|
||||
@@ -57,7 +57,7 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
model.WrapObjsName(files)
|
||||
// call hooks
|
||||
go func(reqPath string, files []model.Obj) {
|
||||
HandleObjsUpdateHook(reqPath, files)
|
||||
HandleObjsUpdateHook(context.WithoutCancel(ctx), reqPath, files)
|
||||
}(utils.GetFullPath(storage.GetStorage().MountPath, path), files)
|
||||
|
||||
// sort objs
|
||||
@@ -168,23 +168,19 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
return nil, nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
|
||||
}
|
||||
|
||||
typeKey := args.Type
|
||||
var typeKeys []string
|
||||
switch storage.Config().LinkCacheType {
|
||||
case 1:
|
||||
if args.IP != "" {
|
||||
typeKey += "/" + args.IP
|
||||
typeKeys = []string{typeKey}
|
||||
}
|
||||
case 2:
|
||||
if ua := args.Header.Get("User-Agent"); ua != "" {
|
||||
typeKey += "/" + ua
|
||||
typeKeys = []string{typeKey}
|
||||
}
|
||||
mode := storage.Config().LinkCacheMode
|
||||
if mode == -1 {
|
||||
mode = storage.(driver.LinkCacheModeResolver).ResolveLinkCacheMode(path)
|
||||
}
|
||||
typeKey := args.Type
|
||||
if mode&driver.LinkCacheIP == 1 {
|
||||
typeKey += "/" + args.IP
|
||||
}
|
||||
if mode&driver.LinkCacheUA == 1 {
|
||||
typeKey += "/" + args.Header.Get("User-Agent")
|
||||
}
|
||||
|
||||
key := Key(storage, path)
|
||||
if ol, exists := Cache.linkCache.GetType(key, args.Type, typeKeys...); exists {
|
||||
if ol, exists := Cache.linkCache.GetType(key, typeKey); exists {
|
||||
if ol.link.Expiration != nil ||
|
||||
ol.link.SyncClosers.AcquireReference() || !ol.link.RequireReference {
|
||||
return ol.link, ol.obj, nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package op
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// Obj
|
||||
type ObjsUpdateHook = func(parent string, objs []model.Obj)
|
||||
type ObjsUpdateHook = func(ctx context.Context, parent string, objs []model.Obj)
|
||||
|
||||
var (
|
||||
objsUpdateHooks = make([]ObjsUpdateHook, 0)
|
||||
@@ -23,9 +24,9 @@ func RegisterObjsUpdateHook(hook ObjsUpdateHook) {
|
||||
objsUpdateHooks = append(objsUpdateHooks, hook)
|
||||
}
|
||||
|
||||
func HandleObjsUpdateHook(parent string, objs []model.Obj) {
|
||||
func HandleObjsUpdateHook(ctx context.Context, parent string, objs []model.Obj) {
|
||||
for _, hook := range objsUpdateHooks {
|
||||
hook(parent, objs)
|
||||
hook(ctx, parent, objs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,3 +137,7 @@ func DeleteSharing(sid string) error {
|
||||
sharingCache.Del(sid)
|
||||
return db.DeleteSharingById(sid)
|
||||
}
|
||||
|
||||
func DeleteSharingsByCreatorId(creatorId uint) error {
|
||||
return db.DeleteSharingsByCreatorId(creatorId)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||
"github.com/OpenListTeam/OpenList/v4/pkg/singleflight"
|
||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var userG singleflight.Group[*model.User]
|
||||
@@ -78,6 +79,9 @@ func DeleteUserById(id uint) error {
|
||||
return errs.DeleteAdminOrGuest
|
||||
}
|
||||
Cache.DeleteUser(old.Username)
|
||||
if err := DeleteSharingsByCreatorId(id); err != nil {
|
||||
return errors.WithMessage(err, "failed to delete user's sharings")
|
||||
}
|
||||
return db.DeleteUserById(id)
|
||||
}
|
||||
|
||||
|
||||
@@ -199,14 +199,13 @@ func Config(ctx context.Context) searcher.Config {
|
||||
return instance.Config()
|
||||
}
|
||||
|
||||
func Update(parent string, objs []model.Obj) {
|
||||
func Update(ctx context.Context, parent string, objs []model.Obj) {
|
||||
if instance == nil || !instance.Config().AutoUpdate || !setting.GetBool(conf.AutoUpdateIndex) || Running() {
|
||||
return
|
||||
}
|
||||
if isIgnorePath(parent) {
|
||||
return
|
||||
}
|
||||
ctx := context.Background()
|
||||
// only update when index have built
|
||||
progress, err := Progress()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user