from flask import Flask, render_template, send_from_directory, request from flask_httpauth import HTTPBasicAuth from werkzeug.security import check_password_hash from gevent.pywsgi import WSGIServer import timeit import sqlite3 import os from PIL import Image import zipfile import gzip from bs4 import BeautifulSoup import re import datetime import sys import time from pathlib import Path from io import BytesIO # for debugging from pprint import pprint #### from opds import fromdir import config,extras app = Flask(__name__, static_url_path="", static_folder="static") auth = HTTPBasicAuth() @auth.verify_password def verify_password(username, password): if not config.TEENYOPDS_ADMIN_PASSWORD: return True elif username in config.users and check_password_hash( config.users.get(username), password ): return username @app.route("/") def startpage(): #result = "Hello, World!" conn = sqlite3.connect('app.db') cursor = conn.cursor() cursor.execute("select * from comics LIMIT " + str(config.DEFAULT_SEARCH_NUMBER) + ";") result = cursor.fetchall() pub_list = ["Marvel", "DC Comics","Dark Horse Comics","Oni Press"] count = [] for i in pub_list: cursor.execute("select count(*) from comics where Publisher = '" + i + "';") count.append(cursor.fetchone()[0]) cursor.execute("SELECT volume, COUNT(volume) FROM comics GROUP BY volume ORDER BY volume;") volume = cursor.fetchall() x = [] y = [] for i in volume: x.append(i[0]) y.append(i[1]) conn.close() return render_template("start.html", result=result,pub_list=pub_list,count=count,x=x,y=y) @app.route("/healthz") def healthz(): return "ok" @app.route("/generate") def generate(): force = request.args.get('force') generated = 0 comiccount = 0 files_without_comicinfo = 0 errorcount = 0 skippedcount = 0 for root, dirs, files in os.walk(os.path.abspath(config.CONTENT_BASE_DIR)): for file in files: f = os.path.join(root, file) if f.endswith('.cbz'): try: comiccount = comiccount + 1 s = zipfile.ZipFile(f) filelist = zipfile.ZipFile.namelist(s) if filelist[0] == 'ComicInfo.xml': Bs_data = BeautifulSoup(s.open('ComicInfo.xml').read(), "xml") CVDB=extras.get_cvdb(Bs_data.select('Notes')) if force == 'True': cover = s.open(filelist[1]).read() image = Image.open(BytesIO(cover)) image.thumbnail(config.MAXSIZE,Image.ANTIALIAS) image.save(config.THUMBNAIL_DIR + "/" + str(CVDB) + ".jpg") # Old way of saving without resize #c = open(config.THUMBNAIL_DIR + "/" + str(CVDB) + ".jpg", 'wb+') #c.write(cover) #c.close() generated = generated + 1 elif Path(config.THUMBNAIL_DIR + "/" + str(CVDB) + ".jpg").exists() == False: cover = s.open(filelist[1]).read() image = Image.open(BytesIO(cover)) image.thumbnail(config.MAXSIZE,Image.ANTIALIAS) image.save(config.THUMBNAIL_DIR + "/" + str(CVDB) + ".jpg") generated = generated + 1 else: skippedcount = skippedcount + 1 else: files_withtout_comicinfo = files_without_comicinfo + 1 except Exception as e: errorcount = errorcount + 1 config._print("Error (/generate): " + str(e)) config._print(f) return "Forced generation: " + str(force) + "
Comics: " + str(comiccount) + "
Generated: " + str(generated) + "
CBZ files without ComicInfo.xml: " + str(files_without_comicinfo) + "
Errors: " + str(errorcount) + "
Skipped: " + str(skippedcount) @app.route('/import') def import2sql(): conn = sqlite3.connect('app.db') list = [] comiccount = 0 importcount = 0 coverscount = 0 skippedcount = 0 errorcount = 0 comics_with_errors = [] start_time = timeit.default_timer() for root, dirs, files in os.walk(os.path.abspath(config.CONTENT_BASE_DIR)): for file in files: f = os.path.join(root, file) if f.endswith('.cbz'): try: comiccount = comiccount + 1 s = zipfile.ZipFile(f) filelist = zipfile.ZipFile.namelist(s) if filelist[0] == 'ComicInfo.xml': filemodtime = os.path.getmtime(f) Bs_data = BeautifulSoup(s.open('ComicInfo.xml').read(), "xml") CVDB=extras.get_cvdb(Bs_data.select('Notes')) ISSUE=Bs_data.select('Number')[0].text SERIES=Bs_data.select('Series')[0].text VOLUME=Bs_data.select('Volume')[0].text PUBLISHER=Bs_data.select('Publisher')[0].text try: TITLE=Bs_data.select('Title')[0].text except: TITLE="" #sometimes title is blank. PATH=f UPDATED=filemodtime #print(UPDATED,file=sys.stdout) #sql="INSERT OR REPLACE INTO COMICS (CVDB,ISSUE,SERIES,VOLUME, PUBLISHER, TITLE, FILE,PATH,UPDATED) VALUES ("+CVDB+",'"+ISSUE+"','"+SERIES+"','"+VOLUME+"','"+PUBLISHER+"','"+TITLE+"','"+file+"','" + f + "','" + UPDATED + "')" #print(sql,file=sys.stdout) #conn.execute(sql); # CREATE TABLE IF MISSING # create table COMICS (CVDB, ISSUE, SERIES,VOLUME,PUBLISHER,TITLE,FILE,PATH,UPDATED,PRIMARY KEY(CVDB)) try: query = "SELECT UPDATED FROM COMICS WHERE CVDB = '" + str(CVDB) + "';" savedmodtime = conn.execute(query).fetchone()[0] except: savedmodtime = 0 if savedmodtime < filemodtime: conn.execute("INSERT OR REPLACE INTO COMICS (CVDB,ISSUE,SERIES,VOLUME, PUBLISHER, TITLE, FILE,PATH,UPDATED) VALUES (?,?,?,?,?,?,?,?,?)", (CVDB, ISSUE, SERIES, VOLUME, PUBLISHER, TITLE, file, f, UPDATED)) conn.commit() config._print("Adding: " + str(CVDB)) importcount = importcount + 1 elif Path(config.THUMBNAIL_DIR + "/" + str(CVDB) + ".jpg").exists() == False: cover = s.open(filelist[1]).read() c = open(config.THUMBNAIL_DIR + "/" + str(CVDB) + ".jpg", 'wb+') c.write(cover) c.close() coverscount = coverscount + 1 else: config._print("Skipping: " + f) skippedcount = skippedcount + 1 except Exception as e: errorcount = errorcount + 1 comics_with_errors.append(f) config._print(e) config._print(comics_with_errors) conn.close() elapsed = timeit.default_timer() - start_time elapsed_time = "IMPORTED IN: " + str(round(elapsed,2)) + "s" import_stats = elapsed_time + "
Comics: " + str(comiccount) + "
Imported: " + str(importcount) + "
Covers: " + str(coverscount) + "
Skipped: " + str(skippedcount) + "
Errors: " + str(errorcount) return import_stats #+ "
" + ['
  • ' + x + '
  • ' for x in comics_with_errors] @app.route("/content/") @auth.login_required def send_content(path): print('content') return send_from_directory(config.CONTENT_BASE_DIR, path) @app.route("/image/") def image(path): return send_from_directory(config.THUMBNAIL_DIR,path) @app.route("/catalog") @app.route("/catalog/") @app.route("/catalog/") @auth.login_required def catalog(path=""): config._print("path: " + path) config._print("root_url: " + request.root_url) config._print("url: " + request.url) config._print("CONTENT_BASE_DIR: " + config.CONTENT_BASE_DIR) #print("PRESSED ON") start_time = timeit.default_timer() #print(request.root_url) c = fromdir(request.root_url, request.url, config.CONTENT_BASE_DIR, path) print("c: ") pprint(vars(c)) for x in c.entries: for y in x.links: pprint(y.href) print("------") elapsed = timeit.default_timer() - start_time print("-----------------------------------------------------------------------------------------------------------------------") print("RENDERED IN: " + str(round(elapsed,2))+"s") return c.render() if __name__ == "__main__": #http_server = WSGIServer(("", 5000), app) #http_server.serve_forever() app.run(debug=True,host='0.0.0.0')