lavavu-osmesa 1.8.43__cp311-cp311-manylinux_2_24_x86_64.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.
Files changed (138) hide show
  1. lavavu/LavaVuPython.py +578 -0
  2. lavavu/_LavaVuPython.cpython-311-x86_64-linux-gnu.so +0 -0
  3. lavavu/__init__.py +15 -0
  4. lavavu/__main__.py +12 -0
  5. lavavu/amalgamate.py +11 -0
  6. lavavu/aserver.py +357 -0
  7. lavavu/control.py +1699 -0
  8. lavavu/convert.py +853 -0
  9. lavavu/dict.json +2485 -0
  10. lavavu/font.bin +0 -0
  11. lavavu/html/LavaVu-amalgamated.css +282 -0
  12. lavavu/html/OK-min.js +99 -0
  13. lavavu/html/baseviewer.js +307 -0
  14. lavavu/html/control.css +104 -0
  15. lavavu/html/control.js +335 -0
  16. lavavu/html/dat-gui-light-theme.css +68 -0
  17. lavavu/html/dat.gui.min.js +2 -0
  18. lavavu/html/draw.js +2259 -0
  19. lavavu/html/drawbox.js +1030 -0
  20. lavavu/html/emscripten-template.js +184 -0
  21. lavavu/html/emscripten.css +92 -0
  22. lavavu/html/favicon.ico +0 -0
  23. lavavu/html/gl-matrix-min.js +47 -0
  24. lavavu/html/gui.css +25 -0
  25. lavavu/html/menu.js +609 -0
  26. lavavu/html/server.js +226 -0
  27. lavavu/html/stats.min.js +5 -0
  28. lavavu/html/styles.css +58 -0
  29. lavavu/html/webview-template.html +43 -0
  30. lavavu/html/webview.html +43 -0
  31. lavavu/lavavu.py +5635 -0
  32. lavavu/points.py +191 -0
  33. lavavu/server.py +343 -0
  34. lavavu/shaders/default.frag +20 -0
  35. lavavu/shaders/default.vert +17 -0
  36. lavavu/shaders/fontShader.frag +25 -0
  37. lavavu/shaders/fontShader.vert +18 -0
  38. lavavu/shaders/lineShader.frag +43 -0
  39. lavavu/shaders/lineShader.vert +28 -0
  40. lavavu/shaders/pointShader.frag +132 -0
  41. lavavu/shaders/pointShader.vert +57 -0
  42. lavavu/shaders/triShader.frag +170 -0
  43. lavavu/shaders/triShader.vert +55 -0
  44. lavavu/shaders/volumeShader.frag +463 -0
  45. lavavu/shaders/volumeShader.vert +5 -0
  46. lavavu/tracers.py +124 -0
  47. lavavu/vutils.py +213 -0
  48. lavavu_osmesa-1.8.43.dist-info/LICENSE.md +179 -0
  49. lavavu_osmesa-1.8.43.dist-info/METADATA +33 -0
  50. lavavu_osmesa-1.8.43.dist-info/RECORD +138 -0
  51. lavavu_osmesa-1.8.43.dist-info/WHEEL +5 -0
  52. lavavu_osmesa-1.8.43.dist-info/entry_points.txt +3 -0
  53. lavavu_osmesa-1.8.43.dist-info/top_level.txt +1 -0
  54. lavavu_osmesa.libs/libLLVM-3-6d00db57.8.so.1 +0 -0
  55. lavavu_osmesa.libs/libOSMesa-29f606eb.so.8.0.0 +0 -0
  56. lavavu_osmesa.libs/libXau-6ab8808d.so.6.0.0 +0 -0
  57. lavavu_osmesa.libs/libXdmcp-911ecd1c.so.6.0.0 +0 -0
  58. lavavu_osmesa.libs/libXfixes-af4baa9b.so.3.1.0 +0 -0
  59. lavavu_osmesa.libs/libavcodec-10cacdd4.so.57.64.101 +0 -0
  60. lavavu_osmesa.libs/libavformat-afa92877.so.57.56.101 +0 -0
  61. lavavu_osmesa.libs/libavutil-837eb790.so.55.34.101 +0 -0
  62. lavavu_osmesa.libs/libbluray-0b5d9dbd.so.1.10.0 +0 -0
  63. lavavu_osmesa.libs/libbsd-4a4ec721.so.0.8.3 +0 -0
  64. lavavu_osmesa.libs/libbz2-9ebec8ea.so.1.0.4 +0 -0
  65. lavavu_osmesa.libs/libcairo-4217ca50.so.2.11400.8 +0 -0
  66. lavavu_osmesa.libs/libchromaprint-c4f82352.so.1.4.2 +0 -0
  67. lavavu_osmesa.libs/libcom_err-b4bd5c72.so.2.1 +0 -0
  68. lavavu_osmesa.libs/libcrystalhd-e4ea0de0.so.3.6 +0 -0
  69. lavavu_osmesa.libs/libdrm-28f5b5e7.so.2.4.0 +0 -0
  70. lavavu_osmesa.libs/libedit-7f8577df.so.2.0.55 +0 -0
  71. lavavu_osmesa.libs/libffi-1c6807d3.so.6.0.4 +0 -0
  72. lavavu_osmesa.libs/libfontconfig-e9a06300.so.1.8.0 +0 -0
  73. lavavu_osmesa.libs/libfreetype-851758a3.so.6.12.3 +0 -0
  74. lavavu_osmesa.libs/libgcrypt-0005395c.so.20.1.6 +0 -0
  75. lavavu_osmesa.libs/libglapi-cf95372b.so.0.0.0 +0 -0
  76. lavavu_osmesa.libs/libgme-5f850ce8.so.0.6.0 +0 -0
  77. lavavu_osmesa.libs/libgmp-742f5e74.so.10.3.2 +0 -0
  78. lavavu_osmesa.libs/libgnutls-1ec2cd13.so.30.13.1 +0 -0
  79. lavavu_osmesa.libs/libgomp-ce6cf6a9.so.1.0.0 +0 -0
  80. lavavu_osmesa.libs/libgpg-error-24781f22.so.0.21.0 +0 -0
  81. lavavu_osmesa.libs/libgsm-aa736f52.so.1.0.12 +0 -0
  82. lavavu_osmesa.libs/libgssapi_krb5-e296a08d.so.2.2 +0 -0
  83. lavavu_osmesa.libs/libhogweed-9d325a8d.so.4.3 +0 -0
  84. lavavu_osmesa.libs/libicudata-79cf9efa.so.57.1 +0 -0
  85. lavavu_osmesa.libs/libicui18n-03536ef3.so.57.1 +0 -0
  86. lavavu_osmesa.libs/libicuuc-5743fca1.so.57.1 +0 -0
  87. lavavu_osmesa.libs/libidn-fd653b64.so.11.6.16 +0 -0
  88. lavavu_osmesa.libs/libjbig-b30cd8bd.so.0 +0 -0
  89. lavavu_osmesa.libs/libjpeg-8afa139c.so.62.2.0 +0 -0
  90. lavavu_osmesa.libs/libk5crypto-93afd15e.so.3.1 +0 -0
  91. lavavu_osmesa.libs/libkeyutils-46318358.so.1.5 +0 -0
  92. lavavu_osmesa.libs/libkrb5-061fe33b.so.3.3 +0 -0
  93. lavavu_osmesa.libs/libkrb5support-86ac49ad.so.0.1 +0 -0
  94. lavavu_osmesa.libs/liblzma-9c0610aa.so.5.2.2 +0 -0
  95. lavavu_osmesa.libs/libmp3lame-249ae4b0.so.0.0.0 +0 -0
  96. lavavu_osmesa.libs/libmpg123-13a39b0e.so.0.42.3 +0 -0
  97. lavavu_osmesa.libs/libncurses-09dfda50.so.5.9 +0 -0
  98. lavavu_osmesa.libs/libnettle-2482db45.so.6.3 +0 -0
  99. lavavu_osmesa.libs/libnuma-c8473f23.so.1.0.0 +0 -0
  100. lavavu_osmesa.libs/libogg-b6ceea65.so.0.8.2 +0 -0
  101. lavavu_osmesa.libs/libopenjp2-a0c5d12e.so.2.1.2 +0 -0
  102. lavavu_osmesa.libs/libopenmpt-58b855da.so.0.0.20 +0 -0
  103. lavavu_osmesa.libs/libopus-37a3229e.so.0.5.3 +0 -0
  104. lavavu_osmesa.libs/libp11-kit-d06ac4a7.so.0.2.0 +0 -0
  105. lavavu_osmesa.libs/libpixman-1-7369dbb3.so.0.34.0 +0 -0
  106. lavavu_osmesa.libs/libpng16-121b9de7.so.16.28.0 +0 -0
  107. lavavu_osmesa.libs/libshine-b48eced9.so.3.0.1 +0 -0
  108. lavavu_osmesa.libs/libsnappy-1125e350.so.1.3.0 +0 -0
  109. lavavu_osmesa.libs/libsoxr-e0d4d3e4.so.0.1.1 +0 -0
  110. lavavu_osmesa.libs/libspeex-6f258c6c.so.1.5.0 +0 -0
  111. lavavu_osmesa.libs/libssh-gcrypt-90c7dd19.so.4.5.1 +0 -0
  112. lavavu_osmesa.libs/libswresample-cb1bf771.so.2.3.100 +0 -0
  113. lavavu_osmesa.libs/libswscale-876dcddb.so.4.2.100 +0 -0
  114. lavavu_osmesa.libs/libtasn1-44938221.so.6.5.3 +0 -0
  115. lavavu_osmesa.libs/libtheoradec-02a81176.so.1.1.4 +0 -0
  116. lavavu_osmesa.libs/libtheoraenc-b508ccf1.so.1.1.2 +0 -0
  117. lavavu_osmesa.libs/libtiff-e2a5092b.so.5.2.6 +0 -0
  118. lavavu_osmesa.libs/libtinfo-33626a82.so.5.9 +0 -0
  119. lavavu_osmesa.libs/libtwolame-6fcc2d32.so.0.0.0 +0 -0
  120. lavavu_osmesa.libs/libva-0f7a824e.so.1.3904.0 +0 -0
  121. lavavu_osmesa.libs/libva-drm-178eb7ac.so.1.3904.0 +0 -0
  122. lavavu_osmesa.libs/libva-x11-d220c497.so.1.3904.0 +0 -0
  123. lavavu_osmesa.libs/libvdpau-0391b780.so.1.0.0 +0 -0
  124. lavavu_osmesa.libs/libvorbis-378bad15.so.0.4.8 +0 -0
  125. lavavu_osmesa.libs/libvorbisenc-d605e0e1.so.2.0.11 +0 -0
  126. lavavu_osmesa.libs/libvorbisfile-5692671f.so.3.3.7 +0 -0
  127. lavavu_osmesa.libs/libvpx-560f7a88.so.4.1.0 +0 -0
  128. lavavu_osmesa.libs/libwavpack-8339806a.so.1.2.0 +0 -0
  129. lavavu_osmesa.libs/libwebp-63152597.so.6.0.2 +0 -0
  130. lavavu_osmesa.libs/libwebpmux-38ab8eb3.so.2.0.2 +0 -0
  131. lavavu_osmesa.libs/libx264-1c898326.so.148 +0 -0
  132. lavavu_osmesa.libs/libx265-51947861.so.95 +0 -0
  133. lavavu_osmesa.libs/libxcb-ffedb2d4.so.1.1.0 +0 -0
  134. lavavu_osmesa.libs/libxcb-render-f7692d1d.so.0.0.0 +0 -0
  135. lavavu_osmesa.libs/libxcb-shm-5702672e.so.0.0.0 +0 -0
  136. lavavu_osmesa.libs/libxml2-b8b306ab.so.2.9.4 +0 -0
  137. lavavu_osmesa.libs/libxvidcore-a773eded.so.4.3 +0 -0
  138. lavavu_osmesa.libs/libzvbi-2db02ce4.so.0.13.2 +0 -0
lavavu/aserver.py ADDED
@@ -0,0 +1,357 @@
1
+ import urllib
2
+ import os
3
+ import time
4
+ import errno
5
+ import weakref
6
+ import base64
7
+ import json
8
+ import socket
9
+
10
+ #ASYNC
11
+ import asyncio
12
+ import aiohttp
13
+ from aiohttp import web
14
+
15
+ from urllib.parse import unquote
16
+
17
+ """
18
+ HTTP Server interface
19
+ """
20
+
21
+ def _get_viewer(ref):
22
+ #Get from weak reference, if deleted raise exception
23
+ lv = ref()
24
+ if not lv:
25
+ raise(Exception("Viewer not found"))
26
+ return lv
27
+
28
+ def _execute(lv, cmds):
29
+ if len(cmds) and cmds[0] == '_':
30
+ #base64 encoded commands or JSON state
31
+ cmds = str(base64.b64decode(cmds).decode('utf-8'))
32
+ #cmds = str(base64.b64decode(cmds), 'utf-8')
33
+
34
+ #Object to select can be provided in preceding angle brackets
35
+ selobj = None
36
+ if cmds[0] == '<':
37
+ pos = cmds.find('>')
38
+ selobj = lv.objects[cmds[1:pos]]
39
+ cmds = cmds[pos+1:]
40
+
41
+ #Execute commands via python API by preceding with '.'
42
+ done = False
43
+ if cmds[0] == '.':
44
+ attr = cmds.split()[0][1:]
45
+ pos = cmds.find(' ')
46
+ params = cmds[pos+1:]
47
+ if selobj:
48
+ #Call on Object
49
+ func = getattr(selobj, attr)
50
+ if func and callable(func):
51
+ func(params)
52
+ done = True
53
+ else:
54
+ #Call on Viewer
55
+ func = getattr(lv, attr)
56
+ if func and callable(func):
57
+ func(params)
58
+ done = True
59
+ elif cmds[0] == '$':
60
+ #Requests prefixed by '$' are sent
61
+ #from property collection controls
62
+ #format is $ID KEY VALUE
63
+ # - ID is the python id() of the properties object
64
+ # All properties collections are stored on their parent
65
+ # object using this id in the _collections dict
66
+ # - KEY is the property name key to set
67
+ # - VALUE is a json string containing the value to set
68
+ S = cmds.split()
69
+ target = S[0][1:]
70
+ if target in lv._collections:
71
+ #Get from _collections by id (weakref)
72
+ props = lv._collections[target]()
73
+ props[S[1]] = json.loads(S[2])
74
+ #Check for callback - if provided, call with updated props
75
+ func = getattr(props, 'callback')
76
+ if func and callable(func):
77
+ func(props)
78
+
79
+ #Default, call via lv.commands() scripting API
80
+ if not done:
81
+ if selobj:
82
+ selobj.select()
83
+ lv.commands(cmds)
84
+
85
+ headers = {'Access-Control-Allow-Origin' : '*',
86
+ 'x-colab-notebook-cache-control' : 'no-cache'} #Colab: disable offline access cache
87
+
88
+ def img_response(lv, query={}):
89
+ global headers
90
+ resp = None
91
+ if 'width' in query and 'height' in query:
92
+ resp = lv.jpeg(resolution=(int(query['width']), int(query['height'])))
93
+ elif 'width' in query:
94
+ resp = lv.jpeg(resolution=(int(query['width']), 0))
95
+ else:
96
+ resp = lv.jpeg()
97
+
98
+ #Ensure the response is valid before serving
99
+ if resp is not None:
100
+ return web.Response(body=resp, content_type='image/jpeg', headers=headers)
101
+ else:
102
+ return web.Response(text='', headers=headers)
103
+
104
+ async def handle_get(request):
105
+ global headers
106
+ lv = _get_viewer(request.app['viewer'])
107
+
108
+ #Default to empty OK 200 response
109
+ response = web.Response(text='', headers=headers)
110
+
111
+ #print(request.path)
112
+ #for q in request.query:
113
+ # print(q, request.query[q])
114
+
115
+ if request.path == '/':
116
+ #Index request returns full screen interactive view
117
+ lv = _get_viewer(request.app['viewer'])
118
+ w = lv.control.Window(align=None, wrapper=None, fullscreen=True)
119
+ code = lv.control.show(True, filename="")
120
+ response = web.Response(text=code, headers=headers, content_type='text/html')
121
+
122
+ elif request.path.startswith('/image'):
123
+ response = img_response(lv, request.query)
124
+
125
+ elif request.path.startswith('/command=') or request.path.startswith('/icommand='):
126
+ pos1 = request.path.find('=')
127
+ pos2 = request.path.find('?')
128
+ if pos2 < 0: pos2 = len(request.path)
129
+ cmds = unquote(request.path[pos1+1:pos2])
130
+
131
+ #Run viewer commands
132
+ _execute(lv, cmds)
133
+
134
+ #Serve image or just respond 200
135
+ if request.path.startswith('/icommand='):
136
+ response = img_response(lv, request.query)
137
+
138
+ elif request.path.startswith('/getstate'):
139
+ state = lv.app.getState()
140
+ response = web.Response(text=state, headers=headers, content_type='application/json')
141
+ elif request.path.startswith('/connect'):
142
+ if 'url' in request.query:
143
+ #Save first valid connection URL on the viewer
144
+ url = request.query['url']
145
+ if len(lv._url) == 0:
146
+ lv._url = url
147
+ uid = id(lv)
148
+ response = web.Response(text=str(uid), headers=headers)
149
+ elif request.path.startswith('/key='):
150
+ pos2 = request.path.find('&')
151
+ cmds = unquote(request.path[1:pos2])
152
+ lv.commands('key ' + cmds, True)
153
+ elif request.path.startswith('/mouse='):
154
+ pos2 = request.path.find('&')
155
+ cmds = unquote(request.path[1:pos2])
156
+ lv.commands('mouse ' + cmds, True)
157
+ elif request.path.startswith('/db'):
158
+ #Send the database
159
+ db = bytes(lv.app.serialize())
160
+ response = web.Response(body=db, headers=headers, content_type='application/octet-stream')
161
+ else:
162
+ #Serve other urls as files if available
163
+ #print("UNKNOWN - ", request.path)
164
+ path = request.path
165
+ if os.path.exists(path):
166
+ #OK to always serve files in cwd?
167
+ response = web.FileResponse(path)
168
+ else:
169
+ #Serve files from lavavu html dir
170
+ #print(' - not found in cwd')
171
+ if path[0] == '/': path = path[1:]
172
+ path2 = os.path.join(lv.htmlpath, path)
173
+ if os.path.exists(path2) and os.path.isfile(path2):
174
+ #print(' - found in htmlpath')
175
+ response = web.FileResponse(path2)
176
+
177
+ return response
178
+
179
+ async def handle_post(request):
180
+ lv = _get_viewer(request.app['viewer'])
181
+ #print("POST", request.path)
182
+ #text = await request.text()
183
+ text = ''
184
+ #Always interpret post data as commands
185
+ #(can perform other actions too based on path later if we want)
186
+ if request.body_exists:
187
+ body = await request.read()
188
+ #body = await request.text()
189
+
190
+ cmds = str(body, 'utf-8') #python3 only
191
+ #from urllib.parse import unquote
192
+ #data_string = unquote(body)
193
+ #cmds = str(data_string.decode('utf-8'))
194
+
195
+ #Run viewer commands
196
+ _execute(lv, cmds)
197
+
198
+ return web.Response(text='', headers=headers)
199
+
200
+
201
+ """
202
+ HTTP Server manager class
203
+ """
204
+ class Server(object):
205
+ def __init__(self, viewer, port=8080, ipv6=False, retries=100):
206
+ #Allows viewer to be garbage collected
207
+ self.viewer = weakref.ref(viewer)
208
+ self.ipv6 = ipv6
209
+ self.retries = retries
210
+ self._closing = False
211
+ self._lock = asyncio.Lock()
212
+
213
+ #Get port/socket before running server in synchronous code
214
+ self.socket, self.port = _listen(port, self.ipv6, self.retries)
215
+
216
+ async def run(self):
217
+ #Server run!
218
+ #Lock until the port is retrieved
219
+ async with self._lock:
220
+ await _serve(self.viewer, self.socket)
221
+
222
+ #ONLY NEED THIS IF NO LOOP EXISTS AND WE ARE MANAGING OUR OWN
223
+ #while not self._closing:
224
+ # await asyncio.sleep(3600) # sleep forever
225
+ # #await asyncio.sleep(1) # sleep forever
226
+ # print('_', end='')
227
+
228
+ #To stop, call cleanup (TODO: put this somewhere in closedown code)
229
+ #await runner.cleanup()
230
+
231
+ def _listen(port, ipv6, retries):
232
+ #Get port/socket before running server in synchronous code
233
+ #avoids race conditions over port number with subsequent code that
234
+ #tries to use server.port before it is confirmed/opened
235
+ hostidx = 0
236
+ for i in range(retries):
237
+ try:
238
+ hosts = []
239
+ socktype = socket.AF_INET
240
+ if ipv6:
241
+ hosts = ['::', 'localhost', '::1']
242
+ host = hosts[hostidx]
243
+ socktype = socket.AF_INET6
244
+ else:
245
+ hosts = ['0.0.0.0', 'localhost', '127.0.0.1']
246
+ host = hosts[hostidx]
247
+
248
+ #https://github.com/aio-libs/aiohttp/issues/1987#issuecomment-309401600
249
+ sock = socket.socket(socktype)
250
+ sock.bind((host, port))
251
+ # Aiohttp will call 'listen' inside.
252
+ # But it must be called before we actually use the port,
253
+ # any attempts to connect before the 'listen' call will
254
+ # be rejected.
255
+ sock.listen(128)
256
+ params = sock.getsockname()
257
+ port = params[1]
258
+ #print("Socket ready on host %s port %s" % (host, port))
259
+ return sock, port
260
+
261
+ except (Exception) as e:
262
+ #Try another port
263
+ if e.errno == errno.EADDRINUSE: #98
264
+ port += 1
265
+ #Try again...
266
+ elif e.errno == errno.EAFNOSUPPORT: #97 : Address family not supported by protocol
267
+ #Try next host name/address
268
+ hostidx += 1
269
+ if hostidx > 2:
270
+ #Try again without ipv6?
271
+ if ipv6:
272
+ ipv6 = False
273
+ else:
274
+ ipv6 = True
275
+ hostidx = 0
276
+ #Try again...
277
+ else:
278
+ print("Socket open failed: ", e, e.errno, host, port)
279
+
280
+ print("Failed to open socket, max retries reached")
281
+ return None, 0
282
+
283
+ async def _serve(viewer, sock):
284
+ try:
285
+ #Create web application manager
286
+ app = web.Application()
287
+ #Store viewer
288
+ app["viewer"] = viewer
289
+ #Add routes
290
+ app.router.add_post('/{tail:.*}', handle_post),
291
+ app.router.add_get('/{tail:.*}', handle_get)
292
+
293
+ #Static routes? https://docs.aiohttp.org/en/stable/web_advanced.html
294
+ #app.add_routes([web.static('/', path_to_static_folder)])
295
+ #routes.static('/prefix', path_to_static_folder)
296
+
297
+ #app.add_routes([web.get('/', handler),
298
+ # web.post('/post', post_handler),
299
+ # web.put('/put', put_handler)])
300
+
301
+ #Can't use this, is blocking
302
+ #web.run_app(app, sock=sock)
303
+
304
+ # set up aiohttp - like run_app, but non-blocking - socket provided version
305
+ runner = aiohttp.web.AppRunner(app, access_log=None)
306
+ await runner.setup()
307
+ site = aiohttp.web_runner.SockSite(runner, sock=sock)
308
+ await site.start()
309
+
310
+ """
311
+ #This works but have to wait for the port to be allocated before using
312
+ # passing socket as above gets port in synchronous code
313
+
314
+ # set up aiohttp - like run_app, but non-blocking
315
+ runner = aiohttp.web.AppRunner(app)
316
+ await runner.setup()
317
+ site = aiohttp.web.TCPSite(runner, host=host, port=port, reuse_address=False)
318
+ await site.start()
319
+
320
+ #Get port from first entry in list of active connections
321
+ for s in runner.sites:
322
+ #print(s.name, s._port)
323
+ return s._port #Get actual port allocated
324
+ return 0
325
+ """
326
+
327
+ except (Exception) as e:
328
+ print("Server start failed: ", e)
329
+
330
+ #Ignore SIGPIPE altogether (does not apply on windows)
331
+ #import sys
332
+ #if sys.platform != 'win32':
333
+ # from signal import signal, SIGPIPE, SIG_IGN
334
+ # signal(SIGPIPE, SIG_IGN)
335
+
336
+ def serve(viewer, port=None, ipv6=False, retries=100):
337
+ s = Server(viewer, port, ipv6, retries)
338
+ #Attach to event loop
339
+ loop = asyncio.get_event_loop()
340
+ loop.create_task(s.run())
341
+ return s
342
+
343
+ """
344
+ Main entry point - run server and open browser interface
345
+ """
346
+ if __name__ == '__main__':
347
+ import lavavu
348
+ import asyncio
349
+ lv = lavavu.Viewer()
350
+
351
+ import logging
352
+ logging.getLogger('aiohttp.server').setLevel(logging.CRITICAL)
353
+
354
+ lv.browser()
355
+ lv.app.loop()
356
+ #lv.interactive()
357
+