-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add 123 link driver (close #4924)
- Loading branch information
Showing
6 changed files
with
349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package _123Link | ||
|
||
import ( | ||
"context" | ||
stdpath "path" | ||
"time" | ||
|
||
"github.com/alist-org/alist/v3/internal/driver" | ||
"github.com/alist-org/alist/v3/internal/errs" | ||
"github.com/alist-org/alist/v3/internal/model" | ||
"github.com/alist-org/alist/v3/pkg/utils" | ||
) | ||
|
||
type Pan123Link struct { | ||
model.Storage | ||
Addition | ||
root *Node | ||
} | ||
|
||
func (d *Pan123Link) Config() driver.Config { | ||
return config | ||
} | ||
|
||
func (d *Pan123Link) GetAddition() driver.Additional { | ||
return &d.Addition | ||
} | ||
|
||
func (d *Pan123Link) Init(ctx context.Context) error { | ||
node, err := BuildTree(d.OriginURLs) | ||
if err != nil { | ||
return err | ||
} | ||
node.calSize() | ||
d.root = node | ||
return nil | ||
} | ||
|
||
func (d *Pan123Link) Drop(ctx context.Context) error { | ||
return nil | ||
} | ||
|
||
func (d *Pan123Link) Get(ctx context.Context, path string) (model.Obj, error) { | ||
node := GetNodeFromRootByPath(d.root, path) | ||
return nodeToObj(node, path) | ||
} | ||
|
||
func (d *Pan123Link) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { | ||
node := GetNodeFromRootByPath(d.root, dir.GetPath()) | ||
if node == nil { | ||
return nil, errs.ObjectNotFound | ||
} | ||
if node.isFile() { | ||
return nil, errs.NotFolder | ||
} | ||
return utils.SliceConvert(node.Children, func(node *Node) (model.Obj, error) { | ||
return nodeToObj(node, stdpath.Join(dir.GetPath(), node.Name)) | ||
}) | ||
} | ||
|
||
func (d *Pan123Link) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { | ||
node := GetNodeFromRootByPath(d.root, file.GetPath()) | ||
if node == nil { | ||
return nil, errs.ObjectNotFound | ||
} | ||
if node.isFile() { | ||
signUrl, err := SignURL(node.Url, d.PrivateKey, d.UID, time.Duration(d.ValidDuration)*time.Minute) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &model.Link{ | ||
URL: signUrl, | ||
}, nil | ||
} | ||
return nil, errs.NotFile | ||
} | ||
|
||
var _ driver.Driver = (*Pan123Link)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package _123Link | ||
|
||
import ( | ||
"github.com/alist-org/alist/v3/internal/driver" | ||
"github.com/alist-org/alist/v3/internal/op" | ||
) | ||
|
||
type Addition struct { | ||
OriginURLs string `json:"origin_urls" type:"text" required:"true" default:"https://vip.123pan.com/29/folder/file.mp3" help:"structure:FolderName:\n [FileSize:][Modified:]Url"` | ||
PrivateKey string `json:"private_key"` | ||
UID uint64 `json:"uid" type:"number"` | ||
ValidDuration int64 `json:"valid_duration" type:"number" default:"30" help:"minutes"` | ||
} | ||
|
||
var config = driver.Config{ | ||
Name: "123PanLink", | ||
} | ||
|
||
func init() { | ||
op.RegisterDriver(func() driver.Driver { | ||
return &Pan123Link{} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package _123Link | ||
|
||
import ( | ||
"fmt" | ||
url2 "net/url" | ||
stdpath "path" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// build tree from text, text structure definition: | ||
/** | ||
* FolderName: | ||
* [FileSize:][Modified:]Url | ||
*/ | ||
/** | ||
* For example: | ||
* folder1: | ||
* name1:url1 | ||
* url2 | ||
* folder2: | ||
* url3 | ||
* url4 | ||
* url5 | ||
* folder3: | ||
* url6 | ||
* url7 | ||
* url8 | ||
*/ | ||
// if there are no name, use the last segment of url as name | ||
func BuildTree(text string) (*Node, error) { | ||
lines := strings.Split(text, "\n") | ||
var root = &Node{Level: -1, Name: "root"} | ||
stack := []*Node{root} | ||
for _, line := range lines { | ||
// calculate indent | ||
indent := 0 | ||
for i := 0; i < len(line); i++ { | ||
if line[i] != ' ' { | ||
break | ||
} | ||
indent++ | ||
} | ||
// if indent is not a multiple of 2, it is an error | ||
if indent%2 != 0 { | ||
return nil, fmt.Errorf("the line '%s' is not a multiple of 2", line) | ||
} | ||
// calculate level | ||
level := indent / 2 | ||
line = strings.TrimSpace(line[indent:]) | ||
// if the line is empty, skip | ||
if line == "" { | ||
continue | ||
} | ||
// if level isn't greater than the level of the top of the stack | ||
// it is not the child of the top of the stack | ||
for level <= stack[len(stack)-1].Level { | ||
// pop the top of the stack | ||
stack = stack[:len(stack)-1] | ||
} | ||
// if the line is a folder | ||
if isFolder(line) { | ||
// create a new node | ||
node := &Node{ | ||
Level: level, | ||
Name: strings.TrimSuffix(line, ":"), | ||
} | ||
// add the node to the top of the stack | ||
stack[len(stack)-1].Children = append(stack[len(stack)-1].Children, node) | ||
// push the node to the stack | ||
stack = append(stack, node) | ||
} else { | ||
// if the line is a file | ||
// create a new node | ||
node, err := parseFileLine(line) | ||
if err != nil { | ||
return nil, err | ||
} | ||
node.Level = level | ||
// add the node to the top of the stack | ||
stack[len(stack)-1].Children = append(stack[len(stack)-1].Children, node) | ||
} | ||
} | ||
return root, nil | ||
} | ||
|
||
func isFolder(line string) bool { | ||
return strings.HasSuffix(line, ":") | ||
} | ||
|
||
// line definition: | ||
// [FileSize:][Modified:]Url | ||
func parseFileLine(line string) (*Node, error) { | ||
// if there is no url, it is an error | ||
if !strings.Contains(line, "http://") && !strings.Contains(line, "https://") { | ||
return nil, fmt.Errorf("invalid line: %s, because url is required for file", line) | ||
} | ||
index := strings.Index(line, "http://") | ||
if index == -1 { | ||
index = strings.Index(line, "https://") | ||
} | ||
url := line[index:] | ||
info := line[:index] | ||
node := &Node{ | ||
Url: url, | ||
} | ||
name := stdpath.Base(url) | ||
unescape, err := url2.PathUnescape(name) | ||
if err == nil { | ||
name = unescape | ||
} | ||
node.Name = name | ||
if index > 0 { | ||
if !strings.HasSuffix(info, ":") { | ||
return nil, fmt.Errorf("invalid line: %s, because file info must end with ':'", line) | ||
} | ||
info = info[:len(info)-1] | ||
if info == "" { | ||
return nil, fmt.Errorf("invalid line: %s, because file name can't be empty", line) | ||
} | ||
infoParts := strings.Split(info, ":") | ||
size, err := strconv.ParseInt(infoParts[0], 10, 64) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid line: %s, because file size must be an integer", line) | ||
} | ||
node.Size = size | ||
if len(infoParts) > 1 { | ||
modified, err := strconv.ParseInt(infoParts[1], 10, 64) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid line: %s, because file modified must be an unix timestamp", line) | ||
} | ||
node.Modified = modified | ||
} else { | ||
node.Modified = time.Now().Unix() | ||
} | ||
} | ||
return node, nil | ||
} | ||
|
||
func splitPath(path string) []string { | ||
if path == "/" { | ||
return []string{"root"} | ||
} | ||
parts := strings.Split(path, "/") | ||
parts[0] = "root" | ||
return parts | ||
} | ||
|
||
func GetNodeFromRootByPath(root *Node, path string) *Node { | ||
return root.getByPath(splitPath(path)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package _123Link | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/alist-org/alist/v3/internal/errs" | ||
"github.com/alist-org/alist/v3/internal/model" | ||
) | ||
|
||
// Node is a node in the folder tree | ||
type Node struct { | ||
Url string | ||
Name string | ||
Level int | ||
Modified int64 | ||
Size int64 | ||
Children []*Node | ||
} | ||
|
||
func (node *Node) getByPath(paths []string) *Node { | ||
if len(paths) == 0 || node == nil { | ||
return nil | ||
} | ||
if node.Name != paths[0] { | ||
return nil | ||
} | ||
if len(paths) == 1 { | ||
return node | ||
} | ||
for _, child := range node.Children { | ||
tmp := child.getByPath(paths[1:]) | ||
if tmp != nil { | ||
return tmp | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (node *Node) isFile() bool { | ||
return node.Url != "" | ||
} | ||
|
||
func (node *Node) calSize() int64 { | ||
if node.isFile() { | ||
return node.Size | ||
} | ||
var size int64 = 0 | ||
for _, child := range node.Children { | ||
size += child.calSize() | ||
} | ||
node.Size = size | ||
return size | ||
} | ||
|
||
func nodeToObj(node *Node, path string) (model.Obj, error) { | ||
if node == nil { | ||
return nil, errs.ObjectNotFound | ||
} | ||
return &model.Object{ | ||
Name: node.Name, | ||
Size: node.Size, | ||
Modified: time.Unix(node.Modified, 0), | ||
IsFolder: !node.isFile(), | ||
Path: path, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package _123Link | ||
|
||
import ( | ||
"crypto/md5" | ||
"fmt" | ||
"math/rand" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
func SignURL(originURL, privateKey string, uid uint64, validDuration time.Duration) (newURL string, err error) { | ||
if privateKey == "" { | ||
return originURL, nil | ||
} | ||
var ( | ||
ts = time.Now().Add(validDuration).Unix() // 有效时间戳 | ||
rInt = rand.Int() // 随机正整数 | ||
objURL *url.URL | ||
) | ||
objURL, err = url.Parse(originURL) | ||
if err != nil { | ||
return "", err | ||
} | ||
authKey := fmt.Sprintf("%d-%d-%d-%x", ts, rInt, uid, md5.Sum([]byte(fmt.Sprintf("%s-%d-%d-%d-%s", | ||
objURL.Path, ts, rInt, uid, privateKey)))) | ||
v := objURL.Query() | ||
v.Add("auth_key", authKey) | ||
objURL.RawQuery = v.Encode() | ||
return objURL.String(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9e0fa77
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
123直链是不是只能一条一条的在
Origin urls
添加直链链接,不能直接指的整个文件夹。9e0fa77
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
是的 和 UrlTree一样 只是适配了一下鉴权