forked from TrueCloudLab/rclone
drive: add --drive-resource-key for accessing link-shared files
This commit is contained in:
parent
e87e331f4c
commit
411013dbdc
1 changed files with 52 additions and 9 deletions
|
@ -565,6 +565,27 @@ If this is set then rclone will not show any dangling shortcuts in listings.
|
||||||
`,
|
`,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
|
}, {
|
||||||
|
Name: "resource_key",
|
||||||
|
Help: `Resource key for accessing a link-shared file.
|
||||||
|
|
||||||
|
If you need to access files shared with a link like this
|
||||||
|
|
||||||
|
https://drive.google.com/drive/folders/XXX?resourcekey=YYY&usp=sharing
|
||||||
|
|
||||||
|
Then you will need to use the first part "XXX" as the "root_folder_id"
|
||||||
|
and the second part "YYY" as the "resource_key" otherwise you will get
|
||||||
|
404 not found errors when trying to access the directory.
|
||||||
|
|
||||||
|
See: https://developers.google.com/drive/api/guides/resource-keys
|
||||||
|
|
||||||
|
This resource key requirement only applies to a subset of old files.
|
||||||
|
|
||||||
|
Note also that opening the folder once in the web interface (with the
|
||||||
|
user you've authenticated rclone with) seems to be enough so that the
|
||||||
|
resource key is no needed.
|
||||||
|
`,
|
||||||
|
Advanced: true,
|
||||||
}, {
|
}, {
|
||||||
Name: config.ConfigEncoding,
|
Name: config.ConfigEncoding,
|
||||||
Help: config.ConfigEncodingHelp,
|
Help: config.ConfigEncodingHelp,
|
||||||
|
@ -626,6 +647,7 @@ type Options struct {
|
||||||
StopOnDownloadLimit bool `config:"stop_on_download_limit"`
|
StopOnDownloadLimit bool `config:"stop_on_download_limit"`
|
||||||
SkipShortcuts bool `config:"skip_shortcuts"`
|
SkipShortcuts bool `config:"skip_shortcuts"`
|
||||||
SkipDanglingShortcuts bool `config:"skip_dangling_shortcuts"`
|
SkipDanglingShortcuts bool `config:"skip_dangling_shortcuts"`
|
||||||
|
ResourceKey string `config:"resource_key"`
|
||||||
Enc encoder.MultiEncoder `config:"encoding"`
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,6 +673,7 @@ type Fs struct {
|
||||||
grouping int32 // number of IDs to search at once in ListR - read with atomic
|
grouping int32 // number of IDs to search at once in ListR - read with atomic
|
||||||
listRmu *sync.Mutex // protects listRempties
|
listRmu *sync.Mutex // protects listRempties
|
||||||
listRempties map[string]struct{} // IDs of supposedly empty directories which triggered grouping disable
|
listRempties map[string]struct{} // IDs of supposedly empty directories which triggered grouping disable
|
||||||
|
dirResourceKeys *sync.Map // map directory ID to resource key
|
||||||
}
|
}
|
||||||
|
|
||||||
type baseObject struct {
|
type baseObject struct {
|
||||||
|
@ -802,6 +825,7 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie
|
||||||
// We must not filter with parent when we try list "ROOT" with drive-shared-with-me
|
// We must not filter with parent when we try list "ROOT" with drive-shared-with-me
|
||||||
// If we need to list file inside those shared folders, we must search it without sharedWithMe
|
// If we need to list file inside those shared folders, we must search it without sharedWithMe
|
||||||
parentsQuery := bytes.NewBufferString("(")
|
parentsQuery := bytes.NewBufferString("(")
|
||||||
|
var resourceKeys []string
|
||||||
for _, dirID := range dirIDs {
|
for _, dirID := range dirIDs {
|
||||||
if dirID == "" {
|
if dirID == "" {
|
||||||
continue
|
continue
|
||||||
|
@ -822,7 +846,12 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie
|
||||||
} else {
|
} else {
|
||||||
_, _ = fmt.Fprintf(parentsQuery, "'%s' in parents", dirID)
|
_, _ = fmt.Fprintf(parentsQuery, "'%s' in parents", dirID)
|
||||||
}
|
}
|
||||||
|
resourceKey, hasResourceKey := f.dirResourceKeys.Load(dirID)
|
||||||
|
if hasResourceKey {
|
||||||
|
resourceKeys = append(resourceKeys, fmt.Sprintf("%s/%s", dirID, resourceKey))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
resourceKeysHeader := strings.Join(resourceKeys, ",")
|
||||||
if parentsQuery.Len() > 1 {
|
if parentsQuery.Len() > 1 {
|
||||||
_ = parentsQuery.WriteByte(')')
|
_ = parentsQuery.WriteByte(')')
|
||||||
query = append(query, parentsQuery.String())
|
query = append(query, parentsQuery.String())
|
||||||
|
@ -894,6 +923,10 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie
|
||||||
if f.rootFolderID == "appDataFolder" {
|
if f.rootFolderID == "appDataFolder" {
|
||||||
list.Spaces("appDataFolder")
|
list.Spaces("appDataFolder")
|
||||||
}
|
}
|
||||||
|
// Add resource Keys if necessary
|
||||||
|
if resourceKeysHeader != "" {
|
||||||
|
list.Header().Add("X-Goog-Drive-Resource-Keys", resourceKeysHeader)
|
||||||
|
}
|
||||||
|
|
||||||
fields := fmt.Sprintf("files(%s),nextPageToken,incompleteSearch", f.fileFields)
|
fields := fmt.Sprintf("files(%s),nextPageToken,incompleteSearch", f.fileFields)
|
||||||
|
|
||||||
|
@ -1153,15 +1186,16 @@ func newFs(ctx context.Context, name, path string, m configmap.Mapper) (*Fs, err
|
||||||
|
|
||||||
ci := fs.GetConfig(ctx)
|
ci := fs.GetConfig(ctx)
|
||||||
f := &Fs{
|
f := &Fs{
|
||||||
name: name,
|
name: name,
|
||||||
root: root,
|
root: root,
|
||||||
opt: *opt,
|
opt: *opt,
|
||||||
ci: ci,
|
ci: ci,
|
||||||
pacer: fs.NewPacer(ctx, pacer.NewGoogleDrive(pacer.MinSleep(opt.PacerMinSleep), pacer.Burst(opt.PacerBurst))),
|
pacer: fs.NewPacer(ctx, pacer.NewGoogleDrive(pacer.MinSleep(opt.PacerMinSleep), pacer.Burst(opt.PacerBurst))),
|
||||||
m: m,
|
m: m,
|
||||||
grouping: listRGrouping,
|
grouping: listRGrouping,
|
||||||
listRmu: new(sync.Mutex),
|
listRmu: new(sync.Mutex),
|
||||||
listRempties: make(map[string]struct{}),
|
listRempties: make(map[string]struct{}),
|
||||||
|
dirResourceKeys: new(sync.Map),
|
||||||
}
|
}
|
||||||
f.isTeamDrive = opt.TeamDriveID != ""
|
f.isTeamDrive = opt.TeamDriveID != ""
|
||||||
f.fileFields = f.getFileFields()
|
f.fileFields = f.getFileFields()
|
||||||
|
@ -1223,6 +1257,11 @@ func NewFs(ctx context.Context, name, path string, m configmap.Mapper) (fs.Fs, e
|
||||||
|
|
||||||
f.dirCache = dircache.New(f.root, f.rootFolderID, f)
|
f.dirCache = dircache.New(f.root, f.rootFolderID, f)
|
||||||
|
|
||||||
|
// If resource key is set then cache it for the root folder id
|
||||||
|
if f.opt.ResourceKey != "" {
|
||||||
|
f.dirResourceKeys.Store(f.rootFolderID, f.opt.ResourceKey)
|
||||||
|
}
|
||||||
|
|
||||||
// Parse extensions
|
// Parse extensions
|
||||||
if f.opt.Extensions != "" {
|
if f.opt.Extensions != "" {
|
||||||
if f.opt.ExportExtensions != defaultExportExtensions {
|
if f.opt.ExportExtensions != defaultExportExtensions {
|
||||||
|
@ -2091,6 +2130,10 @@ func (f *Fs) itemToDirEntry(ctx context.Context, remote string, item *drive.File
|
||||||
case item.MimeType == driveFolderType:
|
case item.MimeType == driveFolderType:
|
||||||
// cache the directory ID for later lookups
|
// cache the directory ID for later lookups
|
||||||
f.dirCache.Put(remote, item.Id)
|
f.dirCache.Put(remote, item.Id)
|
||||||
|
// cache the resource key for later lookups
|
||||||
|
if item.ResourceKey != "" {
|
||||||
|
f.dirResourceKeys.Store(item.Id, item.ResourceKey)
|
||||||
|
}
|
||||||
when, _ := time.Parse(timeFormatIn, item.ModifiedTime)
|
when, _ := time.Parse(timeFormatIn, item.ModifiedTime)
|
||||||
d := fs.NewDir(remote, when).SetID(item.Id)
|
d := fs.NewDir(remote, when).SetID(item.Id)
|
||||||
if len(item.Parents) > 0 {
|
if len(item.Parents) > 0 {
|
||||||
|
|
Loading…
Reference in a new issue