covers/links
This commit is contained in:
parent
75a14fed1e
commit
b6daa19d73
15 changed files with 209 additions and 44 deletions
|
@ -110,8 +110,7 @@
|
|||
"title": "Extension",
|
||||
"type": "string",
|
||||
"columnWidth": 80,
|
||||
"sort": true,
|
||||
"find": true
|
||||
"sort": true
|
||||
},
|
||||
{
|
||||
"id": "size",
|
||||
|
|
|
@ -70,7 +70,7 @@ def autocompleteFolder(request):
|
|||
folder, name = os.path.split(path)
|
||||
if os.path.exists(folder):
|
||||
prefix, folders, files = os.walk(folder).next()
|
||||
folders = [os.path.join(prefix, f) for f in folders if not name or f.startswith(name)]
|
||||
folders = [os.path.join(prefix, f) for f in folders if (not name or f.startswith(name)) and not f.startswith('.')]
|
||||
if prefix == path:
|
||||
folders = [path] + folders
|
||||
else:
|
||||
|
|
|
@ -24,10 +24,10 @@ class Downloads(Thread):
|
|||
for i in item.models.Item.query.filter(
|
||||
item.models.Item.transferadded!=None).filter(
|
||||
item.models.Item.transferprogress<1).order_by(item.models.Item.transferadded):
|
||||
logger.debug('DOWNLOAD %s %s', i, i.users)
|
||||
for p in i.users:
|
||||
if state.nodes.check_online(p.id):
|
||||
r = state.nodes.download(p.id, i)
|
||||
for u in i.users:
|
||||
if state.nodes.check_online(u.id):
|
||||
logger.debug('DOWNLOAD %s %s', i, u)
|
||||
r = state.nodes.download(u.id, i)
|
||||
logger.debug('download ok? %s', r)
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -318,7 +318,7 @@ class Item(db.Model):
|
|||
if cover:
|
||||
img = Image.open(StringIO(cover))
|
||||
self.meta['coverRatio'] = img.size[0]/img.size[1]
|
||||
for p in (':128', ':256'):
|
||||
for p in (':128', ':256', ':512'):
|
||||
del covers['%s%s' % (self.id, p)]
|
||||
return cover
|
||||
|
||||
|
@ -381,6 +381,8 @@ class Item(db.Model):
|
|||
user = state.user()
|
||||
if user in self.users:
|
||||
self.users.remove(user)
|
||||
for l in self.lists.filter_by(user_id=user.id):
|
||||
l.items.remove(self)
|
||||
db.session.commit()
|
||||
if not self.users:
|
||||
db.session.delete(self)
|
||||
|
|
|
@ -2,22 +2,51 @@
|
|||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
from StringIO import StringIO
|
||||
import re
|
||||
|
||||
import Image
|
||||
import stdnum.isbn
|
||||
|
||||
from utils import normalize_isbn, find_isbns
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('oml.media.epub')
|
||||
|
||||
def cover(path):
|
||||
img = Image.new('RGB', (80, 128))
|
||||
o = StringIO()
|
||||
img.save(o, format='jpeg')
|
||||
data = o.getvalue()
|
||||
o.close()
|
||||
logger.debug('cover %s', path)
|
||||
z = zipfile.ZipFile(path)
|
||||
data = None
|
||||
for f in z.filelist:
|
||||
if 'cover' in f.filename and f.filename.split('.')[-1] in ('jpg', 'jpeg', 'png'):
|
||||
logger.debug('using %s', f.filename)
|
||||
data = z.read(f.filename)
|
||||
break
|
||||
if not data:
|
||||
opf = [f.filename for f in z.filelist if f.filename.endswith('opf')]
|
||||
if opf:
|
||||
info = ET.fromstring(z.read(opf[0]))
|
||||
manifest = info.findall('{http://www.idpf.org/2007/opf}manifest')[0]
|
||||
for e in manifest.getchildren():
|
||||
if 'html' in e.attrib['media-type']:
|
||||
filename = e.attrib['href']
|
||||
filename = os.path.normpath(os.path.join(os.path.dirname(opf[0]), filename))
|
||||
html = z.read(filename)
|
||||
img = re.compile('<img.*?src="(.*?)"').findall(html)
|
||||
if img:
|
||||
img = os.path.normpath(os.path.join(os.path.dirname(filename), img[0]))
|
||||
logger.debug('using %s', img)
|
||||
data = z.read(img)
|
||||
break
|
||||
if not data:
|
||||
img = Image.new('RGB', (80, 128))
|
||||
o = StringIO()
|
||||
img.save(o, format='jpeg')
|
||||
data = o.getvalue()
|
||||
o.close()
|
||||
return data
|
||||
|
||||
def info(epub):
|
||||
|
|
|
@ -2,18 +2,19 @@
|
|||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
import os
|
||||
from utils import find_isbns
|
||||
from StringIO import StringIO
|
||||
import Image
|
||||
import tempfile
|
||||
import subprocess
|
||||
|
||||
def cover(path):
|
||||
img = Image.new('RGB', (80, 128))
|
||||
o = StringIO()
|
||||
img.save(o, format='jpeg')
|
||||
data = o.getvalue()
|
||||
o.close()
|
||||
image = tempfile.mkstemp('.jpg')[1]
|
||||
cmd = ['python2', 'static/txt.js/txt.py', '-i', path, '-o', image]
|
||||
p = subprocess.Popen(cmd)
|
||||
p.wait()
|
||||
with open(image, 'rb') as fd:
|
||||
data = fd.read()
|
||||
os.unlink(image)
|
||||
return data
|
||||
|
||||
def info(path):
|
||||
|
|
|
@ -17,6 +17,7 @@ import json
|
|||
from utils import valid, get_public_ipv6
|
||||
import nodeapi
|
||||
import cert
|
||||
from websocket import trigger_event
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('oml.node.server')
|
||||
|
@ -109,12 +110,7 @@ class ShareHandler(tornado.web.RequestHandler):
|
|||
|
||||
|
||||
def publish_node(app):
|
||||
host = get_public_ipv6()
|
||||
state.online = directory.put(settings.sk, {
|
||||
'host': host,
|
||||
'port': settings.server['node_port'],
|
||||
'cert': settings.server['cert']
|
||||
})
|
||||
update_online()
|
||||
if state.online:
|
||||
with app.app_context():
|
||||
for u in user.models.User.query.filter_by(queued=True):
|
||||
|
@ -122,6 +118,32 @@ def publish_node(app):
|
|||
state.nodes.queue('add', u.id)
|
||||
state.check_nodes = PeriodicCallback(lambda: check_nodes(app), 120000)
|
||||
state.check_nodes.start()
|
||||
state._online = PeriodicCallback(update_online, 60000)
|
||||
state._online.start()
|
||||
|
||||
def update_online():
|
||||
host = get_public_ipv6()
|
||||
if not host:
|
||||
if state.online:
|
||||
state.online = False
|
||||
trigger_event('status', {
|
||||
'id': settings.USER_ID,
|
||||
'online': state.online
|
||||
})
|
||||
else:
|
||||
if host != state.host:
|
||||
state.host = host
|
||||
online = directory.put(settings.sk, {
|
||||
'host': host,
|
||||
'port': settings.server['node_port'],
|
||||
'cert': settings.server['cert']
|
||||
})
|
||||
if online != state.online:
|
||||
state.online = online
|
||||
trigger_event('status', {
|
||||
'id': settings.USER_ID,
|
||||
'online': state.online
|
||||
})
|
||||
|
||||
def check_nodes(app):
|
||||
if state.online:
|
||||
|
|
|
@ -2,6 +2,7 @@ websockets = []
|
|||
nodes = False
|
||||
main = None
|
||||
online = False
|
||||
host = None
|
||||
|
||||
activity = {}
|
||||
|
||||
|
|
16
oml/utils.py
16
oml/utils.py
|
@ -45,7 +45,7 @@ def get_by_id(objects, id):
|
|||
return get_by_key(objects, 'id', id)
|
||||
|
||||
def resize_image(data, width=None, size=None):
|
||||
source = Image.open(StringIO(data)).convert('RGB')
|
||||
source = Image.open(StringIO(data)) #.convert('RGB')
|
||||
source_width = source.size[0]
|
||||
source_height = source.size[1]
|
||||
if size:
|
||||
|
@ -106,11 +106,15 @@ def valid(key, value, sig):
|
|||
return True
|
||||
|
||||
def get_public_ipv6():
|
||||
host = ('2a01:4f8:120:3201::3', 25519)
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
s.connect(host)
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
try:
|
||||
host = ('2a01:4f8:120:3201::3', 25519)
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
s.settimeout(1)
|
||||
s.connect(host)
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
ip = None
|
||||
return ip
|
||||
|
||||
def update_dict(root, data):
|
||||
|
|
|
@ -285,7 +285,7 @@ oml.URL = (function() {
|
|||
// pushes a new URL (as string or from state)
|
||||
that.push = function(stateOrURL, expandURL) {
|
||||
var state,
|
||||
title = oml.getPageTitle(stateOrURL)
|
||||
title = oml.getPageTitle(stateOrURL),
|
||||
url;
|
||||
oml.replaceURL = expandURL;
|
||||
if (Ox.isObject(stateOrURL)) {
|
||||
|
|
|
@ -4,7 +4,7 @@ oml.ui.infoView = function(identifyData) {
|
|||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
css = getCSS(ui.coverSize),
|
||||
css = getCSS(ui.coverSize, oml.config.coverRatio),
|
||||
|
||||
that = Ox.Element()
|
||||
.addClass('OxTextPage')
|
||||
|
@ -39,6 +39,7 @@ oml.ui.infoView = function(identifyData) {
|
|||
right: !identifyData ? '176px' : 16 + Ox.UI.SCROLLBAR_SIZE + 'px',
|
||||
top: '16px'
|
||||
})
|
||||
[ui.coverSize == 512 ? 'hide' : 'show']()
|
||||
.appendTo(that),
|
||||
|
||||
$data,
|
||||
|
@ -232,13 +233,6 @@ oml.ui.infoView = function(identifyData) {
|
|||
$image.animate(css.image, 250);
|
||||
$reflectionImage.animate(css.image, 250);
|
||||
$reflection.animate(css.reflection, 250);
|
||||
/*
|
||||
$reflectionGradient.animate({
|
||||
width: iconSize + 'px',
|
||||
height: iconSize / 2 + 'px'
|
||||
}, 250);
|
||||
*/
|
||||
|
||||
oml.UI.set({coverSize: coverSize});
|
||||
}
|
||||
|
||||
|
@ -291,9 +285,19 @@ oml.ui.infoView = function(identifyData) {
|
|||
tooltip: '' // TODO
|
||||
})
|
||||
.on({
|
||||
error: function() {
|
||||
if (size == 512) {
|
||||
$info.show();
|
||||
}
|
||||
},
|
||||
load: function() {
|
||||
ratio = $image[0].width / $image[0].height;
|
||||
updateCover(ratio);
|
||||
if (size == 512) {
|
||||
$info.css({
|
||||
left: getCSS(512, ratio).info.left
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
})
|
||||
.attr({src: src})
|
||||
|
|
|
@ -122,6 +122,17 @@ oml.clearFilters = function() {
|
|||
oml.UI.set({find: find});
|
||||
};
|
||||
|
||||
oml.clickLink = function(e) {
|
||||
if (
|
||||
e.target.hostname == document.location.hostname
|
||||
&& !Ox.startsWith(e.target.pathname, '/static')
|
||||
) {
|
||||
oml.URL.push(e.target.pathname, true);
|
||||
} else {
|
||||
oml.openLink(e.target.href);
|
||||
}
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
oml.doHistory = function(action, items, targets, callback) {
|
||||
|
@ -894,6 +905,15 @@ oml.hasDialogOrScreen = function() {
|
|||
|| !!$('.OxScreen').length;
|
||||
};
|
||||
|
||||
oml.openLink = function(url) {
|
||||
if (Ox.startsWith(url, 'mailto:')) {
|
||||
window.open(url);
|
||||
} else {
|
||||
//window.open('/url=' + encodeURIComponent(url), '_blank');
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
oml.reloadList = function() {
|
||||
oml.$ui.list.updateElement();
|
||||
};
|
||||
|
@ -934,6 +954,7 @@ oml.resizeWindow = function() {
|
|||
oml.$ui.leftPanel && oml.$ui.leftPanel.size(2, oml.getInfoHeight());
|
||||
oml.resizeListFolders();
|
||||
oml.$ui.rightPanel && oml.$ui.rightPanel.updateElement();
|
||||
oml.$ui.list && oml.$ui.list.size();
|
||||
};
|
||||
|
||||
oml.updateFilterMenus = function() {
|
||||
|
|
82
static/txt.js/txt.py
Executable file
82
static/txt.js/txt.py
Executable file
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
from __future__ import division
|
||||
import os
|
||||
|
||||
import Image
|
||||
from optparse import OptionParser
|
||||
from ox.image import drawText, wrapText
|
||||
from zipfile import ZipFile
|
||||
|
||||
|
||||
root_dir = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
|
||||
os.chdir(root_dir)
|
||||
|
||||
|
||||
def render(infile, outfile):
|
||||
|
||||
with open(infile) as f:
|
||||
|
||||
image_size = (768, 1024)
|
||||
margin = 64
|
||||
offset = margin
|
||||
font_file = 'txt.ttf'
|
||||
font_size = 24
|
||||
line_height = 32
|
||||
max_lines = (image_size[1] - 2 * margin) / line_height
|
||||
|
||||
image = Image.new('L', image_size, (255))
|
||||
|
||||
for line in f:
|
||||
|
||||
line = line.decode('utf-8').strip()
|
||||
|
||||
lines = wrapText(
|
||||
line,
|
||||
image_size[0] - 2 * margin,
|
||||
# we don't want the last line that ends with an ellipsis
|
||||
max_lines + 1,
|
||||
'txt.ttf',
|
||||
font_size
|
||||
)
|
||||
|
||||
for line_ in lines:
|
||||
drawText(
|
||||
image,
|
||||
(margin, offset),
|
||||
line_,
|
||||
font_file,
|
||||
font_size,
|
||||
(0)
|
||||
)
|
||||
offset += line_height
|
||||
max_lines -= 1
|
||||
|
||||
if max_lines == 0:
|
||||
break
|
||||
|
||||
if max_lines == 0:
|
||||
break
|
||||
|
||||
image.save(outfile)
|
||||
|
||||
|
||||
def main():
|
||||
parser = OptionParser()
|
||||
parser.add_option(
|
||||
'-i', '--infile', dest='infile', help='txt file to be read'
|
||||
)
|
||||
parser.add_option(
|
||||
'-o', '--outfile', dest='outfile', help='jpg file to be written'
|
||||
)
|
||||
(options, args) = parser.parse_args()
|
||||
if None in (options.infile, options.outfile):
|
||||
parser.print_help()
|
||||
else:
|
||||
render(options.infile, options.outfile)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
BIN
static/txt.js/txt.ttf
Normal file
BIN
static/txt.js/txt.ttf
Normal file
Binary file not shown.
Loading…
Reference in a new issue