covers/links

This commit is contained in:
j 2014-05-19 20:12:02 +02:00
parent 75a14fed1e
commit b6daa19d73
15 changed files with 209 additions and 44 deletions

View File

@ -110,8 +110,7 @@
"title": "Extension",
"type": "string",
"columnWidth": 80,
"sort": true,
"find": true
"sort": true
},
{
"id": "size",

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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:

View File

@ -2,6 +2,7 @@ websockets = []
nodes = False
main = None
online = False
host = None
activity = {}

View File

@ -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):

View File

@ -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)) {

View File

@ -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})

View File

@ -142,4 +142,4 @@ oml.ui.list = function() {
return that;
};
};

View File

@ -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
View 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

Binary file not shown.