remove no support command

This commit is contained in:
tickstep 2024-03-02 10:25:35 +08:00
parent e261e04978
commit 2fe5cc5414
4 changed files with 0 additions and 629 deletions

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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()
}