summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Devaev <[email protected]>2024-10-23 22:14:47 +0300
committerMaxim Devaev <[email protected]>2024-10-23 22:14:47 +0300
commit76d70d08381f4503518eeb30b3259e57db64ed78 (patch)
tree98eda3113b0a5cac0bf4f31c529a4c2451691ece
parenta26aee3543883faf9a5b83832b274604f4f69263 (diff)
new ocr event format
-rw-r--r--kvmd/apps/kvmd/api/streamer.py21
-rw-r--r--kvmd/apps/kvmd/ocr.py30
-rw-r--r--kvmd/apps/kvmd/server.py14
-rw-r--r--web/share/js/kvm/ocr.js45
-rw-r--r--web/share/js/kvm/session.js2
5 files changed, 64 insertions, 48 deletions
diff --git a/kvmd/apps/kvmd/api/streamer.py b/kvmd/apps/kvmd/api/streamer.py
index fc8e01de..81f255d0 100644
--- a/kvmd/apps/kvmd/api/streamer.py
+++ b/kvmd/apps/kvmd/api/streamer.py
@@ -97,25 +97,6 @@ class StreamerApi:
self.__streamer.remove_snapshot()
return make_json_response()
- # =====
-
- async def get_ocr(self) -> dict: # XXX: Ugly hack
- enabled = self.__ocr.is_available()
- default: list[str] = []
- available: list[str] = []
- if enabled:
- default = self.__ocr.get_default_langs()
- available = self.__ocr.get_available_langs()
- return {
- "ocr": {
- "enabled": enabled,
- "langs": {
- "default": default,
- "available": available,
- },
- },
- }
-
@exposed_http("GET", "/streamer/ocr")
async def __ocr_handler(self, _: Request) -> Response:
- return make_json_response(await self.get_ocr())
+ return make_json_response({"ocr": (await self.__ocr.get_state())})
diff --git a/kvmd/apps/kvmd/ocr.py b/kvmd/apps/kvmd/ocr.py
index 0a632d9d..e110a720 100644
--- a/kvmd/apps/kvmd/ocr.py
+++ b/kvmd/apps/kvmd/ocr.py
@@ -37,6 +37,7 @@ from ctypes import c_void_p
from ctypes import c_char
from typing import Generator
+from typing import AsyncGenerator
from PIL import ImageOps
from PIL import Image as PilImage
@@ -107,9 +108,32 @@ class Ocr:
def __init__(self, data_dir_path: str, default_langs: list[str]) -> None:
self.__data_dir_path = data_dir_path
self.__default_langs = default_langs
-
- def is_available(self) -> bool:
- return bool(_libtess)
+ self.__notifier = aiotools.AioNotifier()
+
+ async def get_state(self) -> dict:
+ enabled = bool(_libtess)
+ default: list[str] = []
+ available: list[str] = []
+ if enabled:
+ default = self.get_default_langs()
+ available = self.get_available_langs()
+ return {
+ "enabled": enabled,
+ "langs": {
+ "default": default,
+ "available": available,
+ },
+ }
+
+ async def trigger_state(self) -> None:
+ self.__notifier.notify()
+
+ async def poll_state(self) -> AsyncGenerator[dict, None]:
+ while True:
+ await self.__notifier.wait()
+ yield (await self.get_state())
+
+ # =====
def get_default_langs(self) -> list[str]:
return list(self.__default_langs)
diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py
index 4b6b57a0..b88bc6b7 100644
--- a/kvmd/apps/kvmd/server.py
+++ b/kvmd/apps/kvmd/server.py
@@ -152,6 +152,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
__EV_GPIO_STATE = "gpio_state"
__EV_INFO_STATE = "info_state"
__EV_STREAMER_STATE = "streamer_state"
+ __EV_OCR_STATE = "ocr_state"
def __init__( # pylint: disable=too-many-arguments,too-many-locals
self,
@@ -185,7 +186,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
self.__stream_forever = stream_forever
self.__hid_api = HidApi(hid, keymap_path, ignore_keys, mouse_x_range, mouse_y_range) # Ugly hack to get keymaps state
- self.__streamer_api = StreamerApi(streamer, ocr) # Same hack to get ocr langs state
self.__apis: list[object] = [
self,
AuthApi(auth_manager),
@@ -195,7 +195,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
self.__hid_api,
AtxApi(atx),
MsdApi(msd),
- self.__streamer_api,
+ StreamerApi(streamer, ocr),
ExportApi(info_manager, atx, user_gpio),
RedfishApi(info_manager, atx),
]
@@ -206,7 +206,8 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
_Subsystem.make(hid, "HID", "hid_state").add_source("hid_keymaps_state", self.__hid_api.get_keymaps, None, None),
_Subsystem.make(atx, "ATX", "atx_state"),
_Subsystem.make(msd, "MSD", "msd_state"),
- _Subsystem.make(streamer, "Streamer", "streamer_state").add_source("streamer_ocr_state", self.__streamer_api.get_ocr, None, None),
+ _Subsystem.make(streamer, "Streamer", self.__EV_STREAMER_STATE),
+ _Subsystem.make(ocr, "OCR", self.__EV_OCR_STATE),
_Subsystem.make(info_manager, "Info manager", self.__EV_INFO_STATE),
]
@@ -370,6 +371,8 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
await self.__poll_info_state(poller)
case self.__EV_STREAMER_STATE:
await self.__poll_streamer_state(poller)
+ case self.__EV_OCR_STATE:
+ await self.__poll_ocr_state(poller)
case _:
async for state in poller:
await self._broadcast_ws_event(event_type, state)
@@ -399,3 +402,8 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
prev.update(state)
if "features" in prev: # Complete/Full
await self._broadcast_ws_event(self.__EV_STREAMER_STATE, prev, legacy=True)
+
+ async def __poll_ocr_state(self, poller: AsyncGenerator[dict, None]) -> None:
+ async for state in poller:
+ await self._broadcast_ws_event(self.__EV_OCR_STATE, state, legacy=False)
+ await self._broadcast_ws_event("streamer_ocr_state", {"ocr": state}, legacy=True)
diff --git a/web/share/js/kvm/ocr.js b/web/share/js/kvm/ocr.js
index 13e1b887..849b19b1 100644
--- a/web/share/js/kvm/ocr.js
+++ b/web/share/js/kvm/ocr.js
@@ -34,7 +34,7 @@ export function Ocr(__getGeometry) {
var __start_pos = null;
var __end_pos = null;
- var __selection = null;
+ var __sel = null;
var __init__ = function() {
tools.el.setOnClick($("stream-ocr-button"), function() {
@@ -54,7 +54,7 @@ export function Ocr(__getGeometry) {
$("stream-ocr-window").onkeyup = function(event) {
event.preventDefault();
if (event.code === "Enter") {
- if (__selection) {
+ if (__sel) {
__recognizeSelection();
wm.closeWindow($("stream-ocr-window"));
}
@@ -71,11 +71,14 @@ export function Ocr(__getGeometry) {
/************************************************************************/
self.setState = function(state) {
- let enabled = (state && state.ocr.enabled && !tools.browser.is_mobile);
+ let enabled = (state && state.enabled && !tools.browser.is_mobile);
if (enabled) {
let el = $("stream-ocr-lang-selector");
- tools.selector.setValues(el, state.ocr.langs.available);
- tools.selector.setSelectedValue(el, tools.storage.get("stream.ocr.lang", state.ocr.langs["default"]));
+ el.options.length = 0;
+ for (let lang of state.langs.available) {
+ tools.selector.addOption(el, lang, lang);
+ }
+ el.value = tools.storage.get("stream.ocr.lang", state.langs["default"]);
}
tools.feature.setEnabled($("stream-ocr"), enabled);
$("stream-ocr-led").className = (enabled ? "led-gray" : "hidden");
@@ -94,23 +97,23 @@ export function Ocr(__getGeometry) {
__end_pos = __getGlobalPosition(event);
let width = Math.abs(__start_pos.x - __end_pos.x);
let height = Math.abs(__start_pos.y - __end_pos.y);
- let el_selection = $("stream-ocr-selection");
- el_selection.style.left = Math.min(__start_pos.x, __end_pos.x) + "px";
- el_selection.style.top = Math.min(__start_pos.y, __end_pos.y) + "px";
- el_selection.style.width = width + "px";
- el_selection.style.height = height + "px";
- tools.hidden.setVisible(el_selection, (width > 1 || height > 1));
+ let el = $("stream-ocr-selection");
+ el.style.left = Math.min(__start_pos.x, __end_pos.x) + "px";
+ el.style.top = Math.min(__start_pos.y, __end_pos.y) + "px";
+ el.style.width = width + "px";
+ el.style.height = height + "px";
+ tools.hidden.setVisible(el, (width > 1 || height > 1));
}
};
var __endSelection = function(event) {
__changeSelection(event);
- let el_selection = $("stream-ocr-selection");
+ let el = $("stream-ocr-selection");
let ok = (
- el_selection.offsetWidth > 1 && el_selection.offsetHeight > 1
+ el.offsetWidth > 1 && el.offsetHeight > 1
&& __start_pos !== null && __end_pos !== null
);
- tools.hidden.setVisible(el_selection, ok);
+ tools.hidden.setVisible(el, ok);
if (ok) {
let rect = $("stream-box").getBoundingClientRect();
let rel_left = Math.min(__start_pos.x, __end_pos.x) - rect.left;
@@ -119,14 +122,14 @@ export function Ocr(__getGeometry) {
let rel_top = Math.min(__start_pos.y, __end_pos.y) - rect.top + offset;
let rel_bottom = Math.max(__start_pos.y, __end_pos.y) - rect.top + offset;
let geo = __getGeometry();
- __selection = {
+ __sel = {
"left": tools.remap(rel_left, geo.x, geo.width, 0, geo.real_width),
"right": tools.remap(rel_right, geo.x, geo.width, 0, geo.real_width),
"top": tools.remap(rel_top, geo.y, geo.height, 0, geo.real_height),
"bottom": tools.remap(rel_bottom, geo.y, geo.height, 0, geo.real_height),
};
} else {
- __selection = null;
+ __sel = null;
}
__start_pos = null;
__end_pos = null;
@@ -154,7 +157,7 @@ export function Ocr(__getGeometry) {
tools.hidden.setVisible($("stream-ocr-selection"), false);
__start_pos = null;
__end_pos = null;
- __selection = null;
+ __sel = null;
};
var __recognizeSelection = function() {
@@ -164,10 +167,10 @@ export function Ocr(__getGeometry) {
let params = {
"ocr": 1,
"ocr_langs": $("stream-ocr-lang-selector").value,
- "ocr_left": __selection.left,
- "ocr_top": __selection.top,
- "ocr_right": __selection.right,
- "orc_bottom": __selection.bottom,
+ "ocr_left": __sel.left,
+ "ocr_top": __sel.top,
+ "ocr_right": __sel.right,
+ "orc_bottom": __sel.bottom,
};
tools.httpGet("/api/streamer/snapshot", params, function(http) {
if (http.status === 200) {
diff --git a/web/share/js/kvm/session.js b/web/share/js/kvm/session.js
index 1e7fefcd..523b4097 100644
--- a/web/share/js/kvm/session.js
+++ b/web/share/js/kvm/session.js
@@ -368,7 +368,7 @@ export function Session() {
case "atx_state": __atx.setState(data.event); break;
case "msd_state": __msd.setState(data.event); break;
case "streamer_state": __streamer.setState(data.event); break;
- case "streamer_ocr_state": __ocr.setState(data.event); break;
+ case "ocr_state": __ocr.setState(data.event); break;
}
};