contact thumbserver for video thumbs
This commit is contained in:
parent
44e7bc81d6
commit
613124dbf9
|
@ -601,6 +601,7 @@ def main():
|
|||
default=int(os.environ.get("PHOTOLIB_PORT", 8080)), type=int)
|
||||
parser.add_argument('-l', '--library', default=os.environ.get("STORAGE_URL"), help="library path")
|
||||
parser.add_argument('-c', '--cache', default=os.environ.get("CACHE_URL"), help="cache url")
|
||||
parser.add_argument('-t', '--thumb-service', default=os.environ.get("THUMB_SERVICE_URL"), help="thumbnail service url")
|
||||
# https://docs.sqlalchemy.org/en/13/core/engines.html
|
||||
parser.add_argument('-s', '--database', help="sqlalchemy database connection uri",
|
||||
default=os.environ.get("DATABASE_URL")),
|
||||
|
@ -612,6 +613,9 @@ def main():
|
|||
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(level=logging.INFO if args.debug else logging.WARNING,
|
||||
format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
|
||||
|
||||
if not args.database:
|
||||
parser.error("--database or DATABASE_URL is required")
|
||||
|
||||
|
@ -621,8 +625,8 @@ def main():
|
|||
if not args.cache:
|
||||
parser.error("--cache or CACHE_URL is required")
|
||||
|
||||
logging.basicConfig(level=logging.INFO if args.debug else logging.WARNING,
|
||||
format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
|
||||
if not args.thumb_service:
|
||||
logging.warning("THUMB_SERVICE_URL not set. Video thumbnails will be unavailable")
|
||||
|
||||
# Get database connection
|
||||
engine = get_db_engine(args.database)
|
||||
|
@ -634,7 +638,7 @@ def main():
|
|||
# Create various internal tools
|
||||
library_storage = uri_to_storage(args.library)
|
||||
library_manager = LibraryManager(library_storage)
|
||||
thumbnail_tool = ThumbGenerator(library_manager, uri_to_storage(args.cache))
|
||||
thumbnail_tool = ThumbGenerator(library_manager, uri_to_storage(args.cache), args.thumb_service)
|
||||
|
||||
# Setup and mount web ui
|
||||
tpl_dir = os.path.join(APPROOT, "templates") if not args.debug else "templates"
|
||||
|
|
|
@ -4,9 +4,11 @@ import cherrypy
|
|||
import shutil
|
||||
import tempfile
|
||||
import traceback
|
||||
import requests
|
||||
from threading import Thread
|
||||
from contextlib import closing
|
||||
from queue import Queue, Empty
|
||||
from shutil import copyfileobj
|
||||
from subprocess import check_call
|
||||
from photoapp.dbutils import SAEnginePlugin, SATool, db, get_db_engine, create_db_sessionmaker
|
||||
from photoapp.storage import uri_to_storage
|
||||
|
@ -46,7 +48,7 @@ def setup_thumb_user(engine):
|
|||
|
||||
|
||||
class ThumbWorker(Thread):
|
||||
def __init__(self, engine, library, cache, thumbtool):
|
||||
def __init__(self, engine, library, cache):
|
||||
super().__init__()
|
||||
self.daemon = True
|
||||
|
||||
|
@ -120,6 +122,20 @@ class ThumbServiceWeb(object):
|
|||
yield "ok"
|
||||
|
||||
|
||||
class ThumbClient(object):
|
||||
"""
|
||||
Client for interacting with the thumbserver api
|
||||
"""
|
||||
def __init__(self, server_url):
|
||||
self.server_url = server_url
|
||||
self.session = requests.Session()
|
||||
a = requests.adapters.HTTPAdapter(max_retries=0)
|
||||
self.session.mount('http://', a)
|
||||
|
||||
def request_thumb(self, photo_uuid, style_name):
|
||||
self.session.get(self.server_url, params=dict(uuid=photo_uuid, style=style_name))
|
||||
|
||||
|
||||
# TODO dedupe me
|
||||
def validate_password(realm, username, password):
|
||||
if db.query(User).filter(User.name == username, User.password == pwhash(password)).first():
|
||||
|
@ -170,8 +186,7 @@ def main():
|
|||
library_storage = uri_to_storage(args.library)
|
||||
library_manager = LibraryManager(library_storage)
|
||||
cache_storage = uri_to_storage(args.cache)
|
||||
thumbnail_tool = ThumbGenerator(library_manager, cache_storage)
|
||||
thumbnail_worker = ThumbWorker(engine, library_storage, cache_storage, thumbnail_tool)
|
||||
thumbnail_worker = ThumbWorker(engine, library_storage, cache_storage)
|
||||
thumbnail_worker.start()
|
||||
|
||||
# Setup and mount web ui
|
||||
|
|
|
@ -3,19 +3,22 @@ from collections import defaultdict
|
|||
import tempfile
|
||||
from shutil import copyfileobj
|
||||
from contextlib import closing
|
||||
from photoapp.types import video_mimes
|
||||
from photoapp.thumb import thumb_path, image_file_style_process
|
||||
from photoapp.thumbserver import ThumbClient
|
||||
|
||||
|
||||
class ThumbGenerator(object):
|
||||
def __init__(self, library, storage):
|
||||
def __init__(self, library, storage, thumb_service_url=None):
|
||||
self.library = library
|
||||
self.storage = storage
|
||||
self.thumb_service = ThumbClient(thumb_service_url) if thumb_service_url else None
|
||||
self._failed_thumbs_cache = defaultdict(dict)
|
||||
|
||||
def make_thumb(self, photo, style_name):
|
||||
"""
|
||||
Create a thumbnail of the given photo, scaled/cropped to the given named style
|
||||
:return: local path to thumbnail file or None if creation failed or was blocked
|
||||
:return: file-like object of the thumbnail image's data or None if no thumbnail is available
|
||||
"""
|
||||
dest = thumb_path(style_name, photo.uuid)
|
||||
|
||||
|
@ -23,15 +26,23 @@ class ThumbGenerator(object):
|
|||
return self.storage.open(dest, "rb")
|
||||
|
||||
if photo.uuid in self._failed_thumbs_cache[style_name]:
|
||||
return None
|
||||
return
|
||||
|
||||
# if photo.width is None: # todo better detection of images that PIL can't open
|
||||
# return None
|
||||
# video thumbnails are handled by an external service
|
||||
if photo.format in video_mimes:
|
||||
if self.thumb_service is None: # videos thumbs capability is disabled, show placeholder
|
||||
return
|
||||
|
||||
self.make_video_thumb(photo.uuid, style_name)
|
||||
# thumbserver will eventually generate a thumb (and the exists call above will return it)
|
||||
# for now, return None to show a placeholder
|
||||
return
|
||||
|
||||
fthumblocal = tempfile.NamedTemporaryFile()
|
||||
|
||||
# TODO have the subprocess download the file
|
||||
# TODO thundering herd
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
fthumblocal = tempfile.NamedTemporaryFile(delete=True)
|
||||
fpath = os.path.join(tmpdir, "image")
|
||||
with self.library.storage.open(photo.path, 'rb') as fsrc:
|
||||
with open(fpath, 'wb') as ftmpdest:
|
||||
|
@ -45,4 +56,10 @@ class ThumbGenerator(object):
|
|||
copyfileobj(fthumblocal, fdest)
|
||||
|
||||
fthumblocal.seek(0)
|
||||
# TODO fthumblocal is leaked if we don't hit this return
|
||||
return fthumblocal
|
||||
|
||||
def make_video_thumb(self, photo_uuid, style_name):
|
||||
#TODO make something like ThumbServiceError so we can differentiate requests stuff from other errors without
|
||||
#having to deal with requests outside of thumb_service's module
|
||||
self.thumb_service.request_thumb(photo_uuid, style_name)
|
||||
|
|
Loading…
Reference in New Issue