You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.9 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. from magic import from_file
  2. from gapi.api import API
  3. from posixpath import join as pathjoin
  4. from googleapiclient.http import MediaFileUpload
  5. import re
  6. import os
  7. APPLICATION_NAME = "Google Calendar API Python"
  8. def path_translate(
  9. root,
  10. want,
  11. path,
  12. file
  13. ):
  14. l = len(root)+1
  15. rel = path[l:]
  16. new_rel = rel.replace(os.sep,'/')
  17. return pathjoin(want,new_rel,file)
  18. def is_folder(element):
  19. return element['mimeType'] == 'application/vnd.google-apps.folder'
  20. def split_path_nt(path,rev=False):
  21. s = re.findall(r'?:[a-zA-Z]:\\)|[^/\\\?%\*:\|\"\<\>]+',path)
  22. if rev:
  23. return s[::-1]
  24. else:
  25. return s
  26. def split_path(path,rev=False):
  27. s = re.findall(r'(?:^/|[^/\x00]+)',path)
  28. if rev:
  29. return s[::-1]
  30. else:
  31. return s
  32. if os.name == 'nt':
  33. local_split_path = split_path_nt
  34. else:
  35. local_split_path = split_path
  36. def without(node,*keys):
  37. node_meta = set(node.keys())
  38. nope = set(keys)
  39. ret = {}
  40. for key in node_meta-nope:
  41. ret[key] = node[key]
  42. return ret
  43. def remote_path_join(path,*paths):
  44. ret = path
  45. for path in paths:
  46. if not ret.endswith('/'):
  47. ret += '/'
  48. else:
  49. ret += path
  50. return ret
  51. class drive_api(API):
  52. def __init__(
  53. self,
  54. app_name,
  55. client_secret_file,
  56. credentials_dir,
  57. scopes = 'https://www.googleapis.com/auth/drive',
  58. version = 'v3',
  59. ):
  60. super().__init__('drive',scopes,app_name,client_secret_file,credentials_dir,version)
  61. root = {}
  62. self.filesystem = {'folders':{'/':root}}
  63. root_meta = self.service.files().get(fileId='root').execute()
  64. del root_meta['kind']
  65. root[0] = root_meta
  66. root['parent'] = self.filesystem
  67. self.__fill_in__(root)
  68. def get_file_by_path(self,path,ret_file = True,no_parent = True):
  69. '''gets a file or folder by remote path'''
  70. if isinstance(path,str):
  71. path = split_path(path)
  72. else:
  73. path = path.copy()
  74. parent = self.filesystem
  75. end = path.pop()
  76. for sub in path:
  77. try:
  78. if not 'folders' in parent.keys():
  79. self.__fill_in__(parent)
  80. parent = parent['folders'][sub]
  81. except KeyError:
  82. raise FileNotFoundError
  83. try:
  84. if ret_file:
  85. if not 'files' in parent.keys():
  86. self.__fill_in__(parent)
  87. ret = parent['files'][end]
  88. if no_parent:
  89. ret = without(ret,'parent')
  90. else:
  91. if not 'folders' in parent.keys():
  92. self.__fill_in__(parent)
  93. ret = parent['folders'][end]
  94. if no_parent:
  95. ret[0] = without(ret[0],'parent')
  96. return ret
  97. except KeyError:
  98. raise FileNotFoundError
  99. def mkdirs(self,path):
  100. '''makes directories if they do not exist already'''
  101. if isinstance(path,str):
  102. path = split_path(path)
  103. else:
  104. path = path.copy()
  105. missing = []
  106. for i in range(len(path),-1,-1):
  107. try:
  108. parent = self.get_file_by_path(path[:i],False,no_parent = False)
  109. break
  110. except FileNotFoundError:
  111. missing.append(path[i-1])
  112. while len(missing) > 0:
  113. new_folder = {'folders':{},'files':{}}
  114. new_folder['parent'] = parent
  115. name = missing.pop()
  116. new_meta = self.__create_remote_folder__(name,parent)
  117. del new_meta['name']
  118. del new_meta['kind']
  119. new_folder[0] = new_meta
  120. parent['folders'][name] = new_folder
  121. parent = new_folder
  122. return parent
  123. def upload(self,local_path,remote_path):
  124. if not isinstance(remote_path,str):
  125. remote_path = pathjoin(*remote_path)
  126. if os.path.isdir(local_path):
  127. for root,dirs,files in os.walk(local_path,topdown = False):
  128. for file in files:
  129. new_path = path_translate(local_path,remote_path,root,file)
  130. self.__upload_file__(os.path.join(root,file),new_path)
  131. print(new_path)
  132. return self.get_file_by_path(remote_path,False,False)
  133. else:
  134. return self.__upload_file__(local_path,remote_path)
  135. def __upload_file__(self,local_path,remote_path):
  136. '''creates file if it does not exists otherwise update the file'''
  137. if isinstance(remote_path,str):
  138. remote_path = split_path(remote_path)
  139. else:
  140. remote_path = remote_path.copy()
  141. _upload = MediaFileUpload(local_path)
  142. try:
  143. old_file = self.get_file_by_path(remote_path)
  144. self.service.files().update(fileId=old_file['id'],media_body=_upload).execute()
  145. return old_file
  146. except FileNotFoundError:
  147. path = remote_path
  148. end = path.pop()
  149. parent = self.mkdirs(path)
  150. parent_id = parent[0]['id']
  151. meta = {
  152. 'name':end,
  153. 'parents':[parent_id]
  154. }
  155. new_f_meta = self.service.files().create(
  156. body = meta,
  157. media_body = _upload
  158. ).execute()
  159. new_f_meta = without(new_f_meta,'kind')
  160. new_f_meta['parent'] = parent
  161. parent['files'][end] = new_f_meta
  162. return new_f_meta
  163. def __create_remote_folder__(self,name,parent):
  164. meta = {
  165. 'name':name,
  166. 'mimeType':'application/vnd.google-apps.folder',
  167. 'parents' : [parent[0]['id']]
  168. }
  169. return self.service.files().create(body = meta).execute()
  170. def __fill_in__(self,parent):
  171. '''finds all files and folders from a parent'''
  172. parent_id = parent[0]['id']
  173. child_list = self.__list_children__(parent_id)
  174. parent['files'] = {}
  175. parent['folders'] = {}
  176. for child in child_list:
  177. del child['kind']
  178. name = child.pop('name')
  179. child['parent'] = parent
  180. if is_folder(child):
  181. parent['folders'][name] = {}
  182. parent['folders'][name][0] = child
  183. else:
  184. parent['files'][name] = child
  185. def __list_children__(self,parent_id):
  186. page_token = None
  187. first = True
  188. while page_token or first:
  189. first = False
  190. response = self.service.files().list(q = "'{}' in parents and trashed = false".format(parent_id),pageToken=page_token).execute()
  191. page_token = response.get('nextPageToken',None)
  192. for file in response['files']:
  193. yield file
  194. if __name__ == "__main__":
  195. my_api = drive_api(APPLICATION_NAME,r'..\test\drive\client_secret.json',r'..\test\drive')
  196. service = my_api.service