add download file plugin hook

This commit is contained in:
tickstep 2022-04-21 16:30:12 +08:00
parent 5572f844ed
commit a318796f3b
11 changed files with 228 additions and 18 deletions

View File

@ -0,0 +1,77 @@
// ==========================================================================================
// aliyunpan JS插件回调处理函数
// 支持 JavaScript ECMAScript 5.1 语言规范
//
// 更多内容请查看官方文档https://github.com/tickstep/aliyunpan
// ==========================================================================================
// ------------------------------------------------------------------------------------------
// 函数说明:下载文件前的回调函数
//
// 参数说明
// context - 当前调用的上下文信息
// {
// "appName": "aliyunpan",
// "version": "v0.1.3",
// "userId": "11001d48564f43b3bc5662874f04bb11",
// "nickname": "tickstep",
// "fileDriveId": "19519111",
// "albumDriveId": "29519122"
// }
// appName - 应用名称当前固定为aliyunpan
// version - 版本号
// userId - 当前登录用户的ID
// nickname - 用户昵称
// fileDriveId - 用户文件网盘ID
// albumDriveId - 用户相册网盘ID
//
// params - 文件下载前参数
// {
// "driveId": "19519221",
// "driveFileName": "token.bat",
// "driveFilePath": "aliyunpan/Downloads/token.bat",
// "driveFileSha1": "08FBE28A5B8791A2F50225E2EC5CEEC3C7955A11",
// "driveFileSize": 125330,
// "driveFileType": "file",
// "driveFileUpdatedAt": "2022-04-14 07:05:12",
// "localFilePath": "aliyunpan\\Downloads\\token.bat"
// }
// driveId - 网盘ID
// driveFileName - 网盘文件名
// driveFilePath - 网盘文件绝对完整路径
// driveFileSize - 网盘文件大小单位B
// driveFileSha1 - 网盘文件SHA1
// driveFileType - 网盘文件类型file-文件folder-文件夹
// driveFileUpdatedAt - 网盘文件修改时间
// localFilePath - 下载文件到本地保存的路径,这个是相对路径,相对指定下载的目标文件夹
//
// 返回值说明
// {
// "downloadApproved": "yes",
// "localFilePath": "newfolder\\token.bat"
// }
// downloadApproved - 该文件是否下载yes-确认下载no-取消下载
// localFilePath - 文件保存的本地路径,这个是相对路径,如果为空""代表保持原本的目标路径。
// 这个改动要小心,会导致重名文件下载只会下载一个
// ------------------------------------------------------------------------------------------
function downloadFilePrepareCallback(context, params) {
console.log(params)
var result = {
"downloadApproved": "yes",
"localFilePath": ""
};
// 所有的.dmg.exe文件下载保存的文件后缀名去掉.exe网盘文件不改动
if (params["driveFileName"].lastIndexOf(".dmg.exe") > 0) {
result["localFilePath"] = params["localFilePath"].substr(0, (params["localFilePath"].length - ".exe".length));
}
// 禁止.txt文件下载
if (params["driveFileName"].lastIndexOf(".txt") > 0) {
result["downloadApproved"] = "no";
}
return result;
}

View File

@ -51,7 +51,7 @@
// }
// uploadApproved - 该文件是否确认上传yes-允许上传no-禁止上传
// driveFilePath - 文件保存的网盘路径,这个是相对路径,如果为空""代表保持原本的目标路径。
// 这个改动要小心,会导致重名文件上传失败
// 这个改动要小心,会导致重名文件只会上传一个
// ------------------------------------------------------------------------------------------
function uploadFilePrepareCallback(context, params) {
var result = {

View File

@ -19,13 +19,15 @@ import (
"github.com/tickstep/aliyunpan-api/aliyunpan"
"github.com/tickstep/aliyunpan-api/aliyunpan/apierror"
"github.com/tickstep/aliyunpan/cmder/cmdtable"
"github.com/tickstep/aliyunpan/internal/config"
"github.com/tickstep/aliyunpan/internal/file/downloader"
"github.com/tickstep/aliyunpan/internal/functions"
"github.com/tickstep/aliyunpan/internal/plugins"
"github.com/tickstep/aliyunpan/internal/taskframework"
"github.com/tickstep/aliyunpan/library/requester/transfer"
"github.com/tickstep/library-go/converter"
"github.com/tickstep/library-go/logger"
"github.com/tickstep/library-go/requester"
"github.com/tickstep/aliyunpan/library/requester/transfer"
"github.com/tickstep/library-go/requester/rio/speeds"
"io"
"net/http"
@ -47,7 +49,7 @@ type (
ParentTaskExecutor *taskframework.TaskExecutor
DownloadStatistic *DownloadStatistic // 下载统计
GlobalSpeedsStat *speeds.Speeds // 全局速度统计
GlobalSpeedsStat *speeds.Speeds // 全局速度统计
// 可选项
VerbosePrinter *logger.CmdVerbose
@ -57,10 +59,10 @@ type (
IsOverwrite bool // 是否覆盖已存在的文件
NoCheck bool // 不校验文件
FilePanPath string // 要下载的网盘文件路径
SavePath string // 文件保存在本地的路径
OriginSaveRootPath string // 文件保存在本地的根目录路径
DriveId string
FilePanPath string // 要下载的网盘文件路径
SavePath string // 文件保存在本地的路径
OriginSaveRootPath string // 文件保存在本地的根目录路径
DriveId string
fileInfo *aliyunpan.FileEntity // 文件或目录详情
}
@ -145,12 +147,12 @@ func (dtu *DownloadTaskUnit) download() (err error) {
if dtu.IsPrintStatus {
// 输出所有的worker状态
var (
tb = cmdtable.NewTable(builder)
tb = cmdtable.NewTable(builder)
)
tb.SetHeader([]string{"#", "status", "range", "left", "speeds", "error"})
workersCallback(func(key int, worker *downloader.Worker) bool {
wrange := worker.GetRange()
tb.Append([]string{fmt.Sprint(worker.ID()), worker.GetStatus().StatusText(), wrange.ShowDetails(), strconv.FormatInt(wrange.Len(), 10), converter.ConvertFileSize(worker.GetSpeedsPerSecond(), 2)+"/s", fmt.Sprint(worker.Err())})
tb.Append([]string{fmt.Sprint(worker.ID()), worker.GetStatus().StatusText(), wrange.ShowDetails(), strconv.FormatInt(wrange.Len(), 10), converter.ConvertFileSize(worker.GetSpeedsPerSecond(), 2) + "/s", fmt.Sprint(worker.Err())})
return true
})
@ -270,7 +272,7 @@ func (dtu *DownloadTaskUnit) handleError(result *taskframework.TaskUnitRunResult
result.NeedRetry = true
}
}
time.Sleep(1*time.Second)
time.Sleep(1 * time.Second)
}
//checkFileValid 检测文件有效性
@ -343,6 +345,10 @@ func (dtu *DownloadTaskUnit) OnFailed(lastRunResult *taskframework.TaskUnitRunRe
func (dtu *DownloadTaskUnit) OnComplete(lastRunResult *taskframework.TaskUnitRunResult) {
}
func (dtu *DownloadTaskUnit) OnCancel(lastRunResult *taskframework.TaskUnitRunResult) {
}
func (dtu *DownloadTaskUnit) RetryWait() time.Duration {
return functions.RetryWait(dtu.taskInfo.Retry())
}
@ -363,7 +369,7 @@ func (dtu *DownloadTaskUnit) Run() (result *taskframework.TaskUnitRunResult) {
dtu.handleError(result)
return
}
time.Sleep(1*time.Second)
time.Sleep(1 * time.Second)
}
// 输出文件信息
@ -378,15 +384,15 @@ func (dtu *DownloadTaskUnit) Run() (result *taskframework.TaskUnitRunResult) {
}
// 获取该目录下的文件列表
fileList,apierr := dtu.PanClient.FileListGetAll(&aliyunpan.FileListParam{
fileList, apierr := dtu.PanClient.FileListGetAll(&aliyunpan.FileListParam{
DriveId: dtu.DriveId,
ParentFileId: dtu.fileInfo.FileId,
})
if apierr != nil {
// retry one more time
time.Sleep(3*time.Second)
time.Sleep(3 * time.Second)
fileList,apierr = dtu.PanClient.FileListGetAll(&aliyunpan.FileListParam{
fileList, apierr = dtu.PanClient.FileListGetAll(&aliyunpan.FileListParam{
DriveId: dtu.DriveId,
ParentFileId: dtu.fileInfo.FileId,
})
@ -408,7 +414,7 @@ func (dtu *DownloadTaskUnit) Run() (result *taskframework.TaskUnitRunResult) {
result.NeedRetry = true
return
}
time.Sleep(1*time.Second)
time.Sleep(1 * time.Second)
// 创建对应的任务进行下载
for k := range fileList {
@ -436,6 +442,37 @@ func (dtu *DownloadTaskUnit) Run() (result *taskframework.TaskUnitRunResult) {
return
}
// 调用插件
pluginManger := plugins.NewPluginManager(config.GetPluginDir())
plugin, _ := pluginManger.GetPlugin()
localFilePath := strings.TrimPrefix(dtu.SavePath, dtu.OriginSaveRootPath)
localFilePath = strings.TrimPrefix(strings.TrimPrefix(localFilePath, "\\"), "/")
pluginParam := &plugins.DownloadFilePrepareParams{
DriveId: dtu.fileInfo.DriveId,
DriveFilePath: dtu.fileInfo.Path,
DriveFileName: dtu.fileInfo.FileName,
DriveFileSize: dtu.fileInfo.FileSize,
DriveFileType: "file",
DriveFileSha1: dtu.fileInfo.ContentHash,
DriveFileUpdatedAt: dtu.fileInfo.UpdatedAt,
LocalFilePath: localFilePath,
}
if downloadFilePrepareResult, er := plugin.DownloadFilePrepareCallback(plugins.GetContext(config.Config.ActiveUser()), pluginParam); er == nil && downloadFilePrepareResult != nil {
if strings.Compare("yes", downloadFilePrepareResult.DownloadApproved) != 0 {
// skip download this file
fmt.Printf("插件取消了该文件下载: %s\n", dtu.fileInfo.Path)
result.Succeed = false
result.Cancel = true
return
}
if downloadFilePrepareResult.LocalFilePath != "" {
targetSaveRelativePath := strings.TrimPrefix(downloadFilePrepareResult.LocalFilePath, "/")
targetSaveRelativePath = strings.TrimPrefix(targetSaveRelativePath, "\\")
dtu.SavePath = path.Clean(dtu.OriginSaveRootPath + string(os.PathSeparator) + targetSaveRelativePath)
fmt.Printf("插件修改文件下载保存路径为: %s\n", dtu.SavePath)
}
}
fmt.Printf("[%s] 准备下载: %s\n", dtu.taskInfo.Id(), dtu.FilePanPath)
if !dtu.IsOverwrite && FileExist(dtu.SavePath) {

View File

@ -294,7 +294,9 @@ var ResultUpdateLocalDatabase = &taskframework.TaskUnitRunResult{ResultCode: 2,
func (utu *UploadTaskUnit) OnComplete(lastRunResult *taskframework.TaskUnitRunResult) {
// 任务结束,可能成功也可能失败
}
func (utu *UploadTaskUnit) OnCancel(lastRunResult *taskframework.TaskUnitRunResult) {
}
func (utu *UploadTaskUnit) RetryWait() time.Duration {
return functions.RetryWait(utu.taskInfo.Retry())
}

View File

@ -20,6 +20,10 @@ func (p *IdlePlugin) UploadFilePrepareCallback(context *Context, params *UploadF
return nil, nil
}
func (p *IdlePlugin) DownloadFilePrepareCallback(context *Context, params *DownloadFilePrepareParams) (*DownloadFilePrepareResult, error) {
return nil, nil
}
func (p *IdlePlugin) Stop() error {
return nil
}

View File

@ -105,6 +105,25 @@ func (js *JsPlugin) UploadFileFinishCallback(context *Context, params *UploadFil
return nil
}
// DownloadFilePrepareCallback 下载文件前的回调函数
func (js *JsPlugin) DownloadFilePrepareCallback(context *Context, params *DownloadFilePrepareParams) (*DownloadFilePrepareResult, error) {
var fn func(*Context, *DownloadFilePrepareParams) (*DownloadFilePrepareResult, error)
if !js.isHandlerFuncExisted("downloadFilePrepareCallback") {
return nil, nil
}
err := js.vm.ExportTo(js.vm.Get("downloadFilePrepareCallback"), &fn)
if err != nil {
logger.Verboseln("Js函数映射到 Go 函数失败!")
return nil, nil
}
r, er := fn(context, params)
if er != nil {
logger.Verboseln(er)
return nil, er
}
return r, nil
}
func (js *JsPlugin) Stop() error {
return nil
}

View File

@ -56,6 +56,26 @@ type (
DriveFilePath string `json:"driveFilePath"`
}
// DownloadFilePrepareParams 下载文件前的回调函数-参数
DownloadFilePrepareParams struct {
DriveId string `json:"driveId"`
DriveFileName string `json:"driveFileName"`
DriveFilePath string `json:"driveFilePath"`
DriveFileSha1 string `json:"driveFileSha1"`
DriveFileSize int64 `json:"driveFileSize"`
DriveFileType string `json:"driveFileType"`
DriveFileUpdatedAt string `json:"driveFileUpdatedAt"`
LocalFilePath string `json:"localFilePath"`
}
// DownloadFilePrepareResult 上传文件前的回调函数-返回结果
DownloadFilePrepareResult struct {
// DownloadApproved 确认该文件是否下载。yes-下载 no-不下载
DownloadApproved string `json:"downloadApproved"`
// LocalFilePath 保存本地的修改后的路径。注意该路径是相对路径
LocalFilePath string `json:"localFilePath"`
}
// Plugin 插件接口
Plugin interface {
// Start 启动
@ -67,6 +87,9 @@ type (
// UploadFileFinishCallback 上传文件完成的回调函数
UploadFileFinishCallback(context *Context, params *UploadFileFinishParams) error
// DownloadFilePrepareCallback 下载文件前的回调函数
DownloadFilePrepareCallback(context *Context, params *DownloadFilePrepareParams) (*DownloadFilePrepareResult, error)
// Stop 停止
Stop() error
}

View File

@ -6,7 +6,7 @@ import (
"testing"
)
func TestPlugin(t *testing.T) {
func TestPluginUpload(t *testing.T) {
pluginManager := NewPluginManager("D:\\smb\\feny\\goprojects\\dev")
plugin, err := pluginManager.GetPlugin()
if err != nil {
@ -39,3 +39,38 @@ func TestPlugin(t *testing.T) {
}
fmt.Println(r)
}
func TestPluginDownload(t *testing.T) {
pluginManager := NewPluginManager("/Volumes/Downloads/dev/config/plugin")
plugin, err := pluginManager.GetPlugin()
if err != nil {
fmt.Println(err)
}
ctx := &Context{
AppName: "aliyunpan",
Version: "v0.1.3",
UserId: "11001d48564f43b3bc5662874f04bb11",
Nickname: "tickstep",
FileDriveId: "19519121",
AlbumDriveId: "29519122",
}
params := &DownloadFilePrepareParams{
DriveId: "19519221",
DriveFilePath: "/test/aliyunpan/Downloads/token.bat",
DriveFileName: "token.bat",
DriveFileSize: 125330,
DriveFileType: "file",
DriveFileSha1: "08FBE28A5B8791A2F50225E2EC5CEEC3C7955A11",
DriveFileUpdatedAt: "2022-04-14 07:05:12",
LocalFilePath: "aliyunpan\\Downloads\\token.bat",
}
b, _ := jsoniter.Marshal(ctx)
fmt.Println(string(b))
b, _ = jsoniter.Marshal(params)
fmt.Println(string(b))
r, e := plugin.DownloadFilePrepareCallback(ctx, params)
if e != nil {
fmt.Println(e)
}
fmt.Println(r)
}

View File

@ -116,6 +116,12 @@ func (te *TaskExecutor) Execute() {
return
}
// 取消下载
if result.Cancel {
task.Unit.OnCancel(result)
return
}
if result.Succeed {
task.Unit.OnSuccess(result)
task.Unit.OnComplete(result)

View File

@ -29,14 +29,17 @@ type (
OnFailed(lastRunResult *TaskUnitRunResult)
// 每次执行结束执行的方法, 不管成功失败
OnComplete(lastRunResult *TaskUnitRunResult)
// 取消下载
OnCancel(lastRunResult *TaskUnitRunResult)
// 重试等待的时间
RetryWait() time.Duration
}
// 任务单元执行结果
TaskUnitRunResult struct {
Succeed bool // 是否执行成功
NeedRetry bool // 是否需要重试
Succeed bool // 是否执行成功
NeedRetry bool // 是否需要重试
Cancel bool // 是否取消了任务
// 以下是额外的信息
Err error // 错误信息

View File

@ -51,6 +51,10 @@ func (tu *TestUnit) Run() (result *taskframework.TaskUnitRunResult) {
}
}
func (tu *TestUnit) OnCancel(lastRunResult *taskframework.TaskUnitRunResult) {
}
func (tu *TestUnit) OnRetry(lastRunResult *taskframework.TaskUnitRunResult) {
fmt.Printf("[%s] prepare retry, times [%d/%d]...\n", tu.taskInfo.Id(), tu.taskInfo.Retry(), tu.taskInfo.MaxRetry())
}