diff options
author | Maxim Devaev <[email protected]> | 2024-11-02 10:39:15 +0200 |
---|---|---|
committer | Maxim Devaev <[email protected]> | 2024-11-02 10:39:15 +0200 |
commit | deba110cdf1fbd68d455e3ce20d0dfdfcb9eeacb (patch) | |
tree | 7c8e15f4ba3a89e4e50b695c76f9402fb3312773 | |
parent | 936cc21c4031b8a014dc8a9820959410f3b6f12e (diff) |
partial msd events
-rw-r--r-- | kvmd/apps/kvmd/api/msd.py | 6 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 17 | ||||
-rw-r--r-- | kvmd/plugins/msd/otg/__init__.py | 22 | ||||
-rw-r--r-- | web/kvm/index.html | 24 | ||||
-rw-r--r-- | web/kvm/navbar-msd.pug | 17 | ||||
-rw-r--r-- | web/share/js/kvm/msd.js | 472 |
6 files changed, 314 insertions, 244 deletions
diff --git a/kvmd/apps/kvmd/api/msd.py b/kvmd/apps/kvmd/api/msd.py index 2fa2eb9b..98e85412 100644 --- a/kvmd/apps/kvmd/api/msd.py +++ b/kvmd/apps/kvmd/api/msd.py @@ -63,7 +63,11 @@ class MsdApi: @exposed_http("GET", "/msd") async def __state_handler(self, _: Request) -> Response: - return make_json_response(await self.__msd.get_state()) + state = await self.__msd.get_state() + if state["storage"] and state["storage"]["parts"]: + state["storage"]["size"] = state["storage"]["parts"][""]["size"] # Legacy API + state["storage"]["free"] = state["storage"]["parts"][""]["free"] # Legacy API + return make_json_response(state) @exposed_http("POST", "/msd/set_params") async def __set_params_handler(self, req: Request) -> Response: diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 4f5a10d3..d9e07484 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -153,6 +153,7 @@ class _Subsystem: class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-instance-attributes __EV_GPIO_STATE = "gpio_state" __EV_ATX_STATE = "atx_state" + __EV_MSD_STATE = "msd_state" __EV_STREAMER_STATE = "streamer_state" __EV_OCR_STATE = "ocr_state" __EV_INFO_STATE = "info_state" @@ -208,7 +209,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins _Subsystem.make(user_gpio, "User-GPIO", self.__EV_GPIO_STATE), _Subsystem.make(hid, "HID", "hid_state").add_source("hid_keymaps_state", self.__hid_api.get_keymaps, None, None), _Subsystem.make(atx, "ATX", self.__EV_ATX_STATE), - _Subsystem.make(msd, "MSD", "msd_state"), + _Subsystem.make(msd, "MSD", self.__EV_MSD_STATE), _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), @@ -378,6 +379,8 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins await self.__poll_gpio_state(poller) case self.__EV_INFO_STATE: await self.__poll_info_state(poller) + case self.__EV_MSD_STATE: + await self.__poll_msd_state(poller) case self.__EV_STREAMER_STATE: await self.__poll_streamer_state(poller) case self.__EV_OCR_STATE: @@ -404,6 +407,18 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins for (key, value) in state.items(): await self._broadcast_ws_event(f"info_{key}_state", value, legacy=True) + async def __poll_msd_state(self, poller: AsyncGenerator[dict, None]) -> None: + prev: dict = {"storage": None} + async for state in poller: + await self._broadcast_ws_event(self.__EV_MSD_STATE, state, legacy=False) + prev_storage = prev["storage"] + prev.update(state) + if prev["storage"] is not None and prev_storage is not None: + prev_storage.update(prev["storage"]) + prev["storage"] = prev_storage + if "online" in prev: # Complete/Full + await self._broadcast_ws_event(self.__EV_MSD_STATE, prev, legacy=True) + async def __poll_streamer_state(self, poller: AsyncGenerator[dict, None]) -> None: prev: dict = {} async for state in poller: diff --git a/kvmd/plugins/msd/otg/__init__.py b/kvmd/plugins/msd/otg/__init__.py index fca18e7b..6d0dd776 100644 --- a/kvmd/plugins/msd/otg/__init__.py +++ b/kvmd/plugins/msd/otg/__init__.py @@ -171,6 +171,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes async with self.__state._lock: # pylint: disable=protected-access storage: (dict | None) = None if self.__state.storage: + assert self.__state.vd storage = dataclasses.asdict(self.__state.storage) for name in list(storage["images"]): del storage["images"][name]["name"] @@ -179,21 +180,19 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes for name in list(storage["parts"]): del storage["parts"][name]["name"] - storage["size"] = storage["parts"][""]["size"] # Legacy API - storage["free"] = storage["parts"][""]["free"] # Legacy API - storage["downloading"] = (self.__reader.get_state() if self.__reader else None) storage["uploading"] = (self.__writer.get_state() if self.__writer else None) vd: (dict | None) = None if self.__state.vd: + assert self.__state.storage vd = dataclasses.asdict(self.__state.vd) if vd["image"]: del vd["image"]["path"] return { "enabled": True, - "online": (bool(self.__state.vd) and self.__drive.is_enabled()), + "online": (bool(vd) and self.__drive.is_enabled()), "busy": self.__state.is_busy(), "storage": storage, "drive": vd, @@ -208,9 +207,22 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if (await self.__notifier.wait()) > 0: prev = {} new = await self.get_state() - if new != prev: + if not prev or (prev.get("online") != new["online"]): prev = copy.deepcopy(new) yield new + else: + diff: dict = {} + for sub in ["busy", "drive"]: + if prev.get(sub) != new[sub]: + diff[sub] = new[sub] + for sub in ["images", "parts", "downloading", "uploading"]: + if (prev.get("storage") or {}).get(sub) != (new["storage"] or {}).get(sub): + if "storage" not in diff: + diff["storage"] = {} + diff["storage"][sub] = new["storage"][sub] + if diff: + prev = copy.deepcopy(new) + yield diff @aiotools.atomic_fg async def reset(self) -> None: diff --git a/web/kvm/index.html b/web/kvm/index.html index 311d37a0..d2f264df 100644 --- a/web/kvm/index.html +++ b/web/kvm/index.html @@ -614,17 +614,6 @@ </td> </tr> </table> - <hr> - <table class="kv"> - <tr> - <td class="value">Note:</td> - <td>• Don't close the browser page until the upload is complete.</td> - </tr> - <tr> - <td></td> - <td>• To speed up the upload, close the stream window.</td> - </tr> - </table> </div> <div class="hidden" id="msd-uploading-sub"> <hr> @@ -642,6 +631,19 @@ <div class="progress" id="msd-uploading-progress"><span class="progress-value" id="msd-uploading-progress-value"></span></div> </div> </div> + <div class="hidden" id="msd-new-tips"> + <hr> + <table class="kv"> + <tr> + <td class="value">Note:</td> + <td>• Don't close the browser page until the upload is complete.</td> + </tr> + <tr> + <td></td> + <td>• To speed up the upload, close the stream window.</td> + </tr> + </table> + </div> <hr> <div class="buttons buttons-row"> <button class="row50" disabled id="msd-connect-button">Connect drive to Server</button> diff --git a/web/kvm/navbar-msd.pug b/web/kvm/navbar-msd.pug index 397105df..6f5110fa 100644 --- a/web/kvm/navbar-msd.pug +++ b/web/kvm/navbar-msd.pug @@ -72,14 +72,6 @@ li(id="msd-dropdown" class="right feature-disabled") tr(id="msd-new-part" class="hidden") td Upload partition: td(width="100%") #[select(id="msd-new-part-selector")] - hr - table(class="kv") - tr - td(class="value") Note: - td • Don't close the browser page until the upload is complete. - tr - td - td • To speed up the upload, close the stream window. div(id="msd-uploading-sub" class="hidden") hr table(class="kv") @@ -92,6 +84,15 @@ li(id="msd-dropdown" class="right feature-disabled") div(class="text") div(id="msd-uploading-progress" class="progress") span(id="msd-uploading-progress-value" class="progress-value") + div(id="msd-new-tips" class="hidden") + hr + table(class="kv") + tr + td(class="value") Note: + td • Don't close the browser page until the upload is complete. + tr + td + td • To speed up the upload, close the stream window. hr div(class="buttons buttons-row") button(disabled id="msd-connect-button" class="row50") Connect drive to Server diff --git a/web/share/js/kvm/msd.js b/web/share/js/kvm/msd.js index 3885bd50..3a558b00 100644 --- a/web/share/js/kvm/msd.js +++ b/web/share/js/kvm/msd.js @@ -35,10 +35,6 @@ export function Msd() { var __state = null; var __http = null; - var __parts_names_json = ""; - var __parts_names_len = 0; - var __parts = {}; - var __init__ = function() { $("msd-led").title = "Unknown state"; @@ -68,8 +64,206 @@ export function Msd() { /************************************************************************/ self.setState = function(state) { - __state = state; - __applyState(); + if (state) { + if (!__state) { + __state = {}; + __state.storage = {}; + } + if (state.enabled !== undefined) { + tools.feature.setEnabled($("msd-dropdown"), state.enabled); + __state.enabled = state.enabled; + } + if (__state.enabled !== undefined) { + if (state.online !== undefined) { + __state.online = state.online; + } + if (state.busy !== undefined) { + __state.busy = state.busy; + } + if (state.drive !== undefined || (state.storage && state.storage.images !== undefined)) { + let drive = (state.drive !== undefined ? state.drive : __state.drive); + let images = ( + state.storage && state.storage.images !== undefined + ? state.storage.images + : __state.storage && __state.storage.images !== undefined + ? __state.storage.images + : null + ); + if (drive && images) { + __updateImageSelector(drive, images); + } + __state.drive = drive; + __state.storage.images = images; + } + if (state.storage && state.storage.parts !== undefined) { + __updateParts(state.storage.parts); + __state.storage.parts = state.storage.parts; + } + if (state.storage && state.storage.uploading !== undefined) { + __updateUploading(state.storage.uploading); + __state.storage.uploading = state.storage.uploading; + } + if (state.storage && state.storage.downloading !== undefined) { + __state.storage.downloading = state.storage.downloading; + } + } + } else { + __state = null; + } + __refreshControls(); + }; + + var __refreshControls = function() { + __updateControls(__state && (__state.online !== undefined) ? __state : null); + }; + + var __updateControls = function(state) { + let o = (state && state.online); + let d = (state ? state.drive : null); + let s = (state ? state.storage : null); + let busy = !!(state && state.busy); + + tools.hidden.setVisible($("msd-message-offline"), (state && !state.online)); + tools.hidden.setVisible($("msd-message-image-broken"), (o && d.image && !d.image.complete && !s.uploading)); + tools.hidden.setVisible($("msd-message-too-big-for-cdrom"), (o && d.cdrom && d.image && d.image.size >= 2359296000)); + tools.hidden.setVisible($("msd-message-out-of-storage"), (o && d.image && !d.image.in_storage)); + tools.hidden.setVisible($("msd-message-rw-enabled"), (o && d.rw)); + tools.hidden.setVisible($("msd-message-another-user-uploads"), (o && s.uploading && !__http)); + tools.hidden.setVisible($("msd-message-downloads"), (o && s.downloading)); + + tools.el.setEnabled($("msd-image-selector"), (o && !d.connected && !busy)); + tools.el.setEnabled($("msd-download-button"), (o && d.image && !d.connected && !busy)); + tools.el.setEnabled($("msd-remove-button"), (o && d.image && d.image.removable && !d.connected && !busy)); + + tools.radio.setEnabled("msd-mode-radio", (o && !d.connected && !busy)); + tools.radio.setValue("msd-mode-radio", `${Number(o && d.cdrom)}`); + + tools.el.setEnabled($("msd-rw-switch"), (o && !d.connected && !busy)); + $("msd-rw-switch").checked = (o && d.rw); + + tools.el.setEnabled($("msd-connect-button"), (o && d.image && !d.connected && !busy)); + tools.el.setEnabled($("msd-disconnect-button"), (o && d.connected && !busy)); + + tools.el.setEnabled($("msd-select-new-button"), (o && !d.connected && !__http && !busy)); + tools.el.setEnabled($("msd-upload-new-button"), + (o && !d.connected && (tools.input.getFile($("msd-new-file")) || $("msd-new-url").value.length > 0) && !busy)); + tools.el.setEnabled($("msd-abort-new-button"), (o && __http)); + + tools.el.setEnabled($("msd-reset-button"), (state && state.enabled && !busy)); + + tools.el.setEnabled($("msd-new-file"), (o && !d.connected && !__http && !busy)); + tools.el.setEnabled($("msd-new-url"), (o && !d.connected && !__http && !busy)); + tools.el.setEnabled($("msd-new-part-selector"), (o && !d.connected && !__http && !busy)); + + if (o && s.uploading) { + tools.hidden.setVisible($("msd-new-sub"), false); + $("msd-new-file").value = ""; + $("msd-new-url").value = ""; + } + tools.hidden.setVisible($("msd-uploading-sub"), (o && s.uploading)); + tools.hidden.setVisible($("msd-new-tips"), (o && s.uploading && __http)); + + let led_cls = "led-gray"; + let msg = "Unavailable"; + if (o && d.connected) { + led_cls = "led-green"; + msg = "Connected to Server"; + } else if (o && s.uploading) { + led_cls = "led-yellow-rotating-fast"; + msg = "Uploading new image"; + } else if (o && s.downloading) { + led_cls = "led-yellow-rotating-fast"; + msg = "Serving the image to download"; + } else if (o) { // Sic! + msg = "Disconnected"; + } + $("msd-led").className = led_cls; + $("msd-status").innerText = $("msd-led").title = msg; + }; + + var __updateUploading = function(uploading) { + $("msd-uploading-name").innerText = (uploading ? uploading.name : ""); + $("msd-uploading-size").innerText = (uploading ? tools.formatSize(uploading.size) : ""); + if (uploading) { + tools.progress.setPercentOf($("msd-uploading-progress"), uploading.size, uploading.written); + } + }; + + var __updateParts = function(parts) { + let names = Object.keys(parts).sort(); + { + let writable = names.filter(name => (name === "" || parts[name].writable)); + let writable_json = JSON.stringify(writable); + let el = $("msd-new-part-selector"); + if (el.__writable_json !== writable_json) { + let sel = (el.value || ""); + el.options.length = 0; + for (let name of writable) { + let title = (name || "\u2500 Internal \u2500"); + tools.selector.addOption(el, title, name, (name === sel)); + } + tools.hidden.setVisible($("msd-new-part"), (writable.length > 1)); + el.__writable_json = writable_json; + } + } + { + let names_json = JSON.stringify(names); + let el = $("msd-storages"); + if (el.__names_json !== names_json) { + el.innerHTML = names.map(name => ` + <div class="text"> + <div id="__msd-storage-${tools.makeIdByText(name)}-progress" class="progress"> + <span class="progress-value"></span> + </div> + </div> + `).join("<hr>"); + el.__names_json = names_json; + } + } + for (let name of names) { + let part = parts[name]; + let title = ( + name === "" + ? `${names.length === 1 ? "Storage: %s" : "Internal storage: %s"}` // eslint-disable-line + : `Storage [${name}${part.writable ? "]" : ", read-only]"}: %s` // eslint-disable-line + ); + let id = `__msd-storage-${tools.makeIdByText(name)}-progress`; + tools.progress.setSizeOf($(id), title, part.size, part.free); + } + }; + + var __updateImageSelector = function(drive, images) { + let sel = ""; + let el = $("msd-image-selector"); + el.options.length = 1; + for (let name of Object.keys(images).sort()) { + tools.selector.addSeparator(el); + tools.selector.addOption(el, name, name); + tools.selector.addComment(el, __makeImageSelectorInfo(images[name])); + if (drive.image && drive.image.name === name && drive.image.in_storage) { + sel = name; + } + } + if (drive.image && !drive.image.in_storage) { + sel = ".__external__"; // Just some magic name + tools.selector.addOption(el, drive.image.name, sel); + tools.selector.addComment(el, __makeImageSelectorInfo(drive.image)); + } + el.value = sel; + }; + + var __makeImageSelectorInfo = function(image) { + let text = `\xA0\xA0\xA0\xA0\xA0\u2570 ${tools.formatSize(image.size)}`; + if (!image.complete) { + text += ", broken"; + } + if (image.in_storage !== undefined && !image.in_storage) { + text += ", out of storage"; + } + let ts = new Date(image.mod_ts * 1000); + ts = new Date(ts.getTime() - (ts.getTimezoneOffset() * 60000)); + ts = ts.toISOString().slice(0, -8).replaceAll("-", ".").replace("T", "-"); + return `${text} \u2500 ${ts}`; }; var __selectImage = function() { @@ -80,8 +274,8 @@ export function Msd() { }; var __clickDownloadButton = function() { - let name = $("msd-image-selector").value; - window.open(`/api/msd/read?image=${name}`); + let image = encodeURIComponent($("msd-image-selector").value); + window.open(`/api/msd/read?image=${image}`); }; var __clickRemoveButton = function() { @@ -102,6 +296,7 @@ export function Msd() { if (http.status !== 200) { wm.error("Can't configure Mass Storage", http.responseText); } + __refreshControls(); }); }; @@ -110,60 +305,63 @@ export function Msd() { __http = new XMLHttpRequest(); let prefix = encodeURIComponent($("msd-new-part-selector").value); if (file) { - __http.open("POST", `/api/msd/write?prefix=${prefix}&image=${encodeURIComponent(file.name)}&remove_incomplete=1`, true); + let image = encodeURIComponent(file.name); + __http.open("POST", `/api/msd/write?prefix=${prefix}&image=${image}&remove_incomplete=1`, true); } else { - let url = $("msd-new-url").value; - __http.open("POST", `/api/msd/write_remote?prefix=${prefix}&url=${encodeURIComponent(url)}&remove_incomplete=1`, true); + let url = encodeURIComponent($("msd-new-url").value); + __http.open("POST", `/api/msd/write_remote?prefix=${prefix}&url=${url}&remove_incomplete=1`, true); } __http.upload.timeout = 7 * 24 * 3600; - __http.onreadystatechange = __httpStateChange; + __http.onreadystatechange = __uploadStateChange; __http.send(file); - __applyState(); + __refreshControls(); }; - var __httpStateChange = function() { - if (__http.readyState === 4) { - if (__http.status !== 200) { - wm.error("Can't upload image", __http.responseText); - } else if ($("msd-new-url").value.length > 0) { - let html = ""; - let msg = ""; - try { - let end = __http.responseText.lastIndexOf("\r\n"); - if (end < 0) { - end = __http.responseText.length; - } - let begin = __http.responseText.lastIndexOf("\r\n", end - 2); - if (begin < 0) { - end = 0; - } - let result_str = __http.responseText.slice(begin, end); - let result = JSON.parse(result_str); - if (!result.ok) { - html = "Can't upload image"; - msg = result_str; - } - } catch (ex) { - html = "Can't parse upload result"; - msg = `${ex}`; + var __uploadStateChange = function() { + if (__http.readyState !== 4) { + return; + } + if (__http.status !== 200) { + wm.error("Can't upload image", __http.responseText); + } else if ($("msd-new-url").value.length > 0) { + let html = ""; + let msg = ""; + try { + let end = __http.responseText.lastIndexOf("\r\n"); + if (end < 0) { + end = __http.responseText.length; } - if (html.length > 0) { - wm.error(html, msg); + let begin = __http.responseText.lastIndexOf("\r\n", end - 2); + if (begin < 0) { + end = 0; } + let result_str = __http.responseText.slice(begin, end); + let result = JSON.parse(result_str); + if (!result.ok) { + html = "Can't upload image"; + msg = result_str; + } + } catch (ex) { + html = "Can't parse upload result"; + msg = `${ex}`; + } + if (html.length > 0) { + wm.error(html, msg); } - tools.hidden.setVisible($("msd-new-sub"), false); - $("msd-new-file").value = ""; - $("msd-new-url").value = ""; - __http = null; - __applyState(); } + tools.hidden.setVisible($("msd-new-sub"), false); + $("msd-new-file").value = ""; + $("msd-new-url").value = ""; + __http = null; + __refreshControls(); }; var __clickAbortNewButton = function() { __http.onreadystatechange = null; __http.abort(); __http = null; - tools.progress.setValue($("msd-uploading-progress"), "Aborted", 0); + __refreshControls(); + tools.hidden.setVisible($("msd-new-sub"), true); }; var __clickConnectButton = function(connected) { @@ -171,9 +369,9 @@ export function Msd() { if (http.status !== 200) { wm.error("Can't switch Mass Storage", http.responseText); } - __applyState(); + __refreshControls(); }); - __applyState(); + __refreshControls(); tools.el.setEnabled($(`msd-${connected ? "connect" : "disconnect"}-button`), false); }; @@ -184,9 +382,7 @@ export function Msd() { if (http.status !== 200) { wm.error("Mass Storage reset error", http.responseText); } - __applyState(); }); - __applyState(); } }); }; @@ -194,193 +390,33 @@ export function Msd() { var __toggleSelectSub = function() { let el_sub = $("msd-new-sub"); let visible = tools.hidden.isVisible(el_sub); + tools.hidden.setVisible(el_sub, !visible); if (visible) { $("msd-new-file").value = ""; $("msd-new-url").value = ""; } - tools.hidden.setVisible(el_sub, !visible); - __applyState(); + __refreshControls(); }; var __selectNewFile = function() { - let el_input = $("msd-new-file"); - let file = tools.input.getFile($("msd-new-file")); + let el = $("msd-new-file"); + let file = tools.input.getFile(el); if (file) { $("msd-new-url").value = ""; let part = __state.storage.parts[$("msd-new-part-selector").value]; if (file.size > part.size) { - wm.error(`New image is too big for the Mass Storage partition.<br>Maximum: ${tools.formatSize(part.size)}`); - el_input.value = ""; + wm.error(`The new image is too big for the Mass Storage partition.<br>Maximum: ${tools.formatSize(part.size)}`); + el.value = ""; } } - __applyState(); + __refreshControls(); }; var __selectNewUrl = function() { if ($("msd-new-url").value.length > 0) { $("msd-new-file").value = ""; } - __applyState(); - }; - - var __applyState = function() { - __applyStateStatus(); - - let s = __state; - let online = (s && s.online); - - if (s) { - tools.feature.setEnabled($("msd-dropdown"), s.enabled); - tools.feature.setEnabled($("msd-reset-button"), s.enabled); - } - tools.hidden.setVisible($("msd-message-offline"), (s && !s.online)); - tools.hidden.setVisible($("msd-message-image-broken"), (online && s.drive.image && !s.drive.image.complete && !s.storage.uploading)); - tools.hidden.setVisible($("msd-message-too-big-for-cdrom"), (online && s.drive.cdrom && s.drive.image && s.drive.image.size >= 2359296000)); - tools.hidden.setVisible($("msd-message-out-of-storage"), (online && s.drive.image && !s.drive.image.in_storage)); - tools.hidden.setVisible($("msd-message-rw-enabled"), (online && s.drive.rw)); - tools.hidden.setVisible($("msd-message-another-user-uploads"), (online && s.storage.uploading && !__http)); - tools.hidden.setVisible($("msd-message-downloads"), (online && s.storage.downloading)); - - if (online) { - let names = Object.keys(s.storage.parts).sort(); - let parts_names_json = JSON.stringify(names); - if (__parts_names_json !== parts_names_json) { - $("msd-storages").innerHTML = names.map(name => ` - <div class="text"> - <div id="msd-storage-${tools.makeIdByText(name)}-progress" class="progress"> - <span class="progress-value"></span> - </div> - </div> - `).join("<hr>"); - __parts_names_json = parts_names_json; - __parts_names_len = names.length; - } - __parts = s.storage.parts; - } - for (let name in __parts) { - let part = __parts[name]; - let title = ( - name.length === 0 - ? `${__parts_names_len === 1 ? "Storage: %s" : "Internal storage: %s"}` // eslint-disable-line - : `Storage [${name}${part.writable ? "]" : ", read-only]"}: %s` // eslint-disable-line - ); - let id = `msd-storage-${tools.makeIdByText(name)}-progress`; - if (online) { - tools.progress.setSizeOf($(id), title, part.size, part.free); - } else { - tools.progress.setValue($(id), title.replace("%s", "unavailable"), 0); - } - } - - tools.el.setEnabled($("msd-image-selector"), (online && !s.drive.connected && !s.busy)); - __applyStateImageSelector(); - tools.el.setEnabled($("msd-download-button"), (online && s.drive.image && !s.drive.connected && !s.busy)); - tools.el.setEnabled($("msd-remove-button"), (online && s.drive.image && s.drive.image.removable && !s.drive.connected && !s.busy)); - - tools.radio.setEnabled("msd-mode-radio", (online && !s.drive.connected && !s.busy)); - tools.radio.setValue("msd-mode-radio", `${Number(online && s.drive.cdrom)}`); - - tools.el.setEnabled($("msd-rw-switch"), (online && !s.drive.connected && !s.busy)); - $("msd-rw-switch").checked = (online && s.drive.rw); - - tools.el.setEnabled($("msd-connect-button"), (online && s.drive.image && !s.drive.connected && !s.busy)); - tools.el.setEnabled($("msd-disconnect-button"), (online && s.drive.connected && !s.busy)); - - tools.el.setEnabled($("msd-select-new-button"), (online && !s.drive.connected && !__http && !s.busy)); - tools.el.setEnabled($("msd-upload-new-button"), - (online && !s.drive.connected && (tools.input.getFile($("msd-new-file")) || $("msd-new-url").value.length > 0) && !s.busy)); - tools.el.setEnabled($("msd-abort-new-button"), (online && __http)); - - tools.el.setEnabled($("msd-reset-button"), (s && s.enabled && !s.busy)); - - tools.el.setEnabled($("msd-new-file"), (online && !s.drive.connected && !__http && !s.busy)); - tools.el.setEnabled($("msd-new-url"), (online && !s.drive.connected && !__http && !s.busy)); - tools.el.setEnabled($("msd-new-part-selector"), (online && !s.drive.connected && !__http && !s.busy)); - if (online && !s.storage.uploading && !s.storage.downloading) { - let parts = Object.keys(s.storage.parts).sort().filter(name => (name === "" || s.storage.parts[name].writable)); - tools.selector.setValues($("msd-new-part-selector"), parts, "\u2500 Internal \u2500"); - tools.hidden.setVisible($("msd-new-part"), (parts.length > 1)); - } - - tools.hidden.setVisible($("msd-uploading-sub"), (online && s.storage.uploading)); - $("msd-uploading-name").innerHTML = ((online && s.storage.uploading) ? s.storage.uploading.name : ""); - $("msd-uploading-size").innerHTML = ((online && s.storage.uploading) ? tools.formatSize(s.storage.uploading.size) : ""); - if (online) { - if (s.storage.uploading) { - tools.progress.setPercentOf($("msd-uploading-progress"), s.storage.uploading.size, s.storage.uploading.written); - } else if (!__http) { - tools.progress.setValue($("msd-uploading-progress"), "Waiting for upload (press UPLOAD button) ...", 0); - } - } else { - $("msd-new-file").value = ""; - $("msd-new-url").value = ""; - tools.progress.setValue($("msd-uploading-progress"), "", 0); - } - }; - - var __applyStateStatus = function() { - let s = __state; - let online = (s && s.online); - - let led_cls = "led-gray"; - let msg = "Unavailable"; - - if (online && s.drive.connected) { - led_cls = "led-green"; - msg = "Connected to Server"; - } else if (online && s.storage.uploading) { - led_cls = "led-yellow-rotating-fast"; - msg = "Uploading new image"; - } else if (online && s.storage.downloading) { - led_cls = "led-yellow-rotating-fast"; - msg = "Serving the image to download"; - } else if (online) { // Sic! - msg = "Disconnected"; - } - - $("msd-led").className = led_cls; - $("msd-status").innerHTML = $("msd-led").title = msg; - }; - - var __applyStateImageSelector = function() { - let s = __state; - if (!(s && s.online) || s.storage.uploading || s.storage.downloading) { - return; - } - - let el = $("msd-image-selector"); - el.options.length = 1; - - let selected = ""; - - for (let name of Object.keys(s.storage.images).sort()) { - tools.selector.addSeparator(el); - tools.selector.addOption(el, name, name); - tools.selector.addComment(el, __makeImageSelectorInfo(s.storage.images[name])); - if (s.drive.image && s.drive.image.name === name && s.drive.image.in_storage) { - selected = name; - } - } - - if (s.drive.image && !s.drive.image.in_storage) { - selected = ".__external"; - tools.selector.addOption(el, s.drive.image.name, selected); - tools.selector.addComment(el, __makeImageSelectorInfo(s.drive.image)); - } - - el.value = selected; - }; - - var __makeImageSelectorInfo = function(image) { - let info = `\xA0\xA0\xA0\xA0\xA0\u2570 ${tools.formatSize(image.size)}`; - info += (image.complete ? "" : ", broken"); - if (image.in_storage !== undefined && !image.in_storage) { - info += ", out of storage"; - } - let dt = new Date(image.mod_ts * 1000); - dt = new Date(dt.getTime() - (dt.getTimezoneOffset() * 60000)); - info += " \u2500 " + dt.toISOString().slice(0, -8).replaceAll("-", ".").replace("T", "-"); - return info; + __refreshControls(); }; __init__(); |