Skip to content

Commit

Permalink
feat: add support for Onedrive Sharelink driver (#6793)
Browse files Browse the repository at this point in the history
* feat: add support for Onedrive Sharelink driver

* fix(Onedrive Sharelink): use internal UA
  • Loading branch information
Sakura-Byte authored Jul 17, 2024
1 parent 049575b commit cee0000
Show file tree
Hide file tree
Showing 5 changed files with 604 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
_ "github.com/alist-org/alist/v3/drivers/netease_music"
_ "github.com/alist-org/alist/v3/drivers/onedrive"
_ "github.com/alist-org/alist/v3/drivers/onedrive_app"
_ "github.com/alist-org/alist/v3/drivers/onedrive_sharelink"
_ "github.com/alist-org/alist/v3/drivers/pikpak"
_ "github.com/alist-org/alist/v3/drivers/pikpak_share"
_ "github.com/alist-org/alist/v3/drivers/quark_uc"
Expand Down
131 changes: 131 additions & 0 deletions drivers/onedrive_sharelink/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package onedrive_sharelink

import (
"context"
"strings"
"time"

"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/cron"
"github.com/alist-org/alist/v3/pkg/utils"
log "github.com/sirupsen/logrus"
)

type OnedriveSharelink struct {
model.Storage
cron *cron.Cron
Addition
}

func (d *OnedriveSharelink) Config() driver.Config {
return config
}

func (d *OnedriveSharelink) GetAddition() driver.Additional {
return &d.Addition
}

func (d *OnedriveSharelink) Init(ctx context.Context) error {
// Initialize error variable
var err error

// If there is "-my" in the URL, it is NOT a SharePoint link
d.IsSharepoint = !strings.Contains(d.ShareLinkURL, "-my")

// Initialize cron job to run every hour
d.cron = cron.NewCron(time.Hour * 1)
d.cron.Do(func() {
var err error
d.Headers, err = d.getHeaders()
if err != nil {
log.Errorf("%+v", err)
}
})

// Get initial headers
d.Headers, err = d.getHeaders()
if err != nil {
return err
}

return nil
}

func (d *OnedriveSharelink) Drop(ctx context.Context) error {
return nil
}

func (d *OnedriveSharelink) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
path := dir.GetPath()
files, err := d.getFiles(path)
if err != nil {
return nil, err
}

// Convert the slice of files to the required model.Obj format
return utils.SliceConvert(files, func(src Item) (model.Obj, error) {
return fileToObj(src), nil
})
}

func (d *OnedriveSharelink) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
// Get the unique ID of the file
uniqueId := file.GetID()
// Cut the first char and the last char
uniqueId = uniqueId[1 : len(uniqueId)-1]
url := d.downloadLinkPrefix + uniqueId
header := d.Headers

// If the headers are older than 30 minutes, get new headers
if d.HeaderTime < time.Now().Unix()-1800 {
var err error
log.Debug("headers are older than 30 minutes, get new headers")
header, err = d.getHeaders()
if err != nil {
return nil, err
}
}

return &model.Link{
URL: url,
Header: header,
}, nil
}

func (d *OnedriveSharelink) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
// TODO create folder, optional
return errs.NotImplement
}

func (d *OnedriveSharelink) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO move obj, optional
return errs.NotImplement
}

func (d *OnedriveSharelink) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
// TODO rename obj, optional
return errs.NotImplement
}

func (d *OnedriveSharelink) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO copy obj, optional
return errs.NotImplement
}

func (d *OnedriveSharelink) Remove(ctx context.Context, obj model.Obj) error {
// TODO remove obj, optional
return errs.NotImplement
}

func (d *OnedriveSharelink) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
// TODO upload file, optional
return errs.NotImplement
}

//func (d *OnedriveSharelink) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
// return nil, errs.NotSupport
//}

var _ driver.Driver = (*OnedriveSharelink)(nil)
32 changes: 32 additions & 0 deletions drivers/onedrive_sharelink/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package onedrive_sharelink

import (
"net/http"

"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)

type Addition struct {
driver.RootPath
ShareLinkURL string `json:"url" required:"true"`
ShareLinkPassword string `json:"password"`
IsSharepoint bool
downloadLinkPrefix string
Headers http.Header
HeaderTime int64
}

var config = driver.Config{
Name: "Onedrive Sharelink",
OnlyProxy: true,
NoUpload: true,
DefaultRoot: "/",
CheckStatus: false,
}

func init() {
op.RegisterDriver(func() driver.Driver {
return &OnedriveSharelink{}
})
}
77 changes: 77 additions & 0 deletions drivers/onedrive_sharelink/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package onedrive_sharelink

import (
"strconv"
"time"

"github.com/alist-org/alist/v3/internal/model"
)

// FolderResp represents the structure of the folder response from the OneDrive API.
type FolderResp struct {
// Data holds the nested structure of the response.
Data struct {
Legacy struct {
RenderListData struct {
ListData struct {
Items []Item `json:"Row"` // Items contains the list of items in the folder.
} `json:"ListData"`
} `json:"renderListDataAsStream"`
} `json:"legacy"`
} `json:"data"`
}

// Item represents an individual item in the folder.
type Item struct {
ObjType string `json:"FSObjType"` // ObjType indicates if the item is a file or folder.
Name string `json:"FileLeafRef"` // Name is the name of the item.
ModifiedTime time.Time `json:"Modified."` // ModifiedTime is the last modified time of the item.
Size string `json:"File_x0020_Size"` // Size is the size of the item in string format.
Id string `json:"UniqueId"` // Id is the unique identifier of the item.
}

// fileToObj converts an Item to an ObjThumb.
func fileToObj(f Item) *model.ObjThumb {
// Convert Size from string to int64.
size, _ := strconv.ParseInt(f.Size, 10, 64)
// Convert ObjType from string to int.
objtype, _ := strconv.Atoi(f.ObjType)

// Create a new ObjThumb with the converted values.
file := &model.ObjThumb{
Object: model.Object{
Name: f.Name,
Modified: f.ModifiedTime,
Size: size,
IsFolder: objtype == 1, // Check if the item is a folder.
ID: f.Id,
},
Thumbnail: model.Thumbnail{},
}
return file
}

// GraphQLNEWRequest represents the structure of a new GraphQL request.
type GraphQLNEWRequest struct {
ListData struct {
NextHref string `json:"NextHref"` // NextHref is the link to the next set of data.
Row []Item `json:"Row"` // Row contains the list of items.
} `json:"ListData"`
}

// GraphQLRequest represents the structure of a GraphQL request.
type GraphQLRequest struct {
Data struct {
Legacy struct {
RenderListDataAsStream struct {
ListData struct {
NextHref string `json:"NextHref"` // NextHref is the link to the next set of data.
Row []Item `json:"Row"` // Row contains the list of items.
} `json:"ListData"`
ViewMetadata struct {
ListViewXml string `json:"ListViewXml"` // ListViewXml contains the XML of the list view.
} `json:"ViewMetadata"`
} `json:"renderListDataAsStream"`
} `json:"legacy"`
} `json:"data"`
}
Loading

0 comments on commit cee0000

Please sign in to comment.