diff --git a/drivers/onedrive/driver.go b/drivers/onedrive/driver.go index e01c1f7c1d..2e37456f3b 100644 --- a/drivers/onedrive/driver.go +++ b/drivers/onedrive/driver.go @@ -2,6 +2,7 @@ package onedrive import ( "context" + "errors" "fmt" "net/http" "net/url" @@ -109,12 +110,19 @@ func (d *Onedrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs if err != nil { return nil, err } - if f.File == nil { + isFile := f.File != nil || (f.RemoteItem != nil && f.RemoteItem.File != nil) + if !isFile { return nil, errs.NotFile } u := f.Url + if u == "" && f.RemoteItem != nil { + u = f.RemoteItem.Url + } + if u == "" { + return nil, errors.New("failed to get direct link") + } if d.CustomHost != "" { - _u, err := url.Parse(f.Url) + _u, err := url.Parse(u) if err != nil { return nil, err } diff --git a/drivers/onedrive/types.go b/drivers/onedrive/types.go index 78baa07e46..3034176df1 100644 --- a/drivers/onedrive/types.go +++ b/drivers/onedrive/types.go @@ -32,6 +32,25 @@ type File struct { File *struct { MimeType string `json:"mimeType"` } `json:"file"` + Folder *struct { + ChildCount int `json:"childCount"` + } `json:"folder"` + RemoteItem *struct { + Id string `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + FileSystemInfo *FileSystemInfoFacet `json:"fileSystemInfo"` + Url string `json:"@microsoft.graph.downloadUrl"` + File *struct { + MimeType string `json:"mimeType"` + } `json:"file"` + Folder *struct { + ChildCount int `json:"childCount"` + } `json:"folder"` + ParentReference struct { + DriveId string `json:"driveId"` + } `json:"parentReference"` + } `json:"remoteItem"` Thumbnails []struct { Medium struct { Url string `json:"url"` @@ -52,14 +71,35 @@ func fileToObj(f File, parentID string) *Object { if len(f.Thumbnails) > 0 { thumb = f.Thumbnails[0].Medium.Url } + id := f.Id + name := f.Name + size := f.Size + modified := f.FileSystemInfo.LastModifiedDateTime + isFolder := f.Folder != nil || (f.File == nil && f.RemoteItem == nil) + if f.RemoteItem != nil { + if f.RemoteItem.Id != "" { + id = f.RemoteItem.Id + } + if f.RemoteItem.Size > 0 { + size = f.RemoteItem.Size + } + if f.RemoteItem.FileSystemInfo != nil && !f.RemoteItem.FileSystemInfo.LastModifiedDateTime.IsZero() { + modified = f.RemoteItem.FileSystemInfo.LastModifiedDateTime + } + if f.RemoteItem.Folder != nil { + isFolder = true + } else if f.RemoteItem.File != nil { + isFolder = false + } + } return &Object{ ObjThumb: model.ObjThumb{ Object: model.Object{ - ID: f.Id, - Name: f.Name, - Size: f.Size, - Modified: f.FileSystemInfo.LastModifiedDateTime, - IsFolder: f.File == nil, + ID: id, + Name: name, + Size: size, + Modified: modified, + IsFolder: isFolder, }, Thumbnail: model.Thumbnail{Thumbnail: thumb}, //Url: model.Url{Url: f.Url}, diff --git a/drivers/onedrive/util.go b/drivers/onedrive/util.go index ad300af44c..98c29c15df 100644 --- a/drivers/onedrive/util.go +++ b/drivers/onedrive/util.go @@ -7,6 +7,7 @@ import ( "io" "net/http" stdpath "path" + "strings" "time" "github.com/OpenListTeam/OpenList/v4/drivers/base" @@ -165,7 +166,12 @@ func (d *Onedrive) Request(url string, method string, callback base.ReqCallback, func (d *Onedrive) getFiles(path string) ([]File, error) { var res []File - nextLink := d.GetMetaUrl(false, path) + "/children?$top=1000&$expand=thumbnails($select=medium)&$select=id,name,size,fileSystemInfo,content.downloadUrl,file,parentReference" + var nextLink string + if remoteDriveId, remoteItemId, subPath, ok := d.resolveRemotePath(path); ok { + nextLink = d.getRemoteMetaUrl(remoteDriveId, remoteItemId, subPath) + "/children?$top=1000&$expand=thumbnails($select=medium)&$select=id,name,size,fileSystemInfo,content.downloadUrl,file,parentReference,remoteItem,folder" + } else { + nextLink = d.GetMetaUrl(false, path) + "/children?$top=1000&$expand=thumbnails($select=medium)&$select=id,name,size,fileSystemInfo,content.downloadUrl,file,parentReference,remoteItem,folder" + } for nextLink != "" { var files Files _, err := d.Request(nextLink, http.MethodGet, nil, &files) @@ -179,6 +185,51 @@ func (d *Onedrive) getFiles(path string) ([]File, error) { } func (d *Onedrive) GetFile(path string) (*File, error) { + var file File + var u string + if remoteDriveId, remoteItemId, subPath, ok := d.resolveRemotePath(path); ok { + u = d.getRemoteMetaUrl(remoteDriveId, remoteItemId, subPath) + } else { + u = d.GetMetaUrl(false, path) + } + _, err := d.Request(u, http.MethodGet, nil, &file) + return &file, err +} + +func (d *Onedrive) getRemoteMetaUrl(driveId, itemId, subPath string) string { + host, _ := onedriveHostMap[d.Region] + subPath = strings.TrimPrefix(subPath, "/") + if subPath == "" { + return fmt.Sprintf("%s/v1.0/drives/%s/items/%s", host.Api, driveId, itemId) + } + return fmt.Sprintf("%s/v1.0/drives/%s/items/%s:%s:", host.Api, driveId, itemId, utils.EncodePath("/"+subPath, true)) +} + +func (d *Onedrive) resolveRemotePath(reqPath string) (driveId, itemId, subPath string, ok bool) { + reqPath = stdpath.Clean(reqPath) + if reqPath == "/" || reqPath == "." { + return "", "", "", false + } + parts := strings.Split(strings.Trim(reqPath, "/"), "/") + var currentPath string + for i, part := range parts { + currentPath = stdpath.Join(currentPath, part) + f, err := d.getDirectFile("/" + currentPath) + if err != nil { + return "", "", "", false + } + if f.RemoteItem != nil && f.RemoteItem.ParentReference.DriveId != "" { + remoteDriveId := f.RemoteItem.ParentReference.DriveId + remoteItemId := f.RemoteItem.Id + remaining := parts[i+1:] + sub := strings.Join(remaining, "/") + return remoteDriveId, remoteItemId, sub, true + } + } + return "", "", "", false +} + +func (d *Onedrive) getDirectFile(path string) (*File, error) { var file File u := d.GetMetaUrl(false, path) _, err := d.Request(u, http.MethodGet, nil, &file)