added the Games tab as well as Basic access authentication

This commit is contained in:
Matthew Welch 2020-05-06 14:34:38 -07:00
parent 2a5f7ae67f
commit 51c6b5e572
10 changed files with 326 additions and 35 deletions

View File

@ -142,6 +142,7 @@ def get_comic_page(comic_id, page_number):
response.headers["cache-control"] = "public"
date = pytz.utc.localize(datetime.datetime.utcfromtimestamp(os.path.getmtime(meta.path)))
response.headers["last-modified"] = date.strftime('%a, %d %b %Y %H:%M:%S %Z')
response.headers["Content-Disposition"] = "attachment; filename=\"{} {}_{}{}\"".format(str(meta.series), meta.issuetext, str(page_number), filetype.guess(byte_image).extension)
return response
@ -155,4 +156,5 @@ def get_comic_thumbnail(comic_id, page_number):
date = pytz.utc.localize(datetime.datetime.utcfromtimestamp(os.path.getmtime(meta.path)))
response.headers["last-modified"] = date.strftime('%a, %d %b %Y %H:%M:%S %Z')
response.headers["content-type"] = thumb.type
response.headers["Content-Disposition"] = "attachment; filename=\"{} {}_{}_thumbnail\"".format(str(meta.series), meta.issuetext, str(page_number))
return response

91
games/games.py Normal file
View File

@ -0,0 +1,91 @@
from flask import Blueprint, render_template, request, send_file, current_app, jsonify, abort
from flask_login import login_required
from pathvalidate import sanitize_filename
import os
import inspect
import re
from scripts import database, func
Games = Blueprint("games", __name__, template_folder="templates")
@Games.route("/games")
@login_required
def index():
try:
page = request.args.get("page", 1, type=int)
max_items = request.args.get("max_items", 30, type=int)
games = database.get_all_games()
start = (max_items*(page-1))
end = len(games) if len(games) < max_items*page else max_items*page
return render_template("games/index.html", title="Games", games=games)
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(type(e)) + " " + str(e)
@Games.route('/games/get_games')
@login_required
def get_games():
try:
games = database.get_all_games()
games_json = {}
for game in games:
games_json[game.game_id] = {
"id": game.game_id,
"title": game.title,
"windows": game.windows,
"mac": game.mac,
"linux": game.linux,
"description": game.description,
"poster_path": game.poster_path
}
return jsonify(games_json)
# return jsonify({game["id"]: game for game in games})
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(type(e)) + " " + str(e)
@Games.route('/games/get_game/<int:game_id>')
@login_required
def get_game(game_id):
try:
game = database.get_game(game_id)
if game:
game_json = {
"title": game.title,
"game_id": game.game_id,
"description": game.description,
"poster_path": game.poster_path,
"windows": game.windows,
"mac": game.mac,
"linux": game.linux
}
return jsonify(game_json)
abort(404)
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(type(e)) + " " + str(e)
@Games.route("/games/download/<int:game_id>")
@login_required
def download_game(game_id):
try:
game = database.get_game(game_id)
if game:
files = game.windows["files"]
filename = sanitize_filename(files[0])
folder = re.match(r"(.+)_setup_win.(exe|msi)", filename).group(1)
if len(files) > 1:
filename = sanitize_filename(game.title+".zip")
path = os.path.join(func.GAMES_DIRECTORY, folder, filename)
return send_file(path, as_attachment=True, attachment_filename=filename)
else:
abort(404)
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(type(e)) + " " + str(e)

View File

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
{% for game in games %}
<a class="btn btn-primary" href="{{ url_for("games.index") }}/download/{{ game.game_id }}">Download</a>
<p>{{ game.title }} - {{ game.game_id }}</p>
{% endfor %}
{% endblock %}

View File

@ -1,5 +1,5 @@
from flask import Flask
from flask import render_template, request, g, redirect, url_for, flash, current_app
from flask import render_template, request, g, redirect, url_for, flash, current_app, Response
from flask_login import LoginManager, current_user, login_user, logout_user, login_required
from oauthlib.oauth2 import WebApplicationClient
@ -12,12 +12,14 @@ import datetime
import requests
import json
import os
import base64
import scripts.func as func
from scripts import database
from admin import admin
from comics import comics
from tv_movies import tv_movies
from games import games
class NullHandler(logging.Handler):
@ -46,11 +48,14 @@ app = Flask(__name__)
app.register_blueprint(comics.Comics)
app.register_blueprint(admin.Admin)
app.register_blueprint(tv_movies.TV_Movies)
app.register_blueprint(games.Games)
app.config["SECRET_KEY"] = "***REMOVED***"
app.logger.setLevel("DEBUG")
# app.use_x_sendfile = True
login_manager = LoginManager(app)
login_manager.login_view = "login"
app.config["REMEMBER_COOKIE_DOMAIN"] = "narnian.us"
MOBILE_DEVICES = ["android", "blackberry", "ipad", "iphone"]
@ -91,9 +96,13 @@ def get_tv_shows():
func.get_tv_episode(file_path)
def get_games():
with app.app_context():
func.get_games()
with app.app_context():
current_app.logger.info("server start")
#database.initialize_db()
thread = threading.Thread(target=get_comics, args=())
thread.daemon = True
thread.start()
@ -103,6 +112,9 @@ with app.app_context():
thread3 = threading.Thread(target=get_tv_shows, args=())
thread3.daemon = True
thread3.start()
thread4 = threading.Thread(target=get_games, args=())
thread4.daemon = True
thread4.start()
@app.teardown_appcontext
@ -140,10 +152,18 @@ def update_tv_episodes_db(sender, **kw):
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
def update_games_db(sender, **kw):
try:
database.add_games(kw["games"])
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
func.comic_loaded.connect(update_comic_db)
func.movie_loaded.connect(update_movie_db)
func.tv_show_loaded.connect(update_tv_show_db)
func.tv_episodes_loaded.connect(update_tv_episodes_db)
func.games_loaded.connect(update_games_db)
@login_manager.user_loader
@ -155,6 +175,27 @@ def load_user(email):
return str(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
@login_manager.request_loader
def load_user_from_request(request):
try:
api_key = request.headers.get('Authorization')
if api_key:
api_key = api_key.replace('Basic ', '', 1)
try:
api_key = base64.b64decode(api_key).decode("utf-8")
except TypeError:
pass
email = api_key.split(":")[0]
password = api_key.split(":")[1]
user = load_user(email)
if user and user.check_password(password):
return user
return None
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
@app.route("/login", methods=["GET", "POST"])
def login():
try:
@ -267,16 +308,47 @@ def home():
return str(e)
@app.after_request
def apply_headers(response: Response):
try:
user_name = "anonymous"
if current_user:
user_name = getattr(current_user, "email", "anonymous")
response.set_cookie("rpi_name", user_name)
return response
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(e)
@app.route("/music")
@login_required
def music():
return "No music"
@app.route("/games")
@login_required
def games():
return "No Games"
@app.errorhandler(404)
def resource_not_found(e):
try:
return render_template("404.html"), 404
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return str(e)
@app.errorhandler(Exception)
def handle_exception(e):
return render_template("error.html", e=e), 500
games.Games.register_error_handler(404, resource_not_found)
tv_movies.TV_Movies.register_error_handler(404, resource_not_found)
comics.Comics.register_error_handler(404, resource_not_found)
admin.Admin.register_error_handler(404, resource_not_found)
games.Games.register_error_handler(Exception, handle_exception)
tv_movies.TV_Movies.register_error_handler(Exception, handle_exception)
comics.Comics.register_error_handler(Exception, handle_exception)
admin.Admin.register_error_handler(Exception, handle_exception)
if __name__ == '__main__':

View File

@ -7,13 +7,12 @@ from wand.image import Image
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy import Column, Integer, String, BLOB, Boolean, DateTime, Numeric, func, over, ARRAY, TIMESTAMP
from sqlalchemy import Column, Integer, String, BLOB, Boolean, DateTime, Numeric, func, over, ARRAY, TIMESTAMP, JSON
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.pool import NullPool
from sqlalchemy.sql.expression import cast
import sqlalchemy
import sqlite3
import psycopg2
import os
import inspect
import logging
@ -240,6 +239,27 @@ class DupComic(Base):
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
class Game(Base):
__tablename__ = "games"
title = Column(String)
game_id = Column(Integer, primary_key=True)
description = Column(String)
poster_path = Column(String)
windows = Column(JSON)
mac = Column(JSON)
linux = Column(JSON)
def __init__(self, data):
i = 0
try:
for column in self.__table__.columns:
setattr(self, column.name, data[i])
i += 1
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
class User(Base, UserMixin):
__tablename__ = "users"
@ -335,13 +355,13 @@ class TvMovieKeywords(Base):
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))"""
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = psycopg2.connect(host="bombur", database="rpiwebapp", user="rpiwebapp", password="hello").cursor()
#db.row_factory = sqlite3.Row
return db
# def get_db():
# db = getattr(g, '_database', None)
# if db is None:
# db = g._database = psycopg2.connect(host="bombur", database="rpiwebapp", user="rpiwebapp", password="hello").cursor()
#
# #db.row_factory = sqlite3.Row
# return db
def get_imdb():
@ -454,6 +474,14 @@ def add_comics(meta, thumbnails):
current_app.logger.info("{} comic{} added".format(len(meta), "s" if len(meta)>1 or len(meta)<1 else ""))
def add_games(games):
session = Session()
for game_data in games:
game = Game(game_data)
session.add(game)
session.commit()
def db_get_all_comics():
session = Session()
result = session.query(Comic).all()
@ -549,6 +577,17 @@ def tv_episode_path_in_db(path):
return False
def game_in_db(game_id):
try:
session = Session()
result = session.query(Game).filter(Game.game_id == game_id).one_or_none()
if result:
return True
except Exception as e:
current_app.logger.info(inspect.stack()[0][3] + " " + str(type(e)) + " " + str(e))
return False
def imdb_get_movie(title, year):
row = get_imdb().execute("SELECT tconst, runtimeMinutes FROM title_basics WHERE (originalTitle LIKE ? OR primaryTitle LIKE ?) AND (titleType LIKE '%movie' OR titleType='video') AND startYear=?", (title, title, year)).fetchone()
return row
@ -718,6 +757,18 @@ def db_get_current_tv_show_episode_and_data(parent_imdb_id, episodes):
return most_recent_episode, most_recent_data
def get_all_games():
session = Session()
result = session.query(Game).order_by(Game.title).all()
return result
def get_game(game_id):
session = Session()
result = session.query(Game).filter(Game.game_id == game_id).one_or_none()
return result
def db_search_table_columns_by_query(query, table, columns, group=[], order=[]):
session = Session()
results = {}
@ -811,21 +862,21 @@ def resize_image(image, new_width=256, new_height=256):
return new_image
def fix_thumbnails():
new_height = 256
new_width = 256
print("Start fix thumbnail size")
rows = get_db().execute("SELECT * FROM rpiwebapp.comic_thumbnails")
print("got list of all thumbnails\n")
for row in rows:
image = Image(file=BytesIO(row["image"]))
if image.width > new_width or image.height > new_height:
print("id:", row["id"], "pageNumber:", row["pageNumber"])
get_db().execute("UPDATE rpiwebapp.comic_thumbnails SET image=? WHERE comic_id=? AND pageNumber=?",
[resize_image(image, new_width, new_height).make_blob(), row["id"], row["pageNumber"]])
get_db().commit()
print("Finished fix thumbnail size")
# def fix_thumbnails():
# new_height = 256
# new_width = 256
# print("Start fix thumbnail size")
# rows = get_db().execute("SELECT * FROM rpiwebapp.comic_thumbnails")
# print("got list of all thumbnails\n")
#
# for row in rows:
# image = Image(file=BytesIO(row["image"]))
# if image.width > new_width or image.height > new_height:
# print("id:", row["id"], "pageNumber:", row["pageNumber"])
# get_db().execute("UPDATE rpiwebapp.comic_thumbnails SET image=? WHERE comic_id=? AND pageNumber=?",
# [resize_image(image, new_width, new_height).make_blob(), row["id"], row["pageNumber"]])
# get_db().commit()
# print("Finished fix thumbnail size")
def get_user(email):

View File

@ -9,6 +9,7 @@ import os, re
import inspect
import json
import enzyme
import requests
from scripts import database
@ -17,6 +18,7 @@ comic_loaded = rpi_signals.signal("comic-loaded")
movie_loaded = rpi_signals.signal("movie-loaded")
tv_show_loaded = rpi_signals.signal("tv_show_loaded")
tv_episodes_loaded = rpi_signals.signal("tv_episodes_loaded")
games_loaded = rpi_signals.signal("games_loaded")
publishers_to_ignore = ["***REMOVED***"]
@ -26,16 +28,18 @@ RPI_COMICS_DIRECTORY = "/usb/storage/media/Comics/"
RPI_MOVIES_DIRECTORY = "/usb/storage/media/Videos/Movies/"
RPI_TV_SHOWS_DIRECTORY = "/usb/storage/media/Videos/TV/"
RPI_VIDEOS_DIRECTORY = "/usb/storage/media/Videos/Videos/"
RPI_GAMES_DIRECTORY = "/usb/storage/media/games/"
RPI_GAMES_DIRECTORY = "/usb/storage/Games/"
RPI_MUSIC_DIRECTORY = "/usb/storage/media/Music/"
MC_COMICS_DIRECTORY = "/mnt/c/Users/Matthew/Documents/Comics"
MC_MOVIES_DIRECTORY = "/mnt/c/Users/Matthew/Documents/Movies"
MC_TV_SHOWS_DIRECTORY = "/mnt/c/Users/Matthew/Documents/TV"
MC_COMICS_DIRECTORY = "/mnt/c/Users/Matthew/Documents/Comics/"
MC_MOVIES_DIRECTORY = "/mnt/c/Users/Matthew/Documents/Movies/"
MC_TV_SHOWS_DIRECTORY = "/mnt/c/Users/Matthew/Documents/TV/"
MC_GAMES_DIRECTORY = "/mnt/g/Humble Bundle/rpi/"
COMICS_DIRECTORY = RPI_COMICS_DIRECTORY if os.path.exists(RPI_COMICS_DIRECTORY) else MC_COMICS_DIRECTORY
MOVIES_DIRECTORY = RPI_MOVIES_DIRECTORY if os.path.exists(RPI_MOVIES_DIRECTORY) else MC_MOVIES_DIRECTORY
TV_SHOWS_DIRECTORY = RPI_TV_SHOWS_DIRECTORY if os.path.exists(RPI_TV_SHOWS_DIRECTORY) else MC_TV_SHOWS_DIRECTORY
GAMES_DIRECTORY = RPI_GAMES_DIRECTORY if os.path.exists(RPI_GAMES_DIRECTORY) else MC_GAMES_DIRECTORY
#############
@ -423,3 +427,52 @@ def get_tags(path):
if simple.name == "SUMMARY":
mkv_info["movie"]["summary"] = simple.string
return mkv_info
def get_games():
games = []
cover_url = "https://api-v3.igdb.com/covers"
games_url = "https://api-v3.igdb.com/games"
headers = {
"accept": "application/json",
"user-key": "641f7f0e3af5273dcc1105ce851ea804"
}
i = 0
for folder in sorted(os.listdir(GAMES_DIRECTORY), key=str.casefold):
root = os.path.join(GAMES_DIRECTORY, folder)
if os.path.isdir(os.path.join(root)):
path = os.path.join(root, "info.json")
with open(path, "r") as f:
info = json.load(f)
game_id = info["id"]
if not database.game_in_db(game_id):
current_app.logger.info(f"start loading game: {info['name']}:{info['id']}")
data = f"fields summary;limit 1;where id={game_id};"
r = requests.get(games_url, headers=headers, data=data).json()[0]
description = ""
if "summary" in r.keys():
description = r["summary"]
data = f"fields image_id;limit 1;where game={game_id};"
r = requests.get(cover_url, headers=headers, data=data).json()
poster_path = None
if r:
if "image_id" in r[0].keys():
poster_path = "https://images.igdb.com/igdb/image/upload/t_cover_big/" + r[0]["image_id"] + ".jpg"
windows = None
mac = None
linux = None
if "windows" in info.keys():
windows = info["windows"]
if "mac" in info.keys():
mac = info["mac"]
if "linux" in info.keys():
linux = info["linux"]
game = (info["name"], game_id, description, poster_path, windows, mac, linux)
games.append(game)
i += 1
if i >= 5:
games_loaded.send("anonymous", games=games.copy())
games.clear()
i = 0
games_loaded.send("anonymous", games=games)
current_app.logger.info("finished loading games")

5
templates/404.html Normal file
View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
<h1>Page not found</h1>
{% endblock %}

View File

@ -35,6 +35,9 @@
<li class="nav-item">
<a class="nav-link" href="{{ url_for("tv_movies.index") }}">Tv & Movies</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for("games.index") }}">Games</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">

5
templates/error.html Normal file
View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
{{ e }}
{% endblock %}

View File

@ -20,7 +20,6 @@
<button type="submit">Prev</button>
</form>
{% endif %}
<h1 style="display: inline-block">Episode {{ episode.episode }}: {{ episode.title }}</h1>
{% if loop.index0 + 1 < episodes|length %}
<form style="display: inline-block; float: right; margin-top: 5px" action="" method="get">
<input name="season" value="{{ episodes[loop.index0 + 1].season }}" hidden>
@ -28,6 +27,8 @@
<button type="submit">Next</button>
</form>
{% endif %}
<h1 style="display: inline-block">Episode {{ episode.episode }}: {{ episode.title }}</h1>
{% endif %}
{% endfor %}
<p style="text-align: left">{{ episode.description }}</p>