new ocr event format

This commit is contained in:
Maxim Devaev 2024-10-23 22:14:47 +03:00
parent a26aee3543
commit 76d70d0838
5 changed files with 63 additions and 47 deletions

View File

@ -97,25 +97,6 @@ class StreamerApi:
self.__streamer.remove_snapshot() self.__streamer.remove_snapshot()
return make_json_response() 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") @exposed_http("GET", "/streamer/ocr")
async def __ocr_handler(self, _: Request) -> Response: 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())})

View File

@ -37,6 +37,7 @@ from ctypes import c_void_p
from ctypes import c_char from ctypes import c_char
from typing import Generator from typing import Generator
from typing import AsyncGenerator
from PIL import ImageOps from PIL import ImageOps
from PIL import Image as PilImage from PIL import Image as PilImage
@ -107,9 +108,32 @@ class Ocr:
def __init__(self, data_dir_path: str, default_langs: list[str]) -> None: def __init__(self, data_dir_path: str, default_langs: list[str]) -> None:
self.__data_dir_path = data_dir_path self.__data_dir_path = data_dir_path
self.__default_langs = default_langs self.__default_langs = default_langs
self.__notifier = aiotools.AioNotifier()
def is_available(self) -> bool: async def get_state(self) -> dict:
return bool(_libtess) 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]: def get_default_langs(self) -> list[str]:
return list(self.__default_langs) return list(self.__default_langs)

View File

@ -152,6 +152,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
__EV_GPIO_STATE = "gpio_state" __EV_GPIO_STATE = "gpio_state"
__EV_INFO_STATE = "info_state" __EV_INFO_STATE = "info_state"
__EV_STREAMER_STATE = "streamer_state" __EV_STREAMER_STATE = "streamer_state"
__EV_OCR_STATE = "ocr_state"
def __init__( # pylint: disable=too-many-arguments,too-many-locals def __init__( # pylint: disable=too-many-arguments,too-many-locals
self, self,
@ -185,7 +186,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
self.__stream_forever = stream_forever 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.__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.__apis: list[object] = [
self, self,
AuthApi(auth_manager), AuthApi(auth_manager),
@ -195,7 +195,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
self.__hid_api, self.__hid_api,
AtxApi(atx), AtxApi(atx),
MsdApi(msd), MsdApi(msd),
self.__streamer_api, StreamerApi(streamer, ocr),
ExportApi(info_manager, atx, user_gpio), ExportApi(info_manager, atx, user_gpio),
RedfishApi(info_manager, atx), 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(hid, "HID", "hid_state").add_source("hid_keymaps_state", self.__hid_api.get_keymaps, None, None),
_Subsystem.make(atx, "ATX", "atx_state"), _Subsystem.make(atx, "ATX", "atx_state"),
_Subsystem.make(msd, "MSD", "msd_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), _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) await self.__poll_info_state(poller)
case self.__EV_STREAMER_STATE: case self.__EV_STREAMER_STATE:
await self.__poll_streamer_state(poller) await self.__poll_streamer_state(poller)
case self.__EV_OCR_STATE:
await self.__poll_ocr_state(poller)
case _: case _:
async for state in poller: async for state in poller:
await self._broadcast_ws_event(event_type, state) 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) prev.update(state)
if "features" in prev: # Complete/Full if "features" in prev: # Complete/Full
await self._broadcast_ws_event(self.__EV_STREAMER_STATE, prev, legacy=True) 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)

View File

@ -34,7 +34,7 @@ export function Ocr(__getGeometry) {
var __start_pos = null; var __start_pos = null;
var __end_pos = null; var __end_pos = null;
var __selection = null; var __sel = null;
var __init__ = function() { var __init__ = function() {
tools.el.setOnClick($("stream-ocr-button"), function() { tools.el.setOnClick($("stream-ocr-button"), function() {
@ -54,7 +54,7 @@ export function Ocr(__getGeometry) {
$("stream-ocr-window").onkeyup = function(event) { $("stream-ocr-window").onkeyup = function(event) {
event.preventDefault(); event.preventDefault();
if (event.code === "Enter") { if (event.code === "Enter") {
if (__selection) { if (__sel) {
__recognizeSelection(); __recognizeSelection();
wm.closeWindow($("stream-ocr-window")); wm.closeWindow($("stream-ocr-window"));
} }
@ -71,11 +71,14 @@ export function Ocr(__getGeometry) {
/************************************************************************/ /************************************************************************/
self.setState = function(state) { 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) { if (enabled) {
let el = $("stream-ocr-lang-selector"); let el = $("stream-ocr-lang-selector");
tools.selector.setValues(el, state.ocr.langs.available); el.options.length = 0;
tools.selector.setSelectedValue(el, tools.storage.get("stream.ocr.lang", state.ocr.langs["default"])); 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); tools.feature.setEnabled($("stream-ocr"), enabled);
$("stream-ocr-led").className = (enabled ? "led-gray" : "hidden"); $("stream-ocr-led").className = (enabled ? "led-gray" : "hidden");
@ -94,23 +97,23 @@ export function Ocr(__getGeometry) {
__end_pos = __getGlobalPosition(event); __end_pos = __getGlobalPosition(event);
let width = Math.abs(__start_pos.x - __end_pos.x); let width = Math.abs(__start_pos.x - __end_pos.x);
let height = Math.abs(__start_pos.y - __end_pos.y); let height = Math.abs(__start_pos.y - __end_pos.y);
let el_selection = $("stream-ocr-selection"); let el = $("stream-ocr-selection");
el_selection.style.left = Math.min(__start_pos.x, __end_pos.x) + "px"; el.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.style.top = Math.min(__start_pos.y, __end_pos.y) + "px";
el_selection.style.width = width + "px"; el.style.width = width + "px";
el_selection.style.height = height + "px"; el.style.height = height + "px";
tools.hidden.setVisible(el_selection, (width > 1 || height > 1)); tools.hidden.setVisible(el, (width > 1 || height > 1));
} }
}; };
var __endSelection = function(event) { var __endSelection = function(event) {
__changeSelection(event); __changeSelection(event);
let el_selection = $("stream-ocr-selection"); let el = $("stream-ocr-selection");
let ok = ( let ok = (
el_selection.offsetWidth > 1 && el_selection.offsetHeight > 1 el.offsetWidth > 1 && el.offsetHeight > 1
&& __start_pos !== null && __end_pos !== null && __start_pos !== null && __end_pos !== null
); );
tools.hidden.setVisible(el_selection, ok); tools.hidden.setVisible(el, ok);
if (ok) { if (ok) {
let rect = $("stream-box").getBoundingClientRect(); let rect = $("stream-box").getBoundingClientRect();
let rel_left = Math.min(__start_pos.x, __end_pos.x) - rect.left; 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_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 rel_bottom = Math.max(__start_pos.y, __end_pos.y) - rect.top + offset;
let geo = __getGeometry(); let geo = __getGeometry();
__selection = { __sel = {
"left": tools.remap(rel_left, geo.x, geo.width, 0, geo.real_width), "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), "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), "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), "bottom": tools.remap(rel_bottom, geo.y, geo.height, 0, geo.real_height),
}; };
} else { } else {
__selection = null; __sel = null;
} }
__start_pos = null; __start_pos = null;
__end_pos = null; __end_pos = null;
@ -154,7 +157,7 @@ export function Ocr(__getGeometry) {
tools.hidden.setVisible($("stream-ocr-selection"), false); tools.hidden.setVisible($("stream-ocr-selection"), false);
__start_pos = null; __start_pos = null;
__end_pos = null; __end_pos = null;
__selection = null; __sel = null;
}; };
var __recognizeSelection = function() { var __recognizeSelection = function() {
@ -164,10 +167,10 @@ export function Ocr(__getGeometry) {
let params = { let params = {
"ocr": 1, "ocr": 1,
"ocr_langs": $("stream-ocr-lang-selector").value, "ocr_langs": $("stream-ocr-lang-selector").value,
"ocr_left": __selection.left, "ocr_left": __sel.left,
"ocr_top": __selection.top, "ocr_top": __sel.top,
"ocr_right": __selection.right, "ocr_right": __sel.right,
"orc_bottom": __selection.bottom, "orc_bottom": __sel.bottom,
}; };
tools.httpGet("/api/streamer/snapshot", params, function(http) { tools.httpGet("/api/streamer/snapshot", params, function(http) {
if (http.status === 200) { if (http.status === 200) {

View File

@ -368,7 +368,7 @@ export function Session() {
case "atx_state": __atx.setState(data.event); break; case "atx_state": __atx.setState(data.event); break;
case "msd_state": __msd.setState(data.event); break; case "msd_state": __msd.setState(data.event); break;
case "streamer_state": __streamer.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;
} }
}; };