Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/google_photo/driver.go
1988 views
1
package google_photo
2
3
import (
4
"context"
5
"fmt"
6
"net/http"
7
"strconv"
8
"strings"
9
10
"github.com/alist-org/alist/v3/drivers/base"
11
"github.com/alist-org/alist/v3/internal/driver"
12
"github.com/alist-org/alist/v3/internal/errs"
13
"github.com/alist-org/alist/v3/internal/model"
14
"github.com/alist-org/alist/v3/pkg/utils"
15
"github.com/go-resty/resty/v2"
16
)
17
18
type GooglePhoto struct {
19
model.Storage
20
Addition
21
AccessToken string
22
}
23
24
func (d *GooglePhoto) Config() driver.Config {
25
return config
26
}
27
28
func (d *GooglePhoto) GetAddition() driver.Additional {
29
return &d.Addition
30
}
31
32
func (d *GooglePhoto) Init(ctx context.Context) error {
33
return d.refreshToken()
34
}
35
36
func (d *GooglePhoto) Drop(ctx context.Context) error {
37
return nil
38
}
39
40
func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
41
files, err := d.getFiles(dir.GetID())
42
if err != nil {
43
return nil, err
44
}
45
return utils.SliceConvert(files, func(src MediaItem) (model.Obj, error) {
46
return fileToObj(src), nil
47
})
48
}
49
50
func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
51
f, err := d.getMedia(file.GetID())
52
if err != nil {
53
return nil, err
54
}
55
56
if strings.Contains(f.MimeType, "image/") {
57
return &model.Link{
58
URL: f.BaseURL + "=d",
59
}, nil
60
} else if strings.Contains(f.MimeType, "video/") {
61
return &model.Link{
62
URL: f.BaseURL + "=dv",
63
}, nil
64
}
65
return &model.Link{}, nil
66
}
67
68
func (d *GooglePhoto) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
69
return errs.NotSupport
70
}
71
72
func (d *GooglePhoto) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
73
return errs.NotSupport
74
}
75
76
func (d *GooglePhoto) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
77
return errs.NotSupport
78
}
79
80
func (d *GooglePhoto) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
81
return errs.NotSupport
82
}
83
84
func (d *GooglePhoto) Remove(ctx context.Context, obj model.Obj) error {
85
return errs.NotSupport
86
}
87
88
func (d *GooglePhoto) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
89
var e Error
90
// Create resumable upload url
91
postHeaders := map[string]string{
92
"Authorization": "Bearer " + d.AccessToken,
93
"Content-type": "application/octet-stream",
94
"X-Goog-Upload-Command": "start",
95
"X-Goog-Upload-Content-Type": stream.GetMimetype(),
96
"X-Goog-Upload-Protocol": "resumable",
97
"X-Goog-Upload-Raw-Size": strconv.FormatInt(stream.GetSize(), 10),
98
}
99
url := "https://photoslibrary.googleapis.com/v1/uploads"
100
res, err := base.NoRedirectClient.R().SetHeaders(postHeaders).
101
SetError(&e).
102
Post(url)
103
104
if err != nil {
105
return err
106
}
107
if e.Error.Code != 0 {
108
if e.Error.Code == 401 {
109
err = d.refreshToken()
110
if err != nil {
111
return err
112
}
113
return d.Put(ctx, dstDir, stream, up)
114
}
115
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
116
}
117
118
//Upload to the Google Photo
119
postUrl := res.Header().Get("X-Goog-Upload-URL")
120
//chunkSize := res.Header().Get("X-Goog-Upload-Chunk-Granularity")
121
postHeaders = map[string]string{
122
"X-Goog-Upload-Command": "upload, finalize",
123
"X-Goog-Upload-Offset": "0",
124
}
125
126
resp, err := d.request(postUrl, http.MethodPost, func(req *resty.Request) {
127
req.SetBody(driver.NewLimitedUploadStream(ctx, stream)).SetContext(ctx)
128
}, nil, postHeaders)
129
130
if err != nil {
131
return err
132
}
133
//Create MediaItem
134
createItemUrl := "https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate"
135
136
postHeaders = map[string]string{
137
"X-Goog-Upload-Command": "upload, finalize",
138
"X-Goog-Upload-Offset": "0",
139
}
140
141
data := base.Json{
142
"newMediaItems": []base.Json{
143
{
144
"description": "item-description",
145
"simpleMediaItem": base.Json{
146
"fileName": stream.GetName(),
147
"uploadToken": string(resp),
148
},
149
},
150
},
151
}
152
153
_, err = d.request(createItemUrl, http.MethodPost, func(req *resty.Request) {
154
req.SetBody(data)
155
}, nil, postHeaders)
156
157
return err
158
}
159
160
var _ driver.Driver = (*GooglePhoto)(nil)
161
162