FilePackPlugin.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import os
  2. import re
  3. import gevent
  4. from Plugin import PluginManager
  5. from Config import config
  6. # Keep archive open for faster reponse times for large sites
  7. archive_cache = {}
  8. def closeArchive(archive_path):
  9. if archive_path in archive_cache:
  10. del archive_cache[archive_path]
  11. def openArchive(archive_path):
  12. if archive_path not in archive_cache:
  13. if archive_path.endswith("tar.gz"):
  14. import tarfile
  15. archive_cache[archive_path] = tarfile.open(archive_path, "r:gz")
  16. elif archive_path.endswith("tar.bz2"):
  17. import tarfile
  18. archive_cache[archive_path] = tarfile.open(archive_path, "r:bz2")
  19. else:
  20. import zipfile
  21. archive_cache[archive_path] = zipfile.ZipFile(archive_path)
  22. gevent.spawn_later(5, lambda: closeArchive(archive_path)) # Close after 5 sec
  23. archive = archive_cache[archive_path]
  24. return archive
  25. def openArchiveFile(archive_path, path_within):
  26. archive = openArchive(archive_path)
  27. if archive_path.endswith(".zip"):
  28. return archive.open(path_within)
  29. else:
  30. return archive.extractfile(path_within.encode("utf8"))
  31. @PluginManager.registerTo("UiRequest")
  32. class UiRequestPlugin(object):
  33. def actionSiteMedia(self, path, **kwargs):
  34. if ".zip/" in path or ".tar.gz/" in path:
  35. path_parts = self.parsePath(path)
  36. file_path = u"%s/%s/%s" % (config.data_dir, path_parts["address"], path_parts["inner_path"].decode("utf8"))
  37. match = re.match("^(.*\.(?:tar.gz|tar.bz2|zip))/(.*)", file_path)
  38. archive_path, path_within = match.groups()
  39. if not os.path.isfile(archive_path):
  40. site = self.server.site_manager.get(path_parts["address"])
  41. if not site:
  42. return self.actionSiteAddPrompt(path)
  43. # Wait until file downloads
  44. result = site.needFile(site.storage.getInnerPath(archive_path), priority=10)
  45. # Send virutal file path download finished event to remove loading screen
  46. site.updateWebsocket(file_done=site.storage.getInnerPath(file_path))
  47. if not result:
  48. return self.error404(path)
  49. header_allow_ajax = False
  50. if self.get.get("ajax_key"):
  51. requester_site = self.server.site_manager.get(path_parts["request_address"])
  52. if self.get["ajax_key"] == requester_site.settings["ajax_key"]:
  53. header_allow_ajax = True
  54. else:
  55. return self.error403("Invalid ajax_key")
  56. try:
  57. file = openArchiveFile(archive_path, path_within)
  58. content_type = self.getContentType(file_path)
  59. self.sendHeader(200, content_type=content_type, noscript=kwargs.get("header_noscript", False), allow_ajax=header_allow_ajax)
  60. return self.streamFile(file)
  61. except Exception as err:
  62. self.log.debug("Error opening archive file: %s" % err)
  63. return self.error404(path)
  64. return super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs)
  65. def streamFile(self, file):
  66. for i in range(100): # Read max 6MB
  67. try:
  68. block = file.read(60 * 1024)
  69. if block:
  70. yield block
  71. else:
  72. raise StopIteration
  73. except StopIteration:
  74. file.close()
  75. break
  76. @PluginManager.registerTo("SiteStorage")
  77. class SiteStoragePlugin(object):
  78. def isFile(self, inner_path):
  79. if ".zip/" in inner_path or ".tar.gz/" in inner_path:
  80. match = re.match("^(.*\.(?:tar.gz|tar.bz2|zip))/(.*)", inner_path)
  81. archive_inner_path, path_within = match.groups()
  82. return super(SiteStoragePlugin, self).isFile(archive_inner_path)
  83. else:
  84. return super(SiteStoragePlugin, self).isFile(inner_path)
  85. def walk(self, inner_path, *args, **kwags):
  86. if ".zip" in inner_path or ".tar.gz" in inner_path:
  87. match = re.match("^(.*\.(?:tar.gz|tar.bz2|zip))(.*)", inner_path)
  88. archive_inner_path, path_within = match.groups()
  89. archive = openArchive(self.getPath(archive_inner_path))
  90. if archive_inner_path.endswith(".zip"):
  91. namelist = [name for name in archive.namelist() if not name.endswith("/")]
  92. else:
  93. namelist = [item.name for item in archive.getmembers() if not item.isdir()]
  94. return namelist
  95. else:
  96. return super(SiteStoragePlugin, self).walk(inner_path, *args, **kwags)