|
|
|
@ -17,14 +17,11 @@ from photoapp.types import User, Photo
|
|
|
|
|
from photoapp.utils import require_auth
|
|
|
|
|
from photoapp.common import pwhash
|
|
|
|
|
from photoapp.dbsession import DatabaseSession
|
|
|
|
|
from photoapp.thumb import ThumbGenerator
|
|
|
|
|
from photoapp.thumb import thumb_path, image_file_style
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_video_thumb(srcpath, outpath):
|
|
|
|
|
#TODO
|
|
|
|
|
# download the video file
|
|
|
|
|
# use `ffmpeg -i filename.flv` to inspect the video, parse output for duration
|
|
|
|
|
# take a thumb halfway through the video
|
|
|
|
|
#TODO limit execution time
|
|
|
|
|
|
|
|
|
|
cmd = [
|
|
|
|
|
"ffmpeg",
|
|
|
|
@ -36,6 +33,7 @@ def get_video_thumb(srcpath, outpath):
|
|
|
|
|
outpath
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
#TODO capture output and only log on error
|
|
|
|
|
check_call(cmd)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -56,13 +54,17 @@ class ThumbWorker(Thread):
|
|
|
|
|
self.engine = engine
|
|
|
|
|
self.library = library
|
|
|
|
|
self.cache = cache
|
|
|
|
|
self.thumbtool = thumbtool
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
logged_empty = False
|
|
|
|
|
while True:
|
|
|
|
|
try:
|
|
|
|
|
image_uuid, style_name = self.queue.get(block=True, timeout=5.0)
|
|
|
|
|
logged_empty = False
|
|
|
|
|
except Empty:
|
|
|
|
|
if not logged_empty:
|
|
|
|
|
logging.info("queue empty")
|
|
|
|
|
logged_empty = True
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
@ -73,6 +75,10 @@ class ThumbWorker(Thread):
|
|
|
|
|
self.do_thumb(image_uuid, style_name, s, d)
|
|
|
|
|
except:
|
|
|
|
|
traceback.print_exc() #TODO something like _failed_thumbs_cache
|
|
|
|
|
#TODO handle errors differently, like
|
|
|
|
|
# db error -> kill program
|
|
|
|
|
# filesystem error -> kill program
|
|
|
|
|
# PIL error -> ignore
|
|
|
|
|
|
|
|
|
|
def do_thumb(self, image_uuid, style_name, session, tmpdir):
|
|
|
|
|
"""
|
|
|
|
@ -85,9 +91,14 @@ class ThumbWorker(Thread):
|
|
|
|
|
logging.info("attempted invalid uuid: %s", image_uuid)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Bail if it exists in storage already
|
|
|
|
|
cache_path = thumb_path(style_name, image_uuid)
|
|
|
|
|
if self.cache.exists(cache_path):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# download the image
|
|
|
|
|
local_src_path = os.path.join(tmpdir, image.fname) # TODO fname isn't sanitized?
|
|
|
|
|
thumb_path = os.path.join(tmpdir, "thumb.jpg")
|
|
|
|
|
thumb_tmp_path = os.path.join(tmpdir, "thumb.jpg")
|
|
|
|
|
with (
|
|
|
|
|
self.library.open(image.path, "rb") as src,
|
|
|
|
|
open(local_src_path, "wb") as dest,
|
|
|
|
@ -95,12 +106,20 @@ class ThumbWorker(Thread):
|
|
|
|
|
shutil.copyfileobj(src, dest)
|
|
|
|
|
|
|
|
|
|
# generate a still from the image
|
|
|
|
|
get_video_thumb(local_src_path, thumb_path)
|
|
|
|
|
get_video_thumb(local_src_path, thumb_tmp_path)
|
|
|
|
|
|
|
|
|
|
# copy thumbnail to cache storage
|
|
|
|
|
logging.info("generated %s: %sb", thumb_tmp_path, str(os.path.getsize(thumb_tmp_path)))
|
|
|
|
|
|
|
|
|
|
# Do normal cropping of the thumb
|
|
|
|
|
thumb_cropped_path = os.path.join(tmpdir, "thumb_cropped.jpg")
|
|
|
|
|
image_file_style(thumb_tmp_path, thumb_cropped_path, style_name, image.orientation)
|
|
|
|
|
|
|
|
|
|
# TODO write thumb.jpg to thumb storage
|
|
|
|
|
os.system(f"cp {thumb_path} /Users/dave/Desktop/")
|
|
|
|
|
# copy thumbnail to cache storage
|
|
|
|
|
with (
|
|
|
|
|
open(thumb_cropped_path, 'rb') as fsrc,
|
|
|
|
|
closing(self.cache.open(cache_path, 'wb')) as fdest
|
|
|
|
|
):
|
|
|
|
|
copyfileobj(fsrc, fdest)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ThumbServiceWeb(object):
|
|
|
|
|