diff --git a/internal/command/export_file.go b/internal/command/export_file.go deleted file mode 100644 index 8eb3588..0000000 --- a/internal/command/export_file.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2020 tickstep. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package command - -import ( - "fmt" - "github.com/tickstep/aliyunpan-api/aliyunpan" - "github.com/tickstep/aliyunpan-api/aliyunpan/apierror" - "github.com/tickstep/aliyunpan/cmder" - "github.com/tickstep/aliyunpan/internal/config" - "github.com/tickstep/library-go/logger" - "github.com/urfave/cli" - "log" - "os" - "path" - "strconv" - "time" -) - -func CmdExport() cli.Command { - return cli.Command{ - Name: "export", - Usage: "导出文件/目录元数据", - UsageText: cmder.App().Name + " export <网盘文件/目录的路径1> <文件/目录2> <文件/目录3> ... <本地保存文件路径>", - Description: ` - 导出指定文件/目录下面的所有文件的元数据信息,并保存到指定的本地文件里面。导出的文件元信息可以使用 import 命令(秒传文件功能)导入到网盘中。 - 支持多个文件或目录的导出. - - 示例: - - 导出 /我的资源/1.mp4 元数据到文件 /Users/tickstep/Downloads/export_files.txt - aliyunpan export /我的资源/1.mp4 /Users/tickstep/Downloads/export_files.txt - - 导出 /我的资源 整个目录 元数据到文件 /Users/tickstep/Downloads/export_files.txt - aliyunpan export /我的资源 /Users/tickstep/Downloads/export_files.txt - - 导出 网盘 整个目录 元数据到文件 /Users/tickstep/Downloads/export_files.txt - aliyunpan export / /Users/tickstep/Downloads/export_files.txt -`, - Category: "阿里云盘", - Before: ReloadConfigFunc, - Action: func(c *cli.Context) error { - if c.NArg() < 2 { - cli.ShowCommandHelp(c, c.Command.Name) - return nil - } - - subArgs := c.Args() - RunExportFiles(parseDriveId(c), c.Bool("ow"), subArgs[:len(subArgs)-1], subArgs[len(subArgs)-1]) - return nil - }, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "ow", - Usage: "overwrite, 覆盖已存在的导出文件", - }, - cli.StringFlag{ - Name: "driveId", - Usage: "网盘ID", - Value: "", - }, - }, - } -} - -func RunExportFiles(driveId string, overwrite bool, panPaths []string, saveLocalFilePath string) { - activeUser := config.Config.ActiveUser() - panClient := activeUser.PanClient() - - lfi, _ := os.Stat(saveLocalFilePath) - realSaveFilePath := saveLocalFilePath - if lfi != nil { - if lfi.IsDir() { - realSaveFilePath = path.Join(saveLocalFilePath, "export_file_") + strconv.FormatInt(time.Now().Unix(), 10) + ".txt" - } else { - if !overwrite { - fmt.Println("导出文件已存在") - return - } - } - } else { - // create file - localDir := path.Dir(saveLocalFilePath) - dirFs, _ := os.Stat(localDir) - if dirFs != nil { - if !dirFs.IsDir() { - fmt.Println("指定的保存文件路径不合法") - return - } - } else { - er := os.MkdirAll(localDir, 0755) - if er != nil { - fmt.Println("创建本地文件夹出错") - return - } - } - realSaveFilePath = saveLocalFilePath - } - - totalCount := 0 - saveFile, err := os.OpenFile(realSaveFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { - log.Fatal(err) - return - } - - for _, panPath := range panPaths { - panPath = activeUser.PathJoin(driveId, panPath) - panClient.WebapiPanClient().FilesDirectoriesRecurseList(driveId, panPath, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { - if apiError != nil { - logger.Verbosef("%s\n", apiError) - return true - } - - // 只需要存储文件即可 - if !fd.IsFolder() { - item := newRapidUploadItemFromFileEntity(fd) - jstr := item.createRapidUploadLink(false) - if len(jstr) <= 0 { - logger.Verboseln("create rapid upload link err") - return false - } - saveFile.WriteString(jstr + "\n") - totalCount += 1 - time.Sleep(time.Duration(100) * time.Millisecond) - fmt.Printf("\r导出文件数量: %d", totalCount) - } - return true - }) - } - - // close and save - if err := saveFile.Close(); err != nil { - log.Fatal(err) - } - - fmt.Printf("\r导出文件总数量: %d\n", totalCount) - fmt.Printf("导出文件保存路径: %s\n", realSaveFilePath) -} diff --git a/internal/command/import_file.go b/internal/command/import_file.go deleted file mode 100644 index 58ad0a8..0000000 --- a/internal/command/import_file.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (c) 2020 tickstep. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package command - -import ( - "fmt" - "github.com/tickstep/aliyunpan-api/aliyunpan" - "github.com/tickstep/aliyunpan-api/aliyunpan/apierror" - "github.com/tickstep/aliyunpan/cmder" - "github.com/tickstep/aliyunpan/internal/config" - "github.com/tickstep/library-go/logger" - "github.com/urfave/cli" - "io/ioutil" - "log" - "os" - "path" - "path/filepath" - "strings" - "time" -) - -type ( - dirFileListData struct { - Dir *aliyunpan.MkdirResult - FileList aliyunpan.FileList - } -) - -const ( - DefaultSaveToPanPath = "/aliyunpan" -) - -func CmdImport() cli.Command { - return cli.Command{ - Name: "import", - Usage: "导入文件", - UsageText: cmder.App().Name + " export <本地元数据文件路径>", - Description: ` - 导入文件中记录的元数据文件到网盘。保存到网盘的文件会使用文件元数据记录的路径位置,如果没有指定云盘目录(saveto)则默认导入到目录 aliyunpan 中。 - 导入的文件可以使用 export 命令获得。 - - 导入文件每一行是一个文件元数据,样例如下: - aliyunpan://file.dmg|752FCCBFB2436A6FFCA3B287831D4FAA5654B07E|7005440|pan_folder - - 示例: - 导入文件 /Users/tickstep/Downloads/export_files.txt 存储的所有文件元数据项 - aliyunpan import /Users/tickstep/Downloads/export_files.txt - - 导入文件 /Users/tickstep/Downloads/export_files.txt 存储的所有文件元数据项并保存到目录 /my2021 中 - aliyunpan import -saveto=/my2021 /Users/tickstep/Downloads/export_files.txt - - 导入文件 /Users/tickstep/Downloads/export_files.txt 存储的所有文件元数据项并保存到网盘根目录 / 中 - aliyunpan import -saveto=/ /Users/tickstep/Downloads/export_files.txt -`, - Category: "阿里云盘", - Before: ReloadConfigFunc, - Action: func(c *cli.Context) error { - if c.NArg() < 1 { - cli.ShowCommandHelp(c, c.Command.Name) - return nil - } - - saveTo := "" - if c.String("saveto") != "" { - saveTo = filepath.Clean(c.String("saveto")) - } - - subArgs := c.Args() - RunImportFiles(parseDriveId(c), c.Bool("ow"), saveTo, subArgs[0]) - return nil - }, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "ow", - Usage: "overwrite, 覆盖已存在的网盘文件", - }, - cli.StringFlag{ - Name: "driveId", - Usage: "网盘ID", - Value: "", - }, - cli.StringFlag{ - Name: "saveto", - Usage: "将文件保存到指定的目录", - }, - }, - } -} - -func RunImportFiles(driveId string, overwrite bool, panSavePath, localFilePath string) { - lfi, _ := os.Stat(localFilePath) - if lfi != nil { - if lfi.IsDir() { - fmt.Println("请指定导入文件") - return - } - } else { - // create file - fmt.Println("导入文件不存在") - return - } - - if panSavePath == "" { - // use default - panSavePath = DefaultSaveToPanPath - } - - fmt.Println("导入的文件会存储到目录:" + panSavePath) - - importFile, err := os.OpenFile(localFilePath, os.O_RDONLY, 0755) - if err != nil { - log.Fatal(err) - return - } - defer importFile.Close() - - fileData, err := ioutil.ReadAll(importFile) - if err != nil { - fmt.Println("读取文件出错") - return - } - fileText := string(fileData) - if len(fileText) == 0 { - fmt.Println("文件为空") - return - } - fileText = strings.TrimSpace(fileText) - fileLines := strings.Split(fileText, "\n") - importFileItems := []RapidUploadItem{} - for _, line := range fileLines { - line = strings.TrimSpace(line) - if item, e := newRapidUploadItem(line); e != nil { - fmt.Println(e) - continue - } else { - item.FilePath = strings.ReplaceAll(path.Join(panSavePath, item.FilePath), "\\", "/") - importFileItems = append(importFileItems, *item) - } - } - if len(importFileItems) == 0 { - fmt.Println("没有可以导入的文件项目") - return - } - - fmt.Println("正在准备导入...") - dirMap := prepareMkdir(driveId, importFileItems) - - fmt.Println("正在导入...") - successImportFiles := []RapidUploadItem{} - failedImportFiles := []RapidUploadItem{} - for _, item := range importFileItems { - fmt.Printf("正在处理导入: %s\n", item.FilePath) - result, abort := processOneImport(driveId, overwrite, dirMap, item) - if abort { - fmt.Println("导入任务终止了") - break - } - if result { - successImportFiles = append(successImportFiles, item) - } else { - failedImportFiles = append(failedImportFiles, item) - } - time.Sleep(time.Duration(200) * time.Millisecond) - } - if len(failedImportFiles) > 0 { - fmt.Println("\n以下文件导入失败") - for _, f := range failedImportFiles { - fmt.Printf("%s %s\n", f.FileSha1, f.FilePath) - } - fmt.Println("") - } - fmt.Printf("导入结果, 成功 %d, 失败 %d\n", len(successImportFiles), len(failedImportFiles)) -} - -func processOneImport(driveId string, isOverwrite bool, dirMap map[string]*dirFileListData, item RapidUploadItem) (result, abort bool) { - panClient := config.Config.ActiveUser().PanClient() - panDir, fileName := path.Split(item.FilePath) - dataItem := dirMap[path.Dir(panDir)] - if isOverwrite { - // 标记覆盖旧同名文件 - // 检查同名文件是否存在 - var efi *aliyunpan.FileEntity = nil - for _, fileItem := range dataItem.FileList { - if !fileItem.IsFolder() && fileItem.FileName == fileName { - efi = fileItem - break - } - } - if efi != nil && efi.FileId != "" { - // existed, delete it - fdr, err := panClient.WebapiPanClient().FileDelete([]*aliyunpan.FileBatchActionParam{ - { - DriveId: driveId, - FileId: efi.FileId, - }, - }) - if err != nil || fdr == nil || !fdr[0].Success { - fmt.Println("无法删除文件,请稍后重试") - return false, false - } - time.Sleep(time.Duration(500) * time.Millisecond) - fmt.Println("检测到同名文件,已移动到回收站") - } - } - - appCreateUploadFileParam := &aliyunpan.CreateFileUploadParam{ - DriveId: driveId, - Name: fileName, - Size: item.FileSize, - ContentHash: item.FileSha1, - ParentFileId: dataItem.Dir.FileId, - } - uploadOpEntity, apierr := panClient.WebapiPanClient().CreateUploadFile(appCreateUploadFileParam) - if apierr != nil { - fmt.Println("创建秒传任务失败:" + apierr.Error()) - return false, true - } - - if uploadOpEntity.RapidUpload { - logger.Verboseln("秒传成功, 保存到网盘路径: ", path.Join(panDir, uploadOpEntity.FileName)) - } else { - fmt.Println("失败,文件未曾上传,无法秒传") - return false, false - } - return true, false -} - -func prepareMkdir(driveId string, importFileItems []RapidUploadItem) map[string]*dirFileListData { - panClient := config.Config.ActiveUser().PanClient() - resultMap := map[string]*dirFileListData{} - for _, item := range importFileItems { - var apierr *apierror.ApiError - var rs *aliyunpan.MkdirResult - panDir := path.Dir(item.FilePath) - if resultMap[panDir] != nil { - continue - } - if panDir != "/" { - rs, apierr = panClient.WebapiPanClient().MkdirRecursive(driveId, "", "", 0, strings.Split(path.Clean(panDir), "/")) - if apierr != nil || rs.FileId == "" { - logger.Verboseln("创建云盘文件夹失败") - continue - } - } else { - rs = &aliyunpan.MkdirResult{} - rs.FileId = aliyunpan.DefaultRootParentFileId - } - dataItem := &dirFileListData{} - dataItem.Dir = rs - - // files - param := &aliyunpan.FileListParam{} - param.DriveId = driveId - param.ParentFileId = rs.FileId - allFileInfo, err1 := panClient.WebapiPanClient().FileListGetAll(param, 0) - if err1 != nil { - logger.Verboseln("获取文件信息出错") - continue - } - dataItem.FileList = allFileInfo - - resultMap[panDir] = dataItem - time.Sleep(time.Duration(500) * time.Millisecond) - } - return resultMap -} diff --git a/internal/command/locate.go b/internal/command/locate.go deleted file mode 100644 index fa0cf42..0000000 --- a/internal/command/locate.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2020 tickstep. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package command - -import ( - "fmt" - "github.com/tickstep/aliyunpan-api/aliyunpan" - "github.com/tickstep/aliyunpan/cmder" - "github.com/tickstep/aliyunpan/cmder/cmdtable" - "github.com/tickstep/aliyunpan/internal/config" - "github.com/tickstep/aliyunpan/library/collection" - "github.com/urfave/cli" - "io/ioutil" - "os" - "strconv" - "strings" -) - -func CmdLocateUrl() cli.Command { - return cli.Command{ - Name: "locate", - Usage: "获取文件下载链接", - UsageText: cmder.App().Name + " locate <文件/目录1> <文件/目录2> <文件/目录3> ...", - Description: ` - 获取文件下载直链,支持文件和文件夹。下载链接有效时间为4个小时。 - 导出的下载链接可以使用任何支持http下载的工具进行下载,如果是视频文件还可以使用支持在线流播放的视频软件进行在线播放。 - - 注意:由于阿里云盘网页端有防盗链设置所以不能直接使用网页Token登录,你必须使用手机扫码二维码登录(命令:login -QrCode),否则获取的直链无法正常下载,会提示 403 Forbidden 下载被禁止。 - - 示例: - 获取 /我的资源/1.mp4 下载直链 - aliyunpan locate /我的资源/1.mp4 - - 获取 /我的资源 目录下面所有mp4文件的下载直链,使用通配符 - aliyunpan locate /我的资源/*.mp4 - - 获取 /我的资源 目录下面所有文件的下载直链并保存到本地文件 /Volumes/Downloads/file_url.txt 中 - aliyunpan locate -saveto "/Volumes/Downloads/file_url.txt" /我的资源 -`, - Category: "阿里云盘", - Before: ReloadConfigFunc, - Action: func(c *cli.Context) error { - if c.NArg() == 0 { - cli.ShowCommandHelp(c, c.Command.Name) - return nil - } - saveFilePath := "" - if c.IsSet("saveto") { - saveFilePath = c.String("saveto") - } - RunLocateUrl(parseDriveId(c), c.Args(), saveFilePath) - return nil - }, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "driveId", - Usage: "网盘ID", - Value: "", - }, - cli.StringFlag{ - Name: "saveto", - Usage: "导出链接成文件并保存到指定的位置", - }, - }, - } -} - -// RunLocateUrl 执行下载网盘内文件 -func RunLocateUrl(driveId string, paths []string, saveFilePath string) { - useInternalUrl := config.Config.TransferUrlType == 2 - activeUser := GetActiveUser() - activeUser.PanClient().WebapiPanClient().EnableCache() - activeUser.PanClient().WebapiPanClient().ClearCache() - defer activeUser.PanClient().WebapiPanClient().DisableCache() - - paths, err := makePathAbsolute(driveId, paths...) - if err != nil { - fmt.Println(err) - return - } - fileEntityQueue := collection.NewFifoQueue() - sb := &strings.Builder{} - failedList := []string{} - for _, p := range paths { - fileList, err1 := matchPathByShellPattern(driveId, p) - if err1 != nil { - failedList = append(failedList, p) - continue - } - if fileList == nil || len(fileList) == 0 { - // 文件不存在 - failedList = append(failedList, p) - continue - } - for _, f := range fileList { - // 匹配的文件 - fileEntityQueue.Push(f) - } - } - - for { - item := fileEntityQueue.Pop() - if item == nil { - break - } - fileInfo := item.(*aliyunpan.FileEntity) - if fileInfo.IsFolder() { // 文件夹,获取下面所有文件 - allFilesInFolder, er := activeUser.PanClient().WebapiPanClient().FileListGetAll(&aliyunpan.FileListParam{ - DriveId: driveId, - ParentFileId: fileInfo.FileId, - }, 300) - if er != nil { - failedList = append(failedList, fileInfo.Path) - continue - } - for _, f := range allFilesInFolder { - f.Path = fileInfo.Path + "/" + f.FileName - if f.IsFolder() { - // for next term - fileEntityQueue.Push(f) - continue - } - durl, apierr := activeUser.PanClient().WebapiPanClient().GetFileDownloadUrl(&aliyunpan.GetFileDownloadUrlParam{ - DriveId: driveId, - FileId: f.FileId, - }) - if apierr != nil { - failedList = append(failedList, f.Path) - continue - } - url := durl.Url - if useInternalUrl { - url = durl.InternalUrl - } - if saveFilePath != "" { - fmt.Printf("获取文件下载链接:%s\n", f.Path) - fmt.Fprintf(sb, "\n文件:%s\n%s\n", f.Path, url) - } else { - fmt.Printf("\n文件:%s\n%s\n", f.Path, url) - } - } - } else { // 文件 - durl, apierr := activeUser.PanClient().WebapiPanClient().GetFileDownloadUrl(&aliyunpan.GetFileDownloadUrlParam{ - DriveId: driveId, - FileId: fileInfo.FileId, - }) - if apierr != nil { - failedList = append(failedList, fileInfo.Path) - continue - } - url := durl.Url - if useInternalUrl { - url = durl.InternalUrl - } - if saveFilePath != "" { - fmt.Printf("获取文件下载链接:%s\n", fileInfo.Path) - fmt.Fprintf(sb, "\n文件:%s\n%s\n", fileInfo.Path, url) - } else { - fmt.Printf("\n文件:%s\n%s\n", fileInfo.Path, url) - } - } - } - - if saveFilePath != "" { - // save file - if e := ioutil.WriteFile(saveFilePath, []byte(sb.String()), 0777); e == nil { - fmt.Printf("保存文件成功:%s\n", saveFilePath) - } else { - fmt.Printf("保存文件失败:%s\n", saveFilePath) - } - } - - // 输出失败的文件列表 - if len(failedList) > 0 { - pnt := func() { - tb := cmdtable.NewTable(os.Stdout) - tb.SetHeader([]string{"#", "文件/目录"}) - for k, f := range failedList { - tb.Append([]string{strconv.Itoa(k + 1), f}) - } - tb.Render() - } - fmt.Printf("\n\n以下文件获取直链失败: \n") - pnt() - } -} diff --git a/internal/command/user_info.go b/internal/command/user_info.go index 6a1bae9..fcca84b 100644 --- a/internal/command/user_info.go +++ b/internal/command/user_info.go @@ -15,7 +15,6 @@ package command import ( "fmt" - "github.com/tickstep/aliyunpan-api/aliyunpan" "github.com/tickstep/aliyunpan/cmder" "github.com/tickstep/aliyunpan/internal/config" "github.com/urfave/cli" @@ -135,7 +134,3 @@ func CmdWho() cli.Command { }, } } - -func RunGetUserInfo() (userInfo *aliyunpan.UserInfo, error error) { - return GetActivePanClient().WebapiPanClient().GetUserInfo() -}