Added users and thumbnails

Added users so that the site can only be used if logged in. Thumbnails for each comic are also generated with ImageMagick.
This commit is contained in:
Matthew Welch 2019-07-15 22:51:10 -07:00
parent 2f855db067
commit c49eb99cd2
54 changed files with 367 additions and 98 deletions

152
app.py
View File

@ -1,13 +1,24 @@
from flask import Flask from flask import Flask
from flask import render_template, request, g, redirect, url_for from flask import render_template, request, g, redirect, url_for, make_response, flash, session
from flask_login import LoginManager, current_user, login_user, logout_user, login_required
from flask_log import Logging
from urllib import parse from urllib import parse
import threading from io import BytesIO
import base64 from wand.image import Image
import threading, os, datetime, pytz
import scripts.func as func import scripts.func as func
from scripts import database from scripts import database
app = Flask(__name__) app = Flask(__name__)
app.config["SECRET_KEY"] = "***REMOVED***"
app.config["FLASK_LOG_LEVEL"] = "DEBUG"
flask_log = Logging(app)
login_manager = LoginManager(app)
login_manager.login_view = "login"
def get_comics(): def get_comics():
@ -21,6 +32,7 @@ def verify_paths():
with app.app_context(): with app.app_context():
app.logger.debug("server start")
database.initialize_db() database.initialize_db()
thread = threading.Thread(target=get_comics, args=()) thread = threading.Thread(target=get_comics, args=())
thread.daemon = True thread.daemon = True
@ -47,27 +59,74 @@ def update_comic_db(sender, **kw):
func.comic_loaded.connect(update_comic_db) func.comic_loaded.connect(update_comic_db)
@app.route('/') @login_manager.user_loader
def hello_world(): def load_user(username):
return render_template("index.html", title="Hello World") return database.get_user(username)
@app.route("/login", methods=["GET", "POST"])
def login():
try:
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
user = database.get_user(username)
if user is None or not user.check_password(password):
flash("invalid username or password")
return redirect(url_for("login"))
login_user(user)
next_page = request.args.get("next")
if not next_page:
next_page = url_for("home")
return redirect(next_page)
return render_template("login.html", title="login")
except Exception as e:
return str(e)
@app.route("/logout")
def logout():
try:
logout_user()
return redirect(url_for("login"))
except Exception as e:
return str(e)
@app.route("/")
def root():
return redirect(url_for("home"))
@app.route("/home")
@login_required
def home():
try:
return render_template("home.html", title="Hello World", current_user=current_user)
except Exception as e:
return str(e)
@app.route("/movies") @app.route("/movies")
@login_required
def movies(): def movies():
return "No Movies" return "No Movies"
@app.route("/music") @app.route("/music")
@login_required
def music(): def music():
return "No music" return "No music"
@app.route("/games") @app.route("/games")
@login_required
def games(): def games():
return "No Games" return "No Games"
@app.route("/comics") @app.route("/comics")
@login_required
def comics(): def comics():
polling = request.args.get("polling") polling = request.args.get("polling")
@ -76,12 +135,12 @@ def comics():
return render_template("publisherList.html", comics=database.get_publishers()) return render_template("publisherList.html", comics=database.get_publishers())
except Exception as e: except Exception as e:
print(e) print(e)
return e return str(e)
try: try:
return render_template("publisherView.html", title="Comics", comics=database.get_publishers()) return render_template("publisherView.html", title="Comics", comics=database.get_publishers())
except Exception as e: except Exception as e:
print(e) print(e)
return e return str(e)
@app.route("/comics/") @app.route("/comics/")
@ -90,27 +149,29 @@ def comic_reroute():
@app.route("/comics/<publisher>") @app.route("/comics/<publisher>")
@login_required
def comics_publisher(publisher): def comics_publisher(publisher):
publisher = parse.unquote(publisher) publisher = parse.unquote(publisher)
series = request.args.get("series") series = request.args.get("series")
series_year = request.args.get("seriesYear") series_year = request.args.get("seriesYear")
comic_path = request.args.get("path") issue = request.args.get("issue")
page_number = request.args.get("pageNumber")
if series: if series:
if comic_path: if issue:
comic_path = parse.unquote(comic_path) if page_number:
return comic_viewer(comic_path, publisher, series, series_year) return comic_viewer(publisher, series, series_year, issue)
return render_template("seriesView.html", title="Comics", publisher=publisher, seriesYear=series_year, comics=database.db_get_comics_in_series(series, publisher, series_year)) return comic_gallery(publisher, series, series_year, issue)
return render_template("seriesView.html", title="Comics", comics=database.db_get_comics_in_series(series, publisher, series_year))
return render_template("publisherSeriesView.html", title="Comics", comics=database.db_get_series_by_publisher(publisher)) return render_template("publisherSeriesView.html", title="Comics", comics=database.db_get_series_by_publisher(publisher))
def comic_viewer(comic_path, publisher, series, series_year): def comic_viewer(publisher, series, series_year, issue):
try: try:
comic_path_parsed = parse.quote(comic_path)
publisher_parsed = parse.quote(publisher) publisher_parsed = parse.quote(publisher)
series_parsed = parse.quote(series) series_parsed = parse.quote(series)
page_number = int(request.args.get("pageNumber")) page_number = int(request.args.get("pageNumber"))
comic = func.open_comic(comic_path) meta = database.db_get_comic(publisher, series, series_year, issue)
page_count = comic.getNumberOfPages() page_count = int(meta["pageCount"])
prev_page = page_number - 1 prev_page = page_number - 1
next_page = page_number + 1 next_page = page_number + 1
@ -118,30 +179,49 @@ def comic_viewer(comic_path, publisher, series, series_year):
next_page = 0 next_page = 0
if prev_page < 0: if prev_page < 0:
prev_page = page_count - 1 prev_page = page_count - 1
prev_url = "/comics/{}?series={}&seriesYear={}&issue={}&pageNumber={}".format(publisher_parsed, series_parsed, series_year, issue, prev_page)
page = comic.getPage(page_number) next_url = "/comics/{}?series={}&seriesYear={}&issue={}&pageNumber={}".format(publisher_parsed, series_parsed, series_year, issue, next_page)
page = str(base64.b64encode(page))[2:-1] return render_template("comicView.html", title="Comics", prev_url=prev_url, next_url=next_url, comic=meta, page_number=page_number)
prev_url = "/comics/{}?series={}&seriesYear={}&path={}&pageNumber={}".format(publisher_parsed, series_parsed, series_year, comic_path_parsed, prev_page)
next_url = "/comics/{}?series={}&seriesYear={}&path={}&pageNumber={}".format(publisher_parsed, series_parsed, series_year, comic_path_parsed, next_page)
return render_template("comicView.html", title="Comics", page=page, page_count=page_count, prev_url=prev_url, next_url=next_url)
except Exception as e: except Exception as e:
print(e) print(e)
return e return str(e)
@app.route("/comics/getPage") def comic_gallery(publisher, series, series_year, issue):
def get_comic_page(): try:
path = parse.unquote(request.args.get("path")) meta = database.db_get_comic(publisher, series, series_year, issue)
page_number = int(request.args.get("pageNumber")) return render_template("comicGallery.html", title="Comics", comic=meta)
comic = func.open_comic(path) except Exception as e:
if page_number > comic.getNumberOfPages()-1: print(e)
page_number = 1 return str(e)
if page_number <= 0:
page_number = comic.getNumberOfPages()-1
page = comic.getPage(page_number)
page = base64.b64encode(page)
return page @app.route("/comics/get_comic/<int:comic_id>/<int:page_number>")
@login_required
def get_comic_page(comic_id, page_number):
meta = database.db_get_comic_by_id(comic_id)
comic = func.open_comic(meta["path"])
byteImage = BytesIO(comic.getPage(page_number))
image = Image(file=byteImage)
response = make_response(image.make_blob())
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-type"] = "image/"+image.format
return response
@app.route("/comics/get_comic/<int:comic_id>/<int:page_number>/thumbnail")
@login_required
def get_comic_thumbnail(comic_id, page_number):
meta = database.db_get_comic_by_id(comic_id)
thumb = database.db_get_thumbnail_by_id_page(comic_id, page_number)
response = make_response(thumb["image"])
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-type"] = thumb["type"]
return response
if __name__ == '__main__': if __name__ == '__main__':

0
database.db Normal file
View File

View File

@ -1,5 +1,8 @@
server_name rpi.narnian.us; server_name rpi.narnian.us;
#location / { try_files $uri @rpiWebApp; } #location / { try_files $uri @rpiWebApp; }
location /static/ {
alias /usb/www/matthew/rpiWebApp/static/;
}
location / { location / {
include fastcgi_params; include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_INFO $fastcgi_path_info;

View File

@ -1,17 +1,25 @@
from flask import Flask from flask import Flask
from flask import g from flask import g
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
import sqlite3 import sqlite3
import os, time import os, time
from comicapi.issuestring import IssueString from comicapi.issuestring import IssueString
DATABASE = "/var/lib/rpiWebApp/database.db" DATABASE = "/var/lib/rpiWebApp/database.db"
DATABASE2 = "C:\\Users\\Matthew\\Documents\\MyPrograms\\Websites\\rpi web interface\\database.db"
def get_db(): def get_db():
db = getattr(g, '_database', None) db = getattr(g, '_database', None)
if db is None: if db is None:
db = g._database = sqlite3.connect(DATABASE) try:
db = g._database = sqlite3.connect(DATABASE)
except Exception:
db = g._database = sqlite3.connect(DATABASE2)
db.row_factory = sqlite3.Row
return db return db
@ -45,7 +53,7 @@ def initialize_db():
"format" TEXT, "format" TEXT,
"manga" TEXT, "manga" TEXT,
"blackAndWhite" TEXT, "blackAndWhite" TEXT,
"pageCount" TEXT, "pageCount" INTEGER,
"maturityRating" TEXT, "maturityRating" TEXT,
"storyArc" TEXT, "storyArc" TEXT,
"seriesGroup" TEXT, "seriesGroup" TEXT,
@ -64,9 +72,16 @@ def initialize_db():
get_db().execute("""CREATE TABLE IF NOT EXISTS "comic_thumbnails" ( get_db().execute("""CREATE TABLE IF NOT EXISTS "comic_thumbnails" (
"id" INTEGER, "id" INTEGER,
"pageNumber" INTEGER, "pageNumber" INTEGER,
"image" BLOB "image" BLOB,
"type" TEXT
)""") )""")
get_db().execute("""CREATE TABLE IF NOT EXISTS "users" (
"username" TEXT UNIQUE PRIMARY KEY,
"passwordHash" VARCHAR(128),
"isAdmin" INTEGER NOT NULL DEFAULT 0
)""")
get_db().execute("CREATE INDEX IF NOT EXISTS path_index ON comics(path);") get_db().execute("CREATE INDEX IF NOT EXISTS path_index ON comics(path);")
get_db().execute("CREATE INDEX IF NOT EXISTS id_index ON comic_thumbnails(id);")
get_db().commit() get_db().commit()
@ -99,7 +114,7 @@ def add_comics(meta, thumbnails):
get_db().commit() get_db().commit()
comic_id = get_db().execute("SELECT id from comics ORDER BY id DESC LIMIT 1").fetchone()[0] comic_id = get_db().execute("SELECT id from comics ORDER BY id DESC LIMIT 1").fetchone()[0]
for index in range(len(images)): for index in range(len(images)):
get_db().execute("INSERT INTO comic_thumbnails(id, pageNumber, image) VALUES (?,?,?)", (comic_id, index, images[index])) get_db().execute("INSERT INTO comic_thumbnails(id, pageNumber, image, type) VALUES (?,?,?,?)", (comic_id, index, images[index][0], images[index][1]))
get_db().commit() get_db().commit()
@ -119,23 +134,14 @@ def add_comic_thumbnails(thumbnails):
def db_get_all_comics(): def db_get_all_comics():
original_factory = get_db().row_factory result = get_db().execute("SELECT * FROM comics ORDER BY series, issue").fetchall()
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
get_db().row_factory = dict_factory
result = get_db().execute("SELECT series, issue, title FROM comics ORDER BY series, issue").fetchall()
get_db().commit() get_db().commit()
get_db().row_factory = original_factory
return result return result
def db_get_series_by_publisher(publisher): def db_get_series_by_publisher(publisher):
series = [] series = []
rows = get_db().execute("SELECT DISTINCT series, seriesYear, publisher FROM comics WHERE publisher LIKE ? ORDER BY series, seriesYear", [publisher]).fetchall() rows = get_db().execute("SELECT publisher, series, seriesYear, id, min(issue) FROM comics WHERE publisher=? GROUP BY publisher, series, seriesYear ORDER BY series, seriesYear", [publisher]).fetchall()
for row in rows: for row in rows:
series.append(row) series.append(row)
return series return series
@ -143,7 +149,7 @@ def db_get_series_by_publisher(publisher):
def db_get_comics_in_series(series, publisher, series_year): def db_get_comics_in_series(series, publisher, series_year):
comics = [] comics = []
rows = get_db().execute("SELECT series, issue, title, path FROM comics WHERE series LIKE ? AND publisher LIKE ? AND seriesYear LIKE ? ORDER BY series, issue", [series, publisher, series_year]).fetchall() rows = get_db().execute("SELECT * FROM comics WHERE series=? AND publisher=? AND seriesYear=? ORDER BY series, issue", [series, publisher, series_year]).fetchall()
get_db().commit() get_db().commit()
for row in rows: for row in rows:
comics.append(row) comics.append(row)
@ -159,9 +165,25 @@ def get_publishers():
return publishers return publishers
def db_get_comic_by_id(comic_id):
row = get_db().execute("SELECT * FROM comics WHERE id=?", [comic_id]).fetchone()
return row
def db_get_thumbnail_by_id_page(comic_id, pageNumber):
row = get_db().execute("SELECT * FROM comic_thumbnails WHERE id=? AND pageNumber=?", [comic_id, pageNumber]).fetchone()
return row
def db_get_comic(publisher, series, series_year, issue):
row = get_db().execute("SELECT * FROM comics WHERE publisher=? AND series=? AND seriesYear=? AND issue=?",
[publisher, series, series_year, issue]).fetchone()
return row
def comic_path_in_db(path): def comic_path_in_db(path):
try: try:
result = get_db().execute("SELECT path FROM comics WHERE path LIKE ?", [path]) result = get_db().execute("SELECT path FROM comics WHERE path=?", [path])
get_db().commit() get_db().commit()
if result.fetchone(): if result.fetchone():
@ -181,3 +203,36 @@ def verify_paths():
get_db().execute("DELETE FROM comics WHERE path LIKE ?", [path[0]]) get_db().execute("DELETE FROM comics WHERE path LIKE ?", [path[0]])
get_db().commit() get_db().commit()
time.sleep(60*60*24*5) time.sleep(60*60*24*5)
def get_user(username):
user_data = get_db().execute("SELECT * FROM users WHERE username=?", [username]).fetchone()
if user_data:
return User(user_data)
return None
def verify_user(username, password):
password_hash = get_db().execute("SELECT passwordHash FROM users WHERE username=?", [username]).fetchone()
if password_hash:
valid_password = check_password_hash(password_hash["passwordHash"], password)
if valid_password:
user_data = get_db().execute("SELECT * FROM users WHERE username=?", [username]).fetchone()
user = User(user_data)
return user
else:
return False
class User(UserMixin):
def __init__(self, user):
self.username = user["username"]
self.password_hash = user["passwordHash"]
self.is_admin = user["isAdmin"]
def get_id(self):
return self.username
def check_password(self, password):
result = check_password_hash(self.password_hash, password)
return result

View File

@ -1,7 +1,7 @@
from comicapi import comicarchive from comicapi import comicarchive
from blinker import Namespace from blinker import Namespace
from io import BytesIO from io import BytesIO
from PIL import Image from wand.image import Image
import os, sys import os, sys
@ -10,14 +10,20 @@ from scripts import database
rpi_signals = Namespace() rpi_signals = Namespace()
comic_loaded = rpi_signals.signal("comic-loaded") comic_loaded = rpi_signals.signal("comic-loaded")
publishers_to_ignore = ["***REMOVED***"]
# Directories # Directories
COMICS_DIRECTORY = "/usb/storage/media/Comics/" RPI_COMICS_DIRECTORY = "/usb/storage/media/Comics/"
MOVIES_DIRECTORY = "/usb/storage/media/Videos/Movies/" RPI_MOVIES_DIRECTORY = "/usb/storage/media/Videos/Movies/"
TV_SHOWS_DIRECTORY = "/usb/storage/media/Videos/TV/" RPI_TV_SHOWS_DIRECTORY = "/usb/storage/media/Videos/TV/"
VIDEOS_DIRECTORY = "/usb/storage/media/Videos/Videos/" RPI_VIDEOS_DIRECTORY = "/usb/storage/media/Videos/Videos/"
GAMES_DIRECTORY = "/usb/storage/media/games/" RPI_GAMES_DIRECTORY = "/usb/storage/media/games/"
MUSIC_DIRECTORY = "/usb/storage/media/Music/" RPI_MUSIC_DIRECTORY = "/usb/storage/media/Music/"
MC_COMICS_DIRECTORY = "C:\\Users\\Matthew\\Documents\\Comics"
COMICS_DIRECTORY = RPI_COMICS_DIRECTORY if os.path.exists(RPI_COMICS_DIRECTORY) else MC_COMICS_DIRECTORY
############# #############
@ -41,10 +47,13 @@ def get_comics():
print("encoding failed on:", path) print("encoding failed on:", path)
print(e) print(e)
continue continue
print(path)
archive = open_comic(path) archive = open_comic(path)
md = archive.readCIX()
if md.publisher in publishers_to_ignore:
continue
print(path)
meta.append((path, md))
thumbnails.append(get_comic_thumbnails(archive)) thumbnails.append(get_comic_thumbnails(archive))
meta.append((path, archive.readCIX()))
comics_added += 1 comics_added += 1
comics_in_db += 1 comics_in_db += 1
i += 1 i += 1
@ -63,17 +72,27 @@ def get_comics():
def get_comic_thumbnails(comic): def get_comic_thumbnails(comic):
thumbnails = [] thumbnails = []
size = 128, 128 size = "256x256"
new_height = 256
new_width = 256
for page in range(comic.getNumberOfPages()): for page in range(comic.getNumberOfPages()):
image_bytes = BytesIO(comic.getPage(page)) image_bytes = BytesIO(comic.getPage(page))
image = Image.open(image_bytes) image = Image(file=image_bytes)
image.thumbnail(size) orig_height = image.height
thumbnails.append(image.tobytes()) orig_width = image.width
if (orig_width/orig_height)*new_height <= new_width:
height = int((orig_height/orig_width) * new_width)
width = new_width
else:
width = int((orig_width/orig_height) * new_height)
height = new_height
image.thumbnail(width, height)
thumbnails.append((image.make_blob(), "image/"+image.format))
return thumbnails return thumbnails
def open_comic(path): def open_comic(path):
archive = comicarchive.ComicArchive(path, default_image_path="/usb/www/matthew/rpiWebApp/static/icon.png") archive = comicarchive.ComicArchive(path, default_image_path="static/images/icon.png")
return archive return archive

7
static/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
static/images/Aspen MLT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
static/images/Atlas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
static/images/Bongo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
static/images/Charlton.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/images/DC Comics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/Disney.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/images/EC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
static/images/Harvey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/images/Marvel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
static/images/Prize.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/Top Shelf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/Vertigo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/Wildstorm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
static/images/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

13
static/style.css Normal file
View File

@ -0,0 +1,13 @@
.comic-grid {
display: inline-grid;
grid-template-columns: auto auto auto auto auto;
grid-gap: 10px;
padding: 10px;
}
.comic-page {
max-height: 100vh;
max-width: 100vw;
display: block;
margin: auto;
}

View File

@ -1,3 +1,12 @@
{% for comic in comics %} {% for comic in comics %}
<li><a href="/comics/{{ comic[2]|urlencode }}?series={{ comic[0]|urlencode }}&seriesYear={{ comic[1] }}">{{ comic[0] }} {{ comic[1] }}</a></li> <div class="col-3" style="padding: 10px">
<a href="/comics/{{ comic["publisher"]|urlencode }}?series={{ comic["series"]|urlencode }}&seriesYear={{ comic["seriesYear"] }}">
<div class="card">
<img class="card-img" src="/comics/get_comic/{{ comic['id'] }}/0/thumbnail">
<div class="card-body">
{{ comic["series"] }} {{ comic["seriesYear"] }}
</div>
</div>
</a>
</div>
{% endfor %} {% endfor %}

View File

@ -3,11 +3,38 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{ title }}</title> <title>{{ title }}</title>
<link rel="icon" type="image/png" href="../static/icon.png" sizes="32x32"> <link rel="icon" type="image/png" href="/static/images/icon.png" sizes="32x32">
<link rel="stylesheet" href="/static/bootstrap.min.css">
<link rel="stylesheet" href="/static/style.css">
{% block head %} {% block head %}
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
<nav class="navbar sticky-top navbar-expand navbar-light bg-secondary">
<a class="navbar-brand" href="{{ url_for("home") }}">
<img src="/static/images/icon.png" height="40px" alt="">
</a>
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url_for("home") }}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for("comics") }}">Comics</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for("login") }}">
Login
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for("logout") }}">
Logout
</a>
</li>
</ul>
</nav>
{% block content %} {% block content %}
<p>Hello World!</p> <p>Hello World!</p>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<div style="text-align: center">
<div class="comic-grid">
{% for page_number in range(comic["pageCount"]|int) %}
<div style="margin: auto" class="comic-thumbnail card bg-dark text-white">
<a href="/comics/{{ comic["publisher"]|urlencode }}?series={{ comic["series"]|urlencode }}&seriesYear={{ comic["seriesYear"] }}&issue={{ comic["issue"]|urlencode }}&pageNumber={{ page_number }}">
<img src="/comics/get_comic/{{ comic["id"] }}/{{ page_number }}/thumbnail" alt="" style="display: inline">
<p class="card-text">{{ 1+page_number }}/{{ comic["pageCount"] }}</p>
</a>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -2,8 +2,8 @@
{% block content %} {% block content %}
<a id="prev-button" href="{{ prev_url }}" style="position: fixed; height: 100%; width: 50%; text-align: center"></a> <a href="{{ prev_url }}" style="position: fixed; height: 100%; width: 50%;"></a>
<a id="next-button" href="{{ next_url }}" style="position: fixed; right: 0; height: 100%; width: 50%; text-align: center"></a> <a href="{{ next_url }}" style="position: fixed; right: 0; height: 100%; width: 50%;"></a>
<img id="comic-page" src="data:image/jpeg;base64,{{ page }}" alt="" style="height: 100vh; display: block; margin: auto"> <img class="comic-page" src="/comics/get_comic/{{ comic["id"] }}/{{ page_number }}" alt="">
{% endblock %} {% endblock %}

5
templates/home.html Normal file
View File

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

View File

@ -1 +0,0 @@
{% extends "base.html" %}

24
templates/login.html Normal file
View File

@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block content %}
<div class="container w-25 p-3 align-middle" style="margin: auto">
<form action="" method="post" style="display: inline">
<div class="form-group">
<label for="username">Username</label>
<input class="form-control" name="username" id="username" type="text" placeholder="Username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input class="form-control" name="password" id="password" type="password" placeholder="Password">
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger" role="alert" style="margin-top: 10px">
{{ messages[0] }}
</div>
{% endif %}
{% endwith %}
</div>
{% endblock %}

View File

@ -1,3 +1,12 @@
{% for publisher in comics %} {% for publisher in comics %}
<li><a href="/comics/{{ publisher }}">{{ publisher }}</a></li> <div class="col-4" style="padding: 10px">
<a href="/comics/{{ publisher }}">
<div class="card">
<img class="card-img" src="/static/images/{{ publisher }}.png">
<div class="card-body">
{{ publisher }}
</div>
</div>
</a>
</div>
{% endfor %} {% endfor %}

View File

@ -1,7 +1,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<ul id="series"> <div class="container col-7">
{% include "PublisherSeriesList.html" %} <div class="row justify-content-start">
</ul> {% include "PublisherSeriesList.html" %}
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,7 +1,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<ul id="publishers"> <div class="container col-7">
{% include "publisherList.html" %} <div class="row justify-content-start">
</ul> {% include "publisherList.html" %}
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,7 +1,12 @@
{% for comic in comics %} {% for comic in comics %}
<li> <div class="col-3" style="padding: 10px">
<a href="/comics/{{ publisher|urlencode }}?series={{ comic[0]|urlencode }}&seriesYear={{ seriesYear }}&path={{ comic[3]|urlencode }}&pageNumber=0"> <a href="/comics/{{ comic["publisher"]|urlencode }}?series={{ comic["series"]|urlencode }}&seriesYear={{ comic["seriesYear"] }}&issue={{ comic["issue"]|urlencode }}">
{{ comic[0] }} {% if comic[1] > 0 %}{{ "#{0:g}".format(comic[1]) }}{% endif %} {% if comic[2] != None %}{{ comic[2] }} {% endif %} <div class="card">
<img class="card-img" src="/comics/get_comic/{{ comic['id'] }}/0/thumbnail">
<div class="card-body">
{{ comic["series"] }} {% if comic["issue"] > 0 %}{{ "#{0:g}".format(comic["issue"]) }}{% endif %} {% if comic["title"] != None %}{{ comic["title"] }} {% endif %}
</div>
</div>
</a> </a>
</li> </div>
{% endfor %} {% endfor %}

View File

@ -1,7 +1,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<ul id="comics"> <div class="container col-7">
{% include "seriesList.html" %} <div class="row justify-content-start">
</ul> {% include "seriesList.html" %}
</div>
</div>
{% endblock %} {% endblock %}

14
test.py
View File

@ -7,6 +7,8 @@ from comicapi.issuestring import IssueString
from urllib import parse from urllib import parse
from io import BytesIO from io import BytesIO
from PIL import Image from PIL import Image
import datetime, pytz
from werkzeug.security import generate_password_hash
os.environ["UNRAR_LIB_PATH"] = 'C:\\Program Files (x86)\\UnrarDLL\\UnRAR.dll' os.environ["UNRAR_LIB_PATH"] = 'C:\\Program Files (x86)\\UnrarDLL\\UnRAR.dll'
@ -18,14 +20,4 @@ def get_db():
return db return db
row_id = 1 print(generate_password_hash("guanfacine"))
comic_id = 0
for row in get_db().execute("SELECT pageNumber, ROWID FROM comic_thumbnails ORDER BY ROWID").fetchall():
print("ROWID:", row[1])
if row[0] == 0:
comic_id += 1
print("comic id:", comic_id)
get_db().execute("UPDATE comic_thumbnails SET id=? WHERE ROWID=?", (comic_id, row[1]))
row_id += 1
get_db().commit()