import sys # isort:skip from gevent import monkey # isort:skip if __name__ == "__main__" and "--debug" not in sys.argv[1:]: monkey.patch_all() import os import requests as RQ from flask import ( Flask, abort, flash, redirect, render_template, request, send_file, send_from_directory, session, url_for, ) from flask_bootstrap import Bootstrap from flask_debugtoolbar import DebugToolbarExtension from flask_limiter import Limiter from flask_limiter.util import get_remote_address from flask_login import LoginManager, current_user from flask_login import login_user, logout_user from flask_nav import Nav, register_renderer from flask_nav.elements import Navbar, Text, View from flask_session import Session from flask_wtf.csrf import CSRFProtect import stats_collect from api.user import JellyfinUser from forms import LoginForm from models import RequestUser, db from transcode import profiles from utils import ( BootsrapRenderer, handle_config, is_safe_url, login_required, make_placeholder_image, setup_template_filters, with_application_context, ) from views import register_blueprints def left_nav(): requests_badge = None if current_user.is_authenticated: num_notifications = RequestUser.query.filter( (RequestUser.user_id == current_user.id) & ( RequestUser.updated is True)).count() if num_notifications > 0: requests_badge = (num_notifications, "danger") links = [ View("Home", "home.index"), View("Requests", "requests.index", __badge=requests_badge), View("Containers", "containers.index", container_id=None), View("qBittorrent", "qbittorrent.index", infohash=None), View("Sonarr", "sonarr.index"), View("Radarr", "radarr.index"), View("Jellyfin", "jellyfin.index"), View("Search", "search.index"), View("History", "history.index"), View("Transcode", "transcode.index"), View("Config", "config.index"), View("Remote", "remote.index"), View("Log", "log.index"), ] if current_user.is_authenticated: links.append(View("Logout", "logout")) links[-1].classes = ["btn", "btn-danger", "my-2", "my-sm-0"] else: links.append(View("Login", "login")) links[-1].classes = ["btn", "btn-success", "my-2", "my-sm-0"] for n, link in enumerate(links): adapter = app.url_map.bind("localhost") name, args = adapter.match(link.get_url(), method="GET") func = app.view_functions[name] if getattr(func, "requires_login", False): if not current_user.is_authenticated: links[n] = None if getattr(func, "requires_admin", False): if not (current_user.is_authenticated and current_user.is_admin): links[n] = None links = list(filter(None, links)) return Navbar("PirateDash", *links) def right_nav(): if current_user.is_authenticated: return Text(current_user["Name"]) else: return Text("") def create_app(): templates = os.path.join( os.path.dirname( os.path.abspath(__file__)), "templates") app = Flask(__name__, template_folder=templates) app.config.from_pyfile("config.cfg") app.bootstrap = Bootstrap(app) app.csrf = CSRFProtect(app) app.nav = Nav(app) app.toolbar = DebugToolbarExtension(app) app.jinja_env.add_extension("jinja2.ext.debug") app.jinja_env.add_extension("jinja2.ext.do") app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True register_renderer(app, "bootstrap4", BootsrapRenderer) app.nav.register_element("left_nav", left_nav) app.nav.register_element("right_nav", right_nav) app.db = db app.db.init_app(app) app.login_manager = LoginManager(app) app.login_manager.login_view = "/login" app.config["SESSION_SQLALCHEMY"] = app.db app.session = Session(app) # app.limiter = Limiter( # app, key_func=get_remote_address, default_limits=["120 per minute"] # ) # for handler in app.logger.handlers: # app.limiter.logger.addHandler(handler) return app app = create_app() setup_template_filters(app) register_blueprints(app) @app.errorhandler(500) def internal_error(error): print(error) return "" @app.errorhandler(404) def internal_error(error): print(error) return "" @app.before_request def before_request(): app.config["APP_CONFIG"] = handle_config() # if request.cookies.get('magic')!="FOO": # return "" @app.route("/static/") def send_static(path): return send_from_directory("static", path) @app.route("/placeholder") def placeholder(): return send_file( make_placeholder_image( **request.args), mimetype="image/png") @app.login_manager.user_loader def load_user(user_id): if "jf_user" in session: if session["jf_user"].id == user_id: return session["jf_user"] @app.route("/logout") @login_required def logout(): del session["jf_user"] logout_user() return redirect("/login") @app.route("/login", methods=["GET", "POST"]) def login(): next_url = request.args.get("next") if current_user.is_authenticated: if next_url and not is_safe_url(next_url): next_url = None return redirect(next_url or url_for("home.index")) form = LoginForm() if form.validate_on_submit(): try: jf = JellyfinUser(form.username.data, form.password.data) except RQ.exceptions.HTTPError as e: if e.response.status_code != 401: raise flash("Invalid credentials", "error") return render_template("login.html", form=form) login_user(jf, remember=form.remember.data) session["jf_user"] = jf next_url = request.args.get("next") if next_url and not is_safe_url(next_url): return abort(400) return redirect(next_url or url_for("home.index")) return render_template("login.html", form=form) @app.before_first_request def before_first_request(): app.db.create_all() # stats_collect.loop(60) @with_application_context(app) def init_app(): app.db.create_all() if __name__ == "__main__": port = 5000 if "--init" in sys.argv: init_app() if "--debug" in sys.argv: os.environ["FLASK_ENV"] = "development" app.debug = True app.run(host="0.0.0.0", port=port, debug=True) else: from gevent.pywsgi import WSGIServer server = WSGIServer(("0.0.0.0", port), app) print("Running on {0}:{1}".format(*server.address)) server.serve_forever()