-
Notifications
You must be signed in to change notification settings - Fork 1
/
move.py
145 lines (120 loc) · 5.39 KB
/
move.py
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import argparse
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
SCOPES = ["https://www.googleapis.com/auth/drive"]
BATCH = None
BATCH_SIZE = 0
MAXIMUM_BATCH_SIZE = 100 # https://developers.google.com/drive/api/guides/performance#overview
def get_drive_service():
flow = InstalledAppFlow.from_client_secrets_file("client_secrets.json", SCOPES)
creds = flow.run_local_server(port=0)
service = build("drive", "v3", credentials=creds)
return service
def callback(request_id, response, exception):
if exception:
print(f"\n{exception}")
else:
print("[✓]", end="")
def create_batch(service):
global BATCH
BATCH = service.new_batch_http_request(callback=callback)
global BATCH_SIZE
BATCH_SIZE = 0
def batch_add(service, file_id, destination):
if not BATCH:
create_batch(service)
BATCH.add(service.files().update(
fileId=file_id,
fields="id, parents",
addParents=destination,
supportsAllDrives=True
))
global BATCH_SIZE
BATCH_SIZE += 1
if BATCH_SIZE == MAXIMUM_BATCH_SIZE:
print("\n\nMaximum batch size reached. Executing batch...", end=" ")
BATCH.execute()
print("\nBatch execution finished.")
create_batch(service)
def get_or_create_destination_folder(service, destination_folder_id, folder_name):
try:
folder_name = folder_name.replace("'", "\\'")
return service.files().list(
q=f"'{destination_folder_id}' in parents and name = '{folder_name}'"
f" and mimeType = 'application/vnd.google-apps.folder' and not trashed",
fields="files(id)", supportsAllDrives=True, includeItemsFromAllDrives=True).execute().get("files")[0].get(
"id")
except IndexError:
return service.files().create(
body={
"name": folder_name,
"mimeType": "application/vnd.google-apps.folder",
"parents": [destination_folder_id]
},
fields="id",
supportsAllDrives=True
).execute().get("id")
def remove_empty_folders(service, folder_id_list):
for folder_id in folder_id_list:
items = service.files().list(
q=f"'{folder_id}' in parents and not trashed",
fields="files(id)").execute().get("files")
if not len(items):
try:
print(f"Deleting folder '{folder_id}'...", end=" ")
service.files().delete(fileId=folder_id).execute()
print("[✓]")
except HttpError as e:
print(f"[x] An error occurred: {e}")
def process_all_files(service, destination_folder_id, folder_id, folder_name=None, destination_folder_structure=None):
if not folder_name:
folder_name = service.files().get(fileId=folder_id).execute().get("name")
print(f"\nGathering files in folder '{folder_name}'...", end="")
if not destination_folder_structure:
destination_folder_structure = [destination_folder_id]
processed_folder_list = []
next_page_token = None
while True:
try:
items = service.files().list(q=f"'{folder_id}' in parents and not trashed",
fields="files(id, name, mimeType), nextPageToken",
pageToken=next_page_token).execute()
for item in items["files"]:
if item["mimeType"] == "application/vnd.google-apps.folder":
destination_sub_folder_id = get_or_create_destination_folder(service, destination_folder_id,
item["name"])
destination_folder_structure.append(destination_sub_folder_id)
processed_folder_list.extend(
process_all_files(service, destination_sub_folder_id, item["id"], item["name"],
destination_folder_structure))
destination_folder_structure.pop()
else:
batch_add(service, item["id"], destination_folder_id)
next_page_token = items.get("nextPageToken")
if not next_page_token:
break
except HttpError as e:
print(f"\nAn error occurred: {e}")
break
processed_folder_list.append(folder_id)
return processed_folder_list
def main():
msg = "This script moves all files and folders recursively of a given Google Drive folder."
parser = argparse.ArgumentParser(description=msg)
parser.add_argument("-d", "--destination", help="ID of the destination folder where the files should be moved to.",
required=True)
parser.add_argument("-f", "--folder",
help="ID of the Google Drive folder. The user's root directory will be used if left empty.",
default="root")
args = parser.parse_args()
print(f"Moving all files to '{args.destination}'")
service = get_drive_service()
processed_folders = process_all_files(service, args.destination, args.folder)
if BATCH:
print("\n\nExecuting final batch...", end=" ")
BATCH.execute()
print("\nBatch execution finished.")
remove_empty_folders(service, processed_folders)
if __name__ == "__main__":
main()