|
|
|
@ -203,7 +203,6 @@ func driveScopesContainsAppFolder(scopes []string) bool {
|
|
|
|
|
if scope == scopePrefix+"drive.appfolder" {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
@ -1212,6 +1211,7 @@ func fixMimeType(mimeTypeIn string) string {
|
|
|
|
|
}
|
|
|
|
|
return mimeTypeOut
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func fixMimeTypeMap(in map[string][]string) (out map[string][]string) {
|
|
|
|
|
out = make(map[string][]string, len(in))
|
|
|
|
|
for k, v := range in {
|
|
|
|
@ -1222,9 +1222,11 @@ func fixMimeTypeMap(in map[string][]string) (out map[string][]string) {
|
|
|
|
|
}
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isInternalMimeType(mimeType string) bool {
|
|
|
|
|
return strings.HasPrefix(mimeType, "application/vnd.google-apps.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isLinkMimeType(mimeType string) bool {
|
|
|
|
|
return strings.HasPrefix(mimeType, "application/x-link-")
|
|
|
|
|
}
|
|
|
|
@ -1657,7 +1659,8 @@ func (f *Fs) newObjectWithInfo(ctx context.Context, remote string, info *drive.F
|
|
|
|
|
// When the drive.File cannot be represented as an fs.Object it will return (nil, nil).
|
|
|
|
|
func (f *Fs) newObjectWithExportInfo(
|
|
|
|
|
ctx context.Context, remote string, info *drive.File,
|
|
|
|
|
extension, exportName, exportMimeType string, isDocument bool) (o fs.Object, err error) {
|
|
|
|
|
extension, exportName, exportMimeType string, isDocument bool,
|
|
|
|
|
) (o fs.Object, err error) {
|
|
|
|
|
// Note that resolveShortcut will have been called already if
|
|
|
|
|
// we are being called from a listing. However the drive.Item
|
|
|
|
|
// will have been resolved so this will do nothing.
|
|
|
|
@ -1848,6 +1851,7 @@ func linkTemplate(mt string) *template.Template {
|
|
|
|
|
})
|
|
|
|
|
return _linkTemplates[mt]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Fs) fetchFormats(ctx context.Context) {
|
|
|
|
|
fetchFormatsOnce.Do(func() {
|
|
|
|
|
var about *drive.About
|
|
|
|
@ -1893,7 +1897,8 @@ func (f *Fs) importFormats(ctx context.Context) map[string][]string {
|
|
|
|
|
// Look through the exportExtensions and find the first format that can be
|
|
|
|
|
// converted. If none found then return ("", "", false)
|
|
|
|
|
func (f *Fs) findExportFormatByMimeType(ctx context.Context, itemMimeType string) (
|
|
|
|
|
extension, mimeType string, isDocument bool) {
|
|
|
|
|
extension, mimeType string, isDocument bool,
|
|
|
|
|
) {
|
|
|
|
|
exportMimeTypes, isDocument := f.exportFormats(ctx)[itemMimeType]
|
|
|
|
|
if isDocument {
|
|
|
|
|
for _, _extension := range f.exportExtensions {
|
|
|
|
@ -2689,7 +2694,7 @@ func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
|
|
|
|
|
if shortcutID != "" {
|
|
|
|
|
return f.delete(ctx, shortcutID, f.opt.UseTrash)
|
|
|
|
|
}
|
|
|
|
|
var trashedFiles = false
|
|
|
|
|
trashedFiles := false
|
|
|
|
|
if check {
|
|
|
|
|
found, err := f.list(ctx, []string{directoryID}, "", false, false, f.opt.TrashedOnly, true, func(item *drive.File) bool {
|
|
|
|
|
if !item.Trashed {
|
|
|
|
@ -2926,7 +2931,6 @@ func (f *Fs) CleanUp(ctx context.Context) error {
|
|
|
|
|
err := f.svc.Files.EmptyTrash().Context(ctx).Do()
|
|
|
|
|
return f.shouldRetry(ctx, err)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
@ -3187,6 +3191,7 @@ func (f *Fs) ChangeNotify(ctx context.Context, notifyFunc func(string, fs.EntryT
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Fs) changeNotifyStartPageToken(ctx context.Context) (pageToken string, err error) {
|
|
|
|
|
var startPageToken *drive.StartPageToken
|
|
|
|
|
err = f.pacer.Call(func() (bool, error) {
|
|
|
|
@ -4018,14 +4023,13 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
|
|
|
|
|
case "query":
|
|
|
|
|
if len(arg) == 1 {
|
|
|
|
|
query := arg[0]
|
|
|
|
|
var results, err = f.query(ctx, query)
|
|
|
|
|
results, err := f.query(ctx, query)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to execute query: %q, error: %w", query, err)
|
|
|
|
|
}
|
|
|
|
|
return results, nil
|
|
|
|
|
} else {
|
|
|
|
|
return nil, errors.New("need a query argument")
|
|
|
|
|
}
|
|
|
|
|
return nil, errors.New("need a query argument")
|
|
|
|
|
case "rescue":
|
|
|
|
|
dirID := ""
|
|
|
|
|
_, delete := opt["delete"]
|
|
|
|
@ -4085,6 +4089,7 @@ func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
|
|
|
|
|
}
|
|
|
|
|
return "", hash.ErrUnsupported
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *baseObject) Hash(ctx context.Context, t hash.Type) (string, error) {
|
|
|
|
|
if t != hash.MD5 && t != hash.SHA1 && t != hash.SHA256 {
|
|
|
|
|
return "", hash.ErrUnsupported
|
|
|
|
@ -4099,7 +4104,8 @@ func (o *baseObject) Size() int64 {
|
|
|
|
|
|
|
|
|
|
// getRemoteInfoWithExport returns a drive.File and the export settings for the remote
|
|
|
|
|
func (f *Fs) getRemoteInfoWithExport(ctx context.Context, remote string) (
|
|
|
|
|
info *drive.File, extension, exportName, exportMimeType string, isDocument bool, err error) {
|
|
|
|
|
info *drive.File, extension, exportName, exportMimeType string, isDocument bool, err error,
|
|
|
|
|
) {
|
|
|
|
|
leaf, directoryID, err := f.dirCache.FindPath(ctx, remote, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == fs.ErrorDirNotFound {
|
|
|
|
@ -4312,12 +4318,13 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
|
|
|
|
|
}
|
|
|
|
|
return o.baseObject.open(ctx, o.url, options...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *documentObject) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
|
|
|
|
|
// Update the size with what we are reading as it can change from
|
|
|
|
|
// the HEAD in the listing to this GET. This stops rclone marking
|
|
|
|
|
// the transfer as corrupted.
|
|
|
|
|
var offset, end int64 = 0, -1
|
|
|
|
|
var newOptions = options[:0]
|
|
|
|
|
newOptions := options[:0]
|
|
|
|
|
for _, o := range options {
|
|
|
|
|
// Note that Range requests don't work on Google docs:
|
|
|
|
|
// https://developers.google.com/drive/v3/web/manage-downloads#partial_download
|
|
|
|
@ -4344,9 +4351,10 @@ func (o *documentObject) Open(ctx context.Context, options ...fs.OpenOption) (in
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *linkObject) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
|
|
|
|
|
var offset, limit int64 = 0, -1
|
|
|
|
|
var data = o.content
|
|
|
|
|
data := o.content
|
|
|
|
|
for _, option := range options {
|
|
|
|
|
switch x := option.(type) {
|
|
|
|
|
case *fs.SeekOption:
|
|
|
|
@ -4371,7 +4379,8 @@ func (o *linkObject) Open(ctx context.Context, options ...fs.OpenOption) (in io.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *baseObject) update(ctx context.Context, updateInfo *drive.File, uploadMimeType string, in io.Reader,
|
|
|
|
|
src fs.ObjectInfo) (info *drive.File, err error) {
|
|
|
|
|
src fs.ObjectInfo,
|
|
|
|
|
) (info *drive.File, err error) {
|
|
|
|
|
// Make the API request to upload metadata and file data.
|
|
|
|
|
size := src.Size()
|
|
|
|
|
if size >= 0 && size < int64(o.fs.opt.UploadCutoff) {
|
|
|
|
@ -4449,6 +4458,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *documentObject) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
|
|
|
|
srcMimeType := fs.MimeType(ctx, src)
|
|
|
|
|
importMimeType := ""
|
|
|
|
@ -4544,6 +4554,7 @@ func (o *baseObject) Metadata(ctx context.Context) (metadata fs.Metadata, err er
|
|
|
|
|
func (o *documentObject) ext() string {
|
|
|
|
|
return o.baseObject.remote[len(o.baseObject.remote)-o.extLen:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *linkObject) ext() string {
|
|
|
|
|
return o.baseObject.remote[len(o.baseObject.remote)-o.extLen:]
|
|
|
|
|
}
|
|
|
|
|