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.

236 lines
7.1 KiB

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