From 6b225d0bc21bf1d6dd8127b703e3ec888df3c4ca Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 16 Jan 2025 11:00:23 -0500 Subject: [PATCH] gallery add http fallback Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 3 +++ javascript/gallery.js | 40 ++++++++++++++++++++++++++++++++++++---- modules/api/gallery.py | 20 +++++++++++++++++++- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d788eedb8..f610e1514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,13 @@ ## Update for 2025-01-16 +- **Gallery**: + - add http fallback for slow/unreliable links - **Fixes**: - non-full vae decode - send-to image transfer - sana vae tiling + - increase gallery timeouts ## Update for 2025-01-15 diff --git a/javascript/gallery.js b/javascript/gallery.js index 7433391f6..a2c225441 100644 --- a/javascript/gallery.js +++ b/javascript/gallery.js @@ -46,7 +46,7 @@ class GalleryFolder extends HTMLElement { else folder.shadow.children[1].classList.remove('gallery-folder-selected'); } }); - div.addEventListener('click', fetchFiles); // eslint-disable-line no-use-before-define + div.addEventListener('click', fetchFilesWS); // eslint-disable-line no-use-before-define this.shadow.appendChild(div); } } @@ -224,7 +224,7 @@ async function getHash(str, algo = 'SHA-256') { } } -async function wsConnect(socket, timeout = 2000) { +async function wsConnect(socket, timeout = 5000) { const intrasleep = 100; const ttl = timeout / intrasleep; const isOpened = () => (socket.readyState === WebSocket.OPEN); @@ -327,19 +327,51 @@ async function gallerySort(btn) { el.status.innerText = `Sort | ${arr.length.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`; } -async function fetchFiles(evt) { // fetch file-by-file list over websockets +async function fetchFilesHT(evt) { + el.status.innerText = `Folder | ${evt.target.name}`; + const t0 = performance.now(); + const fragment = document.createDocumentFragment(); + el.status.innerText = `Folder | ${evt.target.name} | in-progress`; + let numFiles = 0; + + const res = await fetch(`/sdapi/v1/browser/files?folder=${encodeURI(evt.target.name)}`); + if (!res || res.status !== 200) { + el.status.innerText = `Folder | ${evt.target.name} | failed: ${res?.statusText}`; + return; + } + const jsonData = await res.json(); + for (const line of jsonData) { + const data = decodeURI(line).split('##F##'); + numFiles++; + const f = new GalleryFile(data[0], data[1]); + fragment.appendChild(f); + } + + el.files.appendChild(fragment); + + const t1 = performance.now(); + log(`gallery: folder=${evt.target.name} num=${numFiles} time=${Math.floor(t1 - t0)}ms`); + el.status.innerText = `Folder | ${evt.target.name} | ${numFiles.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`; + addSeparators(); +} + +async function fetchFilesWS(evt) { // fetch file-by-file list over websockets el.files.innerHTML = ''; if (!url) return; if (ws && ws.readyState === WebSocket.OPEN) ws.close(); // abort previous request let wsConnected = false; try { ws = new WebSocket(`${url}/sdapi/v1/browser/files`); - wsConnected = await wsConnect(ws, 30000); + wsConnected = await wsConnect(ws); } catch (err) { log('gallery: ws connect error', err); return; } log(`gallery: connected=${wsConnected} state=${ws?.readyState} url=${ws?.url}`); + if (!wsConnected) { + await fetchFilesHT(evt); // fallback to http + return; + } el.status.innerText = `Folder | ${evt.target.name}`; const t0 = performance.now(); let numFiles = 0; diff --git a/modules/api/gallery.py b/modules/api/gallery.py index 52455ffa1..ea33a9b27 100644 --- a/modules/api/gallery.py +++ b/modules/api/gallery.py @@ -154,6 +154,24 @@ async def get_thumb(file: str): content = { 'error': str(e) } return JSONResponse(content=content) + @app.get("/sdapi/v1/browser/files", response_model=list) + async def ht_files(folder: str): + try: + t0 = time.time() + files = files_cache.directory_files(folder, recursive=True) + lines = [] + for f in files: + file = os.path.relpath(f, folder) + msg = quote(folder) + '##F##' + quote(file) + msg = msg[:1] + ":" + msg[4:] if msg[1:4] == "%3A" else msg + lines.append(msg) + t1 = time.time() + shared.log.debug(f'Gallery: type=ht folder="{folder}" files={len(lines)} time={t1-t0:.3f}') + return lines + except Exception as e: + shared.log.error(f'Gallery: {folder} {e}') + return [] + @app.websocket("/sdapi/v1/browser/files") async def ws_files(ws: WebSocket): try: @@ -173,7 +191,7 @@ async def ws_files(ws: WebSocket): await manager.send(ws, msg) await manager.send(ws, '#END#') t1 = time.time() - shared.log.debug(f'Gallery: folder="{folder}" files={numFiles} time={t1-t0:.3f}') + shared.log.debug(f'Gallery: type=ws folder="{folder}" files={numFiles} time={t1-t0:.3f}') except WebSocketDisconnect: debug('Browser WS unexpected disconnect') manager.disconnect(ws)