UiWebsocketPlugin.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import re
  2. import time
  3. import cgi
  4. import gevent
  5. from Plugin import PluginManager
  6. from Config import config
  7. from util import helper
  8. from Translate import Translate
  9. if "_" not in locals():
  10. _ = Translate("plugins/OptionalManager/languages/")
  11. @PluginManager.registerTo("UiWebsocket")
  12. class UiWebsocketPlugin(object):
  13. def __init__(self, *args, **kwargs):
  14. self.time_peer_numbers_updated = 0
  15. super(UiWebsocketPlugin, self).__init__(*args, **kwargs)
  16. def actionFileWrite(self, to, inner_path, *args, **kwargs):
  17. super(UiWebsocketPlugin, self).actionFileWrite(to, inner_path, *args, **kwargs)
  18. # Add file to content.db and set it as pinned
  19. content_db = self.site.content_manager.contents.db
  20. content_db.my_optional_files[self.site.address + "/" + inner_path] = time.time()
  21. if len(content_db.my_optional_files) > 50: # Keep only last 50
  22. oldest_key = min(
  23. content_db.my_optional_files.iterkeys(),
  24. key=(lambda key: content_db.my_optional_files[key])
  25. )
  26. del content_db.my_optional_files[oldest_key]
  27. def updatePeerNumbers(self):
  28. content_db = self.site.content_manager.contents.db
  29. content_db.updatePeerNumbers()
  30. self.site.updateWebsocket(peernumber_updated=True)
  31. # Optional file functions
  32. def actionOptionalFileList(self, to, address=None, orderby="time_downloaded DESC", limit=10):
  33. if not address:
  34. address = self.site.address
  35. # Update peer numbers if necessary
  36. content_db = self.site.content_manager.contents.db
  37. if time.time() - content_db.time_peer_numbers_updated > 60 * 1 and time.time() - self.time_peer_numbers_updated > 60 * 5:
  38. # Start in new thread to avoid blocking
  39. self.time_peer_numbers_updated = time.time()
  40. gevent.spawn(self.updatePeerNumbers)
  41. if not self.hasSitePermission(address):
  42. return self.response(to, {"error": "Forbidden"})
  43. if not all([re.match("^[a-z_*/+-]+( DESC| ASC|)$", part.strip()) for part in orderby.split(",")]):
  44. return self.response(to, "Invalid order_by")
  45. if type(limit) != int:
  46. return self.response(to, "Invalid limit")
  47. back = []
  48. content_db = self.site.content_manager.contents.db
  49. site_id = content_db.site_ids[address]
  50. query = "SELECT * FROM file_optional WHERE site_id = %s AND is_downloaded = 1 ORDER BY %s LIMIT %s" % (site_id, orderby, limit)
  51. for row in content_db.execute(query):
  52. back.append(dict(row))
  53. self.response(to, back)
  54. def actionOptionalFileInfo(self, to, inner_path):
  55. content_db = self.site.content_manager.contents.db
  56. site_id = content_db.site_ids[self.site.address]
  57. # Update peer numbers if necessary
  58. if time.time() - content_db.time_peer_numbers_updated > 60 * 1 and time.time() - self.time_peer_numbers_updated > 60 * 5:
  59. # Start in new thread to avoid blocking
  60. self.time_peer_numbers_updated = time.time()
  61. gevent.spawn(self.updatePeerNumbers)
  62. query = "SELECT * FROM file_optional WHERE site_id = :site_id AND inner_path = :inner_path LIMIT 1"
  63. res = content_db.execute(query, {"site_id": site_id, "inner_path": inner_path})
  64. row = next(res, None)
  65. if row:
  66. self.response(to, dict(row))
  67. else:
  68. self.response(to, None)
  69. def setPin(self, inner_path, is_pinned, address=None):
  70. if not address:
  71. address = self.site.address
  72. if not self.hasSitePermission(address):
  73. return {"error": "Forbidden"}
  74. site = self.server.sites[address]
  75. content_db = site.content_manager.contents.db
  76. site_id = content_db.site_ids[site.address]
  77. content_db.execute("UPDATE file_optional SET is_pinned = %s WHERE ?" % is_pinned, {"site_id": site_id, "inner_path": inner_path})
  78. return "ok"
  79. def actionOptionalFilePin(self, to, inner_path, address=None):
  80. back = self.setPin(inner_path, 1, address)
  81. if back == "ok":
  82. self.cmd("notification", ["done", _["Pinned %s files"] % len(inner_path) if type(inner_path) is list else 1, 5000])
  83. self.response(to, back)
  84. def actionOptionalFileUnpin(self, to, inner_path, address=None):
  85. back = self.setPin(inner_path, 0, address)
  86. if back == "ok":
  87. self.cmd("notification", ["done", _["Removed pin from %s files"] % len(inner_path) if type(inner_path) is list else 1, 5000])
  88. self.response(to, back)
  89. def actionOptionalFileDelete(self, to, inner_path, address=None):
  90. if not address:
  91. address = self.site.address
  92. if not self.hasSitePermission(address):
  93. return self.response(to, {"error": "Forbidden"})
  94. site = self.server.sites[address]
  95. content_db = site.content_manager.contents.db
  96. site_id = content_db.site_ids[site.address]
  97. res = content_db.execute("SELECT * FROM file_optional WHERE ? LIMIT 1", {"site_id": site_id, "inner_path": inner_path})
  98. row = next(res, None)
  99. if not row:
  100. return self.response(to, {"error": "Not found in content.db"})
  101. removed = site.content_manager.optionalRemove(inner_path, row["hash_id"], row["size"])
  102. # if not removed:
  103. # return self.response(to, {"error": "Not found in hash_id: %s" % row["hash_id"]})
  104. content_db.execute("UPDATE file_optional SET is_downloaded = 0, is_pinned = 0, peer = peer - 1 WHERE ?", {"site_id": site_id, "inner_path": inner_path})
  105. try:
  106. site.storage.delete(inner_path)
  107. except Exception, err:
  108. return self.response(to, {"error": "File delete error: %s" % err})
  109. self.response(to, "ok")
  110. # Limit functions
  111. def actionOptionalLimitStats(self, to):
  112. if "ADMIN" not in self.site.settings["permissions"]:
  113. return self.response(to, "Forbidden")
  114. back = {}
  115. back["limit"] = config.optional_limit
  116. back["used"] = self.site.content_manager.contents.db.execute(
  117. "SELECT SUM(size) FROM file_optional WHERE is_downloaded = 1 AND is_pinned = 0"
  118. ).fetchone()[0]
  119. back["free"] = helper.getFreeSpace()
  120. self.response(to, back)
  121. def actionOptionalLimitSet(self, to, limit):
  122. if "ADMIN" not in self.site.settings["permissions"]:
  123. return self.response(to, {"error": "Forbidden"})
  124. config.optional_limit = re.sub("\.0+$", "", limit) # Remove unnecessary digits from end
  125. config.saveValue("optional_limit", limit)
  126. self.response(to, "ok")
  127. # Distribute help functions
  128. def actionOptionalHelpList(self, to, address=None):
  129. if not address:
  130. address = self.site.address
  131. if not self.hasSitePermission(address):
  132. return self.response(to, {"error": "Forbidden"})
  133. site = self.server.sites[address]
  134. self.response(to, site.settings.get("optional_help", {}))
  135. def actionOptionalHelp(self, to, directory, title, address=None):
  136. if not address:
  137. address = self.site.address
  138. if not self.hasSitePermission(address):
  139. return self.response(to, {"error": "Forbidden"})
  140. site = self.server.sites[address]
  141. content_db = site.content_manager.contents.db
  142. site_id = content_db.site_ids[address]
  143. if "optional_help" not in site.settings:
  144. site.settings["optional_help"] = {}
  145. stats = content_db.execute(
  146. "SELECT COUNT(*) AS num, SUM(size) AS size FROM file_optional WHERE site_id = :site_id AND inner_path LIKE :inner_path",
  147. {"site_id": site_id, "inner_path": directory + "%"}
  148. ).fetchone()
  149. stats = dict(stats)
  150. if not stats["size"]:
  151. stats["size"] = 0
  152. if not stats["num"]:
  153. stats["num"] = 0
  154. self.cmd("notification", [
  155. "done",
  156. _["You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>"] %
  157. (cgi.escape(title), cgi.escape(directory)),
  158. 10000
  159. ])
  160. site.settings["optional_help"][directory] = title
  161. self.response(to, dict(stats))
  162. def actionOptionalHelpRemove(self, to, directory, address=None):
  163. if not address:
  164. address = self.site.address
  165. if not self.hasSitePermission(address):
  166. return self.response(to, {"error": "Forbidden"})
  167. site = self.server.sites[address]
  168. try:
  169. del site.settings["optional_help"][directory]
  170. self.response(to, "ok")
  171. except Exception:
  172. self.response(to, {"error": "Not found"})
  173. def cbOptionalHelpAll(self, to, site, value):
  174. site.settings["autodownloadoptional"] = value
  175. self.response(to, value)
  176. def actionOptionalHelpAll(self, to, value, address=None):
  177. if not address:
  178. address = self.site.address
  179. if not self.hasSitePermission(address):
  180. return self.response(to, {"error": "Forbidden"})
  181. site = self.server.sites[address]
  182. if value:
  183. if "ADMIN" in self.site.settings["permissions"]:
  184. self.cbOptionalHelpAll(to, site, True)
  185. else:
  186. site_title = site.content_manager.contents["content.json"].get("title", address)
  187. self.cmd(
  188. "confirm",
  189. [
  190. _["Help distribute all new optional files on site <b>%s</b>"] % cgi.escape(site_title),
  191. _["Yes, I want to help!"]
  192. ],
  193. lambda (res): self.cbOptionalHelpAll(to, site, True)
  194. )
  195. else:
  196. site.settings["autodownloadoptional"] = False
  197. self.response(to, False)