2 # -*- coding: utf-8 -*-
4 # Carla bridge for LV2 modguis
5 # Copyright (C) 2015-2020 Filipe Coelho <falktx@falktx.com>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; either version 2 of
10 # the License, or any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # For a full copy of the GNU General Public License see the doc/GPL.txt file.
19 # ------------------------------------------------------------------------------------------------------------
24 from PyQt5
.QtCore
import pyqtSignal
, QThread
26 # ------------------------------------------------------------------------------------------------------------
27 # Generate a random port number between 9000 and 18000
29 from random
import random
31 PORTn
= 8998 + int(random()*9000)
33 # ------------------------------------------------------------------------------------------------------------
37 from asyncio
import new_event_loop
, set_event_loop
42 # ------------------------------------------------------------------------------------------------------------
45 from tornado
.log
import enable_pretty_logging
46 from tornado
.ioloop
import IOLoop
47 from tornado
.util
import unicode_type
48 from tornado
.web
import HTTPError
49 from tornado
.web
import Application
, RequestHandler
, StaticFileHandler
51 # ------------------------------------------------------------------------------------------------------------
52 # Set up environment for the webserver
55 ROOT
= "/usr/share/mod"
56 DATA_DIR
= os
.path
.expanduser("~/.local/share/mod-data/")
57 HTML_DIR
= os
.path
.join(ROOT
, "html")
59 os
.environ
['MOD_DEV_HOST'] = "1"
60 os
.environ
['MOD_DEV_HMI'] = "1"
61 os
.environ
['MOD_DESKTOP'] = "1"
63 os
.environ
['MOD_DATA_DIR'] = DATA_DIR
64 os
.environ
['MOD_HTML_DIR'] = HTML_DIR
65 os
.environ
['MOD_KEY_PATH'] = os
.path
.join(DATA_DIR
, "keys")
66 os
.environ
['MOD_CLOUD_PUB'] = os
.path
.join(ROOT
, "keys", "cloud_key.pub")
67 os
.environ
['MOD_PLUGIN_LIBRARY_DIR'] = os
.path
.join(DATA_DIR
, "lib")
69 os
.environ
['MOD_PHANTOM_BINARY'] = "/usr/bin/phantomjs"
70 os
.environ
['MOD_SCREENSHOT_JS'] = os
.path
.join(ROOT
, "screenshot.js")
71 os
.environ
['MOD_DEVICE_WEBSERVER_PORT'] = PORT
73 # ------------------------------------------------------------------------------------------------------------
76 from modtools
.utils
import get_plugin_info
, get_plugin_gui
, get_plugin_gui_mini
78 # ------------------------------------------------------------------------------------------------------------
81 class JsonRequestHandler(RequestHandler
):
82 def write(self
, data
):
83 if isinstance(data
, (bytes
, unicode_type
, dict)):
84 RequestHandler
.write(self
, data
)
90 self
.set_header("Content-Type", "application/json; charset=UTF-8")
94 self
.set_header("Content-Type", "application/json; charset=UTF-8")
97 data
= json
.dumps(data
)
98 self
.set_header("Content-Type", "application/json; charset=UTF-8")
100 RequestHandler
.write(self
, data
)
103 class EffectGet(JsonRequestHandler
):
105 uri
= self
.get_argument('uri')
108 data
= get_plugin_info(uri
)
110 print("ERROR: get_plugin_info for '%s' failed" % uri
)
115 class EffectFile(StaticFileHandler
):
116 def initialize(self
):
117 # return custom type directly. The browser will do the parsing
118 self
.custom_type
= None
120 uri
= self
.get_argument('uri')
123 self
.modgui
= get_plugin_gui(uri
)
128 root
= self
.modgui
['resourcesDirectory']
132 return StaticFileHandler
.initialize(self
, root
)
134 def parse_url_path(self
, prop
):
136 path
= self
.modgui
[prop
]
140 if prop
in ("iconTemplate", "settingsTemplate", "stylesheet", "javascript"):
141 self
.custom_type
= "text/plain"
145 def get_content_type(self
):
146 if self
.custom_type
is not None:
147 return self
.custom_type
148 return StaticFileHandler
.get_content_type(self
)
150 class EffectResource(StaticFileHandler
):
152 def initialize(self
):
153 # Overrides StaticFileHandler initialize
158 uri
= self
.get_argument('uri')
160 return self
.shared_resource(path
)
163 modgui
= get_plugin_gui_mini(uri
)
168 root
= modgui
['resourcesDirectory']
173 super(EffectResource
, self
).initialize(root
)
174 return super(EffectResource
, self
).get(path
)
175 except HTTPError
as e
:
176 if e
.status_code
!= 404:
178 return self
.shared_resource(path
)
182 def shared_resource(self
, path
):
183 super(EffectResource
, self
).initialize(os
.path
.join(HTML_DIR
, 'resources'))
184 return super(EffectResource
, self
).get(path
)
186 # ------------------------------------------------------------------------------------------------------------
189 class WebServerThread(QThread
):
191 running
= pyqtSignal()
193 def __init__(self
, parent
=None):
194 QThread
.__init
__(self
, parent
)
196 self
.fApplication
= Application(
198 (r
"/effect/get/?", EffectGet
),
199 (r
"/effect/file/(.*)", EffectFile
),
200 (r
"/resources/(.*)", EffectResource
),
201 (r
"/(.*)", StaticFileHandler
, {"path": HTML_DIR
}),
205 self
.fPrepareWasCalled
= False
206 self
.fEventLoop
= None
209 if not self
.fPrepareWasCalled
:
210 self
.fPrepareWasCalled
= True
212 self
.fEventLoop
= new_event_loop()
213 set_event_loop(self
.fEventLoop
)
214 self
.fApplication
.listen(PORT
, address
="0.0.0.0")
215 if int(os
.getenv("MOD_LOG", "0")):
216 enable_pretty_logging()
219 IOLoop
.instance().start()
222 IOLoop
.instance().stop()
223 if self
.fEventLoop
is not None:
224 self
.fEventLoop
.call_soon_threadsafe(self
.fEventLoop
.stop
)
225 return self
.wait(5000)