runtimepy 5.15.5__py3-none-any.whl → 5.15.7__py3-none-any.whl

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.
runtimepy/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # =====================================
2
2
  # generator=datazen
3
3
  # version=3.2.3
4
- # hash=8ce7abe0f8c0e64d9afc0b5e4de6e2ad
4
+ # hash=7591d3ee09b156575678bdf806e1b5c7
5
5
  # =====================================
6
6
 
7
7
  """
@@ -10,7 +10,7 @@ Useful defaults and other package metadata.
10
10
 
11
11
  DESCRIPTION = "A framework for implementing Python services."
12
12
  PKG_NAME = "runtimepy"
13
- VERSION = "5.15.5"
13
+ VERSION = "5.15.7"
14
14
 
15
15
  # runtimepy-specific content.
16
16
  METRICS_NAME = "metrics"
@@ -82,3 +82,7 @@ body > :first-child {
82
82
  .channel-filter-min-width {
83
83
  min-width: 5.5em;
84
84
  }
85
+
86
+ .media-preview {
87
+ max-width: 70vw;
88
+ }
@@ -4,10 +4,13 @@ A module implementing web server markdown interfaces.
4
4
 
5
5
  # built-in
6
6
  from io import StringIO
7
+ import mimetypes
8
+ from os import stat_result
7
9
  from pathlib import Path
8
10
  from typing import Iterable, cast
9
11
 
10
12
  # third-party
13
+ from svgen.element.html import div
11
14
  from vcorelib.io.file_writer import IndentedFileWriter
12
15
  from vcorelib.math.time import byte_count_str
13
16
  from vcorelib.paths import rel
@@ -15,6 +18,86 @@ from vcorelib.paths import stats as _stats
15
18
 
16
19
  LOGO_MARKDOWN = "[![logo](/static/png/chip-circle-bootstrap/128x128.png)](/)"
17
20
  DIR_FILE = "dir.html"
21
+ AUTOPLAY_PREVIEW_SIZE = 100 * (1024 * 1024) # 100 MiB
22
+
23
+
24
+ def file_preview(path: Path, link: Path, stats: stat_result) -> str:
25
+ """Get possible preview text for a file."""
26
+
27
+ preview = ""
28
+
29
+ if not path.is_file():
30
+ return preview
31
+
32
+ mime, _ = mimetypes.guess_type(path, strict=False)
33
+ if mime:
34
+ # Image previews.
35
+ if mime.startswith("image"):
36
+ preview = div(
37
+ tag="img",
38
+ src=f"/{link}",
39
+ alt=str(link),
40
+ class_str="media-preview",
41
+ ).encode_str(newlines=False)
42
+
43
+ # Video previews.
44
+ elif mime.startswith("video"):
45
+ elem = div(tag="video", class_str="media-preview")
46
+ elem.booleans.add("loop")
47
+ elem.booleans.add("controls")
48
+
49
+ if stats.st_size < AUTOPLAY_PREVIEW_SIZE:
50
+ elem.booleans.add("autoplay")
51
+
52
+ div(parent=elem, tag="source", src=f"/{link}", type=mime)
53
+
54
+ preview = elem.encode_str(newlines=False)
55
+
56
+ # Audio previews.
57
+ elif mime.startswith("audio"):
58
+ elem = div(tag="audio", src=f"/{link}")
59
+ elem.booleans.add("controls")
60
+ preview = elem.encode_str(newlines=False)
61
+
62
+ return preview
63
+
64
+
65
+ def write_markdown_dir(
66
+ writer: IndentedFileWriter, path: Path, base: Path
67
+ ) -> None:
68
+ """Write markdown contents for a single directory."""
69
+
70
+ curr_dir = rel(path, base=base)
71
+
72
+ line = f"### `{base.name}/{curr_dir}`"
73
+
74
+ # Link to go up a directory.
75
+ if curr_dir != Path():
76
+ line += f" ([..](/{curr_dir.parent}/{DIR_FILE}))"
77
+
78
+ writer.write(line)
79
+ writer.empty()
80
+
81
+ writer.write("| name | size | preview |")
82
+ writer.write("|------|------|---------|")
83
+
84
+ for item in sorted(path.iterdir()):
85
+ curr = rel(item, base=base)
86
+
87
+ name = f"`{curr.name}`"
88
+ if item.is_dir():
89
+ name = f"**{name}**"
90
+
91
+ stats = _stats(item)
92
+ assert stats
93
+ size_str = byte_count_str(stats.st_size) if item.is_file() else ""
94
+
95
+ writer.write(
96
+ f"| [{name}](/{curr}) | {size_str} | "
97
+ f"{file_preview(item, curr, stats)} |"
98
+ )
99
+
100
+ writer.empty()
18
101
 
19
102
 
20
103
  def markdown_for_dir(
@@ -40,31 +123,7 @@ def markdown_for_dir(
40
123
  writer.empty()
41
124
 
42
125
  for path, base in paths_bases:
43
- curr_dir = rel(path, base=base)
44
- writer.write(f"### `{base.name}/{curr_dir}`")
45
- writer.empty()
46
-
47
- # Link to go up a directory.
48
- if curr_dir != Path():
49
- writer.write(f"* [..](/{curr_dir.parent}/{DIR_FILE})")
50
-
51
- writer.write("| name | size |")
52
- writer.write("|------|------|")
53
- for item in sorted(path.iterdir()):
54
- curr = rel(item, base=base)
55
-
56
- name = f"`{curr.name}`"
57
- if item.is_dir():
58
- name = f"**{name}**"
59
-
60
- stats = _stats(item)
61
- assert stats
62
- size_str = (
63
- byte_count_str(stats.st_size) if item.is_file() else ""
64
- )
65
- writer.write(f"| [{name}](/{curr}) | {size_str} |")
66
-
67
- writer.empty()
126
+ write_markdown_dir(writer, path, base)
68
127
 
69
128
  result: str = cast(StringIO, writer.stream).getvalue()
70
129
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runtimepy
3
- Version: 5.15.5
3
+ Version: 5.15.7
4
4
  Summary: A framework for implementing Python services.
5
5
  Home-page: https://github.com/libre-embedded/runtimepy
6
6
  Author: Libre Embedded
@@ -17,11 +17,11 @@ Classifier: Development Status :: 5 - Production/Stable
17
17
  Requires-Python: >=3.12
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: psutil
21
- Requires-Dist: svgen>=0.8.0
22
20
  Requires-Dist: aiofiles
23
- Requires-Dist: vcorelib>=3.6.4
24
21
  Requires-Dist: websockets
22
+ Requires-Dist: svgen>=0.8.0
23
+ Requires-Dist: vcorelib>=3.6.4
24
+ Requires-Dist: psutil
25
25
  Provides-Extra: test
26
26
  Requires-Dist: pylint; extra == "test"
27
27
  Requires-Dist: flake8; extra == "test"
@@ -51,11 +51,11 @@ Dynamic: requires-python
51
51
  =====================================
52
52
  generator=datazen
53
53
  version=3.2.3
54
- hash=fd0e57419cf9437a64c4f82cae3625f0
54
+ hash=fd0b9f87927033d8ee2eb5448d8aec76
55
55
  =====================================
56
56
  -->
57
57
 
58
- # runtimepy ([5.15.5](https://pypi.org/project/runtimepy/))
58
+ # runtimepy ([5.15.7](https://pypi.org/project/runtimepy/))
59
59
 
60
60
  [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
61
61
  ![Build Status](https://github.com/libre-embedded/runtimepy/workflows/Python%20Package/badge.svg)
@@ -1,4 +1,4 @@
1
- runtimepy/__init__.py,sha256=wVKBUb0d3kdEu2RAyDqlguPD6weDDr_KQAXrx0YQlCY,391
1
+ runtimepy/__init__.py,sha256=7thnFUqjyVartp9aKm12dnNSAnzERRQ8j2AimmJPTPk,391
2
2
  runtimepy/__main__.py,sha256=IKioH2xOtsXwrwb9zABDQEJvuAX--Lnh84TeSz0XSs0,332
3
3
  runtimepy/app.py,sha256=Er1ZKKrG9U0FV0gQg_GYF9xDb89HgYnVzS5SjxGa2Tg,970
4
4
  runtimepy/dev_requirements.txt,sha256=VZhW6bJ5YbwaoN4d_XxZFuN5BbDLaG7ngKrGnugVPRw,245
@@ -55,7 +55,7 @@ runtimepy/data/server_dev.yaml,sha256=nQsPh7LuQig3pzHfdg_aD3yOUiCj1sKKfI-WwW3hXm
55
55
  runtimepy/data/tftp_server.yaml,sha256=-bFOWJSagI-fEQQcT8k7eDMJVfSPm2XAxLVG3dqUTa4,204
56
56
  runtimepy/data/css/bootstrap_extra.css,sha256=0NiTnZWnIYf04n9NHVe-nVSOLwePJ6X-3uCgUgAEKPI,3101
57
57
  runtimepy/data/css/font.css,sha256=Pe82E66rNi-cwlQ-_1GHAuhPGu5L4x5KqgV0dbDe51w,977
58
- runtimepy/data/css/main.css,sha256=B3cBrwz6Leox1tYHTzJ7dW7Le0gf5_qZDCNlRsu-0aY,1316
58
+ runtimepy/data/css/main.css,sha256=ohZ98-ffw6U1h2Azj8ToYiXFurQo1rH9r6kFQZkrtQ0,1355
59
59
  runtimepy/data/js/DataConnection.js,sha256=DnX8FMehjJXqmI62UMYXSvl_XdfQMzq3XUDFbLu2GgI,98
60
60
  runtimepy/data/js/JsonConnection.js,sha256=rclZrbmWc_zSs6I_JhOgxnVPFIyPMo5WdjAe8alyZ3o,2729
61
61
  runtimepy/data/js/audio.js,sha256=bLkBqbeHMiGGidfL3iXjmVoF9seK-ZeZ3kwgOrcpgk4,1092
@@ -205,7 +205,7 @@ runtimepy/net/http/version.py,sha256=mp6rgIM7-VUVKLCA0Uw96CmBkL0ET860lDVVEewpZ7w
205
205
  runtimepy/net/server/__init__.py,sha256=ppfMgymYGRz13MvHvwXyy9WAos2wWgaZDf1TW8D1ILU,11813
206
206
  runtimepy/net/server/html.py,sha256=OB2K37kA7MHtcKqp0pZE1q_XgiyvfooigzS0-OQFCCM,1900
207
207
  runtimepy/net/server/json.py,sha256=AaMPw-G-7xX67mf1LvQAipNdwrnmbLDunVlkWf6iOz0,2526
208
- runtimepy/net/server/markdown.py,sha256=UIJ9R9WM5XodPs8WEevFNJ3LX9tMevhUe8QKKdeY4ak,2143
208
+ runtimepy/net/server/markdown.py,sha256=kKKSGF_HwH8ADnNm5fNnWq4XXh7Td9Iu_QUj_u-qmts,3595
209
209
  runtimepy/net/server/mux.py,sha256=ejET7NpJzzJjkVwR8laSuZYR79DOT3XazmAIm6rENSE,598
210
210
  runtimepy/net/server/app/__init__.py,sha256=0eCcqYvmby_wQTGqAiGbGM5LCxKM2qpjuIT0gLHsoDU,3157
211
211
  runtimepy/net/server/app/base.py,sha256=46aOqZwRss_nh_WfEH1cMJ9GUVoLJjERd7cTRFu6mXE,1878
@@ -311,9 +311,9 @@ runtimepy/tui/channels/__init__.py,sha256=evDaiIn-YS9uGhdo8ZGtP9VK1ek6sr_P1nJ9Ju
311
311
  runtimepy/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
312
312
  runtimepy/ui/button.py,sha256=8MXnhqmVvANwbbTA8gu3b8N4IIvRYicnwxFQD6SZy3U,2045
313
313
  runtimepy/ui/controls.py,sha256=L55Af-4vGq6ZHewdoA7C_mAYq35WXl8NzOdcsmQIo7M,1868
314
- runtimepy-5.15.5.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
315
- runtimepy-5.15.5.dist-info/METADATA,sha256=zAY7eYIZrG229MNSE6k3wZUyz7nVDQ78DJPS4nlPFGA,9268
316
- runtimepy-5.15.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
317
- runtimepy-5.15.5.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
318
- runtimepy-5.15.5.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
319
- runtimepy-5.15.5.dist-info/RECORD,,
314
+ runtimepy-5.15.7.dist-info/licenses/LICENSE,sha256=yKBRwbO-cOPBrlpsZmJkkSa33DfY31aE8t7lZ0DwlUo,1071
315
+ runtimepy-5.15.7.dist-info/METADATA,sha256=sZIDlVdKnti18Hr1tMV7HOmFlKoh-PF2XQPC942dRPM,9268
316
+ runtimepy-5.15.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
317
+ runtimepy-5.15.7.dist-info/entry_points.txt,sha256=-btVBkYv7ybcopqZ_pRky-bEzu3vhbaG3W3Z7ERBiFE,51
318
+ runtimepy-5.15.7.dist-info/top_level.txt,sha256=0jPmh6yqHyyJJDwEID-LpQly-9kQ3WRMjH7Lix8peLg,10
319
+ runtimepy-5.15.7.dist-info/RECORD,,