|
|
|
@ -12,7 +12,7 @@ from urllib.error import URLError, HTTPError |
|
|
|
import urllib.parse |
|
|
|
import urllib.parse |
|
|
|
import urllib.request |
|
|
|
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 util |
|
|
|
from .. import db |
|
|
|
from .. import db |
|
|
|
@ -73,6 +73,18 @@ class view_result: |
|
|
|
poster_path: str = 'no_image.png' |
|
|
|
poster_path: str = 'no_image.png' |
|
|
|
tmdb_link: str = '' |
|
|
|
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 |
|
|
|
# flask routes |
|
|
|
@ -102,11 +114,11 @@ def list_movie_test(): |
|
|
|
url_prefix = current_app.config['VIDEO_URL_PREFIX'] |
|
|
|
url_prefix = current_app.config['VIDEO_URL_PREFIX'] |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/query_movie', methods=['GET', 'POST']) |
|
|
|
@bp.route('/query_movie', methods=['GET', 'POST']) |
|
|
|
def query_movie(): |
|
|
|
def query_movie(): |
|
|
|
errors = [] |
|
|
|
errors = [] |
|
|
|
query = query_data() |
|
|
|
query = query_data() |
|
|
|
response = response_data() |
|
|
|
|
|
|
|
view_results = [] |
|
|
|
view_results = [] |
|
|
|
|
|
|
|
|
|
|
|
# NOTE: query string can be submitted from a link or form input |
|
|
|
# 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.raw_query_str = request.args['query'] |
|
|
|
query.rel_path = request.args['rel_path'] |
|
|
|
query.rel_path = request.args['rel_path'] |
|
|
|
|
|
|
|
|
|
|
|
# TODO: if query_obj.query_type is still 'UNKNOWN' from parseQueryStr(), make queries to |
|
|
|
q_res = doQueryAlgorithm(query) |
|
|
|
# both tmdb endpoints (movie and tv) |
|
|
|
errors = q_res['errors'] |
|
|
|
if query.raw_query_str and parseQueryStr(query): |
|
|
|
view_results = q_res['results'] |
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if errors: |
|
|
|
if errors: |
|
|
|
view_results.clear() |
|
|
|
view_results.clear() |
|
|
|
@ -155,14 +144,40 @@ def query_movie(): |
|
|
|
'tmdb/query.html', |
|
|
|
'tmdb/query.html', |
|
|
|
view_data = view_results, |
|
|
|
view_data = view_results, |
|
|
|
rel_path = query.rel_path, |
|
|
|
rel_path = query.rel_path, |
|
|
|
debug_str = '{} \n {} \n {} \n'.format( |
|
|
|
debug_str = '{} \n {} \n'.format( |
|
|
|
util.dumpObject(query), |
|
|
|
util.dumpObject(query), |
|
|
|
util.dumpObject(response), |
|
|
|
|
|
|
|
util.dumpObject(view_results) |
|
|
|
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: add option for tv shows |
|
|
|
|
|
|
|
# TODO: this endpoint should be http POST only |
|
|
|
@bp.route('/create_mapping') |
|
|
|
@bp.route('/create_mapping') |
|
|
|
def create_mapping(): |
|
|
|
def create_mapping(): |
|
|
|
movie_id = request.args.get('movie_id') |
|
|
|
movie_id = request.args.get('movie_id') |
|
|
|
@ -197,6 +212,41 @@ def create_mapping(): |
|
|
|
################### |
|
|
|
################### |
|
|
|
# helpers |
|
|
|
# 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)" |
|
|
|
# TODO: need a way to correctly parse titles like: "2001: A Space Odyssey (1968)" |
|
|
|
# with legitimate dates in the title |
|
|
|
# with legitimate dates in the title |
|
|
|
def parseQueryStr(query_obj): |
|
|
|
def parseQueryStr(query_obj): |
|
|
|
@ -225,16 +275,19 @@ def parseQueryStr(query_obj): |
|
|
|
|
|
|
|
|
|
|
|
return True |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def isQueryCached(query_str): |
|
|
|
def isQueryCached(query_str): |
|
|
|
conn = db.get_db() |
|
|
|
conn = db.get_db() |
|
|
|
cur = conn.cursor() |
|
|
|
cur = conn.cursor() |
|
|
|
cur.execute('SELECT COUNT(*) FROM movie_queries WHERE query=?', (query_str,)) |
|
|
|
cur.execute('SELECT COUNT(*) FROM movie_queries WHERE query=?', (query_str,)) |
|
|
|
count = cur.fetchone()[0] |
|
|
|
count = cur.fetchone()[0] |
|
|
|
|
|
|
|
current_app.logger.debug( |
|
|
|
current_app.logger.debug('movie_queries count: "{}, query: {}"'.format(count, query_str)) |
|
|
|
f'{util.getFunctionName()}, movie_queries count: {count}, query: "{query_str}"' |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
return (count > 0) |
|
|
|
return (count > 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: returns list of 'view_result' objects that match the query_obj.parsed_query_str |
|
|
|
# NOTE: returns list of 'view_result' objects that match the query_obj.parsed_query_str |
|
|
|
# stored in the local db cache |
|
|
|
# stored in the local db cache |
|
|
|
def makeLocalQuery(query_obj): |
|
|
|
def makeLocalQuery(query_obj): |
|
|
|
@ -256,6 +309,7 @@ def makeLocalQuery(query_obj): |
|
|
|
|
|
|
|
|
|
|
|
return results |
|
|
|
return results |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def buildViewResult(db_row): |
|
|
|
def buildViewResult(db_row): |
|
|
|
vr = view_result() |
|
|
|
vr = view_result() |
|
|
|
vr.id = db_row['movie_id'] |
|
|
|
vr.id = db_row['movie_id'] |
|
|
|
@ -270,6 +324,8 @@ def buildViewResult(db_row): |
|
|
|
|
|
|
|
|
|
|
|
return vr |
|
|
|
return vr |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: add info and debug logging for network requests |
|
|
|
def makeTMDBQuery(query_obj, response_obj): |
|
|
|
def makeTMDBQuery(query_obj, response_obj): |
|
|
|
if query_obj.query_type == query_type.MOVIE: |
|
|
|
if query_obj.query_type == query_type.MOVIE: |
|
|
|
search_part = TMDB_SEARCH_MOVIE |
|
|
|
search_part = TMDB_SEARCH_MOVIE |
|
|
|
@ -319,6 +375,7 @@ def makeTMDBQuery(query_obj, response_obj): |
|
|
|
|
|
|
|
|
|
|
|
return True |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: @param result is parsed JSON object from tmdb API |
|
|
|
# NOTE: @param result is parsed JSON object from tmdb API |
|
|
|
def parseTMDBResult(result, query_type): |
|
|
|
def parseTMDBResult(result, query_type): |
|
|
|
parsed = view_result() |
|
|
|
parsed = view_result() |
|
|
|
@ -338,6 +395,7 @@ def parseTMDBResult(result, query_type): |
|
|
|
|
|
|
|
|
|
|
|
return parsed |
|
|
|
return parsed |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def storeTMDBQueryResults(query_obj, result_list): |
|
|
|
def storeTMDBQueryResults(query_obj, result_list): |
|
|
|
conn = db.get_db() |
|
|
|
conn = db.get_db() |
|
|
|
cur = conn.cursor() |
|
|
|
cur = conn.cursor() |
|
|
|
|