|
|
|
@ -2,7 +2,7 @@ |
|
|
|
import cgi |
|
|
|
import cgi |
|
|
|
from dataclasses import dataclass, field |
|
|
|
from dataclasses import dataclass, field |
|
|
|
from datetime import date, datetime |
|
|
|
from datetime import date, datetime |
|
|
|
from enum import Enum |
|
|
|
from enum import IntEnum |
|
|
|
import json |
|
|
|
import json |
|
|
|
import re |
|
|
|
import re |
|
|
|
from sqlite3 import DatabaseError |
|
|
|
from sqlite3 import DatabaseError |
|
|
|
@ -37,7 +37,7 @@ bp = Blueprint('tmdb', __name__, url_prefix='/tmdb') |
|
|
|
#################### |
|
|
|
#################### |
|
|
|
# data structures |
|
|
|
# data structures |
|
|
|
|
|
|
|
|
|
|
|
class media_type(Enum): |
|
|
|
class media_type(IntEnum): |
|
|
|
UNKNOWN = 0 |
|
|
|
UNKNOWN = 0 |
|
|
|
MOVIE = 1 |
|
|
|
MOVIE = 1 |
|
|
|
TV_SHOW = 2 |
|
|
|
TV_SHOW = 2 |
|
|
|
@ -64,6 +64,7 @@ class response_data: |
|
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
|
|
@dataclass |
|
|
|
class view_result: |
|
|
|
class view_result: |
|
|
|
|
|
|
|
media_type:str = 'unknown' |
|
|
|
id: int = 0 |
|
|
|
id: int = 0 |
|
|
|
title: str = '' |
|
|
|
title: str = '' |
|
|
|
release_date: str = '' |
|
|
|
release_date: str = '' |
|
|
|
@ -162,8 +163,8 @@ def manual_query(): |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@bp.route('/query_movie_json') |
|
|
|
@bp.route('/query_media_json') |
|
|
|
def query_movie_json(): |
|
|
|
def query_media_json(): |
|
|
|
query = query_data() |
|
|
|
query = query_data() |
|
|
|
results = [] |
|
|
|
results = [] |
|
|
|
|
|
|
|
|
|
|
|
@ -191,20 +192,28 @@ def query_movie_json(): |
|
|
|
def query_path_json(): |
|
|
|
def query_path_json(): |
|
|
|
errors = [] |
|
|
|
errors = [] |
|
|
|
|
|
|
|
|
|
|
|
if 'rel_path' in request.args: |
|
|
|
if not 'rel_path' in request.args: |
|
|
|
|
|
|
|
errors.append(f'{util.getFunctionName()}, required parameters: rel_path') |
|
|
|
|
|
|
|
else: |
|
|
|
path = request.args['rel_path'] |
|
|
|
path = request.args['rel_path'] |
|
|
|
cur = db.get_db().cursor() |
|
|
|
cur = db.get_db().cursor() |
|
|
|
q = '''SELECT |
|
|
|
|
|
|
|
md.*, pc.* |
|
|
|
# NOTE: need to make an extra query to see what our id column is, tv_id or movie_id |
|
|
|
FROM |
|
|
|
mr = cur.execute('SELECT media_type FROM path_cache WHERE path=?', (path, )).fetchone() |
|
|
|
path_cache pc |
|
|
|
if mr != None: |
|
|
|
JOIN query_details md ON md.movie_id = pc.movie_id |
|
|
|
id_column = getIDColumn(mr['media_type']) |
|
|
|
WHERE |
|
|
|
|
|
|
|
pc.path = ? |
|
|
|
q = f'''SELECT |
|
|
|
LIMIT 1''' |
|
|
|
qd.*, pc.* |
|
|
|
|
|
|
|
FROM |
|
|
|
cur.execute(q, (path, )) |
|
|
|
path_cache pc |
|
|
|
row = cur.fetchone() |
|
|
|
JOIN query_details qd ON qd.{id_column} = pc.{id_column} |
|
|
|
|
|
|
|
WHERE |
|
|
|
|
|
|
|
pc.path = ? |
|
|
|
|
|
|
|
LIMIT 1''' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cur.execute(q, (path, )) |
|
|
|
|
|
|
|
row = cur.fetchone() |
|
|
|
|
|
|
|
|
|
|
|
if errors: |
|
|
|
if errors: |
|
|
|
for e in errors: |
|
|
|
for e in errors: |
|
|
|
@ -215,23 +224,28 @@ def query_path_json(): |
|
|
|
return res |
|
|
|
return res |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: add option for tv shows |
|
|
|
@bp.route('/create_path_mapping', methods=['POST']) |
|
|
|
@bp.route('/create_mapping', methods=['POST']) |
|
|
|
def create_path_mapping(): |
|
|
|
def create_mapping(): |
|
|
|
|
|
|
|
movie_id = request.form['movie_id'] |
|
|
|
|
|
|
|
rel_path = request.form['rel_path'] |
|
|
|
|
|
|
|
message = '' |
|
|
|
message = '' |
|
|
|
errors = [] |
|
|
|
errors = [] |
|
|
|
|
|
|
|
|
|
|
|
if not movie_id or not rel_path: |
|
|
|
if not ('rel_path' in request.form |
|
|
|
errors.append('invalid parameter') |
|
|
|
and 'media_type' in request.form |
|
|
|
|
|
|
|
and 'id' in request.form |
|
|
|
|
|
|
|
): |
|
|
|
|
|
|
|
errors.append(f'{util.getFunctionName()}, required parameters: rel_path, media_type, id') |
|
|
|
else: |
|
|
|
else: |
|
|
|
|
|
|
|
id = request.form['id'] |
|
|
|
|
|
|
|
media_type = request.form['media_type'] |
|
|
|
|
|
|
|
rel_path = request.form['rel_path'] |
|
|
|
|
|
|
|
id_column = getIDColumn(media_type) |
|
|
|
conn = db.get_db() |
|
|
|
conn = db.get_db() |
|
|
|
cur = conn.cursor() |
|
|
|
cur = conn.cursor() |
|
|
|
current_app.logger.debug(f'{util.getFunctionName()}, mapping path "{rel_path}" for id: {movie_id}') |
|
|
|
current_app.logger.debug(f'{util.getFunctionName()}, mapping path "{rel_path}" for id: {id}') |
|
|
|
try: |
|
|
|
try: |
|
|
|
cur.execute('INSERT INTO path_cache(movie_id, path, add_date) VALUES(?, ?, datetime())', |
|
|
|
cur.execute(f'INSERT INTO path_cache({id_column}, media_type, path, add_date) ' |
|
|
|
(movie_id, rel_path )) |
|
|
|
f'VALUES(?,?,?, datetime())', |
|
|
|
|
|
|
|
(id, media_type, rel_path )) |
|
|
|
except DatabaseError as e: |
|
|
|
except DatabaseError as e: |
|
|
|
errors.append(str(e)) |
|
|
|
errors.append(str(e)) |
|
|
|
|
|
|
|
|
|
|
|
@ -330,23 +344,33 @@ def isQueryCached(query_str): |
|
|
|
return (count > 0) |
|
|
|
return (count > 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def getIDColumn(mediaType:int) -> str: |
|
|
|
|
|
|
|
if int(mediaType) == media_type.MOVIE: |
|
|
|
|
|
|
|
return 'movie_id' |
|
|
|
|
|
|
|
elif int(mediaType) == media_type.TV_SHOW: |
|
|
|
|
|
|
|
return 'tv_id' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 'unknown' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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): |
|
|
|
conn = db.get_db() |
|
|
|
conn = db.get_db() |
|
|
|
cur = conn.cursor() |
|
|
|
cur = conn.cursor() |
|
|
|
results = [] |
|
|
|
results = [] |
|
|
|
|
|
|
|
id_column = getIDColumn(query_obj.media_type) |
|
|
|
|
|
|
|
|
|
|
|
fancy_query = '''SELECT |
|
|
|
query = f'''SELECT |
|
|
|
md.* |
|
|
|
qd.* |
|
|
|
FROM |
|
|
|
FROM |
|
|
|
query_cache mq |
|
|
|
query_cache mq |
|
|
|
JOIN query_mapping map ON mq.query_id = map.query_id |
|
|
|
JOIN query_mapping map ON mq.query_id = map.query_id |
|
|
|
JOIN query_details md ON md.movie_id = map.movie_id |
|
|
|
JOIN query_details qd ON qd.{id_column} = map.{id_column} |
|
|
|
WHERE |
|
|
|
WHERE |
|
|
|
mq.query = ?''' |
|
|
|
mq.query = ?''' |
|
|
|
|
|
|
|
|
|
|
|
for row in cur.execute(fancy_query, (query_obj.parsed_query_str, )): |
|
|
|
for row in cur.execute(query, (query_obj.parsed_query_str, )): |
|
|
|
results.append(buildViewResult(row)) |
|
|
|
results.append(buildViewResult(row)) |
|
|
|
|
|
|
|
|
|
|
|
return results |
|
|
|
return results |
|
|
|
@ -354,7 +378,8 @@ def makeLocalQuery(query_obj): |
|
|
|
|
|
|
|
|
|
|
|
def buildViewResult(db_row): |
|
|
|
def buildViewResult(db_row): |
|
|
|
vr = view_result() |
|
|
|
vr = view_result() |
|
|
|
vr.id = db_row['movie_id'] |
|
|
|
vr.media_type = db_row['media_type'] |
|
|
|
|
|
|
|
vr.id = db_row[getIDColumn(vr.media_type)] |
|
|
|
vr.title = db_row['title'] |
|
|
|
vr.title = db_row['title'] |
|
|
|
vr.overview = db_row['overview'] |
|
|
|
vr.overview = db_row['overview'] |
|
|
|
vr.release_date = db_row['release_date'] |
|
|
|
vr.release_date = db_row['release_date'] |
|
|
|
@ -367,7 +392,6 @@ def buildViewResult(db_row): |
|
|
|
return vr |
|
|
|
return vr |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: add info and debug logging for network requests |
|
|
|
|
|
|
|
# TODO: need to check response headers for rate limiting requests |
|
|
|
# TODO: need to check response headers for rate limiting requests |
|
|
|
def makeTMDBQuery(query_obj, response_obj): |
|
|
|
def makeTMDBQuery(query_obj, response_obj): |
|
|
|
if query_obj.media_type == media_type.MOVIE: |
|
|
|
if query_obj.media_type == media_type.MOVIE: |
|
|
|
@ -380,7 +404,7 @@ def makeTMDBQuery(query_obj, response_obj): |
|
|
|
|
|
|
|
|
|
|
|
current_app.logger.info( |
|
|
|
current_app.logger.info( |
|
|
|
f'{util.getFunctionName()}, making remote query to TMDB API: ' |
|
|
|
f'{util.getFunctionName()}, making remote query to TMDB API: ' |
|
|
|
f'{query_obj.media_type}, "{query_obj.parsed_query_str}"' |
|
|
|
f'{query_obj.media_type.name}, "{query_obj.parsed_query_str}"' |
|
|
|
) |
|
|
|
) |
|
|
|
request_headers = {"Accept": "text/html,application/xhtml+xml,application/xml"} |
|
|
|
request_headers = {"Accept": "text/html,application/xhtml+xml,application/xml"} |
|
|
|
query_obj.query_params = urllib.parse.urlencode( |
|
|
|
query_obj.query_params = urllib.parse.urlencode( |
|
|
|
@ -424,7 +448,7 @@ def makeTMDBQuery(query_obj, response_obj): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: @param result is parsed JSON object from tmdb API |
|
|
|
# NOTE: @param result is parsed JSON object from tmdb API |
|
|
|
def parseTMDBResult(result, media_type): |
|
|
|
def parseTMDBResult(result:object, media_type:int) -> view_result: |
|
|
|
parsed = view_result() |
|
|
|
parsed = view_result() |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
try: |
|
|
|
@ -433,6 +457,7 @@ def parseTMDBResult(result, media_type): |
|
|
|
parsed.genre_ids = result['genre_ids'] |
|
|
|
parsed.genre_ids = result['genre_ids'] |
|
|
|
parsed.original_language = result['original_language'] |
|
|
|
parsed.original_language = result['original_language'] |
|
|
|
parsed.poster_path = result['poster_path'] |
|
|
|
parsed.poster_path = result['poster_path'] |
|
|
|
|
|
|
|
parsed.media_type = media_type |
|
|
|
|
|
|
|
|
|
|
|
if media_type == media_type.MOVIE: |
|
|
|
if media_type == media_type.MOVIE: |
|
|
|
parsed.tmdb_link = TMDB_MOVIE_LINK_URL + str(result['id']) |
|
|
|
parsed.tmdb_link = TMDB_MOVIE_LINK_URL + str(result['id']) |
|
|
|
@ -465,32 +490,32 @@ def storeTMDBQueryResults(query_obj, result_list): |
|
|
|
except DatabaseError as e: |
|
|
|
except DatabaseError as e: |
|
|
|
query_obj.errors.append(e) |
|
|
|
query_obj.errors.append(e) |
|
|
|
|
|
|
|
|
|
|
|
for result in result_list: |
|
|
|
for r in result_list: |
|
|
|
|
|
|
|
id_column = getIDColumn(r.media_type) |
|
|
|
|
|
|
|
|
|
|
|
# NOTE: check stored details before trying to add to local db to avoid unique key |
|
|
|
# NOTE: check stored details before trying to add to local db to avoid unique key |
|
|
|
# constraint error |
|
|
|
# constraint error |
|
|
|
cur.execute('SELECT COUNT(*) FROM query_details WHERE movie_id=?', (result.id,)) |
|
|
|
cur.execute(f'SELECT COUNT(*) FROM query_details WHERE {id_column}=?', (r.id, )) |
|
|
|
count = cur.fetchone()[0] |
|
|
|
count = cur.fetchone()[0] |
|
|
|
if count > 0: |
|
|
|
if count > 0: |
|
|
|
current_app.logger.debug(f'{util.getFunctionName()}, ' |
|
|
|
current_app.logger.debug(f'{util.getFunctionName()}, ' |
|
|
|
f'skipping result that is already saved. id: {result.id}, title: {result.title}' |
|
|
|
f'skipping result that is already saved. id: {r.id}, title: {r.title}' |
|
|
|
) |
|
|
|
) |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
try: |
|
|
|
current_app.logger.debug(f'{util.getFunctionName()}, adding query details for "{result.title}"') |
|
|
|
current_app.logger.debug(f'{util.getFunctionName()}, adding query details for "{r.title}"') |
|
|
|
# TODO: need to add tv_id to query_cache |
|
|
|
cur.execute(f'INSERT INTO query_details({id_column}, ' |
|
|
|
cur.execute("INSERT INTO query_details(" |
|
|
|
+ 'media_type, title, overview, release_date, original_language, poster_path)' |
|
|
|
+ "movie_id, title, overview, release_date, original_language, poster_path)" |
|
|
|
+ ' VALUES (?,?,?,?,?,?,?)', |
|
|
|
+ " VALUES (?,?,?,?,?,?)", |
|
|
|
(r.id, r.media_type, r.title, r.overview, r.release_date, r.original_language, r.poster_path) |
|
|
|
(result.id, result.title, result.overview, result.release_date, |
|
|
|
|
|
|
|
result.original_language, result.poster_path) |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
cur.execute("INSERT INTO query_mapping(query_id, movie_id)" |
|
|
|
cur.execute(f'INSERT INTO query_mapping(query_id, {id_column}, media_type)' |
|
|
|
+ " VALUES (?,?)", (query_id, result.id) |
|
|
|
+ ' VALUES (?,?,?)', (query_id, r.id, r.media_type) |
|
|
|
) |
|
|
|
) |
|
|
|
except DatabaseError as e: |
|
|
|
except DatabaseError as e: |
|
|
|
query_obj.errors.append(f'{util.getFunctionName()}, database error \'{e}\'') |
|
|
|
query_obj.errors.append(f'{util.getFunctionName()}():{util.getLineNum()}, database error \'{e}\'') |
|
|
|
|
|
|
|
|
|
|
|
if len(query_obj.errors) == 0: |
|
|
|
if len(query_obj.errors) == 0: |
|
|
|
conn.commit() |
|
|
|
conn.commit() |
|
|
|
|