diff --git a/video_metadata/views/tmdb.py b/video_metadata/views/tmdb.py index 869e47d..d714f5c 100644 --- a/video_metadata/views/tmdb.py +++ b/video_metadata/views/tmdb.py @@ -12,7 +12,7 @@ from urllib.error import URLError, HTTPError import urllib.parse import urllib.request -from flask import Blueprint, current_app, flash, Flask, request, render_template +from flask import Blueprint, current_app, flash, Flask, jsonify, request, render_template, Response from .. import util from .. import db @@ -73,6 +73,18 @@ class view_result: poster_path: str = 'no_image.png' tmdb_link: str = '' +# NOTE: haven't found a better way to json encode dataclasses :( +class result_encoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, view_result): + return obj.__dict__ + + try: + ret = json.JSONEncoder.default(self, obj) + except TypeError: + return '' + return ret + #################### # flask routes @@ -102,11 +114,11 @@ def list_movie_test(): url_prefix = current_app.config['VIDEO_URL_PREFIX'] ) + @bp.route('/query_movie', methods=['GET', 'POST']) def query_movie(): errors = [] query = query_data() - response = response_data() view_results = [] # NOTE: query string can be submitted from a link or form input @@ -118,32 +130,9 @@ def query_movie(): query.raw_query_str = request.args['query'] query.rel_path = request.args['rel_path'] - # TODO: if query_obj.query_type is still 'UNKNOWN' from parseQueryStr(), make queries to - # both tmdb endpoints (movie and tv) - if query.raw_query_str and parseQueryStr(query): - if isQueryCached(query.parsed_query_str): - view_results = makeLocalQuery(query) - else: - makeTMDBQuery(query, response) - - try: - response.parsed_response = json.loads(response.response_body) - except json.JSONDecodeError as e: - view_data.errors.append(e) - - if 'total_results' in response.parsed_response: - response.result_count = response.parsed_response['total_results'] - - # TODO: it's possible for the query results to span multiple 'pages' - # see) https://developers.themoviedb.org/3/search/search-movies - if 'results' in response.parsed_response: - for result in response.parsed_response['results']: - view_results.append(parseTMDBResult(result, query.query_type)) - - storeTMDBQueryResults(query, view_results) - - errors.extend(query.errors) - errors.extend(response.errors) + q_res = doQueryAlgorithm(query) + errors = q_res['errors'] + view_results = q_res['results'] if errors: view_results.clear() @@ -155,14 +144,40 @@ def query_movie(): 'tmdb/query.html', view_data = view_results, rel_path = query.rel_path, - debug_str = '{} \n {} \n {} \n'.format( + debug_str = '{} \n {} \n'.format( util.dumpObject(query), - util.dumpObject(response), util.dumpObject(view_results) ) ) + +@bp.route('/query_movie_json') +def query_movie_json(): + query = query_data() + results = [] + + if 'query' in request.args and 'rel_path' in request.args: + query.raw_query_str = request.args['query'] + query.rel_path = request.args['rel_path'] + q_res = doQueryAlgorithm(query) + errors = q_res['errors'] + results = q_res['results'] + current_app.logger.debug(f'{util.getFunctionName()}, results length: {len(results)}') + else: + errors = ['required parameters: query, rel_path'] + + if errors: + for e in errors: + current_app.logger.error(e) + return jsonify(errors=errors) + + res = Response(json.dumps(results, cls=result_encoder)) + res.headers.set('Content-Type', 'application/json') + return res + + # TODO: add option for tv shows +# TODO: this endpoint should be http POST only @bp.route('/create_mapping') def create_mapping(): movie_id = request.args.get('movie_id') @@ -197,6 +212,41 @@ def create_mapping(): ################### # helpers +def doQueryAlgorithm(query:query_data) -> list: + errors, view_results = [], [] + response = response_data() + + # TODO: if query_obj.query_type is still 'UNKNOWN' from parseQueryStr(), make queries to + # both tmdb endpoints (movie and tv) + if query.raw_query_str and parseQueryStr(query): + if isQueryCached(query.parsed_query_str): + view_results = makeLocalQuery(query) + current_app.logger.debug(f'{util.getFunctionName()}, view_results length: {len(view_results)}') + else: + makeTMDBQuery(query, response) + + try: + response.parsed_response = json.loads(response.response_body) + except json.JSONDecodeError as e: + errors.append(e) + + if 'total_results' in response.parsed_response: + response.result_count = response.parsed_response['total_results'] + + # TODO: it's possible for the query results to span multiple 'pages' + # see) https://developers.themoviedb.org/3/search/search-movies + if 'results' in response.parsed_response: + for result in response.parsed_response['results']: + view_results.append(parseTMDBResult(result, query.query_type)) + + storeTMDBQueryResults(query, view_results) + + errors.extend(query.errors) + errors.extend(response.errors) + + return {'errors': errors, 'results': view_results} + + # TODO: need a way to correctly parse titles like: "2001: A Space Odyssey (1968)" # with legitimate dates in the title def parseQueryStr(query_obj): @@ -225,16 +275,19 @@ def parseQueryStr(query_obj): return True + def isQueryCached(query_str): conn = db.get_db() cur = conn.cursor() cur.execute('SELECT COUNT(*) FROM movie_queries WHERE query=?', (query_str,)) count = cur.fetchone()[0] - - current_app.logger.debug('movie_queries count: "{}, query: {}"'.format(count, query_str)) + current_app.logger.debug( + f'{util.getFunctionName()}, movie_queries count: {count}, query: "{query_str}"' + ) return (count > 0) + # NOTE: returns list of 'view_result' objects that match the query_obj.parsed_query_str # stored in the local db cache def makeLocalQuery(query_obj): @@ -256,6 +309,7 @@ def makeLocalQuery(query_obj): return results + def buildViewResult(db_row): vr = view_result() vr.id = db_row['movie_id'] @@ -270,6 +324,8 @@ def buildViewResult(db_row): return vr + +# TODO: add info and debug logging for network requests def makeTMDBQuery(query_obj, response_obj): if query_obj.query_type == query_type.MOVIE: search_part = TMDB_SEARCH_MOVIE @@ -319,6 +375,7 @@ def makeTMDBQuery(query_obj, response_obj): return True + # NOTE: @param result is parsed JSON object from tmdb API def parseTMDBResult(result, query_type): parsed = view_result() @@ -338,6 +395,7 @@ def parseTMDBResult(result, query_type): return parsed + def storeTMDBQueryResults(query_obj, result_list): conn = db.get_db() cur = conn.cursor()