aboutsummaryrefslogtreecommitdiffstats
path: root/drive/sync_download.go
diff options
context:
space:
mode:
authorPetter Rasmussen2016-02-03 20:33:43 +0100
committerPetter Rasmussen2016-02-03 20:33:43 +0100
commit6d0a23e4bdf5ffa36cecb2cb85a6291f9235f619 (patch)
tree46af7038993e94e7258b5b54432e626299c4fca5 /drive/sync_download.go
parent1131605c14244c575991999051649f04f2e9f8bd (diff)
downloadgdrive-6d0a23e4bdf5ffa36cecb2cb85a6291f9235f619.tar.bz2
Rename files
Diffstat (limited to 'drive/sync_download.go')
-rw-r--r--drive/sync_download.go216
1 files changed, 216 insertions, 0 deletions
diff --git a/drive/sync_download.go b/drive/sync_download.go
new file mode 100644
index 0000000..37c78cb
--- /dev/null
+++ b/drive/sync_download.go
@@ -0,0 +1,216 @@
+package drive
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "sort"
+ "time"
+ "path/filepath"
+ "google.golang.org/api/googleapi"
+ "google.golang.org/api/drive/v3"
+)
+
+type DownloadSyncArgs struct {
+ Out io.Writer
+ Progress io.Writer
+ RootId string
+ Path string
+ DeleteExtraneous bool
+}
+
+func (self *Drive) DownloadSync(args DownloadSyncArgs) error {
+ fmt.Fprintln(args.Out, "Starting sync...")
+ started := time.Now()
+
+ // Get remote root dir
+ rootDir, err := self.getSyncRoot(args.RootId)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprintln(args.Out, "Collecting local and remote file information...")
+ files, err := self.prepareSyncFiles(args.Path, rootDir)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprintf(args.Out, "Found %d local files and %d remote files\n", len(files.local), len(files.remote))
+
+ // Create missing directories
+ files, err = self.createMissingLocalDirs(files, args)
+ if err != nil {
+ return err
+ }
+
+ // Download missing files
+ err = self.downloadMissingFiles(files, args)
+ if err != nil {
+ return err
+ }
+
+ // Download files that has changed
+ err = self.downloadChangedFiles(files, args)
+ if err != nil {
+ return err
+ }
+
+ // Delete extraneous local files
+ if args.DeleteExtraneous {
+ err = self.deleteExtraneousLocalFiles(files, args)
+ if err != nil {
+ return err
+ }
+ }
+ fmt.Fprintf(args.Out, "Sync finished in %s\n", time.Since(started))
+
+ return nil
+}
+
+func (self *Drive) getSyncRoot(rootId string) (*drive.File, error) {
+ fields := []googleapi.Field{"id", "name", "mimeType", "appProperties"}
+ f, err := self.service.Files.Get(rootId).Fields(fields...).Do()
+ if err != nil {
+ return nil, fmt.Errorf("Failed to find root dir: %s", err)
+ }
+
+ // Ensure file is a directory
+ if !isDir(f) {
+ return nil, fmt.Errorf("Provided root id is not a directory")
+ }
+
+ // Ensure directory is a proper syncRoot
+ if _, ok := f.AppProperties["isSyncRoot"]; !ok {
+ return nil, fmt.Errorf("Provided id is not a sync root directory")
+ }
+
+ return f, nil
+}
+
+func (self *Drive) createMissingLocalDirs(files *syncFiles, args DownloadSyncArgs) (*syncFiles, error) {
+ missingDirs := files.filterMissingLocalDirs()
+ missingCount := len(missingDirs)
+
+ if missingCount > 0 {
+ fmt.Fprintf(args.Out, "\n%d local directories are missing\n", missingCount)
+ }
+
+ // Sort directories so that the dirs with the shortest path comes first
+ sort.Sort(byRemotePathLength(missingDirs))
+
+ for i, rf := range missingDirs {
+ path, err := filepath.Abs(filepath.Join(args.Path, rf.relPath))
+ if err != nil {
+ return nil, fmt.Errorf("Failed to determine local absolute path: %s", err)
+ }
+ fmt.Fprintf(args.Out, "[%04d/%04d] Creating directory: %s\n", i + 1, missingCount, path)
+ mkdir(path)
+ }
+
+ return files, nil
+}
+
+func (self *Drive) downloadMissingFiles(files *syncFiles, args DownloadSyncArgs) error {
+ missingFiles := files.filterMissingLocalFiles()
+ missingCount := len(missingFiles)
+
+ if missingCount > 0 {
+ fmt.Fprintf(args.Out, "\n%d local files are missing\n", missingCount)
+ }
+
+ for i, rf := range missingFiles {
+ remotePath := filepath.Join(files.root.file.Name, rf.relPath)
+ localPath, err := filepath.Abs(filepath.Join(args.Path, rf.relPath))
+ if err != nil {
+ return fmt.Errorf("Failed to determine local absolute path: %s", err)
+ }
+ fmt.Fprintf(args.Out, "[%04d/%04d] Downloading %s -> %s\n", i + 1, missingCount, remotePath, localPath)
+ err = self.downloadRemoteFile(rf.file.Id, localPath, args)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (self *Drive) downloadChangedFiles(files *syncFiles, args DownloadSyncArgs) error {
+ changedFiles := files.filterChangedRemoteFiles()
+ changedCount := len(changedFiles)
+
+ if changedCount > 0 {
+ fmt.Fprintf(args.Out, "\n%d remote files has changed\n", changedCount)
+ }
+
+ for i, cf := range changedFiles {
+ remotePath := filepath.Join(files.root.file.Name, cf.remote.relPath)
+ localPath, err := filepath.Abs(filepath.Join(args.Path, cf.remote.relPath))
+ if err != nil {
+ return fmt.Errorf("Failed to determine local absolute path: %s", err)
+ }
+ fmt.Fprintf(args.Out, "[%04d/%04d] Downloading %s -> %s\n", i + 1, changedCount, remotePath, localPath)
+ err = self.downloadRemoteFile(cf.remote.file.Id, localPath, args)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (self *Drive) downloadRemoteFile(id, fpath string, args DownloadSyncArgs) error {
+ res, err := self.service.Files.Get(id).Download()
+ if err != nil {
+ return fmt.Errorf("Failed to download file: %s", err)
+ }
+
+ // Close body on function exit
+ defer res.Body.Close()
+
+ // Wrap response body in progress reader
+ srcReader := getProgressReader(res.Body, args.Progress, res.ContentLength)
+
+ // Ensure any parent directories exists
+ if err = mkdir(fpath); err != nil {
+ return err
+ }
+
+ // Create new file
+ outFile, err := os.Create(fpath)
+ if err != nil {
+ return fmt.Errorf("Unable to create local file: %s", err)
+ }
+
+ // Close file on function exit
+ defer outFile.Close()
+
+ // Save file to disk
+ _, err = io.Copy(outFile, srcReader)
+ if err != nil {
+ return fmt.Errorf("Download was interrupted: %s", err)
+ }
+
+ return nil
+}
+
+func (self *Drive) deleteExtraneousLocalFiles(files *syncFiles, args DownloadSyncArgs) error {
+ extraneousFiles := files.filterExtraneousLocalFiles()
+ extraneousCount := len(extraneousFiles)
+
+ if extraneousCount > 0 {
+ fmt.Fprintf(args.Out, "\n%d local files are extraneous\n", extraneousCount)
+ }
+
+ // Sort files so that the files with the longest path comes first
+ sort.Sort(sort.Reverse(byLocalPathLength(extraneousFiles)))
+
+ for i, lf := range extraneousFiles {
+ fmt.Fprintf(args.Out, "[%04d/%04d] Deleting %s\n", i + 1, extraneousCount, lf.absPath)
+ err := os.Remove(lf.absPath)
+ if err != nil {
+ return fmt.Errorf("Failed to delete local file: %s", err)
+ }
+ }
+
+ return nil
+}