player updates, layout updates, base oidc added

This commit is contained in:
maximstewart 2020-12-21 13:02:34 -06:00
parent d833ec437d
commit a4755dd281
18 changed files with 279 additions and 71 deletions

40
create_venv.sh Executable file
View 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 $@;

View File

@ -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 $@;

View File

@ -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 $@;

View File

@ -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
View 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"
}
}

View File

@ -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'])

View File

@ -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)

View File

@ -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 {

View File

@ -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;

View File

@ -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.";

View File

@ -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();

View File

@ -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()
} }

View File

@ -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 %}

View File

@ -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"

View File

@ -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... -->

View File

@ -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
View 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