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}"
echo "Working Dir: " $(pwd)
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 $@;

View File

@ -9,9 +9,9 @@ function main() {
SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
cd "${SCRIPTPATH}"
echo "Working Dir: " $(pwd)
source "./venv/bin/activate"
mkdir /tmp/apps
./venv/bin/gunicorn \
--bind unix:/tmp/apps/webfm.sock wsgi:app
gunicorn --bind unix:/tmp/apps/webfm.sock wsgi:app -p app.pid
}
main $@;

View File

@ -1,29 +1,58 @@
# system import
# System import
import os, json, secrets
from datetime import timedelta
# Flask imports
from flask import Flask, Blueprint
# Lib imports
from flask import Flask
from flask_oidc import OpenIDConnect
from flask_login import current_user, login_user, logout_user, LoginManager
from flask_bcrypt import Bcrypt
# Apoplication imports
# Configs and 'init'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///static/db/webfm.db"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['TITLE'] = 'WebFM'
APP_NAME = 'WebFM'
ROOT_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
CONFIG_FILE = ROOT_FILE_PTH + "/config.json"
# 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
THIS_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
CONFIG_FILE = THIS_FILE_PTH + "/config.json"
def retrieveSettings():
returnData = []
@ -39,7 +68,7 @@ config = retrieveSettings()
from .forms import LoginForm, RegisterForm
from .models import db, Favorites, Settings, User
from webfm import routes
with app.app_context():
db.create_all()
db.init_app(app)
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()
TITLE = app.config['TITLE']
@app.route('/login', methods=['GET', 'POST'])
def login():
@ -28,7 +27,7 @@ def login():
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'])

View File

@ -10,7 +10,6 @@ from ...utils import MessageHandler # Get simple message processor
msgHandler = MessageHandler()
TITLE = app.config['TITLE']
@app.route('/register', methods=['GET', 'POST'])
def register():
@ -27,5 +26,4 @@ def register():
return redirect("/login")
return render_template('register.html',
title=TITLE,
form=_form)
form = _form)

View File

@ -22,12 +22,11 @@
#file-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-row-gap: 1em;
margin: 5em auto;
margin: 2em auto;
width: 85%;
padding: 2em;
overflow-y: auto;
max-height: 25em;
}
@ -35,12 +34,33 @@
#path { max-width: inherit; }
#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 */
.scroller {
scrollbar-color: #00000084 #ffffff64;
scrollbar-width: thin;
scrollbar-color: #00000084 #ffffff64;
scrollbar-width: thin;
}
.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 {
background-color: #32383e74;
border-color: #f8940674;

View File

@ -30,12 +30,7 @@ const doAjax = (actionPath, data, action) => {
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
if (this.responseText != null) { // this.responseXML if getting XML data
if (xhttp.responseURL.includes("/register") ||
xhttp.responseURL.includes("/login")) {
window.location.href = xhttp.responseURL;
} else {
postAjaxController(JSON.parse(this.responseText), action);
}
postAjaxController(JSON.parse(this.responseText), action);
} else {
let type = "danger"
let msg = "No content returned. Check the target path.";

View File

@ -1,7 +1,6 @@
// let eventSource = new EventSource( formatURL("stream") );
// eventSource.onmessage = function(e) {
// console.log(e.data);
// };
let fullScreenState = 0;
let shouldPlay = null;
let clicktapwait = 200;
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) {
searchPage();

View File

@ -15,4 +15,7 @@ const manageFavorites = (elm) => {
const loadFave = (id) => {
loadFavoriteLink(id);
document.getElementById("favorites-modal")
.getElementsByClassName("modal-footer")[0]
.children[0].click()
}

View File

@ -1,7 +1,5 @@
{% extends "layout.html" %}
{% block content %}
<div class="container">
<h1>{{title}}</h1>
<p>{{message}}</p>
</div>
<h1>{{title}}</h1>
<p>{{message}}</p>
{% endblock content %}

View File

@ -2,26 +2,14 @@
{% block body_header_additional %}
<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/bootstrap/bootstrap-table.min.css')}}">
{% 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 %}
<div class="container-fluid">
<div class="row sticky-top">
<div class="col">
<!-- Server messages -->
@ -48,7 +36,7 @@
<button title="File viewer..." class="btn btn-secondary btn-sm"
data-toggle="modal" data-target="#file-view-modal">🖼</button>
{% if isLoggedIn %}
{% if current_user.is_authenticated or oidc_loggedin() %}
<a href="/logout">
<button title="Logout..." class="btn btn-danger btn-sm">
Logout
@ -83,11 +71,10 @@
<div class="row">
<div class="col">
<ul id="file-grid">
<ul id="file-grid" class="scroller">
</ul>
</div>
</div>
</div>
<!-- Favorites modal -->
@ -132,11 +119,18 @@
<div class="row">
<div class="col scroller" style="max-height: 30em; overflow: auto;">
<!-- For video -->
<video id="video-viewer"
src=""
controls="" style="width: 30em;" autoplay=""
poster="{{ url_for('static', filename='imgs/icons/loading.gif')}}">
</video>
<div id="video-container" style="display: contents;">
<video id="video-viewer"
src=""
autoplay=""
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 -->
<img id="image-viewer"

View File

@ -5,7 +5,7 @@
{% if title %}
<title>{{title}}</title>
{% else %}
<title>App</title>
<title>{{TITLE}}</title>
{% endif %}
{% 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-datepicker.css')}}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/overrides.css')}}">
{% block header_css_additional %}
{% endblock header_css_additional %}
{% endblock %}
@ -26,10 +27,7 @@
</head>
<body>
{% block body_header %}
{% block body_header_additional %}
{% endblock body_header_additional%}
{% endblock %}
<div class="container-fluid">
<!-- System flashed messages! -->
{% with messages = get_flashed_messages(with_categories=true) %}
@ -42,6 +40,11 @@
{% endif %}
{% endwith %}
{% block body_header %}
{% block body_header_additional %}
{% endblock body_header_additional%}
{% endblock %}
{% block body_content %}
{% block body_content_additional %}
{% endblock body_content_additional%}
@ -53,6 +56,7 @@
{% endblock body_footer_additional%}
{% endblock %}
</div>
{% block body_scripts %}
<!-- For Bootstrap in this exact order... -->

View File

@ -9,7 +9,7 @@
<div class="col">
<!-- <div class="col justify-content-center text-center"> -->
<form action="" method="POST">
{{ form.hidden_tag() }}
<!-- {{ form.hidden_tag() }} -->
<fieldset class="form-group">
<legend class="border-bottom mb-4">Login</legend>
<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