openmedialibrary/oml/item/icons.py

242 lines
6.7 KiB
Python
Raw Normal View History

2014-05-04 19:26:43 +02:00
# -*- coding: utf-8 -*-
import os
2014-08-12 10:16:57 +02:00
import sqlite3
2019-01-18 19:06:39 +05:30
from concurrent.futures import ThreadPoolExecutor
2014-05-19 23:39:52 +02:00
2014-08-12 10:16:57 +02:00
import tornado.concurrent
import tornado.gen
2014-05-19 23:39:52 +02:00
import tornado.ioloop
import tornado.web
2018-08-26 16:03:48 +02:00
from tornado.concurrent import run_on_executor
2019-01-18 19:06:39 +05:30
import ox
2018-08-26 16:03:48 +02:00
2019-01-18 19:06:39 +05:30
from settings import static_path
from utils import resize_image, is_svg
2014-08-09 18:14:14 +02:00
import db
2014-05-04 19:26:43 +02:00
2014-05-21 02:02:21 +02:00
import logging
2015-11-29 15:56:38 +01:00
logger = logging.getLogger(__name__)
2014-05-21 02:02:21 +02:00
2018-08-26 16:03:48 +02:00
MAX_WORKERS = 4
2014-05-21 02:02:21 +02:00
class Icons(dict):
2019-01-25 18:42:49 +05:30
2014-05-04 19:26:43 +02:00
def __init__(self, db):
self._db = db
2014-05-17 11:24:17 +02:00
self.create()
2014-05-04 19:26:43 +02:00
2019-01-25 18:42:49 +05:30
def is_available(self):
folder = os.path.dirname(self._db)
2019-01-30 16:35:44 +05:30
base = os.path.dirname(os.path.dirname(folder))
if os.path.exists(base):
if not os.path.exists(folder):
ox.makedirs(folder)
2019-01-25 18:42:49 +05:30
if not os.path.exists(self._db):
self.create()
return os.path.exists(self._db)
2014-05-04 19:26:43 +02:00
def connect(self):
2016-01-29 22:21:13 +05:30
conn = sqlite3.connect(self._db, timeout=90)
2014-05-17 11:24:17 +02:00
return conn
2014-05-04 19:26:43 +02:00
def create(self):
2019-01-25 18:42:49 +05:30
folder = os.path.dirname(self._db)
if os.path.exists(folder):
conn = self.connect()
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS icon (id varchar(64) unique, data blob)')
c.execute('CREATE TABLE IF NOT EXISTS setting (key varchar(256) unique, value text)')
if int(self.get_setting(c, 'version', 0)) < 1:
self.set_setting(c, 'version', 1)
2014-05-04 19:26:43 +02:00
def get_setting(self, c, key, default=None):
2014-09-03 00:32:44 +02:00
c.execute('SELECT value FROM setting WHERE key = ?', (key, ))
2014-05-04 19:26:43 +02:00
for row in c:
return row[0]
return default
def set_setting(self, c, key, value):
2014-09-03 00:32:44 +02:00
c.execute('INSERT OR REPLACE INTO setting values (?, ?)', (key, str(value)))
2014-05-04 19:26:43 +02:00
def default_cover(self):
with open(os.path.join(static_path, 'png', 'cover.png'), 'rb') as f:
data = f.read()
2014-05-04 19:26:43 +02:00
return data
def __getitem__(self, id, default=None):
2014-09-03 00:32:44 +02:00
sql = 'SELECT data FROM icon WHERE id=?'
2014-05-17 11:24:17 +02:00
conn = self.connect()
c = conn.cursor()
2014-05-04 19:26:43 +02:00
c.execute(sql, (id, ))
data = default
for row in c:
data = row[0]
break
c.close()
2014-05-17 11:24:17 +02:00
conn.close()
2014-05-04 19:26:43 +02:00
return data
def __setitem__(self, id, data):
2014-09-03 00:32:44 +02:00
sql = 'INSERT OR REPLACE INTO icon values (?, ?)'
try:
conn = self.connect()
c = conn.cursor()
data = sqlite3.Binary(data)
c.execute(sql, (id, data))
conn.commit()
c.close()
conn.close()
except:
2019-01-22 19:13:12 +05:30
logger.debug('failed to insert icon %s (%s)', id, self._db, exc_info=True)
2014-05-04 19:26:43 +02:00
def __delitem__(self, id):
2014-09-03 00:32:44 +02:00
sql = 'DELETE FROM icon WHERE id = ?'
try:
conn = self.connect()
c = conn.cursor()
c.execute(sql, (id, ))
conn.commit()
c.close()
conn.close()
except:
2019-01-22 19:13:12 +05:30
logger.debug('failed to delete icon %s (%s)', id, self._db)
2014-05-04 19:26:43 +02:00
2016-01-16 10:47:52 +05:30
def clear(self, prefix):
try:
conn = self.connect()
c = conn.cursor()
sql = 'DELETE FROM icon WHERE id = ?'
for size in (64, 128, 256, 512, 1024):
id = '%s%s' % (prefix, size)
c.execute(sql, (id, ))
conn.commit()
c.close()
conn.close()
except:
2019-01-24 18:19:01 +05:30
logger.debug('failed to clear icon %s', prefix)
def vacuum(self, ids):
conn = self.connect()
c = conn.cursor()
sql = 'SELECT id from icon'
c.execute(sql)
icons = [row[0] for row in c]
sql = 'DELETE FROM icon WHERE id = ?'
for i in icons:
id = i.split(':')[1]
if id not in ids:
c.execute(sql, (id, ))
conn.commit()
sql = 'VACUUM'
c.execute(sql)
conn.commit()
c.close()
conn.close()
2016-01-16 10:47:52 +05:30
2019-01-18 19:06:39 +05:30
def get_icons_db_path():
import settings
import shutil
2019-01-30 22:22:16 +05:30
library = os.path.normpath(os.path.expanduser(settings.preferences['libraryPath']))
metadata = os.path.join(library, 'Metadata')
2019-01-18 19:06:39 +05:30
icons_db_path = os.path.join(metadata, 'icons.db')
if os.path.exists(os.path.dirname(library)):
ox.makedirs(metadata)
old_icons_db_path = os.path.join(settings.data_path, 'icons.db')
if not os.path.exists(icons_db_path) and os.path.exists(old_icons_db_path):
shutil.move(old_icons_db_path, icons_db_path)
2019-01-18 19:06:39 +05:30
return icons_db_path
2014-05-19 23:39:52 +02:00
def get_icon_sync(id, type_, size):
2019-01-25 18:42:49 +05:30
if not icons.is_available():
return ''
2014-05-26 12:11:13 +02:00
if size:
skey = '%s:%s:%s' % (type_, id, size)
data = icons[skey]
if data:
return bytes(data)
2014-05-26 12:11:13 +02:00
key = '%s:%s' % (type_, id)
2014-05-27 11:59:23 +02:00
data = icons[key]
if is_svg(data):
return bytes(data)
2014-05-27 11:59:23 +02:00
if not data:
2014-05-26 12:11:13 +02:00
type_ = 'preview' if type_ == 'cover' else 'cover'
2014-05-27 11:59:23 +02:00
key = '%s:%s' % (type_, id)
2014-05-26 12:11:13 +02:00
if size:
skey = '%s:%s:%s' % (type_, id, size)
if size:
data = icons[skey]
if data:
size = None
if not data:
data = icons[key]
if not data:
skey = '%s:%s:%s' % ('default', 'cover', size)
if size:
data = icons[skey]
if data:
size = None
if not data:
data = icons.default_cover()
if size:
try:
data = resize_image(data, size=size)
except:
logger.debug('failed to resize default cover %s %s %s', id, size, skey)
data = None
if data:
icons[skey] = data
2014-05-26 12:11:13 +02:00
size = None
if size:
try:
data = resize_image(data, size=size)
except:
logger.debug('failed to resize %s %s %s', id, size, skey)
data = None
if data:
icons[skey] = data
if data:
data = bytes(data)
else:
data = ''
return data
def clear_default_cover_cache():
2016-01-16 10:47:52 +05:30
icons.clear('default:cover:')
2014-05-19 23:39:52 +02:00
2014-05-21 02:02:21 +02:00
class IconHandler(tornado.web.RequestHandler):
2018-08-26 16:03:48 +02:00
executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
2014-05-19 23:39:52 +02:00
2014-08-09 19:06:06 +02:00
def initialize(self):
pass
2014-05-19 23:39:52 +02:00
2018-08-26 16:03:48 +02:00
@run_on_executor
def get_icon(self, id, type_, size):
return get_icon_sync(id, type_, size)
2014-05-19 23:39:52 +02:00
@tornado.gen.coroutine
2014-05-21 02:02:21 +02:00
def get(self, id, type_, size=None):
size = int(size) if size else None
if type_ not in ('cover', 'preview'):
2014-05-25 22:32:00 +02:00
self.set_status(404)
2014-05-21 02:02:21 +02:00
return
self.set_header('Content-Type', 'image/jpeg')
2018-08-26 16:03:48 +02:00
response = yield self.get_icon(id, type_, size)
2014-05-21 02:02:21 +02:00
if not response:
2014-05-25 22:32:00 +02:00
self.set_status(404)
2014-05-21 02:02:21 +02:00
return
if self._finished:
return
self.write(response)
2019-01-18 19:06:39 +05:30
icons = Icons(get_icons_db_path())