mirror of https://github.com/Xhofe/alist
parent
4a0e47dbac
commit
a1ded3a339
|
@ -9,6 +9,8 @@ import (
|
|||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
|
@ -22,6 +24,8 @@ type BaiduPhoto struct {
|
|||
Addition
|
||||
|
||||
AccessToken string
|
||||
Uk int64
|
||||
root model.Obj
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Config() driver.Config {
|
||||
|
@ -33,146 +37,178 @@ func (d *BaiduPhoto) GetAddition() driver.Additional {
|
|||
}
|
||||
|
||||
func (d *BaiduPhoto) Init(ctx context.Context) error {
|
||||
return d.refreshToken()
|
||||
if err := d.refreshToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// root
|
||||
if d.AlbumID != "" {
|
||||
albumID := strings.Split(d.AlbumID, "|")[0]
|
||||
album, err := d.GetAlbumDetail(ctx, albumID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.root = album
|
||||
} else {
|
||||
d.root = &Root{
|
||||
Name: "root",
|
||||
Modified: d.Modified,
|
||||
IsFolder: true,
|
||||
}
|
||||
}
|
||||
|
||||
// uk
|
||||
info, err := d.uInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Uk, err = strconv.ParseInt(info.YouaID, 10, 64)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) GetRoot(ctx context.Context) (model.Obj, error) {
|
||||
return d.root, nil
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Drop(ctx context.Context) error {
|
||||
d.AccessToken = ""
|
||||
d.Uk = 0
|
||||
d.root = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
var objs []model.Obj
|
||||
var err error
|
||||
if IsRoot(dir) {
|
||||
var albums []Album
|
||||
if d.ShowType != "root_only_file" {
|
||||
albums, err = d.GetAllAlbum(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var files []File
|
||||
if d.ShowType != "root_only_album" {
|
||||
files, err = d.GetAllFile(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
alubmName := make(map[string]int)
|
||||
objs, _ = utils.SliceConvert(albums, func(album Album) (model.Obj, error) {
|
||||
i := alubmName[album.GetName()]
|
||||
if i != 0 {
|
||||
alubmName[album.GetName()]++
|
||||
album.Title = fmt.Sprintf("%s(%d)", album.Title, i)
|
||||
}
|
||||
alubmName[album.GetName()]++
|
||||
return &album, nil
|
||||
})
|
||||
for i := 0; i < len(files); i++ {
|
||||
objs = append(objs, &files[i])
|
||||
}
|
||||
} else if IsAlbum(dir) || IsAlbumRoot(dir) {
|
||||
/* album */
|
||||
if album, ok := dir.(*Album); ok {
|
||||
var files []AlbumFile
|
||||
files, err = d.GetAllAlbumFile(ctx, splitID(dir.GetID())[0], "")
|
||||
files, err = d.GetAllAlbumFile(ctx, album, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = make([]model.Obj, 0, len(files))
|
||||
for i := 0; i < len(files); i++ {
|
||||
objs = append(objs, &files[i])
|
||||
|
||||
return utils.MustSliceConvert(files, func(file AlbumFile) model.Obj {
|
||||
return &file
|
||||
}), nil
|
||||
}
|
||||
|
||||
/* root */
|
||||
var albums []Album
|
||||
if d.ShowType != "root_only_file" {
|
||||
albums, err = d.GetAllAlbum(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return objs, nil
|
||||
|
||||
var files []File
|
||||
if d.ShowType != "root_only_album" {
|
||||
files, err = d.GetAllFile(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return append(
|
||||
utils.MustSliceConvert(albums, func(album Album) model.Obj {
|
||||
return &album
|
||||
}),
|
||||
utils.MustSliceConvert(files, func(album File) model.Obj {
|
||||
return &album
|
||||
})...,
|
||||
), nil
|
||||
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
if IsAlbumFile(file) {
|
||||
return d.linkAlbum(ctx, file, args)
|
||||
} else if IsFile(file) {
|
||||
switch file := file.(type) {
|
||||
case *File:
|
||||
return d.linkFile(ctx, file, args)
|
||||
case *AlbumFile:
|
||||
return d.linkAlbum(ctx, file, args)
|
||||
}
|
||||
return nil, errs.NotFile
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
if IsRoot(parentDir) {
|
||||
code := regexp.MustCompile(`(?i)join:([\S]*)`).FindStringSubmatch(dirName)
|
||||
var joinReg = regexp.MustCompile(`(?i)join:([\S]*)`)
|
||||
|
||||
func (d *BaiduPhoto) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
|
||||
if _, ok := parentDir.(*Root); ok {
|
||||
code := joinReg.FindStringSubmatch(dirName)
|
||||
if len(code) > 1 {
|
||||
return d.JoinAlbum(ctx, code[1])
|
||||
}
|
||||
return d.CreateAlbum(ctx, dirName)
|
||||
}
|
||||
return errs.NotSupport
|
||||
return nil, errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
if IsFile(srcObj) {
|
||||
if IsAlbum(dstDir) {
|
||||
func (d *BaiduPhoto) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
||||
switch file := srcObj.(type) {
|
||||
case *File:
|
||||
if album, ok := dstDir.(*Album); ok {
|
||||
//rootfile -> album
|
||||
e := splitID(dstDir.GetID())
|
||||
return d.AddAlbumFile(ctx, e[0], e[1], srcObj.GetID())
|
||||
return d.AddAlbumFile(ctx, album, file)
|
||||
}
|
||||
} else if IsAlbumFile(srcObj) {
|
||||
if IsRoot(dstDir) {
|
||||
case *AlbumFile:
|
||||
switch album := dstDir.(type) {
|
||||
case *Root:
|
||||
//albumfile -> root
|
||||
e := splitID(srcObj.GetID())
|
||||
_, err := d.CopyAlbumFile(ctx, e[1], e[2], e[3], srcObj.GetID())
|
||||
return err
|
||||
} else if IsAlbum(dstDir) {
|
||||
return d.CopyAlbumFile(ctx, file)
|
||||
case *Album:
|
||||
// albumfile -> root -> album
|
||||
e := splitID(srcObj.GetID())
|
||||
file, err := d.CopyAlbumFile(ctx, e[1], e[2], e[3], srcObj.GetID())
|
||||
rootfile, err := d.CopyAlbumFile(ctx, file)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
e = splitID(dstDir.GetID())
|
||||
return d.AddAlbumFile(ctx, e[0], e[1], fmt.Sprint(file.Fsid))
|
||||
return d.AddAlbumFile(ctx, album, rootfile)
|
||||
}
|
||||
}
|
||||
return errs.NotSupport
|
||||
return nil, errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
func (d *BaiduPhoto) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
||||
// 仅支持相册之间移动
|
||||
if IsAlbumFile(srcObj) && IsAlbum(dstDir) {
|
||||
err := d.Copy(ctx, srcObj, dstDir)
|
||||
if err != nil {
|
||||
return err
|
||||
if file, ok := srcObj.(*AlbumFile); ok {
|
||||
if _, ok := dstDir.(*Album); ok {
|
||||
newObj, err := d.Copy(ctx, srcObj, dstDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 删除原相册文件
|
||||
_ = d.DeleteAlbumFile(ctx, file)
|
||||
return newObj, nil
|
||||
}
|
||||
e := splitID(srcObj.GetID())
|
||||
return d.DeleteAlbumFile(ctx, e[1], e[2], srcObj.GetID())
|
||||
}
|
||||
return errs.NotSupport
|
||||
return nil, errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
func (d *BaiduPhoto) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
|
||||
// 仅支持相册改名
|
||||
if IsAlbum(srcObj) {
|
||||
e := splitID(srcObj.GetID())
|
||||
return d.SetAlbumName(ctx, e[0], e[1], newName)
|
||||
if album, ok := srcObj.(*Album); ok {
|
||||
return d.SetAlbumName(ctx, album, newName)
|
||||
}
|
||||
return errs.NotSupport
|
||||
return nil, errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Remove(ctx context.Context, obj model.Obj) error {
|
||||
e := splitID(obj.GetID())
|
||||
if IsFile(obj) {
|
||||
return d.DeleteFile(ctx, e[0])
|
||||
} else if IsAlbum(obj) {
|
||||
return d.DeleteAlbum(ctx, e[0], e[1])
|
||||
} else if IsAlbumFile(obj) {
|
||||
return d.DeleteAlbumFile(ctx, e[1], e[2], obj.GetID())
|
||||
switch obj := obj.(type) {
|
||||
case *File:
|
||||
return d.DeleteFile(ctx, obj)
|
||||
case *AlbumFile:
|
||||
return d.DeleteAlbumFile(ctx, obj)
|
||||
case *Album:
|
||||
return d.DeleteAlbum(ctx, obj)
|
||||
}
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
|
||||
// 需要获取完整文件md5,必须支持 io.Seek
|
||||
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
|
@ -190,20 +226,19 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||
sliceMd52 := md5.New()
|
||||
slicemd52Write := utils.LimitWriter(sliceMd52, SliceSize)
|
||||
for i := 1; i <= count; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if utils.IsCanceled(ctx) {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
_, err := io.CopyN(io.MultiWriter(fileMd5, sliceMd5, slicemd52Write), tempFile, DEFAULT)
|
||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
sliceMD5List = append(sliceMD5List, hex.EncodeToString(sliceMd5.Sum(nil)))
|
||||
sliceMd5.Reset()
|
||||
}
|
||||
if _, err = tempFile.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
content_md5 := hex.EncodeToString(fileMd5.Sum(nil))
|
||||
slice_md5 := hex.EncodeToString(sliceMd52.Sum(nil))
|
||||
|
@ -228,7 +263,7 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||
r.SetFormData(params)
|
||||
}, &precreateResp)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch precreateResp.ReturnType {
|
||||
|
@ -241,7 +276,7 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||
|
||||
for i := 0; i < count; i++ {
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
uploadParams["partseq"] = fmt.Sprint(i)
|
||||
_, err = d.Post("https://c3.pcs.baidu.com/rest/2.0/pcs/superfile2", func(r *resty.Request) {
|
||||
|
@ -250,7 +285,7 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||
r.SetFileReader("file", stream.GetName(), io.LimitReader(tempFile, DEFAULT))
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
up(i * 100 / count)
|
||||
}
|
||||
|
@ -262,19 +297,24 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||
r.SetFormData(params)
|
||||
}, &precreateResp)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
fallthrough
|
||||
case 3: // 增加到相册
|
||||
if IsAlbum(dstDir) || IsAlbumRoot(dstDir) {
|
||||
e := splitID(dstDir.GetID())
|
||||
err = d.AddAlbumFile(ctx, e[0], e[1], fmt.Sprint(precreateResp.Data.FsID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootfile := precreateResp.Data.toFile()
|
||||
if album, ok := dstDir.(*Album); ok {
|
||||
return d.AddAlbumFile(ctx, album, rootfile)
|
||||
}
|
||||
return rootfile, nil
|
||||
}
|
||||
return nil
|
||||
return nil, errs.NotSupport
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*BaiduPhoto)(nil)
|
||||
var _ driver.Getter = (*BaiduPhoto)(nil)
|
||||
var _ driver.MkdirResult = (*BaiduPhoto)(nil)
|
||||
var _ driver.CopyResult = (*BaiduPhoto)(nil)
|
||||
var _ driver.MoveResult = (*BaiduPhoto)(nil)
|
||||
var _ driver.Remove = (*BaiduPhoto)(nil)
|
||||
var _ driver.PutResult = (*BaiduPhoto)(nil)
|
||||
var _ driver.RenameResult = (*BaiduPhoto)(nil)
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
//Tid生成
|
||||
// Tid生成
|
||||
func getTid() string {
|
||||
return fmt.Sprintf("3%d%.0f", time.Now().Unix(), math.Floor(9000000*rand.Float64()+1000000))
|
||||
}
|
||||
|
@ -26,82 +26,52 @@ func toTime(t int64) *time.Time {
|
|||
return &tm
|
||||
}
|
||||
|
||||
func fsidsFormat(ids ...string) string {
|
||||
var buf []string
|
||||
for _, id := range ids {
|
||||
e := splitID(id)
|
||||
buf = append(buf, fmt.Sprintf(`{"fsid":%s,"uk":%s}`, e[0], e[3]))
|
||||
}
|
||||
func fsidsFormatNotUk(ids ...int64) string {
|
||||
buf := utils.MustSliceConvert(ids, func(id int64) string {
|
||||
return fmt.Sprintf(`{"fsid":%d}`, id)
|
||||
})
|
||||
return fmt.Sprintf("[%s]", strings.Join(buf, ","))
|
||||
}
|
||||
|
||||
func fsidsFormatNotUk(ids ...string) string {
|
||||
var buf []string
|
||||
for _, id := range ids {
|
||||
buf = append(buf, fmt.Sprintf(`{"fsid":%s}`, splitID(id)[0]))
|
||||
}
|
||||
return fmt.Sprintf("[%s]", strings.Join(buf, ","))
|
||||
}
|
||||
|
||||
/*
|
||||
结构
|
||||
|
||||
{fsid} 文件
|
||||
|
||||
{album_id}|{tid} 相册
|
||||
|
||||
{fsid}|{album_id}|{tid}|{uk} 相册文件
|
||||
*/
|
||||
func splitID(id string) []string {
|
||||
return strings.SplitN(id, "|", 4)[:4]
|
||||
}
|
||||
|
||||
/*
|
||||
结构
|
||||
|
||||
{fsid} 文件
|
||||
|
||||
{album_id}|{tid} 相册
|
||||
|
||||
{fsid}|{album_id}|{tid}|{uk} 相册文件
|
||||
*/
|
||||
func joinID(ids ...interface{}) string {
|
||||
idsStr := make([]string, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
idsStr = append(idsStr, fmt.Sprint(id))
|
||||
}
|
||||
return strings.Join(idsStr, "|")
|
||||
}
|
||||
|
||||
func getFileName(path string) string {
|
||||
return path[strings.LastIndex(path, "/")+1:]
|
||||
}
|
||||
|
||||
// 相册
|
||||
func IsAlbum(obj model.Obj) bool {
|
||||
return obj.IsDir() && obj.GetPath() == "album"
|
||||
}
|
||||
|
||||
// 根目录
|
||||
func IsRoot(obj model.Obj) bool {
|
||||
return obj.IsDir() && obj.GetPath() == "" && obj.GetID() == ""
|
||||
}
|
||||
|
||||
// 以相册为根目录
|
||||
func IsAlbumRoot(obj model.Obj) bool {
|
||||
return obj.IsDir() && obj.GetPath() == "" && obj.GetID() != ""
|
||||
}
|
||||
|
||||
// 根文件
|
||||
func IsFile(obj model.Obj) bool {
|
||||
return !obj.IsDir() && obj.GetPath() == "file"
|
||||
}
|
||||
|
||||
// 相册文件
|
||||
func IsAlbumFile(obj model.Obj) bool {
|
||||
return !obj.IsDir() && obj.GetPath() == "albumfile"
|
||||
}
|
||||
|
||||
func MustString(str string, err error) string {
|
||||
return str
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理文件变化
|
||||
* 最大程度利用重复数据
|
||||
**/
|
||||
func copyFile(file *AlbumFile, cf *CopyFile) *File {
|
||||
return &File{
|
||||
Fsid: cf.Fsid,
|
||||
Path: cf.Path,
|
||||
Ctime: cf.Ctime,
|
||||
Mtime: cf.Ctime,
|
||||
Size: file.Size,
|
||||
Thumburl: file.Thumburl,
|
||||
}
|
||||
}
|
||||
|
||||
func moveFileToAlbumFile(file *File, album *Album, uk int64) *AlbumFile {
|
||||
return &AlbumFile{
|
||||
File: *file,
|
||||
AlbumID: album.AlbumID,
|
||||
Tid: album.Tid,
|
||||
Uk: uk,
|
||||
}
|
||||
}
|
||||
|
||||
func renameAlbum(album *Album, newName string) *Album {
|
||||
return &Album{
|
||||
AlbumID: album.AlbumID,
|
||||
Tid: album.Tid,
|
||||
JoinTime: album.JoinTime,
|
||||
CreateTime: album.CreateTime,
|
||||
Title: newName,
|
||||
Mtime: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,6 @@ type Addition struct {
|
|||
ClientSecret string `json:"client_secret" required:"true" default:"jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG"`
|
||||
}
|
||||
|
||||
func (a Addition) GetRootId() string {
|
||||
return a.AlbumID
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "BaiduPhoto",
|
||||
LocalSort: true,
|
||||
|
|
|
@ -3,6 +3,8 @@ package baiduphoto
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
type TokenErrResp struct {
|
||||
|
@ -19,6 +21,12 @@ type Erron struct {
|
|||
RequestID int `json:"request_id"`
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
type UInfo struct {
|
||||
// uk
|
||||
YouaID string `json:"youa_id"`
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
HasMore int `json:"has_more"`
|
||||
Cursor string `json:"cursor"`
|
||||
|
@ -28,6 +36,8 @@ func (p Page) HasNextPage() bool {
|
|||
return p.HasMore == 1
|
||||
}
|
||||
|
||||
type Root = model.Object
|
||||
|
||||
type (
|
||||
FileListResp struct {
|
||||
Page
|
||||
|
@ -55,8 +65,8 @@ func (c *File) ModTime() time.Time {
|
|||
return *c.parseTime
|
||||
}
|
||||
func (c *File) IsDir() bool { return false }
|
||||
func (c *File) GetID() string { return joinID(c.Fsid) }
|
||||
func (c *File) GetPath() string { return "file" }
|
||||
func (c *File) GetID() string { return "" }
|
||||
func (c *File) GetPath() string { return "" }
|
||||
func (c *File) Thumb() string {
|
||||
if len(c.Thumburl) > 0 {
|
||||
return c.Thumburl[0]
|
||||
|
@ -108,11 +118,8 @@ func (a *Album) ModTime() time.Time {
|
|||
return *a.parseTime
|
||||
}
|
||||
func (a *Album) IsDir() bool { return true }
|
||||
func (a *Album) GetID() string { return joinID(a.AlbumID, a.Tid) }
|
||||
func (a *Album) GetPath() string { return "album" }
|
||||
|
||||
func (af *AlbumFile) GetID() string { return joinID(af.Fsid, af.AlbumID, af.Tid, af.Uk) }
|
||||
func (c *AlbumFile) GetPath() string { return "albumfile" }
|
||||
func (a *Album) GetID() string { return "" }
|
||||
func (a *Album) GetPath() string { return "" }
|
||||
|
||||
type (
|
||||
CopyFileResp struct {
|
||||
|
@ -120,7 +127,8 @@ type (
|
|||
}
|
||||
CopyFile struct {
|
||||
FromFsid int64 `json:"from_fsid"` // 源ID
|
||||
Fsid int64 `json:"fsid"` // 目标ID
|
||||
Ctime int64 `json:"ctime"`
|
||||
Fsid int64 `json:"fsid"` // 目标ID
|
||||
Path string `json:"path"`
|
||||
ShootTime int `json:"shoot_time"`
|
||||
}
|
||||
|
@ -134,8 +142,8 @@ type (
|
|||
Md5 string `json:"md5"`
|
||||
ServerFilename string `json:"server_filename"`
|
||||
Path string `json:"path"`
|
||||
Ctime int `json:"ctime"`
|
||||
Mtime int `json:"mtime"`
|
||||
Ctime int64 `json:"ctime"`
|
||||
Mtime int64 `json:"mtime"`
|
||||
Isdir int `json:"isdir"`
|
||||
Category int `json:"category"`
|
||||
ServerMd5 string `json:"server_md5"`
|
||||
|
@ -158,6 +166,18 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
func (f *UploadFile) toFile() *File {
|
||||
return &File{
|
||||
Fsid: f.FsID,
|
||||
Path: f.Path,
|
||||
Size: f.Size,
|
||||
Ctime: f.Ctime,
|
||||
Mtime: f.Mtime,
|
||||
Thumburl: nil,
|
||||
}
|
||||
}
|
||||
|
||||
/* 共享相册部分 */
|
||||
type InviteResp struct {
|
||||
Pdata struct {
|
||||
// 邀请码
|
||||
|
@ -167,3 +187,9 @@ type InviteResp struct {
|
|||
ShareID string `json:"share_id"`
|
||||
} `json:"pdata"`
|
||||
}
|
||||
|
||||
/* 加入相册部分 */
|
||||
type JoinOrCreateAlbumResp struct {
|
||||
AlbumID string `json:"album_id"`
|
||||
AlreadyExists int `json:"already_exists"`
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
|
@ -17,6 +16,7 @@ import (
|
|||
|
||||
const (
|
||||
API_URL = "https://photo.baidu.com/youai"
|
||||
USER_API_URL = API_URL + "/user/v1"
|
||||
ALBUM_API_URL = API_URL + "/album/v1"
|
||||
FILE_API_URL_V1 = API_URL + "/file/v1"
|
||||
FILE_API_URL_V2 = API_URL + "/file/v2"
|
||||
|
@ -116,11 +116,11 @@ func (p *BaiduPhoto) GetAllFile(ctx context.Context) (files []File, err error) {
|
|||
}
|
||||
|
||||
// 删除根文件
|
||||
func (p *BaiduPhoto) DeleteFile(ctx context.Context, fileIDs ...string) error {
|
||||
func (p *BaiduPhoto) DeleteFile(ctx context.Context, file *File) error {
|
||||
_, err := p.Get(FILE_API_URL_V1+"/delete", func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetQueryParams(map[string]string{
|
||||
"fsid_list": fmt.Sprintf("[%s]", strings.Join(fileIDs, ",")),
|
||||
"fsid_list": fmt.Sprintf("[%d]", file.Fsid),
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
|
@ -156,14 +156,14 @@ func (p *BaiduPhoto) GetAllAlbum(ctx context.Context) (albums []Album, err error
|
|||
}
|
||||
|
||||
// 获取相册中所有文件
|
||||
func (p *BaiduPhoto) GetAllAlbumFile(ctx context.Context, albumID, passwd string) (files []AlbumFile, err error) {
|
||||
func (p *BaiduPhoto) GetAllAlbumFile(ctx context.Context, album *Album, passwd string) (files []AlbumFile, err error) {
|
||||
var cursor string
|
||||
for {
|
||||
var resp AlbumFileListResp
|
||||
_, err = p.Get(ALBUM_API_URL+"/listfile", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetQueryParams(map[string]string{
|
||||
"album_id": albumID,
|
||||
"album_id": album.AlbumID,
|
||||
"need_amount": "1",
|
||||
"limit": "1000",
|
||||
"passwd": passwd,
|
||||
|
@ -187,45 +187,52 @@ func (p *BaiduPhoto) GetAllAlbumFile(ctx context.Context, albumID, passwd string
|
|||
}
|
||||
|
||||
// 创建相册
|
||||
func (p *BaiduPhoto) CreateAlbum(ctx context.Context, name string) error {
|
||||
func (p *BaiduPhoto) CreateAlbum(ctx context.Context, name string) (*Album, error) {
|
||||
if !checkName(name) {
|
||||
return ErrNotSupportName
|
||||
return nil, ErrNotSupportName
|
||||
}
|
||||
var resp JoinOrCreateAlbumResp
|
||||
_, err := p.Post(ALBUM_API_URL+"/create", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetContext(ctx).SetResult(&resp)
|
||||
r.SetQueryParams(map[string]string{
|
||||
"title": name,
|
||||
"tid": getTid(),
|
||||
"source": "0",
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.GetAlbumDetail(ctx, resp.AlbumID)
|
||||
}
|
||||
|
||||
// 相册改名
|
||||
func (p *BaiduPhoto) SetAlbumName(ctx context.Context, albumID, tID, name string) error {
|
||||
func (p *BaiduPhoto) SetAlbumName(ctx context.Context, album *Album, name string) (*Album, error) {
|
||||
if !checkName(name) {
|
||||
return ErrNotSupportName
|
||||
return nil, ErrNotSupportName
|
||||
}
|
||||
|
||||
_, err := p.Post(ALBUM_API_URL+"/settitle", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetFormData(map[string]string{
|
||||
"title": name,
|
||||
"album_id": albumID,
|
||||
"tid": tID,
|
||||
"album_id": album.AlbumID,
|
||||
"tid": fmt.Sprint(album.Tid),
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return renameAlbum(album, name), nil
|
||||
}
|
||||
|
||||
// 删除相册
|
||||
func (p *BaiduPhoto) DeleteAlbum(ctx context.Context, albumID, tID string) error {
|
||||
func (p *BaiduPhoto) DeleteAlbum(ctx context.Context, album *Album) error {
|
||||
_, err := p.Post(ALBUM_API_URL+"/delete", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetFormData(map[string]string{
|
||||
"album_id": albumID,
|
||||
"tid": tID,
|
||||
"album_id": album.AlbumID,
|
||||
"tid": fmt.Sprint(album.Tid),
|
||||
"delete_origin_image": "0", // 是否删除原图 0 不删除 1 删除
|
||||
})
|
||||
}, nil)
|
||||
|
@ -233,13 +240,13 @@ func (p *BaiduPhoto) DeleteAlbum(ctx context.Context, albumID, tID string) error
|
|||
}
|
||||
|
||||
// 删除相册文件
|
||||
func (p *BaiduPhoto) DeleteAlbumFile(ctx context.Context, albumID, tID string, fileIDs ...string) error {
|
||||
func (p *BaiduPhoto) DeleteAlbumFile(ctx context.Context, file *AlbumFile) error {
|
||||
_, err := p.Post(ALBUM_API_URL+"/delfile", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetFormData(map[string]string{
|
||||
"album_id": albumID,
|
||||
"tid": tID,
|
||||
"list": fsidsFormat(fileIDs...),
|
||||
"album_id": fmt.Sprint(file.AlbumID),
|
||||
"tid": fmt.Sprint(file.Tid),
|
||||
"list": fmt.Sprintf(`[{"fsid":%d,"uk":%d}]`, file.Fsid, file.Uk),
|
||||
"del_origin": "0", // 是否删除原图 0 不删除 1 删除
|
||||
})
|
||||
}, nil)
|
||||
|
@ -247,39 +254,42 @@ func (p *BaiduPhoto) DeleteAlbumFile(ctx context.Context, albumID, tID string, f
|
|||
}
|
||||
|
||||
// 增加相册文件
|
||||
func (p *BaiduPhoto) AddAlbumFile(ctx context.Context, albumID, tID string, fileIDs ...string) error {
|
||||
func (p *BaiduPhoto) AddAlbumFile(ctx context.Context, album *Album, file *File) (*AlbumFile, error) {
|
||||
_, err := p.Get(ALBUM_API_URL+"/addfile", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetQueryParams(map[string]string{
|
||||
"album_id": albumID,
|
||||
"tid": tID,
|
||||
"list": fsidsFormatNotUk(fileIDs...),
|
||||
"album_id": fmt.Sprint(album.AlbumID),
|
||||
"tid": fmt.Sprint(album.Tid),
|
||||
"list": fsidsFormatNotUk(file.Fsid),
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return moveFileToAlbumFile(file, album, p.Uk), nil
|
||||
}
|
||||
|
||||
// 保存相册文件为根文件
|
||||
func (p *BaiduPhoto) CopyAlbumFile(ctx context.Context, albumID, tID, uk string, fileID ...string) (*CopyFile, error) {
|
||||
func (p *BaiduPhoto) CopyAlbumFile(ctx context.Context, file *AlbumFile) (*File, error) {
|
||||
var resp CopyFileResp
|
||||
_, err := p.Post(ALBUM_API_URL+"/copyfile", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetFormData(map[string]string{
|
||||
"album_id": albumID,
|
||||
"tid": tID,
|
||||
"uk": uk,
|
||||
"list": fsidsFormatNotUk(fileID...),
|
||||
"album_id": file.AlbumID,
|
||||
"tid": fmt.Sprint(file.Tid),
|
||||
"uk": fmt.Sprint(file.Uk),
|
||||
"list": fsidsFormatNotUk(file.Fsid),
|
||||
})
|
||||
r.SetResult(&resp)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp.List[0], nil
|
||||
return copyFile(file, &resp.List[0]), nil
|
||||
}
|
||||
|
||||
// 加入相册
|
||||
func (p *BaiduPhoto) JoinAlbum(ctx context.Context, code string) error {
|
||||
func (p *BaiduPhoto) JoinAlbum(ctx context.Context, code string) (*Album, error) {
|
||||
var resp InviteResp
|
||||
_, err := p.Get(ALBUM_API_URL+"/querypcode", func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
|
@ -289,18 +299,37 @@ func (p *BaiduPhoto) JoinAlbum(ctx context.Context, code string) error {
|
|||
})
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
var resp2 JoinOrCreateAlbumResp
|
||||
_, err = p.Get(ALBUM_API_URL+"/join", func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetQueryParams(map[string]string{
|
||||
"invite_code": resp.Pdata.InviteCode,
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
}, &resp2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.GetAlbumDetail(ctx, resp2.AlbumID)
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) linkAlbum(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
// 获取相册详细信息
|
||||
func (p *BaiduPhoto) GetAlbumDetail(ctx context.Context, albumID string) (*Album, error) {
|
||||
var album Album
|
||||
_, err := p.Get(ALBUM_API_URL+"/detail", func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetResult(&album)
|
||||
req.SetQueryParams(map[string]string{
|
||||
"album_id": albumID,
|
||||
})
|
||||
}, &album)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &album, nil
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) linkAlbum(ctx context.Context, file *AlbumFile, args model.LinkArgs) (*model.Link, error) {
|
||||
headers := map[string]string{
|
||||
"User-Agent": base.UserAgent,
|
||||
}
|
||||
|
@ -311,16 +340,15 @@ func (d *BaiduPhoto) linkAlbum(ctx context.Context, file model.Obj, args model.L
|
|||
headers["X-Forwarded-For"] = args.IP
|
||||
}
|
||||
|
||||
e := splitID(file.GetID())
|
||||
res, err := base.NoRedirectClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeaders(headers).
|
||||
SetQueryParams(map[string]string{
|
||||
"access_token": d.AccessToken,
|
||||
"fsid": e[0],
|
||||
"album_id": e[1],
|
||||
"tid": e[2],
|
||||
"uk": e[3],
|
||||
"fsid": fmt.Sprint(file.Fsid),
|
||||
"album_id": file.AlbumID,
|
||||
"tid": fmt.Sprint(file.Tid),
|
||||
"uk": fmt.Sprint(file.Uk),
|
||||
}).
|
||||
Head(ALBUM_API_URL + "/download")
|
||||
|
||||
|
@ -328,19 +356,17 @@ func (d *BaiduPhoto) linkAlbum(ctx context.Context, file model.Obj, args model.L
|
|||
return nil, err
|
||||
}
|
||||
|
||||
//exp := 8 * time.Hour
|
||||
link := &model.Link{
|
||||
URL: res.Header().Get("location"),
|
||||
Header: http.Header{
|
||||
"User-Agent": []string{headers["User-Agent"]},
|
||||
"Referer": []string{"https://photo.baidu.com/"},
|
||||
},
|
||||
//Expiration: &exp,
|
||||
}
|
||||
return link, nil
|
||||
}
|
||||
|
||||
func (d *BaiduPhoto) linkFile(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
func (d *BaiduPhoto) linkFile(ctx context.Context, file *File, args model.LinkArgs) (*model.Link, error) {
|
||||
headers := map[string]string{
|
||||
"User-Agent": base.UserAgent,
|
||||
}
|
||||
|
@ -358,21 +384,31 @@ func (d *BaiduPhoto) linkFile(ctx context.Context, file model.Obj, args model.Li
|
|||
r.SetContext(ctx)
|
||||
r.SetHeaders(headers)
|
||||
r.SetQueryParams(map[string]string{
|
||||
"fsid": splitID(file.GetID())[0],
|
||||
"fsid": fmt.Sprint(file.Fsid),
|
||||
})
|
||||
}, &downloadUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//exp := 8 * time.Hour
|
||||
link := &model.Link{
|
||||
URL: downloadUrl.Dlink,
|
||||
Header: http.Header{
|
||||
"User-Agent": []string{headers["User-Agent"]},
|
||||
"Referer": []string{"https://photo.baidu.com/"},
|
||||
},
|
||||
//Expiration: &exp,
|
||||
}
|
||||
return link, nil
|
||||
}
|
||||
|
||||
// 获取uk
|
||||
func (d *BaiduPhoto) uInfo() (*UInfo, error) {
|
||||
var info UInfo
|
||||
_, err := d.Get(USER_API_URL+"/getuinfo", func(req *resty.Request) {
|
||||
|
||||
}, &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func SliceContains[T comparable](arr []T, v T) bool {
|
|||
|
||||
// SliceConvert convert slice to another type slice
|
||||
func SliceConvert[S any, D any](srcS []S, convert func(src S) (D, error)) ([]D, error) {
|
||||
var res []D
|
||||
res := make([]D, 0, len(srcS))
|
||||
for i := range srcS {
|
||||
dst, err := convert(srcS[i])
|
||||
if err != nil {
|
||||
|
@ -43,7 +43,7 @@ func SliceConvert[S any, D any](srcS []S, convert func(src S) (D, error)) ([]D,
|
|||
}
|
||||
|
||||
func MustSliceConvert[S any, D any](srcS []S, convert func(src S) D) []D {
|
||||
var res []D
|
||||
res := make([]D, 0, len(srcS))
|
||||
for i := range srcS {
|
||||
dst := convert(srcS[i])
|
||||
res = append(res, dst)
|
||||
|
|
Loading…
Reference in New Issue