Converted to python flask #1
40
create_venv.sh
Executable file
40
create_venv.sh
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
. CONFIG.sh
|
||||||
|
|
||||||
|
# set -o xtrace ## To debug scripts
|
||||||
|
# set -o errexit ## To exit on error
|
||||||
|
# set -o errunset ## To exit if a variable is referenced but not set
|
||||||
|
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
rm -rf venv/
|
||||||
|
|
||||||
|
clear
|
||||||
|
python -m venv venv/
|
||||||
|
sleep 2
|
||||||
|
source "./venv/bin/activate"
|
||||||
|
|
||||||
|
ANSR="-1"
|
||||||
|
while [[ $ANSR != "0" ]] && [[ $ANSR != "1" ]] && [[ $ANSR != "2" ]]; do
|
||||||
|
clear
|
||||||
|
menu_mesage
|
||||||
|
read -p "--> : " ANSR
|
||||||
|
done
|
||||||
|
case $ANSR in
|
||||||
|
"1" ) pip install -r linux-requirements.txt;;
|
||||||
|
"2" ) pip install -r windows-requirements.txt;;
|
||||||
|
"0" ) exit;;
|
||||||
|
* ) echo "Don't know how you got here but that's a bad sign...";;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function menu_mesage() {
|
||||||
|
echo "NOTE: Make sure to have Python 3 installed!"
|
||||||
|
echo -e "\nWhat do you want to do?"
|
||||||
|
echo -e "\t1) Generate Linux/Mac supported venv. (Installs Repuirements)"
|
||||||
|
echo -e "\t2) Generate Windows supported venv. (Installs Repuirements)"
|
||||||
|
echo -e "\t0) EXIT"
|
||||||
|
}
|
||||||
|
|
||||||
|
main $@;
|
@ -10,6 +10,6 @@ function main() {
|
|||||||
cd "${SCRIPTPATH}"
|
cd "${SCRIPTPATH}"
|
||||||
echo "Working Dir: " $(pwd)
|
echo "Working Dir: " $(pwd)
|
||||||
source "./venv/bin/activate"
|
source "./venv/bin/activate"
|
||||||
gunicorn wsgi:app -b 0.0.0.0:8080 # <module>:<app> IE <file>:<flask app variable>
|
gunicorn wsgi:app -b 0.0.0.0:8080 -p app.pid # <module>:<app> IE <file>:<flask app variable>
|
||||||
}
|
}
|
||||||
main $@;
|
main $@;
|
@ -9,9 +9,9 @@ function main() {
|
|||||||
SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
|
SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
|
||||||
cd "${SCRIPTPATH}"
|
cd "${SCRIPTPATH}"
|
||||||
echo "Working Dir: " $(pwd)
|
echo "Working Dir: " $(pwd)
|
||||||
|
source "./venv/bin/activate"
|
||||||
mkdir /tmp/apps
|
mkdir /tmp/apps
|
||||||
./venv/bin/gunicorn \
|
gunicorn --bind unix:/tmp/apps/webfm.sock wsgi:app -p app.pid
|
||||||
--bind unix:/tmp/apps/webfm.sock wsgi:app
|
|
||||||
|
|
||||||
}
|
}
|
||||||
main $@;
|
main $@;
|
||||||
|
@ -1,29 +1,58 @@
|
|||||||
# system import
|
# System import
|
||||||
import os, json, secrets
|
import os, json, secrets
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
# Flask imports
|
# Lib imports
|
||||||
from flask import Flask, Blueprint
|
from flask import Flask
|
||||||
|
from flask_oidc import OpenIDConnect
|
||||||
from flask_login import current_user, login_user, logout_user, LoginManager
|
from flask_login import current_user, login_user, logout_user, LoginManager
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
|
|
||||||
|
|
||||||
|
# Apoplication imports
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Configs and 'init'
|
# Configs and 'init'
|
||||||
app = Flask(__name__)
|
APP_NAME = 'WebFM'
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///static/db/webfm.db"
|
ROOT_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
||||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
CONFIG_FILE = ROOT_FILE_PTH + "/config.json"
|
||||||
app.config['TITLE'] = 'WebFM'
|
# This path is submitted as the redirect URI in certain code flows.
|
||||||
|
REDIRECT_LINK = "https%3A%2F%2Fwww.webfm.com%2F"
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.update({
|
||||||
|
"TITLE": APP_NAME,
|
||||||
|
'DEBUG': False,
|
||||||
|
'SECRET_KEY': secrets.token_hex(32),
|
||||||
|
'PERMANENT_SESSION_LIFETIME': timedelta(days = 7).total_seconds(),
|
||||||
|
"SQLALCHEMY_DATABASE_URI": "sqlite:///static/db/webfm.db",
|
||||||
|
"SQLALCHEMY_TRACK_MODIFICATIONS": False,
|
||||||
|
"APP_REDIRECT_URI": REDIRECT_LINK,
|
||||||
|
'OIDC_CLIENT_SECRETS': ROOT_FILE_PTH + '/client_secrets.json',
|
||||||
|
'OIDC_ID_TOKEN_COOKIE_SECURE': True, # Only set false in development setups...
|
||||||
|
'OIDC_REQUIRE_VERIFIED_EMAIL': False,
|
||||||
|
'OIDC_USER_INFO_ENABLED': True,
|
||||||
|
'OIDC_VALID_ISSUERS': [
|
||||||
|
'http://www.ssoapps.com/auth/realms/apps',
|
||||||
|
'https://www.ssoapps.com/auth/realms/apps'
|
||||||
|
],
|
||||||
|
'OIDC_TOKEN_TYPE_HINT': 'access_token'
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
login_manager = LoginManager(app)
|
||||||
|
bcrypt = Bcrypt(app)
|
||||||
|
oidc = OpenIDConnect(app)
|
||||||
|
def oidc_loggedin():
|
||||||
|
return oidc.user_loggedin
|
||||||
|
app.jinja_env.globals['oidc_loggedin'] = oidc_loggedin
|
||||||
|
app.jinja_env.globals['TITLE'] = APP_NAME
|
||||||
|
|
||||||
|
|
||||||
# For csrf and some other stuff...
|
|
||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days = 7)
|
|
||||||
app.config['SECRET_KEY'] = secrets.token_hex(32)
|
|
||||||
login_manager = LoginManager(app)
|
|
||||||
bcrypt = Bcrypt(app)
|
|
||||||
|
|
||||||
# Settings data
|
# Settings data
|
||||||
THIS_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
CONFIG_FILE = THIS_FILE_PTH + "/config.json"
|
|
||||||
def retrieveSettings():
|
def retrieveSettings():
|
||||||
returnData = []
|
returnData = []
|
||||||
|
|
||||||
@ -39,7 +68,7 @@ config = retrieveSettings()
|
|||||||
|
|
||||||
from .forms import LoginForm, RegisterForm
|
from .forms import LoginForm, RegisterForm
|
||||||
from .models import db, Favorites, Settings, User
|
from .models import db, Favorites, Settings, User
|
||||||
from webfm import routes
|
|
||||||
|
|
||||||
with app.app_context():
|
db.init_app(app)
|
||||||
db.create_all()
|
with app.app_context(): db.create_all()
|
||||||
|
from webfm import routes
|
||||||
|
15
webfm/client_secrets.json
Normal file
15
webfm/client_secrets.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"web": {
|
||||||
|
"auth_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/auth",
|
||||||
|
"client_id": "apps",
|
||||||
|
"issuer": "https://www.ssoapps.com/auth/realms/apps",
|
||||||
|
"client_secret": "[Add your secret key here]",
|
||||||
|
"redirect_uris": [
|
||||||
|
"http://www.webfm.com/home",
|
||||||
|
"https://www.webfm.com/home"
|
||||||
|
],
|
||||||
|
"userinfo_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/userinfo",
|
||||||
|
"token_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/token",
|
||||||
|
"token_introspection_uri": "https://www.ssoapps.com/auth/realms/apps/protocol/openid-connect/token/introspect"
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ from ...utils import MessageHandler # Get simple message processor
|
|||||||
|
|
||||||
|
|
||||||
msgHandler = MessageHandler()
|
msgHandler = MessageHandler()
|
||||||
TITLE = app.config['TITLE']
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
@ -28,7 +27,7 @@ def login():
|
|||||||
|
|
||||||
flash("Username or password incorrect! Please try again...", "danger")
|
flash("Username or password incorrect! Please try again...", "danger")
|
||||||
|
|
||||||
return render_template('login.html', title=TITLE, form=_form)
|
return render_template('login.html', form = _form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/logout', methods=['GET', 'POST'])
|
@app.route('/logout', methods=['GET', 'POST'])
|
||||||
|
@ -10,7 +10,6 @@ from ...utils import MessageHandler # Get simple message processor
|
|||||||
|
|
||||||
|
|
||||||
msgHandler = MessageHandler()
|
msgHandler = MessageHandler()
|
||||||
TITLE = app.config['TITLE']
|
|
||||||
|
|
||||||
@app.route('/register', methods=['GET', 'POST'])
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
@ -27,5 +26,4 @@ def register():
|
|||||||
return redirect("/login")
|
return redirect("/login")
|
||||||
|
|
||||||
return render_template('register.html',
|
return render_template('register.html',
|
||||||
title=TITLE,
|
form = _form)
|
||||||
form=_form)
|
|
||||||
|
@ -22,12 +22,11 @@
|
|||||||
|
|
||||||
#file-grid {
|
#file-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(18em, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(14em, 1fr));
|
||||||
grid-column-gap: 1em;
|
grid-column-gap: 1em;
|
||||||
grid-row-gap: 1em;
|
grid-row-gap: 1em;
|
||||||
margin: 5em auto;
|
margin: 2em auto;
|
||||||
width: 85%;
|
width: 85%;
|
||||||
padding: 2em;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 25em;
|
max-height: 25em;
|
||||||
}
|
}
|
||||||
@ -35,12 +34,33 @@
|
|||||||
#path { max-width: inherit; }
|
#path { max-width: inherit; }
|
||||||
#faves-list > li { width: 100%; }
|
#faves-list > li { width: 100%; }
|
||||||
|
|
||||||
|
#video-container {
|
||||||
|
width: 30em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#video-viewer {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#video-controls {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0.5em;
|
||||||
|
background-color: rgba(0, 0, 0, 0.64);
|
||||||
|
width: inherit;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* CLASSES */
|
/* CLASSES */
|
||||||
.scroller {
|
.scroller {
|
||||||
scrollbar-color: #00000084 #ffffff64;
|
scrollbar-color: #00000084 #ffffff64;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dir-style, .video-style, .file-style {
|
.dir-style, .video-style, .file-style {
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
#video-container::-webkit-media-controls {
|
||||||
|
display:none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#video-container::-webkit-media-controls-enclosure {
|
||||||
|
display:none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
background-color: #32383e74;
|
background-color: #32383e74;
|
||||||
border-color: #f8940674;
|
border-color: #f8940674;
|
||||||
|
@ -30,12 +30,7 @@ const doAjax = (actionPath, data, action) => {
|
|||||||
xhttp.onreadystatechange = function() {
|
xhttp.onreadystatechange = function() {
|
||||||
if (this.readyState === 4 && this.status === 200) {
|
if (this.readyState === 4 && this.status === 200) {
|
||||||
if (this.responseText != null) { // this.responseXML if getting XML data
|
if (this.responseText != null) { // this.responseXML if getting XML data
|
||||||
if (xhttp.responseURL.includes("/register") ||
|
postAjaxController(JSON.parse(this.responseText), action);
|
||||||
xhttp.responseURL.includes("/login")) {
|
|
||||||
window.location.href = xhttp.responseURL;
|
|
||||||
} else {
|
|
||||||
postAjaxController(JSON.parse(this.responseText), action);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let type = "danger"
|
let type = "danger"
|
||||||
let msg = "No content returned. Check the target path.";
|
let msg = "No content returned. Check the target path.";
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// let eventSource = new EventSource( formatURL("stream") );
|
let fullScreenState = 0;
|
||||||
// eventSource.onmessage = function(e) {
|
let shouldPlay = null;
|
||||||
// console.log(e.data);
|
let clicktapwait = 200;
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
document.body.onload = (eve) => {
|
document.body.onload = (eve) => {
|
||||||
@ -23,6 +22,100 @@ document.body.onload = (eve) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function togglePlay(video) {
|
||||||
|
shouldPlay = setTimeout(function () {
|
||||||
|
shouldPlay = null;
|
||||||
|
if (video.paused) {
|
||||||
|
video.play();
|
||||||
|
} else {
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFullscreen(video) {
|
||||||
|
containerElm = document.getElementById("video-container");
|
||||||
|
parentElm = video.parentElement;
|
||||||
|
|
||||||
|
|
||||||
|
if (video.requestFullscreen) {
|
||||||
|
parentElm.requestFullscreen();
|
||||||
|
containerElm.style.display = "block";
|
||||||
|
} else if (video.webkitRequestFullscreen) { /* Safari */
|
||||||
|
parentElm.webkitRequestFullscreen();
|
||||||
|
containerElm.style.display = "block";
|
||||||
|
} else if (video.msRequestFullscreen) { /* IE11 */
|
||||||
|
parentElm.msRequestFullscreen();
|
||||||
|
containerElm.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullScreenState == 2) {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
containerElm.style.display = "contents";
|
||||||
|
} else if (document.webkitExitFullscreen) { /* Safari */
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
containerElm.style.display = "contents";
|
||||||
|
} else if (document.msExitFullscreen) { /* IE11 */
|
||||||
|
document.msExitFullscreen();
|
||||||
|
containerElm.style.display = "contents";
|
||||||
|
}
|
||||||
|
|
||||||
|
fullScreenState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullScreenState += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#video-viewer").on("click", function(eve){
|
||||||
|
const video = eve.target;
|
||||||
|
|
||||||
|
if(!shouldPlay) {
|
||||||
|
shouldPlay = setTimeout( function() {
|
||||||
|
shouldPlay = null;
|
||||||
|
togglePlay(video);
|
||||||
|
}, clicktapwait);
|
||||||
|
} else {
|
||||||
|
clearTimeout(shouldPlay); // Stop single tap callback
|
||||||
|
shouldPlay = null;
|
||||||
|
toggleFullscreen(video);
|
||||||
|
}
|
||||||
|
eve.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#video-viewer").on("touchend", function(eve){
|
||||||
|
const video = eve.target;
|
||||||
|
|
||||||
|
if(!shouldPlay) {
|
||||||
|
shouldPlay = setTimeout( function() {
|
||||||
|
shouldPlay = null;
|
||||||
|
togglePlay(video);
|
||||||
|
}, clicktapwait);
|
||||||
|
} else {
|
||||||
|
clearTimeout(shouldPlay); // Stop single tap callback
|
||||||
|
shouldPlay = null;
|
||||||
|
toggleFullscreen(video);
|
||||||
|
}
|
||||||
|
eve.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#video-viewer" ).bind( "timeupdate", async function(eve) {
|
||||||
|
const video = eve.target;
|
||||||
|
const seekto = document.getElementById("seek-slider");
|
||||||
|
const vt = video.currentTime * (100 / video.duration);
|
||||||
|
seekto.value = vt;
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#seek-slider" ).bind( "change", async function(eve) {
|
||||||
|
const slider = eve.target;
|
||||||
|
let video = document.getElementById("video-viewer");
|
||||||
|
let seekto = video.duration * (slider.value / 100);
|
||||||
|
video.currentTime = seekto;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$( "#search-files-field" ).bind( "keyup", async function(eve) {
|
$( "#search-files-field" ).bind( "keyup", async function(eve) {
|
||||||
searchPage();
|
searchPage();
|
||||||
|
@ -15,4 +15,7 @@ const manageFavorites = (elm) => {
|
|||||||
|
|
||||||
const loadFave = (id) => {
|
const loadFave = (id) => {
|
||||||
loadFavoriteLink(id);
|
loadFavoriteLink(id);
|
||||||
|
document.getElementById("favorites-modal")
|
||||||
|
.getElementsByClassName("modal-footer")[0]
|
||||||
|
.children[0].click()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<h1>{{title}}</h1>
|
||||||
<h1>{{title}}</h1>
|
<p>{{message}}</p>
|
||||||
<p>{{message}}</p>
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -2,26 +2,14 @@
|
|||||||
|
|
||||||
{% block body_header_additional %}
|
{% block body_header_additional %}
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/base.css')}}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/base.css')}}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/overrides.css')}}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css')}}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css')}}">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap/bootstrap-table.min.css')}}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap/bootstrap-table.min.css')}}">
|
||||||
{% endblock body_header_additional%}
|
{% endblock body_header_additional%}
|
||||||
|
|
||||||
<!-- System flashed messages! -->
|
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
||||||
{% if messages %}
|
|
||||||
<div class=flashes>
|
|
||||||
{% for category, message in messages %}
|
|
||||||
<li class="alert alert-{{ category }}">{{ message }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
{% block body_content %}
|
{% block body_content %}
|
||||||
<div class="container-fluid">
|
|
||||||
|
|
||||||
<div class="row sticky-top">
|
<div class="row sticky-top">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<!-- Server messages -->
|
<!-- Server messages -->
|
||||||
@ -48,7 +36,7 @@
|
|||||||
|
|
||||||
<button title="File viewer..." class="btn btn-secondary btn-sm"
|
<button title="File viewer..." class="btn btn-secondary btn-sm"
|
||||||
data-toggle="modal" data-target="#file-view-modal">🖼</button>
|
data-toggle="modal" data-target="#file-view-modal">🖼</button>
|
||||||
{% if isLoggedIn %}
|
{% if current_user.is_authenticated or oidc_loggedin() %}
|
||||||
<a href="/logout">
|
<a href="/logout">
|
||||||
<button title="Logout..." class="btn btn-danger btn-sm">
|
<button title="Logout..." class="btn btn-danger btn-sm">
|
||||||
Logout
|
Logout
|
||||||
@ -83,11 +71,10 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ul id="file-grid">
|
<ul id="file-grid" class="scroller">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Favorites modal -->
|
<!-- Favorites modal -->
|
||||||
@ -132,11 +119,18 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col scroller" style="max-height: 30em; overflow: auto;">
|
<div class="col scroller" style="max-height: 30em; overflow: auto;">
|
||||||
<!-- For video -->
|
<!-- For video -->
|
||||||
<video id="video-viewer"
|
<div id="video-container" style="display: contents;">
|
||||||
src=""
|
<video id="video-viewer"
|
||||||
controls="" style="width: 30em;" autoplay=""
|
src=""
|
||||||
poster="{{ url_for('static', filename='imgs/icons/loading.gif')}}">
|
autoplay=""
|
||||||
</video>
|
loop
|
||||||
|
poster="{{ url_for('static', filename='imgs/icons/loading.gif')}}">
|
||||||
|
</video>
|
||||||
|
<div id="video-controls">
|
||||||
|
<input id="seek-slider" type="range" min="0" max="100" step="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- For image -->
|
<!-- For image -->
|
||||||
<img id="image-viewer"
|
<img id="image-viewer"
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% if title %}
|
{% if title %}
|
||||||
<title>{{title}}</title>
|
<title>{{title}}</title>
|
||||||
{% else %}
|
{% else %}
|
||||||
<title>App</title>
|
<title>{{TITLE}}</title>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% block header_css %}
|
{% block header_css %}
|
||||||
@ -14,6 +14,7 @@
|
|||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap/bootstrap.min.css')}}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap/bootstrap.min.css')}}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap/bootstrap-datepicker.css')}}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap/bootstrap-datepicker.css')}}">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/overrides.css')}}">
|
||||||
{% block header_css_additional %}
|
{% block header_css_additional %}
|
||||||
{% endblock header_css_additional %}
|
{% endblock header_css_additional %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -26,10 +27,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
{% block body_header %}
|
<div class="container-fluid">
|
||||||
{% block body_header_additional %}
|
|
||||||
{% endblock body_header_additional%}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<!-- System flashed messages! -->
|
<!-- System flashed messages! -->
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
@ -42,6 +40,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
{% block body_header %}
|
||||||
|
{% block body_header_additional %}
|
||||||
|
{% endblock body_header_additional%}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body_content %}
|
{% block body_content %}
|
||||||
{% block body_content_additional %}
|
{% block body_content_additional %}
|
||||||
{% endblock body_content_additional%}
|
{% endblock body_content_additional%}
|
||||||
@ -53,6 +56,7 @@
|
|||||||
{% endblock body_footer_additional%}
|
{% endblock body_footer_additional%}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block body_scripts %}
|
{% block body_scripts %}
|
||||||
<!-- For Bootstrap in this exact order... -->
|
<!-- For Bootstrap in this exact order... -->
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<!-- <div class="col justify-content-center text-center"> -->
|
<!-- <div class="col justify-content-center text-center"> -->
|
||||||
<form action="" method="POST">
|
<form action="" method="POST">
|
||||||
{{ form.hidden_tag() }}
|
<!-- {{ form.hidden_tag() }} -->
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<legend class="border-bottom mb-4">Login</legend>
|
<legend class="border-bottom mb-4">Login</legend>
|
||||||
<u>
|
<u>
|
||||||
|
9
windows-requirements.txt
Normal file
9
windows-requirements.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Click==7.0
|
||||||
|
Flask==1.1.1
|
||||||
|
Flask-SQLAlchemy==2.4.1
|
||||||
|
waitress==1.4.3
|
||||||
|
itsdangerous==1.1.0
|
||||||
|
Jinja2==2.10.3
|
||||||
|
MarkupSafe==1.1.1
|
||||||
|
SQLAlchemy==1.3.11
|
||||||
|
Werkzeug==0.16.0
|
Loading…
Reference in New Issue
Block a user