# HG changeset patch # User Goffi # Date 1687444561 -7200 # Node ID 106945841fbca2a4951d939ccd2f99fd740f79e0 # Parent d7c78722e4f8263cc433eb35963c093d41a058fd _browser (album): moved code to upload file to a separate `file_uploader` module: this way, uploading logic can be re-used elsewhere. diff -r d7c78722e4f8 -r 106945841fbc libervia/web/pages/_browser/file_uploader.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/web/pages/_browser/file_uploader.py Thu Jun 22 16:36:01 2023 +0200 @@ -0,0 +1,110 @@ +import json +from bridge import Bridge +from browser import console as log, window +import dialog +from template import Template +bridge = Bridge() + +log.warning = log.warn + + +class FileUploader: + """Class handling HTTP upload and progress display""" + + def __init__(self, files_service, template_file, files_path=None, on_delete_cb=None): + self.files_service = files_service + self.files_path = files_path + self.item_tpl = Template(template_file) + self.on_delete_cb = on_delete_cb + self.uploaded = {} + + def on_progress(self, ev, item_elt): + if ev.lengthComputable: + percent = int(ev.loaded/ev.total*100) + self.update_progress(item_elt, percent) + + def on_load(self, file_, item_elt): + self.update_progress(item_elt, 100) + item_elt.classList.add("progress_finished") + item_elt.classList.remove("progress_started") + if self.on_delete_cb is not None: + delete_btn = item_elt.select_one('.action_delete') + if delete_btn is not None: + delete_btn.bind("click", self.on_delete_cb) + log.info(f"file {file_.name} uploaded correctly") + + def on_error(self, failure, file_, item_elt): + dialog.notification.show( + f"can't upload {file_.name}: {failure}", + level="error" + ) + + def update_progress(self, item_elt, new_value): + progress_elt = item_elt.select_one("progress") + if progress_elt is not None: + progress_elt.value = new_value + progress_elt.text = f"{new_value}%" + + def on_slot_cb(self, file_, upload_slot, item_elt): + put_url, get_url, headers = upload_slot + xhr = window.XMLHttpRequest.new() + xhr.open("PUT", put_url, True) + xhr.upload.bind('progress', lambda ev: self.on_progress(ev, item_elt)) + xhr.upload.bind('load', lambda ev: self.on_load(file_, item_elt)) + xhr.upload.bind('error', lambda ev: self.on_error(xhr.response, file_, item_elt)) + if self.files_path is not None: + xhr.setRequestHeader( + 'Xmpp-File-Path', + window.encodeURIComponent(self.files_path) + ) + xhr.setRequestHeader('Xmpp-File-No-Http', "true") + xhr.send(file_) + + # we update file data with the get URL + file_data = json.loads(item_elt.getAttribute("data-file")) + file_data["url"] = get_url + item_elt.setAttribute("data-file", json.dumps(file_data)) + + def on_slot_eb(self, file_, failure, item_elt): + dialog.notification.show( + f"Can't get upload slot: {failure['message']}", + level="error" + ) + item_elt.remove() + + def upload_files(self, files, container_elt): + """Start file upload, and add item_tpl to container_elt""" + log.info(f"uploading {len(files)} files") + for file_ in files: + url = window.URL.createObjectURL(file_) + + file_data = { + "name": file_.name, + "mime_type": file_.type, + } + template_file_data = file_data.copy() + # we don't want to open the file on click, it's not yet the + # uploaded URL + template_file_data["url"] = url + # we have no thumb yet, so we use the whole image + # TODO: reduce image for preview + template_file_data["thumb_url"] = url + + item_elt = self.item_tpl.get_elt({ + "file": template_file_data, + "uploading": True, + }) + item_elt.setAttribute("data-file", json.dumps(file_data)) + item_elt.classList.add("progress_started") + container_elt <= item_elt + + bridge.file_http_upload_get_slot( + file_.name, + file_.size, + file_.type or '', + self.files_service, + callback=lambda upload_slot, file_=file_, item_elt=item_elt: + self.on_slot_cb(file_, upload_slot, item_elt), + errback=lambda failure, file_=file_, item_elt=item_elt: + self.on_slot_eb(file_, failure, item_elt), + ) diff -r d7c78722e4f8 -r 106945841fbc libervia/web/pages/photos/album/_browser/__init__.py --- a/libervia/web/pages/photos/album/_browser/__init__.py Thu Jun 22 16:35:34 2023 +0200 +++ b/libervia/web/pages/photos/album/_browser/__init__.py Thu Jun 22 16:36:01 2023 +0200 @@ -1,14 +1,14 @@ -from browser import document, window, bind, html, DOMNode, aio +import alt_media_player +from bridge import AsyncBridge, Bridge +from browser import DOMNode, aio, bind, document, html, window +import dialog +from file_uploader import FileUploader +from invitation import InvitationManager from javascript import JSON -from bridge import Bridge, AsyncBridge -from template import Template -import dialog +import loading from slideshow import SlideShow -from invitation import InvitationManager -import alt_media_player -# we use tmp_aio because `blob` is not handled in Brython's aio +from template import Template import tmp_aio -import loading cache_path = window.cache_path @@ -28,83 +28,7 @@ # file upload -def on_progress(ev, photo_elt): - if ev.lengthComputable: - percent = int(ev.loaded/ev.total*100) - update_progress(photo_elt, percent) - - -def on_load(file_, photo_elt): - update_progress(photo_elt, 100) - photo_elt.classList.add("progress_finished") - photo_elt.classList.remove("progress_started") - photo_elt.select_one('.action_delete').bind("click", on_delete) - print(f"file {file_.name} uploaded correctly") - - -def on_error(failure, file_, photo_elt): - dialog.notification.show( - f"can't upload {file_.name}: {failure}", - level="error" - ) - - -def update_progress(photo_elt, new_value): - progress_elt = photo_elt.select_one("progress") - progress_elt.value = new_value - progress_elt.text = f"{new_value}%" - - -def on_slot_cb(file_, upload_slot, photo_elt): - put_url, get_url, headers = upload_slot - xhr = window.XMLHttpRequest.new() - xhr.open("PUT", put_url, True) - xhr.upload.bind('progress', lambda ev: on_progress(ev, photo_elt)) - xhr.upload.bind('load', lambda ev: on_load(file_, photo_elt)) - xhr.upload.bind('error', lambda ev: on_error(xhr.response, file_, photo_elt)) - xhr.setRequestHeader('Xmpp-File-Path', window.encodeURIComponent(files_path)) - xhr.setRequestHeader('Xmpp-File-No-Http', "true") - xhr.send(file_) - - -def on_slot_eb(file_, failure, photo_elt): - dialog.notification.show( - f"Can't get upload slot: {failure['message']}", - level="error" - ) - photo_elt.remove() - - -def upload_files(files): - print(f"uploading {len(files)} files") - album_items = document['album_items'] - for file_ in files: - url = window.URL.createObjectURL(file_) - photo_elt = photo_tpl.get_elt({ - "file": { - "name": file_.name, - # we don't want to open the file on click, it's not yet the - # uploaded URL - "url": url, - # we have no thumb yet, so we use the whole image - # TODO: reduce image for preview - "thumb_url": url, - }, - "uploading": True, - }) - photo_elt.classList.add("progress_started") - album_items <= photo_elt - - bridge.file_http_upload_get_slot( - file_.name, - file_.size, - file_.type or '', - files_service, - callback=lambda upload_slot, file_=file_, photo_elt=photo_elt: - on_slot_cb(file_, upload_slot, photo_elt), - errback=lambda failure, file_=file_, photo_elt=photo_elt: - on_slot_eb(file_, failure, photo_elt), - ) +file_uploader = FileUploader(files_service, "photo/item.html", files_path) @bind("#file_drop", "drop") @@ -112,7 +36,7 @@ evt.stopPropagation() evt.preventDefault() files = evt.dataTransfer.files - upload_files(files) + file_uploader.upload_files(files, document["album_items"]) @bind("#file_drop", "dragover") @@ -125,7 +49,7 @@ @bind("#file_input", "change") def on_file_input_change(evt): files = evt.currentTarget.files - upload_files(files) + file_uploader.upload_files(files, document["album_items"]) # delete @@ -173,6 +97,8 @@ cancel_cb=lambda evt, notif_elt: delete_cancel(evt, notif_elt, item_elt, item), ) +file_uploader.on_delete_cb = on_delete + # cover async def cover_ok(evt, notif_elt, item_elt, item):