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