Spaces:
Runtime error
Runtime error
package google_photo | |
import ( | |
"context" | |
"fmt" | |
"net/http" | |
"strconv" | |
"strings" | |
"github.com/alist-org/alist/v3/drivers/base" | |
"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/utils" | |
"github.com/go-resty/resty/v2" | |
) | |
type GooglePhoto struct { | |
model.Storage | |
Addition | |
AccessToken string | |
} | |
func (d *GooglePhoto) Config() driver.Config { | |
return config | |
} | |
func (d *GooglePhoto) GetAddition() driver.Additional { | |
return &d.Addition | |
} | |
func (d *GooglePhoto) Init(ctx context.Context) error { | |
return d.refreshToken() | |
} | |
func (d *GooglePhoto) Drop(ctx context.Context) error { | |
return nil | |
} | |
func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { | |
files, err := d.getFiles(dir.GetID()) | |
if err != nil { | |
return nil, err | |
} | |
return utils.SliceConvert(files, func(src MediaItem) (model.Obj, error) { | |
return fileToObj(src), nil | |
}) | |
} | |
func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { | |
f, err := d.getMedia(file.GetID()) | |
if err != nil { | |
return nil, err | |
} | |
if strings.Contains(f.MimeType, "image/") { | |
return &model.Link{ | |
URL: f.BaseURL + "=d", | |
}, nil | |
} else if strings.Contains(f.MimeType, "video/") { | |
var width, height int | |
fmt.Sscanf(f.MediaMetadata.Width, "%d", &width) | |
fmt.Sscanf(f.MediaMetadata.Height, "%d", &height) | |
switch { | |
// 1080P | |
case width == 1920 && height == 1080: | |
return &model.Link{ | |
URL: f.BaseURL + "=m37", | |
}, nil | |
// 720P | |
case width == 1280 && height == 720: | |
return &model.Link{ | |
URL: f.BaseURL + "=m22", | |
}, nil | |
// 360P | |
case width == 640 && height == 360: | |
return &model.Link{ | |
URL: f.BaseURL + "=m18", | |
}, nil | |
default: | |
return &model.Link{ | |
URL: f.BaseURL + "=dv", | |
}, nil | |
} | |
} | |
return &model.Link{}, nil | |
} | |
func (d *GooglePhoto) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { | |
return errs.NotSupport | |
} | |
func (d *GooglePhoto) Move(ctx context.Context, srcObj, dstDir model.Obj) error { | |
return errs.NotSupport | |
} | |
func (d *GooglePhoto) Rename(ctx context.Context, srcObj model.Obj, newName string) error { | |
return errs.NotSupport | |
} | |
func (d *GooglePhoto) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { | |
return errs.NotSupport | |
} | |
func (d *GooglePhoto) Remove(ctx context.Context, obj model.Obj) error { | |
return errs.NotSupport | |
} | |
func (d *GooglePhoto) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { | |
var e Error | |
// Create resumable upload url | |
postHeaders := map[string]string{ | |
"Authorization": "Bearer " + d.AccessToken, | |
"Content-type": "application/octet-stream", | |
"X-Goog-Upload-Command": "start", | |
"X-Goog-Upload-Content-Type": stream.GetMimetype(), | |
"X-Goog-Upload-Protocol": "resumable", | |
"X-Goog-Upload-Raw-Size": strconv.FormatInt(stream.GetSize(), 10), | |
} | |
url := "https://photoslibrary.googleapis.com/v1/uploads" | |
res, err := base.NoRedirectClient.R().SetHeaders(postHeaders). | |
SetError(&e). | |
Post(url) | |
if err != nil { | |
return err | |
} | |
if e.Error.Code != 0 { | |
if e.Error.Code == 401 { | |
err = d.refreshToken() | |
if err != nil { | |
return err | |
} | |
return d.Put(ctx, dstDir, stream, up) | |
} | |
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) | |
} | |
//Upload to the Google Photo | |
postUrl := res.Header().Get("X-Goog-Upload-URL") | |
//chunkSize := res.Header().Get("X-Goog-Upload-Chunk-Granularity") | |
postHeaders = map[string]string{ | |
"X-Goog-Upload-Command": "upload, finalize", | |
"X-Goog-Upload-Offset": "0", | |
} | |
resp, err := d.request(postUrl, http.MethodPost, func(req *resty.Request) { | |
req.SetBody(stream).SetContext(ctx) | |
}, nil, postHeaders) | |
if err != nil { | |
return err | |
} | |
//Create MediaItem | |
createItemUrl := "https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate" | |
postHeaders = map[string]string{ | |
"X-Goog-Upload-Command": "upload, finalize", | |
"X-Goog-Upload-Offset": "0", | |
} | |
data := base.Json{ | |
"newMediaItems": []base.Json{ | |
{ | |
"description": "item-description", | |
"simpleMediaItem": base.Json{ | |
"fileName": stream.GetName(), | |
"uploadToken": string(resp), | |
}, | |
}, | |
}, | |
} | |
_, err = d.request(createItemUrl, http.MethodPost, func(req *resty.Request) { | |
req.SetBody(data) | |
}, nil, postHeaders) | |
return err | |
} | |
var _ driver.Driver = (*GooglePhoto)(nil) | |