Home Upload to Google Drive from CLI | Python3 | Drive API v3
Post
Cancel

Upload to Google Drive from CLI | Python3 | Drive API v3

Want to directly upload files from an ancient raspberry pi with a lite os or any device that has no GUI but has trust issues with third-party tools..?

Pre-requisites

  • Install the Google Libraries:
1
2
$ pip install — upgrade google-api-python-client
$ pip install — upgrade google-auth-oauthlib google-auth-httplib2
  • Create or select a project in the Google API Console. Complete the following tasks in the API Console for your project:
    • In the library panel, search for the Drive API and make sure the API is enabled for your project if not done already.
    • In the credentials panel, create an OAuth 2.0 client ID. Set the application type to TV and limited input devices. You need to use OAuth 2.0 credentials for requests that require user authorization in this case, this is required.
    • Download the JSON file that contains your OAuth 2.0 credentials. The file has a name like client_secret_CLIENTID.json, where CLIENTID is the client ID for your project.

      the session will be generated for offline access however I am not sure if Google will allow permanent logins with unverified apps so this may demand amendments for token refreshes but that’s out of scope.

    • Move the JSON to a location and note down the path and update the CLIENT_SECRETS_FILE on the script.
    • At first, you will have to generate a token from a machine that has GUI. This is a one-time process:
      • Run the script on a machine with GUI, visit the Google URL provided by the script to authorize the application, which if done correctly will give you a token.json. Use the token.json file to do the authentication on the devices with no GUI.
    • Yes, as you guessed already, move the token.json file to the device with no GUI. (hint: SCP transfer)
    • Update TOKEN_FILE on the script with the path to the token.json
    • Window shadow Update PARENT_DRIVE_FOLDER on the script with the id of the folder to which the files will be uploaded. The id can be fetched using the drive API or it (as highlighted in the image) can be easily found from the URL. To navigate to the web ui, login to Google Drive and then navigate to an existing folder or you could create a folder that you would like to be the parent folder for all the files and folders uploaded using the script. Then copy the parent id from the address bar.

Code in Detail

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/python3

import argparse
import os
import re

import google.oauth2.credentials
import google_auth_oauthlib.flow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google_auth_oauthlib.flow import InstalledAppFlow
from google.oauth2.credentials import Credentials
from apiclient.http import MediaFileUpload
import mimetypes

CLIENT_SECRETS_FILE = 'client_secret.json'
TOKEN_FILE = 'token.json'
PARENT_DRIVE_FOLDER = '<id_of_the_google_drive_folder>'

# This OAuth 2.0 access scope allows for read-only access to the authenticated
# user's account, but not other types of account access.
SCOPES = ['https://www.googleapis.com/auth/drive']
API_SERVICE_NAME = 'drive'
API_VERSION = 'v3'


def get_authenticated_service_drive():
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists(TOKEN_FILE):
        creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
        return build(API_SERVICE_NAME, API_VERSION, credentials = creds)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                CLIENT_SECRETS_FILE, SCOPES)
            creds = flow.run_console()
        # Save the credentials for the next run
        with open(TOKEN_FILE, 'w') as token:
            token.write(creds.to_json())
        return build(API_SERVICE_NAME, API_VERSION, credentials = creds)

def drive_upload(service, filename):
    if os.path.isdir(filename):
        for subdir, dirs, files in os.walk(filename):
            for file in files:
                head, filename_forDrive = os.path.split(file)

                mimeTypeOfFile = mimetypes.guess_type(f"{file}")[0]

                exists, file_id= search_if_file_exists(service, filename_forDrive, mimeTypeOfFile)
                try:
                    file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{file}"),'parents':[PARENT_DRIVE_FOLDER]}
                    media = MediaFileUpload(str(os.path.join(subdir, file)), chunksize=50*1024*1024, resumable=True)
                    if exists:
                        file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{file}")}
                        request = service.files().update(fileId=file_id, body=file_metadata,media_body=media,fields='id')
                    else:
                        request = service.files().create(body=file_metadata,media_body=media,fields='id')
                    response = None
                    while response is None:
                        status, response = request.next_chunk()
                        if status:
                            print ("Uploaded %d%%." % int(status.progress() * 100))
                    file = request.execute()
                    print (f'Uploaded {filename_forDrive}')
                except Exception as e:
                    print (str(e))
    else:
        head, filename_forDrive = os.path.split(filename)
        files_list = [f'{filename}']
        for file in files_list:
            mimetypee = mimetypes.guess_type(f"{filename}"[0])
            exists = search_if_file_exists(service, filename_forDrive, mimetypee)
            try:
                file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{filename}"),'parents':[PARENT_DRIVE_FOLDER]}
                media = MediaFileUpload(str(file), chunksize=50*1024*1024, resumable=True)
                #request = service.files().create(body=file_metadata,media_body=media,fields='id')
                if exists:
                    file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{filename}")}
                    request = service.files().update(body=file_metadata,media_body=media,fields='id')
                else:
                    request = service.files().create(body=file_metadata,media_body=media,fields='id')
                response = None
                while response is None:
                    status, response = request.next_chunk()
                    if status:
                        print ("Uploaded %d%%." % int(status.progress() * 100))
                file = request.execute()
                print (f'Uploaded {filename_forDrive}')
            except Exception as e:
                print (str(e))

def search_if_file_exists(service, filename_forDrive, mimeTypeOfFile):
    page_token = None
    while True:
        if mimeTypeOfFile is None:
            response = service.files().list(q=f"name='{filename_forDrive}' and parents in '{PARENT_DRIVE_FOLDER}' and trashed = false",
                                              spaces='drive',
                                              fields='nextPageToken, files(id, name, parents, mimeType)',
                                              pageToken=page_token).execute()
        else:
            response = service.files().list(q=f"mimeType='{mimeTypeOfFile}' and name='{filename_forDrive}' and parents in '{PARENT_DRIVE_FOLDER}' and trashed = false",
                                          spaces='drive',
                                          fields='nextPageToken, files(id, name, parents, mimeType)',
                                          pageToken=page_token).execute()
        if response:
            for file in response.get('files', []):
                print ("File already exists")
                print ('Found file: %s (%s)' % (file.get('name'), file.get('id')))
                return True, file.get('id')
            page_token = response.get('nextPageToken', None)
            if page_token is None:
                return False, 'Duh'


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--file_path', help='File Path', required=True)
    args = parser.parse_args()
    if args.file_path:
        drive_service = get_authenticated_service_drive()
        drive_upload(drive_service, args.file_path)
    else:
        print("Invalid path, nothing to upload to drive.")

Run the Code

To upload the files in a folder and its subfolders:

1
$ python3 drive_upload.py –file_path /home/pi/drive_folder

To upload a file:

1
$ python3 drive_upload.py — file_path /home/pi/drive_file.zip

P.S.

If you are on Linux, just add an alias on .bashrc or .zshrc or whatever you use:

1
alias drive=”python3 /home/pi/drive/drive_upload.py — file_path”

…so that you can simply do the following to upload the files in the temp directory on the home directory, for example:

1
$ drive ~/temp/

Thank you!

This post is licensed under CC BY 4.0 by the author.