From 6fbe49473d9eee3f828252b031d78fa038a6cd3f Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Thu, 23 Feb 2023 18:52:21 +0100 Subject: [PATCH] Annotation export to self-contained zip --- app/webserver.py | 70 ++++++++++++++++++++++++++++++++++++++++++ app/www/annotate.js | 4 +++ app/www/annotations.js | 6 ++++ 3 files changed, 80 insertions(+) diff --git a/app/webserver.py b/app/webserver.py index 8aac527..01c3043 100644 --- a/app/webserver.py +++ b/app/webserver.py @@ -2,7 +2,9 @@ import json import logging import os import shutil +import tempfile from urllib.error import HTTPError +from zipfile import ZipFile import tornado.ioloop import tornado.web import tornado.websocket @@ -240,6 +242,73 @@ class AudioListingHandler(tornado.web.RequestHandler): print(names) self.write(json.dumps(names)) +class ExportHandler(tornado.web.RequestHandler): + """ + Export a player to a zip file + """ + def initialize(self, config, index: svganim.strokes.AnnotationIndex): + self.config = config + self.index = index + + async def get(self, filename): + logger.info(f"file {filename=}") + if filename not in self.index.drawings: + raise tornado.web.HTTPError(404) + + t_in = self.get_argument('t_in', None) + t_out = self.get_argument('t_out', None) + + animation = self.index.drawings[filename].get_animation() + + if t_in is not None and t_out is not None: + animation = animation.getSlice(float(t_in), float(t_out)) + + with tempfile.TemporaryDirectory() as tdir: + with ZipFile(tdir + '/annotation.zip', 'w') as archive: + logger.info('write svg') + svgstring = animation.get_as_svg() + archive.writestr('drawing.svg', svgstring) + + logger.info('write png') + archive.writestr('drawing.png', cairosvg.svg2png(bytestring=svgstring)) + + logger.info('write mp3') + audio = await animation.audio.export(format="mp3") + archive.writestr('drawing.mp3', audio.read()) + + + logger.info('write json') + data = animation.asDict() + data['audio']['file'] = 'drawing.mp3'; + archive.writestr('annotation.json', json.dumps(data)) + + + logger.info('write js') + with open('www/annotate.js', 'r') as fp: + archive.writestr('annotate.js', fp.read()) + with open('www/assets/wNumb-1.2.0.min.js', 'r') as fp: + archive.writestr('wNumb-1.2.0.min.js', fp.read()) + + logger.info('write html') + html = """ + + + + + + + + + + """ + archive.writestr('drawing.html', html) + + with open(tdir + '/annotation.zip', 'rb') as fp: + self.set_header("Content-Type", "application/zip") + self.write(fp.read()) + logger.info('done') + + class AnimationHandler(tornado.web.RequestHandler): def initialize(self, config, index: svganim.strokes.AnnotationIndex): @@ -669,6 +738,7 @@ class Server: }, ), (r"/files/(.*)", AnimationHandler, {"config": self.config, "index": self.index}), + (r"/export/(.*)", ExportHandler, {"config": self.config, "index": self.index}), ( r"/audio/(.+)", tornado.web.StaticFileHandler, diff --git a/app/www/annotate.js b/app/www/annotate.js index bac1b56..d35849b 100644 --- a/app/www/annotate.js +++ b/app/www/annotate.js @@ -1669,6 +1669,10 @@ class AnnotationPlayer extends HTMLElement { position: absolute; background: rgba(255, 255, 255, 0.7); } + + details{ + color: white; + } summary{ list-style: none; diff --git a/app/www/annotations.js b/app/www/annotations.js index a0ab6ae..12cf0a1 100644 --- a/app/www/annotations.js +++ b/app/www/annotations.js @@ -163,6 +163,12 @@ class AnnotationManager { infoEl.classList.add('annotation-info'); infoEl.innerText = `[${tag.get_name()}] ${annotation.comment}`; + const downloadEl = document.createElement('a'); + downloadEl.href = annotation.url.replace('files', 'export'); + downloadEl.innerHTML = '↓'; + + infoEl.appendChild(downloadEl); + liEl.appendChild(playerEl); liEl.appendChild(selectEl); liEl.appendChild(infoEl);