Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: refactor offline download #5331

Merged
merged 6 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions internal/bootstrap/data/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,6 @@ func InitialSettings() []model.SettingItem {
{Key: conf.ForwardDirectLinkParams, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL},
{Key: conf.WebauthnLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC},

// aria2 settings
{Key: conf.Aria2Uri, Value: "http://localhost:6800/jsonrpc", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
{Key: conf.Aria2Secret, Value: "", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},

// single settings
{Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
{Key: conf.SearchIndex, Value: "none", Type: conf.TypeSelect, Options: "database,database_non_full_text,bleve,none", Group: model.INDEX},
Expand Down
2 changes: 1 addition & 1 deletion internal/model/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const (
STYLE
PREVIEW
GLOBAL
ARIA2
OFFLINE_DOWNLOAD
INDEX
SSO
)
Expand Down
76 changes: 76 additions & 0 deletions internal/offline_download/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package offline_download

import (
"context"
"fmt"
"path/filepath"

"github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/task"
"github.com/google/uuid"
"github.com/pkg/errors"
)

type AddURIArgs struct {
URI string
DstDirPath string
Tool string
}

func AddURI(ctx context.Context, args *AddURIArgs) error {
// get tool
tool, err := Tools.Get(args.Tool)
if err != nil {
return errors.Wrapf(err, "failed get tool")
}
// check storage
storage, dstDirActualPath, err := op.GetStorageAndActualPath(args.DstDirPath)
if err != nil {
return errors.WithMessage(err, "failed get storage")
}
// check is it could upload
if storage.Config().NoUpload {
return errors.WithStack(errs.UploadNotSupported)
}
// check path is valid
obj, err := op.Get(ctx, storage, dstDirActualPath)
if err != nil {
if !errs.IsObjectNotFound(err) {
return errors.WithMessage(err, "failed get object")
}
} else {
if !obj.IsDir() {
// can't add to a file
return errors.WithStack(errs.NotFolder)
}
}

uid := uuid.NewString()
tempDir := filepath.Join(conf.Conf.TempDir, args.Tool, uid)
signal := make(chan int)
gid, err := tool.AddURI(&AddUriArgs{
Uri: args.URI,
UID: uid,
TempDir: tempDir,
Signal: signal,
})
if err != nil {
return errors.Wrapf(err, "failed to add uri %s", args.URI)
}
DownTaskManager.Submit(task.WithCancelCtx(&task.Task[string]{
ID: gid,
Name: fmt.Sprintf("download %s to [%s](%s)", args.URI, storage.GetStorage().MountPath, dstDirActualPath),
Func: func(tsk *task.Task[string]) error {
m := &Monitor{
tsk: tsk,
tempDir: tempDir,
dstDirPath: args.DstDirPath,
signal: signal,
}
return m.Loop()
},
}))
return nil
}
13 changes: 13 additions & 0 deletions internal/offline_download/all_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package offline_download

import "testing"

func TestGetFiles(t *testing.T) {
files, err := GetFiles("..")
if err != nil {
t.Fatal(err)
}
for _, file := range files {
t.Log(file.Name, file.Size, file.Path)
}
}
73 changes: 73 additions & 0 deletions internal/offline_download/aria2/aria2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package aria2

import (
"context"
"fmt"
"time"

"github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/offline_download"
"github.com/alist-org/alist/v3/internal/setting"
"github.com/alist-org/alist/v3/pkg/aria2/rpc"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)

var notify = NewNotify()

type Aria2 struct {
client rpc.Client
}

func (a *Aria2) Items() []model.SettingItem {
// aria2 settings
return []model.SettingItem{
{Key: conf.Aria2Uri, Value: "http://localhost:6800/jsonrpc", Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
{Key: conf.Aria2Secret, Value: "", Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
}
}

func (a *Aria2) Init() (string, error) {
a.client = nil
uri := setting.GetStr(conf.Aria2Uri)
secret := setting.GetStr(conf.Aria2Secret)
c, err := rpc.New(context.Background(), uri, secret, 4*time.Second, notify)
if err != nil {
return "", errors.Wrap(err, "failed to init aria2 client")
}
version, err := c.GetVersion()
if err != nil {
return "", errors.Wrapf(err, "failed get aria2 version")
}
a.client = c
log.Infof("using aria2 version: %s", version.Version)
return fmt.Sprintf("aria2 version: %s", version.Version), nil
}

func (a *Aria2) IsReady() bool {
//TODO implement me
panic("implement me")
}

func (a *Aria2) AddURI(args *offline_download.AddUriArgs) (string, error) {
//TODO implement me
panic("implement me")
}

func (a *Aria2) Remove(tid string) error {
//TODO implement me
panic("implement me")
}

func (a *Aria2) Status(tid string) (*offline_download.Status, error) {
//TODO implement me
panic("implement me")
}

func (a *Aria2) GetFile(tid string) *offline_download.File {
//TODO implement me
panic("implement me")
}

var _ offline_download.Tool = (*Aria2)(nil)
70 changes: 70 additions & 0 deletions internal/offline_download/aria2/notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package aria2

import (
"github.com/alist-org/alist/v3/pkg/aria2/rpc"
"github.com/alist-org/alist/v3/pkg/generic_sync"
)

const (
Downloading = iota
Paused
Stopped
Completed
Errored
)

type Notify struct {
Signals generic_sync.MapOf[string, chan int]
}

func NewNotify() *Notify {
return &Notify{Signals: generic_sync.MapOf[string, chan int]{}}
}

func (n *Notify) OnDownloadStart(events []rpc.Event) {
for _, e := range events {
if signal, ok := n.Signals.Load(e.Gid); ok {
signal <- Downloading
}
}
}

func (n *Notify) OnDownloadPause(events []rpc.Event) {
for _, e := range events {
if signal, ok := n.Signals.Load(e.Gid); ok {
signal <- Paused
}
}
}

func (n *Notify) OnDownloadStop(events []rpc.Event) {
for _, e := range events {
if signal, ok := n.Signals.Load(e.Gid); ok {
signal <- Stopped
}
}
}

func (n *Notify) OnDownloadComplete(events []rpc.Event) {
for _, e := range events {
if signal, ok := n.Signals.Load(e.Gid); ok {
signal <- Completed
}
}
}

func (n *Notify) OnDownloadError(events []rpc.Event) {
for _, e := range events {
if signal, ok := n.Signals.Load(e.Gid); ok {
signal <- Errored
}
}
}

func (n *Notify) OnBtDownloadComplete(events []rpc.Event) {
for _, e := range events {
if signal, ok := n.Signals.Load(e.Gid); ok {
signal <- Completed
}
}
}
56 changes: 56 additions & 0 deletions internal/offline_download/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package offline_download

import (
"io"
"os"

"github.com/alist-org/alist/v3/internal/model"
)

type AddUriArgs struct {
Uri string
UID string
TempDir string
Signal chan int
}

type Status struct {
Progress int
NewTID string
Completed bool
Status string
Err error
}

type Tool interface {
// Items return the setting items the tool need
Items() []model.SettingItem
Init() (string, error)
IsReady() bool
// AddURI add an uri to download, return the task id
AddURI(args *AddUriArgs) (string, error)
// Remove the task if an error occurred
Remove(tid string) error
// Status return the status of the download task, if an error occurred, return the error in Status.Err
Status(tid string) (*Status, error)
// GetFile return an io.ReadCloser as the download file, if nil, means walk the temp dir to get the files
GetFile(tid string) *File
}

type File struct {
io.ReadCloser
Name string
Size int64
Path string
}

func (f *File) GetReadCloser() (io.ReadCloser, error) {
if f.ReadCloser != nil {
return f.ReadCloser, nil
}
file, err := os.Open(f.Path)
if err != nil {
return nil, err
}
return file, nil
}
Loading