cista 1.0.0__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. {cista-1.0.0 → cista-1.1.0}/PKG-INFO +1 -1
  2. {cista-1.0.0 → cista-1.1.0}/cista/_version.py +1 -1
  3. {cista-1.0.0 → cista-1.1.0}/cista/api.py +5 -1
  4. {cista-1.0.0 → cista-1.1.0}/cista/app.py +5 -3
  5. {cista-1.0.0 → cista-1.1.0}/cista/preview.py +33 -21
  6. {cista-1.0.0 → cista-1.1.0}/cista/serve.py +0 -9
  7. {cista-1.0.0 → cista-1.1.0}/cista/watching.py +2 -2
  8. {cista-1.0.0 → cista-1.1.0}/.gitignore +0 -0
  9. {cista-1.0.0 → cista-1.1.0}/README.md +0 -0
  10. {cista-1.0.0 → cista-1.1.0}/cista/__init__.py +0 -0
  11. {cista-1.0.0 → cista-1.1.0}/cista/__main__.py +0 -0
  12. {cista-1.0.0 → cista-1.1.0}/cista/auth.py +0 -0
  13. {cista-1.0.0 → cista-1.1.0}/cista/config.py +0 -0
  14. {cista-1.0.0 → cista-1.1.0}/cista/droppy.py +0 -0
  15. {cista-1.0.0 → cista-1.1.0}/cista/fileio.py +0 -0
  16. {cista-1.0.0 → cista-1.1.0}/cista/protocol.py +0 -0
  17. {cista-1.0.0 → cista-1.1.0}/cista/server80.py +0 -0
  18. {cista-1.0.0 → cista-1.1.0}/cista/session.py +0 -0
  19. {cista-1.0.0 → cista-1.1.0}/cista/util/__init__.py +0 -0
  20. {cista-1.0.0 → cista-1.1.0}/cista/util/apphelpers.py +0 -0
  21. {cista-1.0.0 → cista-1.1.0}/cista/util/asynclink.py +0 -0
  22. {cista-1.0.0 → cista-1.1.0}/cista/util/filename.py +0 -0
  23. {cista-1.0.0 → cista-1.1.0}/cista/util/lrucache.py +0 -0
  24. {cista-1.0.0 → cista-1.1.0}/cista/util/pwgen.py +0 -0
  25. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/add-file-6a41e251.js +0 -0
  26. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/add-folder-62d934ef.js +0 -0
  27. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/arrow-a3a583f8.js +0 -0
  28. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/arrows-h-d9e36082.js +0 -0
  29. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/arrows-v-5223491a.js +0 -0
  30. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/check-f7566407.js +0 -0
  31. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/code-8e0123d4.js +0 -0
  32. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/copy-a70a3d1c.js +0 -0
  33. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/create-file-d995875e.js +0 -0
  34. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/create-folder-c448deda.js +0 -0
  35. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/cross-e74ed6f4.js +0 -0
  36. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/disk-f1376321.js +0 -0
  37. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/download-bad67930.js +0 -0
  38. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/exclamation-afec06bb.js +0 -0
  39. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/eye-5fde34a1.js +0 -0
  40. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/find-11711648.js +0 -0
  41. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/fullscreen-7f61e522.js +0 -0
  42. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/github-c9475017.js +0 -0
  43. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/index-0ccc4ea2.js +0 -0
  44. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/index-89d850f4.css +0 -0
  45. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/info-b3d76e57.js +0 -0
  46. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/link-5cd893e4.js +0 -0
  47. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/logo-6090454d.js +0 -0
  48. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/logo-97d1d7eb.svg +0 -0
  49. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/loop-531f6994.js +0 -0
  50. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/menu-ed0d8c47.js +0 -0
  51. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/next-ee82241a.js +0 -0
  52. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/open-8364df82.js +0 -0
  53. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/paste-0f86e193.js +0 -0
  54. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/pause-a2dd4670.js +0 -0
  55. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/pencil-bfd02151.js +0 -0
  56. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/play-83e40a03.js +0 -0
  57. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/plus-f463cfbc.js +0 -0
  58. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/previous-cbb778b5.js +0 -0
  59. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/reload-907eb866.js +0 -0
  60. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/rename-51dadde6.js +0 -0
  61. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/scissors-d32350cc.js +0 -0
  62. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/shuffle-00a003ad.js +0 -0
  63. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/signin-d7bd57fd.js +0 -0
  64. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/signout-4c4ff7fb.js +0 -0
  65. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/skip-0069d8d7.js +0 -0
  66. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/spinner-164c9b34.js +0 -0
  67. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/stop-3717c27d.js +0 -0
  68. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/trash-449f81ef.js +0 -0
  69. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/triangle-3ecaf29c.js +0 -0
  70. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/unfullscreen-175d46cd.js +0 -0
  71. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/up-arrow-164a366e.js +0 -0
  72. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/upload-cloud-df377f1a.js +0 -0
  73. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/user-3b5a32bc.js +0 -0
  74. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/user-cog-27e2c201.js +0 -0
  75. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/volume-high-f4e07edd.js +0 -0
  76. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/volume-low-f754322d.js +0 -0
  77. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/volume-medium-a5806088.js +0 -0
  78. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/volume-mute-405763b1.js +0 -0
  79. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/window-cross-86cb8236.js +0 -0
  80. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/window-d99968ac.js +0 -0
  81. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/wordwrap-cdcd3f93.js +0 -0
  82. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/zoomin-7d13bb9b.js +0 -0
  83. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/assets/zoomout-daf52018.js +0 -0
  84. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/index.html +0 -0
  85. {cista-1.0.0 → cista-1.1.0}/cista/wwwroot/robots.txt +0 -0
  86. {cista-1.0.0 → cista-1.1.0}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cista
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Dropbox-like file server with modern web interface
5
5
  Project-URL: Homepage, https://git.zi.fi/Vasanko/cista-storage
6
6
  Author: Vasanko
@@ -1,2 +1,2 @@
1
1
  # This file is automatically generated by hatch build.
2
- __version__ = '1.0.0'
2
+ __version__ = '1.1.0'
@@ -119,8 +119,12 @@ async def watch(req, ws):
119
119
  # Send updates
120
120
  while True:
121
121
  await ws.send(await q.get())
122
+ except RuntimeError as e:
123
+ if str(e) == "cannot schedule new futures after shutdown":
124
+ return # Server shutting down, drop the WebSocket
125
+ raise
122
126
  finally:
123
- del watching.pubsub[uuid]
127
+ watching.pubsub.pop(uuid, None) # Remove whether it got added yet or not
124
128
 
125
129
 
126
130
  def subscribe(uuid, ws):
@@ -43,14 +43,16 @@ async def main_start(app, loop):
43
43
  app.ctx.threadexec = ThreadPoolExecutor(
44
44
  max_workers=workers, thread_name_prefix="cista-ioworker"
45
45
  )
46
- await watching.start(app, loop)
46
+ watching.start(app, loop)
47
47
 
48
48
 
49
- @app.after_server_stop
49
+ # Sanic sometimes fails to execute after_server_stop, so we do it before instead (potentially interrupting handlers)
50
+ @app.before_server_stop
50
51
  async def main_stop(app, loop):
51
52
  quit.set()
52
- await watching.stop(app, loop)
53
+ watching.stop(app)
53
54
  app.ctx.threadexec.shutdown()
55
+ logger.debug("Cista worker threads all finished")
54
56
 
55
57
 
56
58
  @app.on_request
@@ -24,6 +24,17 @@ pillow_heif.register_heif_opener()
24
24
 
25
25
  bp = Blueprint("preview", url_prefix="/preview")
26
26
 
27
+ # Map EXIF Orientation value to a corresponding PIL transpose
28
+ EXIF_ORI = {
29
+ 2: Image.Transpose.FLIP_LEFT_RIGHT,
30
+ 3: Image.Transpose.ROTATE_180,
31
+ 4: Image.Transpose.FLIP_TOP_BOTTOM,
32
+ 5: Image.Transpose.TRANSPOSE,
33
+ 6: Image.Transpose.ROTATE_270,
34
+ 7: Image.Transpose.TRANSVERSE,
35
+ 8: Image.Transpose.ROTATE_90,
36
+ }
37
+
27
38
 
28
39
  @bp.get("/<path:path>")
29
40
  async def preview(req, path):
@@ -69,34 +80,35 @@ def dispatch(path, quality, maxsize, maxzoom):
69
80
 
70
81
 
71
82
  def process_image(path, *, maxsize, quality):
72
- t_load_start = perf_counter()
73
- img = Image.open(path)
74
- # Force decode to include I/O in load timing
75
- img.load()
76
- t_load_end = perf_counter()
77
- # Resize
78
- orig_w, orig_h = img.size
79
- t_proc_start = perf_counter()
80
- img.thumbnail((min(orig_w, maxsize), min(orig_h, maxsize)))
81
- t_proc_end = perf_counter()
82
- # Save as AVIF
83
- imgdata = io.BytesIO()
84
- t_save_start = perf_counter()
85
- img.save(imgdata, format="avif", quality=quality, speed=10, max_threads=1)
86
- t_save_end = perf_counter()
87
-
83
+ t_load = perf_counter()
84
+ with Image.open(path) as img:
85
+ # Force decode to include I/O in load timing
86
+ img.load()
87
+ t_proc = perf_counter()
88
+ # Resize
89
+ w, h = img.size
90
+ img.thumbnail((min(w, maxsize), min(h, maxsize)))
91
+ # Transpose pixels according to EXIF Orientation
92
+ orientation = img.getexif().get(274, 1)
93
+ if orientation in EXIF_ORI:
94
+ img = img.transpose(EXIF_ORI[orientation])
95
+ # Save as AVIF
96
+ imgdata = io.BytesIO()
97
+ t_save = perf_counter()
98
+ img.save(imgdata, format="avif", quality=quality, speed=10, max_threads=1)
99
+
100
+ t_end = perf_counter()
88
101
  ret = imgdata.getvalue()
89
102
 
90
- load_ms = (t_load_end - t_load_start) * 1000
91
- proc_ms = (t_proc_end - t_proc_start) * 1000
92
- save_ms = (t_save_end - t_save_start) * 1000
103
+ load_ms = (t_proc - t_load) * 1000
104
+ proc_ms = (t_save - t_proc) * 1000
105
+ save_ms = (t_end - t_save) * 1000
93
106
  logger.debug(
94
- "Preview image %s: load=%.1fms process=%.1fms save=%.1fms out=%.1fKB",
107
+ "Preview image %s: load=%.1fms process=%.1fms save=%.1fms",
95
108
  path.name,
96
109
  load_ms,
97
110
  proc_ms,
98
111
  save_ms,
99
- len(ret) / 1024,
100
112
  )
101
113
 
102
114
  return ret
@@ -1,6 +1,5 @@
1
1
  import os
2
2
  import re
3
- import signal
4
3
  from pathlib import Path
5
4
 
6
5
  from sanic import Sanic
@@ -12,14 +11,6 @@ def run(*, dev=False):
12
11
  """Run Sanic main process that spawns worker processes to serve HTTP requests."""
13
12
  from .app import app
14
13
 
15
- # Set up immediate exit on Ctrl+C for faster termination
16
- def signal_handler(signum, frame):
17
- print("\nReceived interrupt signal, exiting immediately...")
18
- os._exit(0)
19
-
20
- signal.signal(signal.SIGINT, signal_handler)
21
- signal.signal(signal.SIGTERM, signal_handler)
22
-
23
14
  url, opts = parse_listen(config.config.listen)
24
15
  # Silence Sanic's warning about running in production rather than debug
25
16
  os.environ["SANIC_IGNORE_PRODUCTION_WARNING"] = "1"
@@ -440,7 +440,7 @@ def watcher_poll(loop):
440
440
  quit.wait(0.1 + 8 * dur)
441
441
 
442
442
 
443
- async def start(app, loop):
443
+ def start(app, loop):
444
444
  global rootpath
445
445
  config.load_config()
446
446
  rootpath = config.config.path
@@ -454,6 +454,6 @@ async def start(app, loop):
454
454
  app.ctx.watcher.start()
455
455
 
456
456
 
457
- async def stop(app, loop):
457
+ def stop(app):
458
458
  quit.set()
459
459
  app.ctx.watcher.join()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes