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", "title": "Extension",
"type": "string", "type": "string",
"columnWidth": 80, "columnWidth": 80,
"sort": true, "sort": true
"find": true
}, },
{ {
"id": "size", "id": "size",

View File

@ -70,7 +70,7 @@ def autocompleteFolder(request):
folder, name = os.path.split(path) folder, name = os.path.split(path)
if os.path.exists(folder): if os.path.exists(folder):
prefix, folders, files = os.walk(folder).next() 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: if prefix == path:
folders = [path] + folders folders = [path] + folders
else: else:

View File

@ -24,10 +24,10 @@ class Downloads(Thread):
for i in item.models.Item.query.filter( for i in item.models.Item.query.filter(
item.models.Item.transferadded!=None).filter( item.models.Item.transferadded!=None).filter(
item.models.Item.transferprogress<1).order_by(item.models.Item.transferadded): item.models.Item.transferprogress<1).order_by(item.models.Item.transferadded):
logger.debug('DOWNLOAD %s %s', i, i.users) for u in i.users:
for p in i.users: if state.nodes.check_online(u.id):
if state.nodes.check_online(p.id): logger.debug('DOWNLOAD %s %s', i, u)
r = state.nodes.download(p.id, i) r = state.nodes.download(u.id, i)
logger.debug('download ok? %s', r) logger.debug('download ok? %s', r)
return True return True
return False return False

View File

@ -318,7 +318,7 @@ class Item(db.Model):
if cover: if cover:
img = Image.open(StringIO(cover)) img = Image.open(StringIO(cover))
self.meta['coverRatio'] = img.size[0]/img.size[1] 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)] del covers['%s%s' % (self.id, p)]
return cover return cover
@ -381,6 +381,8 @@ class Item(db.Model):
user = state.user() user = state.user()
if user in self.users: if user in self.users:
self.users.remove(user) self.users.remove(user)
for l in self.lists.filter_by(user_id=user.id):
l.items.remove(self)
db.session.commit() db.session.commit()
if not self.users: if not self.users:
db.session.delete(self) db.session.delete(self)

View File

@ -2,22 +2,51 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division from __future__ import division
import sys import os
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import zipfile import zipfile
from StringIO import StringIO from StringIO import StringIO
import re
import Image import Image
import stdnum.isbn import stdnum.isbn
from utils import normalize_isbn, find_isbns from utils import normalize_isbn, find_isbns
import logging
logger = logging.getLogger('oml.media.epub')
def cover(path): def cover(path):
img = Image.new('RGB', (80, 128)) logger.debug('cover %s', path)
o = StringIO() z = zipfile.ZipFile(path)
img.save(o, format='jpeg') data = None
data = o.getvalue() for f in z.filelist:
o.close() 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 return data
def info(epub): def info(epub):

View File

@ -2,18 +2,19 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
from __future__ import division from __future__ import division
import sys
import os import os
from utils import find_isbns from utils import find_isbns
from StringIO import StringIO import tempfile
import Image import subprocess
def cover(path): def cover(path):
img = Image.new('RGB', (80, 128)) image = tempfile.mkstemp('.jpg')[1]
o = StringIO() cmd = ['python2', 'static/txt.js/txt.py', '-i', path, '-o', image]
img.save(o, format='jpeg') p = subprocess.Popen(cmd)
data = o.getvalue() p.wait()
o.close() with open(image, 'rb') as fd:
data = fd.read()
os.unlink(image)
return data return data
def info(path): def info(path):

View File

@ -17,6 +17,7 @@ import json
from utils import valid, get_public_ipv6 from utils import valid, get_public_ipv6
import nodeapi import nodeapi
import cert import cert
from websocket import trigger_event
import logging import logging
logger = logging.getLogger('oml.node.server') logger = logging.getLogger('oml.node.server')
@ -109,12 +110,7 @@ class ShareHandler(tornado.web.RequestHandler):
def publish_node(app): def publish_node(app):
host = get_public_ipv6() update_online()
state.online = directory.put(settings.sk, {
'host': host,
'port': settings.server['node_port'],
'cert': settings.server['cert']
})
if state.online: if state.online:
with app.app_context(): with app.app_context():
for u in user.models.User.query.filter_by(queued=True): 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.nodes.queue('add', u.id)
state.check_nodes = PeriodicCallback(lambda: check_nodes(app), 120000) state.check_nodes = PeriodicCallback(lambda: check_nodes(app), 120000)
state.check_nodes.start() 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): def check_nodes(app):
if state.online: if state.online:

View File

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

View File

@ -45,7 +45,7 @@ def get_by_id(objects, id):
return get_by_key(objects, 'id', id) return get_by_key(objects, 'id', id)
def resize_image(data, width=None, size=None): 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_width = source.size[0]
source_height = source.size[1] source_height = source.size[1]
if size: if size:
@ -106,11 +106,15 @@ def valid(key, value, sig):
return True return True
def get_public_ipv6(): def get_public_ipv6():
host = ('2a01:4f8:120:3201::3', 25519) try:
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) host = ('2a01:4f8:120:3201::3', 25519)
s.connect(host) s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
ip = s.getsockname()[0] s.settimeout(1)
s.close() s.connect(host)
ip = s.getsockname()[0]
s.close()
except:
ip = None
return ip return ip
def update_dict(root, data): def update_dict(root, data):

View File

@ -285,7 +285,7 @@ oml.URL = (function() {
// pushes a new URL (as string or from state) // pushes a new URL (as string or from state)
that.push = function(stateOrURL, expandURL) { that.push = function(stateOrURL, expandURL) {
var state, var state,
title = oml.getPageTitle(stateOrURL) title = oml.getPageTitle(stateOrURL),
url; url;
oml.replaceURL = expandURL; oml.replaceURL = expandURL;
if (Ox.isObject(stateOrURL)) { if (Ox.isObject(stateOrURL)) {

View File

@ -4,7 +4,7 @@ oml.ui.infoView = function(identifyData) {
var ui = oml.user.ui, var ui = oml.user.ui,
css = getCSS(ui.coverSize), css = getCSS(ui.coverSize, oml.config.coverRatio),
that = Ox.Element() that = Ox.Element()
.addClass('OxTextPage') .addClass('OxTextPage')
@ -39,6 +39,7 @@ oml.ui.infoView = function(identifyData) {
right: !identifyData ? '176px' : 16 + Ox.UI.SCROLLBAR_SIZE + 'px', right: !identifyData ? '176px' : 16 + Ox.UI.SCROLLBAR_SIZE + 'px',
top: '16px' top: '16px'
}) })
[ui.coverSize == 512 ? 'hide' : 'show']()
.appendTo(that), .appendTo(that),
$data, $data,
@ -232,13 +233,6 @@ oml.ui.infoView = function(identifyData) {
$image.animate(css.image, 250); $image.animate(css.image, 250);
$reflectionImage.animate(css.image, 250); $reflectionImage.animate(css.image, 250);
$reflection.animate(css.reflection, 250); $reflection.animate(css.reflection, 250);
/*
$reflectionGradient.animate({
width: iconSize + 'px',
height: iconSize / 2 + 'px'
}, 250);
*/
oml.UI.set({coverSize: coverSize}); oml.UI.set({coverSize: coverSize});
} }
@ -291,9 +285,19 @@ oml.ui.infoView = function(identifyData) {
tooltip: '' // TODO tooltip: '' // TODO
}) })
.on({ .on({
error: function() {
if (size == 512) {
$info.show();
}
},
load: function() { load: function() {
ratio = $image[0].width / $image[0].height; ratio = $image[0].width / $image[0].height;
updateCover(ratio); updateCover(ratio);
if (size == 512) {
$info.css({
left: getCSS(512, ratio).info.left
}).show();
}
} }
}) })
.attr({src: src}) .attr({src: src})

View File

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

View File

@ -122,6 +122,17 @@ oml.clearFilters = function() {
oml.UI.set({find: find}); 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() { (function() {
oml.doHistory = function(action, items, targets, callback) { oml.doHistory = function(action, items, targets, callback) {
@ -894,6 +905,15 @@ oml.hasDialogOrScreen = function() {
|| !!$('.OxScreen').length; || !!$('.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.reloadList = function() {
oml.$ui.list.updateElement(); oml.$ui.list.updateElement();
}; };
@ -934,6 +954,7 @@ oml.resizeWindow = function() {
oml.$ui.leftPanel && oml.$ui.leftPanel.size(2, oml.getInfoHeight()); oml.$ui.leftPanel && oml.$ui.leftPanel.size(2, oml.getInfoHeight());
oml.resizeListFolders(); oml.resizeListFolders();
oml.$ui.rightPanel && oml.$ui.rightPanel.updateElement(); oml.$ui.rightPanel && oml.$ui.rightPanel.updateElement();
oml.$ui.list && oml.$ui.list.size();
}; };
oml.updateFilterMenus = function() { 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.