diff --git a/drivers/google_photo/driver.go b/drivers/google_photo/driver.go index 6de1ae9d..8606e7dd 100644 --- a/drivers/google_photo/driver.go +++ b/drivers/google_photo/driver.go @@ -43,7 +43,7 @@ func (d *GooglePhoto) Drop(ctx context.Context) error { } func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { - files, err := d.getFiles() + files, err := d.getFiles(dir.GetID()) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListAr //} func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { - f, err := d.getFile(file.GetID()) + f, err := d.getMedia(file.GetID()) if err != nil { return nil, err } diff --git a/drivers/google_photo/meta.go b/drivers/google_photo/meta.go index 9258dce9..f6ee7402 100644 --- a/drivers/google_photo/meta.go +++ b/drivers/google_photo/meta.go @@ -10,6 +10,7 @@ type Addition struct { RefreshToken string `json:"refresh_token" required:"true"` ClientID string `json:"client_id" required:"true" default:"202264815644.apps.googleusercontent.com"` ClientSecret string `json:"client_secret" required:"true" default:"X4Z3ca8xfWDb1Voo-F9a7ZxJ"` + ShowArchive bool `json:"show_archive"` } var config = driver.Config{ diff --git a/drivers/google_photo/types.go b/drivers/google_photo/types.go index 84ddb886..1a53ae09 100644 --- a/drivers/google_photo/types.go +++ b/drivers/google_photo/types.go @@ -1,6 +1,7 @@ package google_photo import ( + "reflect" "time" "github.com/alist-org/alist/v3/internal/model" @@ -11,17 +12,21 @@ type TokenError struct { ErrorDescription string `json:"error_description"` } -type Files struct { +type Items struct { NextPageToken string `json:"nextPageToken"` - MediaItems []MediaItem `json:"mediaItems"` + MediaItems []MediaItem `json:"mediaItems,omitempty"` + Albums []MediaItem `json:"albums,omitempty"` + SharedAlbums []MediaItem `json:"sharedAlbums,omitempty"` } type MediaItem struct { - Id string `json:"id"` - BaseURL string `json:"baseUrl"` - MimeType string `json:"mimeType"` - FileName string `json:"filename"` - MediaMetadata MediaMetadata `json:"mediaMetadata"` + Id string `json:"id"` + Title string `json:"title,omitempty"` + BaseURL string `json:"baseUrl,omitempty"` + CoverPhotoBaseUrl string `json:"coverPhotoBaseUrl,omitempty"` + MimeType string `json:"mimeType,omitempty"` + FileName string `json:"filename,omitempty"` + MediaMetadata MediaMetadata `json:"mediaMetadata,omitempty"` } type MediaMetadata struct { @@ -39,18 +44,29 @@ type Video struct { } func fileToObj(f MediaItem) *model.ObjThumb { - //size, _ := strconv.ParseInt(f.Size, 10, 64) + if !reflect.DeepEqual(f.MediaMetadata, MediaMetadata{}){ + return &model.ObjThumb{ + Object: model.Object{ + ID: f.Id, + Name: f.FileName, + Size: 0, + Modified: f.MediaMetadata.CreationTime, + IsFolder: false, + }, + Thumbnail: model.Thumbnail{ + Thumbnail: f.BaseURL + "=w100-h100-c", + }, + } + } return &model.ObjThumb{ Object: model.Object{ ID: f.Id, - Name: f.FileName, + Name: f.Title, Size: 0, - Modified: f.MediaMetadata.CreationTime, - IsFolder: false, - }, - Thumbnail: model.Thumbnail{ - Thumbnail: f.BaseURL + "=w100-h100-c", + Modified: time.Time{}, + IsFolder: true, }, + Thumbnail: model.Thumbnail{}, } } diff --git a/drivers/google_photo/util.go b/drivers/google_photo/util.go index 571ce9fc..fbbff9ab 100644 --- a/drivers/google_photo/util.go +++ b/drivers/google_photo/util.go @@ -10,6 +10,13 @@ import ( // do others that not defined in Driver interface +const ( + FETCH_ALL = "all" + FETCH_ALBUMS = "albums" + FETCH_ROOT = "root" + FETCH_SHARE_ALBUMS = "share_albums" +) + func (d *GooglePhoto) refreshToken() error { url := "https://www.googleapis.com/oauth2/v4/token" var resp base.TokenResp @@ -34,6 +41,7 @@ func (d *GooglePhoto) refreshToken() error { func (d *GooglePhoto) request(url string, method string, callback base.ReqCallback, resp interface{}, headers map[string]string) ([]byte, error) { req := base.RestyClient.R() req.SetHeader("Authorization", "Bearer "+d.AccessToken) + req.SetHeader("Accept-Encoding", "gzip") if headers != nil { req.SetHeaders(headers) } @@ -63,32 +71,83 @@ func (d *GooglePhoto) request(url string, method string, callback base.ReqCallba return res.Body(), nil } -func (d *GooglePhoto) getFiles() ([]MediaItem, error) { - pageToken := "first" - res := make([]MediaItem, 0) - for pageToken != "" { - if pageToken == "first" { - pageToken = "" - } - var resp Files - query := map[string]string{ - "fields": "mediaItems(id,baseUrl,mimeType,mediaMetadata,filename),nextPageToken", - "pageSize": "100", - "pageToken": pageToken, - } - _, err := d.request("https://photoslibrary.googleapis.com/v1/mediaItems", http.MethodGet, func(req *resty.Request) { - req.SetQueryParams(query) - }, &resp, nil) - if err != nil { - return nil, err - } - pageToken = resp.NextPageToken - res = append(res, resp.MediaItems...) +func (d *GooglePhoto) getFiles(id string) ([]MediaItem, error) { + switch id { + case FETCH_ALL: + return d.getAllMedias() + case FETCH_ALBUMS: + return d.getAlbums() + case FETCH_SHARE_ALBUMS: + return d.getShareAlbums() + case FETCH_ROOT: + return d.getFakeRoot() + default: + return d.getMedias(id) } - return res, nil } -func (d *GooglePhoto) getFile(id string) (MediaItem, error) { +func (d *GooglePhoto) getFakeRoot() ([]MediaItem, error) { + return []MediaItem{ + { + Id: FETCH_ALL, + Title: "全部媒体", + }, + { + Id: FETCH_ALBUMS, + Title: "全部影集", + }, + { + Id: FETCH_SHARE_ALBUMS, + Title: "共享影集", + }, + }, nil +} + +func (d *GooglePhoto) getAlbums() ([]MediaItem, error) { + return d.fetchItems( + "https://photoslibrary.googleapis.com/v1/albums", + map[string]string{ + "fields": "albums(id,title,coverPhotoBaseUrl),nextPageToken", + "pageSize": "50", + "pageToken": "first", + }, + http.MethodGet) +} + +func (d *GooglePhoto) getShareAlbums() ([]MediaItem, error) { + return d.fetchItems( + "https://photoslibrary.googleapis.com/v1/sharedAlbums", + map[string]string{ + "fields": "sharedAlbums(id,title,coverPhotoBaseUrl),nextPageToken", + "pageSize": "50", + "pageToken": "first", + }, + http.MethodGet) +} + +func (d *GooglePhoto) getMedias(albumId string) ([]MediaItem, error) { + return d.fetchItems( + "https://photoslibrary.googleapis.com/v1/mediaItems:search", + map[string]string{ + "fields": "mediaItems(id,baseUrl,mimeType,mediaMetadata,filename),nextPageToken", + "pageSize": "100", + "albumId": albumId, + "pageToken": "first", + }, http.MethodPost) +} + +func (d *GooglePhoto) getAllMedias() ([]MediaItem, error) { + return d.fetchItems( + "https://photoslibrary.googleapis.com/v1/mediaItems", + map[string]string{ + "fields": "mediaItems(id,baseUrl,mimeType,mediaMetadata,filename),nextPageToken", + "pageSize": "100", + "pageToken": "first", + }, + http.MethodGet) +} + +func (d *GooglePhoto) getMedia(id string) (MediaItem, error) { var resp MediaItem query := map[string]string{ @@ -103,3 +162,25 @@ func (d *GooglePhoto) getFile(id string) (MediaItem, error) { return resp, nil } + +func (d *GooglePhoto) fetchItems(url string, query map[string]string, method string) ([]MediaItem, error){ + res := make([]MediaItem, 0) + for query["pageToken"] != "" { + if query["pageToken"] == "first" { + query["pageToken"] = "" + } + var resp Items + + _, err := d.request(url, method, func(req *resty.Request) { + req.SetQueryParams(query) + }, &resp, nil) + if err != nil { + return nil, err + } + query["pageToken"] = resp.NextPageToken + res = append(res, resp.MediaItems...) + res = append(res, resp.Albums...) + res = append(res, resp.SharedAlbums...) + } + return res, nil +}