Annotation export to self-contained zip
This commit is contained in:
parent
8be08ce9d6
commit
6fbe49473d
3 changed files with 80 additions and 0 deletions
|
@ -2,7 +2,9 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import tempfile
|
||||||
from urllib.error import HTTPError
|
from urllib.error import HTTPError
|
||||||
|
from zipfile import ZipFile
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
import tornado.web
|
import tornado.web
|
||||||
import tornado.websocket
|
import tornado.websocket
|
||||||
|
@ -240,6 +242,73 @@ class AudioListingHandler(tornado.web.RequestHandler):
|
||||||
print(names)
|
print(names)
|
||||||
self.write(json.dumps(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 = """
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="wNumb-1.2.0.min.js"></script>
|
||||||
|
<script src="annotate.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<annotation-player data-poster-url="drawing.svg" data-annotation-url="annotation.json">
|
||||||
|
</body>
|
||||||
|
</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):
|
class AnimationHandler(tornado.web.RequestHandler):
|
||||||
def initialize(self, config, index: svganim.strokes.AnnotationIndex):
|
def initialize(self, config, index: svganim.strokes.AnnotationIndex):
|
||||||
|
@ -669,6 +738,7 @@ class Server:
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(r"/files/(.*)", AnimationHandler, {"config": self.config, "index": self.index}),
|
(r"/files/(.*)", AnimationHandler, {"config": self.config, "index": self.index}),
|
||||||
|
(r"/export/(.*)", ExportHandler, {"config": self.config, "index": self.index}),
|
||||||
(
|
(
|
||||||
r"/audio/(.+)",
|
r"/audio/(.+)",
|
||||||
tornado.web.StaticFileHandler,
|
tornado.web.StaticFileHandler,
|
||||||
|
|
|
@ -1670,6 +1670,10 @@ class AnnotationPlayer extends HTMLElement {
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
summary{
|
summary{
|
||||||
list-style: none;
|
list-style: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -163,6 +163,12 @@ class AnnotationManager {
|
||||||
infoEl.classList.add('annotation-info');
|
infoEl.classList.add('annotation-info');
|
||||||
infoEl.innerText = `[${tag.get_name()}] ${annotation.comment}`;
|
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(playerEl);
|
||||||
liEl.appendChild(selectEl);
|
liEl.appendChild(selectEl);
|
||||||
liEl.appendChild(infoEl);
|
liEl.appendChild(infoEl);
|
||||||
|
|
Loading…
Reference in a new issue