diff --git a/video_metadata/views/tmdb.py b/video_metadata/views/tmdb.py index 637a158..c8c0f56 100644 --- a/video_metadata/views/tmdb.py +++ b/video_metadata/views/tmdb.py @@ -4,6 +4,8 @@ from dataclasses import dataclass, field from datetime import date, datetime from enum import IntEnum import json +import os +from pathlib import Path import re from sqlite3 import DatabaseError import ssl @@ -12,8 +14,8 @@ from urllib.error import URLError, HTTPError import urllib.parse import urllib.request -from flask import Blueprint, current_app, flash, Flask, jsonify, request, redirect, \ - render_template, Response, url_for +from flask import Blueprint, current_app, flash, Flask, jsonify, make_response, request, \ + redirect, render_template, Response, url_for from .. import util from .. import db @@ -31,6 +33,7 @@ TMDB_TV_LINK_URL = 'https://www.themoviedb.org/tv/' TMDB_POSTER_PREFIX = 'https://image.tmdb.org/t/p/w154' VALID_IMAGE_EXTENSIONS = ('.jpg', '.png', '.svg') DEFAULT_POSTER_IMAGE = 'no_poster_w154.jpg' +LOCAL_POSTER_PATH = 'image_cache' #################### @@ -78,7 +81,7 @@ class view_result: overview: str = '' genre_ids: List[int] = field(default_factory=list) original_language: str = '' - poster_path: str = 'no_image.png' + poster_path: str = '' tmdb_link: str = '' # TODO: flask 1.1 adds support for JSON encoding dataclasses @@ -279,26 +282,54 @@ def create_path_mapping(): return jsonify({'errors':errors, 'message':message}) +@bp.route('/images') @bp.route('/images/') def cache_image(image_name:str = ''): - current_app.logger.debug(f'image_name: {image_name}') - - if image_name != '': - s = re.sub(r'/', '', image_name) - ext = re.compile(r'.[a-zA-Z]{3}$').search(image_name).group(0) - print(f'ext: "{ext}"') - - if not ext in VALID_IMAGE_EXTENSIONS: - print('oops') - else: - print(f'seems okay: {ext}') - - url = f'{TMDB_POSTER_PREFIX}/{s}' - - #res = Response(json.dumps(results, cls=result_encoder)) - #res.headers.set('Content-Type', 'application/json') - #return str(f'image_name: {image_name}, url: {url}') - return redirect(url_for('static', filename=DEFAULT_POSTER_IMAGE)) + # validate image name + if (image_name == '' + or re.search(r'[^ a-zA-z0-9./]', image_name) + or not Path(image_name).suffix in VALID_IMAGE_EXTENSIONS): + current_app.logger.error(f'invalid image name: {image_name}') + return redirect(url_for('static', filename=DEFAULT_POSTER_IMAGE)) + + # look for image in cache folder + local_dir = Path(current_app.instance_path).joinpath(LOCAL_POSTER_PATH) + image = Path(image_name).name + p = local_dir.joinpath(image) + current_app.logger.debug(f'checking for local path: {p}') + + if not p.exists(): + # make request for remote image + response = util.URLResponse + + if util.getURL(f'{TMDB_POSTER_PREFIX}/{image}', response, current_app.logger): + # store remote image + current_app.logger.debug(f'content-type for {image}: {response.content_type}') + try: + fp = open(p, 'wb') + fp.write(response.body) + fp.close() + except OSError as e: + current_app.logger.error(e) + return redirect(url_for('static', filename=DEFAULT_POSTER_IMAGE)) + + # redirect to saved image + try: + fp = open(p, 'rb') + image_bytes = fp.read() + except OSError as e: + current_app.logger.error(e) + return redirect(url_for('static', filename=DEFAULT_POSTER_IMAGE)) + + def getCType(s): + if s is '.jpg': + return 'image/jpeg' + elif s is '.png': + return 'image/png' + elif s is '.svg': + return 'image/svg+xml' + + return make_response(tuple([image_bytes, {'Content-Type': getCType(p.suffix)} ])) ###################