Compare commits
65 Commits
php-legacy
...
master
Author | SHA1 | Date |
---|---|---|
itdominator | d6a3187f2c | |
itdominator | 93706aeed6 | |
itdominator | f517dca01b | |
itdominator | 3cfd521b60 | |
itdominator | 627f30789d | |
itdominator | ae0486f020 | |
itdominator | 20800fc750 | |
itdominator | bd8e349110 | |
itdominator | 0aeb071215 | |
itdominator | 0c945c72b0 | |
itdominator | 22c437e8ed | |
itdominator | b30a8f4b44 | |
itdominator | 978d4de212 | |
itdominator | 57d52fcddf | |
itdominator | a937d1f313 | |
itdominator | cec3ee0993 | |
itdominator | ffb0459cfe | |
itdominator | be1c12ee86 | |
itdominator | 927be36d9f | |
itdominator | 633cb5b955 | |
itdominator | f61789ffb8 | |
itdominator | 92c7a76a63 | |
itdominator | a7d56bae10 | |
maximstewart | 4dafc328a5 | |
maximstewart | 86b652e8d7 | |
maximstewart | 022046cd3f | |
maximstewart | 94017f61ad | |
maximstewart | d3a30067fa | |
maximstewart | 73192d136f | |
maximstewart | 649a88cfcd | |
maximstewart | 2652810ba1 | |
maximstewart | 0633f25691 | |
maximstewart | 7ac5d7c496 | |
maximstewart | e26495882f | |
maximstewart | 4192726cf6 | |
maximstewart | 92c94f8ae6 | |
maximstewart | 5ad243f475 | |
maximstewart | 781d2ece5a | |
maximstewart | e7d588691e | |
maximstewart | 0ebdc31f19 | |
maximstewart | 7fedf2f367 | |
maximstewart | 664c288fea | |
maximstewart | 16ae96b9cf | |
maximstewart | 2a45797c82 | |
maximstewart | 340b9a54fd | |
maximstewart | a477654021 | |
maximstewart | 47a03155d5 | |
maximstewart | fb429cd1b6 | |
maximstewart | e6531f2ce1 | |
maximstewart | 6c8317e598 | |
maximstewart | f6f7cfda58 | |
maximstewart | 840ffc8255 | |
maximstewart | 0ba35f8f1c | |
maximstewart | 011a2ec370 | |
maximstewart | 786687e0f1 | |
maximstewart | 339c031d28 | |
maximstewart | e9e47f68a5 | |
maximstewart | a7f9028ce7 | |
maximstewart | 59c2d0860f | |
maximstewart | c7e76533ec | |
maximstewart | a4755dd281 | |
Maxim Stewart | d833ec437d | |
Maxim Stewart | 9318b7768a | |
Maxim Stewart | fc21329546 | |
Maxim Stewart | f65f809fb9 |
|
@ -0,0 +1,143 @@
|
||||||
|
*.db
|
||||||
|
*.pyc
|
||||||
|
app.pid
|
||||||
|
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
Before Width: | Height: | Size: 386 KiB |
BIN
Images/000.jpg
Before Width: | Height: | Size: 210 KiB |
BIN
Images/pic1.png
Before Width: | Height: | Size: 1.4 MiB |
BIN
Images/pic2.png
Before Width: | Height: | Size: 2.0 MiB |
BIN
Images/pic3.png
Before Width: | Height: | Size: 1.8 MiB |
BIN
Images/pic4.png
Before Width: | Height: | Size: 2.1 MiB |
BIN
Images/pic5.png
Before Width: | Height: | Size: 2.0 MiB |
BIN
Images/pic6.png
Before Width: | Height: | Size: 2.4 MiB |
BIN
Music/000.jpg
Before Width: | Height: | Size: 1019 KiB |
34
README.md
|
@ -2,22 +2,17 @@
|
||||||
WebFM is a media and file viewer aspiring to become a full fledged file manager in the browser.
|
WebFM is a media and file viewer aspiring to become a full fledged file manager in the browser.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
1. Install php7, php-sqlite3, and ffmpeg on the system this will be on.
|
1. Install python, sqlite3, and ffmpeg on the system this will be on.
|
||||||
2. Use php -S 0.0.0.0:yourDesiredPort
|
3. Use ufw or gufw to open the port on your computer to the local network.
|
||||||
3. Use ufw or gufw to open the port on your computer to the network.
|
4. Use hosts file (or other methods) to redirect webfm.com and ssoapps.com to local app.
|
||||||
4. Place files or start uploading some to the folders.
|
5. Update client_secrets.json > 'client_secret' field with your Keycloak key. (Current one was local, not public, and has been expired)
|
||||||
5. Double click thumbnails and container outlines to open files.
|
6. Place files or start uploading some to the folders.
|
||||||
6. Double click the text name to change the file's or folder's name and press enter to set it.
|
7. Place an image such as a jpg, png, or gif labeled "000.itsExtension" in a directory and the viewer will use it as the background image for that folder/directory.
|
||||||
7. Right-click to get context menu options.
|
7. Password protect folder based on core/utils/shellfm/windows/Settings.py file settings.
|
||||||
8. Place an image such as a jpg, png, or gif labeled "000.itsExtension" in a directory then the viewer will use it as the background image for that folder/directory.
|
8. Save paths to favorites list for quick access.
|
||||||
9. Password protect folder based on resources/php/config.php file setting.
|
|
||||||
10. Save paths to favorites list for quick access.
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
1. The provided folders except "resources" are optional. You can add and remove them as you please.
|
n/a
|
||||||
2. The media and image pane can be moved by dragging from the transparentish bar that has the close button and other controls.
|
|
||||||
3. Edit the resources/php/config.php file and put your own programs there.
|
|
||||||
4. Edit your php.ini file "upload_max_filesize" and "post_max_size" to be higher to upload larger files.
|
|
||||||
|
|
||||||
# TO-DO
|
# TO-DO
|
||||||
1. Allow for move and copy.
|
1. Allow for move and copy.
|
||||||
|
@ -25,9 +20,8 @@ Notes:
|
||||||
|
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
![1 Home](Images/pic1.png)
|
![1 Videos List](images/pic1.png)
|
||||||
![2 Images Listed](Images/pic2.png)
|
![2 Video Playing](images/pic2.png)
|
||||||
![3 Videos Listed](Images/pic3.png)
|
![3 Images List](images/pic3.png)
|
||||||
![4 Image Open](Images/pic4.png)
|
![4 Context menu](images/pic4.png)
|
||||||
![5 Image Open And Video Playing](Images/pic5.png)
|
![5 Settings Pane With Upload And Create Functionality](images/pic5.png)
|
||||||
![6 Alternate Background](Images/pic6.png)
|
|
||||||
|
|
BIN
Videos/000.jpg
Before Width: | Height: | Size: 1.9 MiB |
|
@ -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 $@;
|
|
@ -1 +0,0 @@
|
||||||
LOL...Not really!
|
|
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 370 KiB |
After Width: | Height: | Size: 299 KiB |
120
index.html
|
@ -1,120 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta content="" charset="utf-8"/>
|
|
||||||
<title>Web File Manager</title>
|
|
||||||
<link type="text/css" rel="stylesheet" href="resources/css/base.css"/>
|
|
||||||
<link type="text/css" rel="stylesheet" href="resources/css/main.css"/>
|
|
||||||
<link rel="shortcut icon" type="image/png" href="favicon.png"/>
|
|
||||||
</head>
|
|
||||||
<body onload="onloadSetBG(); getDir('/')" contextmenu="menu">
|
|
||||||
<!-- Background -->
|
|
||||||
<img id="bg" />
|
|
||||||
|
|
||||||
<video id="video" src="" controls >
|
|
||||||
</video>
|
|
||||||
|
|
||||||
<!-- Controls -->
|
|
||||||
<!-- Create the menu -->
|
|
||||||
<menu type="context" id="menu">
|
|
||||||
<menuitem label="Home Directory" onclick="clearDirCookie()"></menuitem>
|
|
||||||
<menuitem label="Show Server Messages" onclick="tgglElmView('serverMsgView')"></menuitem>
|
|
||||||
<menuitem label="Clear Upload List" onclick="clearDlList()"></menuitem>
|
|
||||||
<menuitem label="Download" onclick="downloadItem()"></menuitem>
|
|
||||||
<menuitem label="Delete" onclick="deleteItem()"></menuitem>
|
|
||||||
</menu>
|
|
||||||
|
|
||||||
<!-- Uploader -->
|
|
||||||
<h2 id="controls">
|
|
||||||
<button type="button" title="Other Options" onclick="tgglElmView('popOutControls')">⚙</button>
|
|
||||||
<button type="button" title="Refresh" onclick="getDir('./')">↻</button>
|
|
||||||
<button type="button" title="Back" onclick="getDir('../')">⇐</button>
|
|
||||||
<input type="text" placeholder="Search..." onkeyup="searchPage(this)" name="" value="">
|
|
||||||
<button type="button" onclick="clearSearch()" title="Clears search..." >Clear Search</button>
|
|
||||||
<button onclick="getFavesList(); tgglElmView('favesList')">Faves List ↕</button>
|
|
||||||
<button type="button" onclick="lockFolders()" title="Lock unlocked folders..." >Lock Unlocked Folders</button>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<button type="button" id="faves" onclick="faveManager(this)" title="Add/Delete from favorites..." >☆</button>
|
|
||||||
Path:<span id="path"></span>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div id="popOutControls" style="display:none;">
|
|
||||||
<center>
|
|
||||||
<form>
|
|
||||||
<input class="ulFile" type="file" title="files To Upload" name="filesToUpload[]" data-multiple-caption="{count} files selected" multiple />
|
|
||||||
<input type="button" onclick="uploadFiles()" name="UploadFiles" title="Upload File(s)" value="Upload File(s)" />
|
|
||||||
<input type="reset" title="Clear" id="CLEARBTTN" value="Clear" style="display:none;">
|
|
||||||
<input type="text" id="DIRPATHUL" name="DIRPATHUL" value="">
|
|
||||||
</form>
|
|
||||||
<br/>
|
|
||||||
<input type="text" id="NewItem" value=""/>
|
|
||||||
<input type="button" value="New Dir" onclick="createItem('dir')"/>
|
|
||||||
<input type="button" value="New File" onclick="createItem('file')"/>
|
|
||||||
<input type="button" value="Show Server Messages" onclick="tgglElmView('serverMsgView')"/>
|
|
||||||
<br/>
|
|
||||||
<input id="MergeType" type="checkbox" onchange="getDir('./')" />
|
|
||||||
<label for="MergeType">Show seassons in same list.</label>
|
|
||||||
</center>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Dynamic content targets -->
|
|
||||||
<ul id="favesList" style="display: none;"> </ul>
|
|
||||||
<ul id="dynUl"></ul>
|
|
||||||
|
|
||||||
<!-- Uploader processor -->
|
|
||||||
<div id="serverMsgView" style="display:none;"> </div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Templates -->
|
|
||||||
<template id="dirTemplate">
|
|
||||||
<li class="dirStyle" tabindex="1">
|
|
||||||
<img id="dirID" class="systemIcon" src="" />
|
|
||||||
<input id="titleID" class="dirTitle" type="text" value="" readonly="true" />
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
<template id="vidTemplate">
|
|
||||||
<li id="movieID" class="movieStyle" tabindex="1" style="background-image: url('')">
|
|
||||||
<span class="popOutBttnInner" title="Open In Local Program">∽</span>
|
|
||||||
<input id="titleID" class="movieTitle" type="text" value="" readonly="true" />
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
<template id="imgTemplate">
|
|
||||||
<img id="imageID" class="iconImg" src="" alt="">
|
|
||||||
</template>
|
|
||||||
<template id="filTemplate">
|
|
||||||
<li class="fileStyle">
|
|
||||||
<img id="fileID" class="systemIcon" src="" />
|
|
||||||
<input id="titleID" class="fileTitle" type="text" value="" readonly="true" />
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
if (window.self !== window.top) {
|
|
||||||
setTimeout(function () {
|
|
||||||
let elm = document.getElementById("bg");
|
|
||||||
elm.parentElement.removeChild(elm);
|
|
||||||
|
|
||||||
// Stylesheet for iframe views
|
|
||||||
var link = document.createElement("link");
|
|
||||||
link.href = "resources/css/iframe.css";
|
|
||||||
link.type = "text/css";
|
|
||||||
link.rel = "stylesheet";
|
|
||||||
document.getElementsByTagName("head")[0].appendChild(link);
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="resources/js/favorites.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/passwordFieldInsert.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/cookieHandler.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/jsonParser.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/ajax.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/uiActions.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/filesystemActions.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="resources/js/uiEvents.js" charset="utf-8"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
bcrypt==4.0.1
|
||||||
|
certifi==2022.12.7
|
||||||
|
charset-normalizer==3.0.1
|
||||||
|
click==7.1.2
|
||||||
|
dnspython==1.16.0
|
||||||
|
email-validator==1.1.2
|
||||||
|
eventlet==0.30.1
|
||||||
|
Flask==1.1.2
|
||||||
|
Flask-Bcrypt==0.7.1
|
||||||
|
Flask-Login==0.5.0
|
||||||
|
flask-oidc==1.4.0
|
||||||
|
Flask-SQLAlchemy==2.4.4
|
||||||
|
Flask-Uploads==0.2.1
|
||||||
|
Flask-WTF==0.14.3
|
||||||
|
greenlet==1.0.0
|
||||||
|
gunicorn==20.0.4
|
||||||
|
httplib2==0.19.0
|
||||||
|
idna==3.4
|
||||||
|
itsdangerous==1.1.0
|
||||||
|
Jinja2==2.11.3
|
||||||
|
MarkupSafe==1.1.1
|
||||||
|
oauth2client==4.1.3
|
||||||
|
Pillow==9.4.0
|
||||||
|
pyasn1==0.4.8
|
||||||
|
pyasn1-modules==0.2.8
|
||||||
|
pycairo==1.23.0
|
||||||
|
PyGObject==3.42.2
|
||||||
|
pyparsing==2.4.7
|
||||||
|
pyxdg==0.28
|
||||||
|
requests==2.28.2
|
||||||
|
rsa==4.7
|
||||||
|
six==1.15.0
|
||||||
|
SQLAlchemy==1.3.23
|
||||||
|
urllib3==1.26.14
|
||||||
|
Werkzeug==1.0.1
|
||||||
|
WTForms==2.3.3
|
|
@ -1,8 +0,0 @@
|
||||||
html {
|
|
||||||
margin: 0em;
|
|
||||||
padding: 0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol, ul, li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
#controls, #fullPathHeader, #dynDiv,
|
|
||||||
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
|
|
||||||
background-color: rgba(0,0,0,0.2);
|
|
||||||
}
|
|
|
@ -1,304 +0,0 @@
|
||||||
/* IDs */
|
|
||||||
|
|
||||||
#DIRPATHUL {
|
|
||||||
display: none;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#video,
|
|
||||||
#bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -999;
|
|
||||||
}
|
|
||||||
|
|
||||||
#video,
|
|
||||||
#bg img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -999;
|
|
||||||
}
|
|
||||||
|
|
||||||
#video {
|
|
||||||
position: fixed;
|
|
||||||
display: none;
|
|
||||||
background-color: rgba(0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls, #dynUl,
|
|
||||||
.errorStyling, .dirStyle, .movieStyle, .fileStyle {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
overflow: auto;
|
|
||||||
padding-bottom: 0.5em;
|
|
||||||
color: #ffffff;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.2em;
|
|
||||||
background-color: rgba(0,0,0,0.64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#favesList {
|
|
||||||
border-style: solid;
|
|
||||||
border-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-width: 0.2em;
|
|
||||||
background-color: rgba(7, 150, 159, 0.8);
|
|
||||||
position: fixed;
|
|
||||||
font-size: 2em;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: scroll;
|
|
||||||
padding: 1.5em;
|
|
||||||
max-height: 632px;
|
|
||||||
color: #ffffff;
|
|
||||||
z-index: 888;
|
|
||||||
}
|
|
||||||
|
|
||||||
#favesList > li:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgba(92, 199, 35, 0.8);
|
|
||||||
padding-left: 1em;
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 999;
|
|
||||||
top: 0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dynUl {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(18em, 1fr));
|
|
||||||
grid-column-gap: 1em;
|
|
||||||
grid-row-gap: 1em;
|
|
||||||
margin: 5em auto;
|
|
||||||
width: 85%;
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#imgView, #imgArea, #fileView {
|
|
||||||
width: 800px;
|
|
||||||
height: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#imgView, #fileView {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0em;
|
|
||||||
z-index: 100;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: rgb(114,184,199);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#fileView {
|
|
||||||
display: block;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fileViewInner {
|
|
||||||
position: sticky;
|
|
||||||
display: inline;
|
|
||||||
width: 100%;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#imgArea {
|
|
||||||
width: 800px;
|
|
||||||
height: 600px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
#imgView {
|
|
||||||
overflow: hidden;
|
|
||||||
left: 15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#NewItem {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #000000;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#popOutControls {
|
|
||||||
position: fixed;
|
|
||||||
top: 15%;
|
|
||||||
width: 99%;
|
|
||||||
height: 15em;
|
|
||||||
padding-top: 6em;
|
|
||||||
opacity: 0.94;
|
|
||||||
background: radial-gradient(circle,#3f3f3f,#000000);
|
|
||||||
color: #ffffff;
|
|
||||||
text-align: center;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
#serverMsgView {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0em;
|
|
||||||
height: 5em;
|
|
||||||
overflow-y: scroll;
|
|
||||||
width: 100%;
|
|
||||||
background-color: rgba(0,0,0,0.64);
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchField {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchField:focus {
|
|
||||||
height: 2em;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: thin;
|
|
||||||
border-color: rgba(55, 204, 209, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Classes */
|
|
||||||
.imgViewImg {
|
|
||||||
width: inherit;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.dirStyle { background-color: rgba(0, 0, 0, 0.56); }
|
|
||||||
.movieStyle, .fileStyle { background-color: rgba(101, 101, 101, 0.56); }
|
|
||||||
|
|
||||||
.movieStyle {
|
|
||||||
min-height: 6.5em;
|
|
||||||
overflow: hidden;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.videoInputField {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 5.5em;
|
|
||||||
background-color: rgba(0, 0, 0, 0.64);
|
|
||||||
color: rgb(255, 255, 255);
|
|
||||||
text-align: center;
|
|
||||||
border-top: 1px solid rgb(255, 255, 255);
|
|
||||||
border-bottom: 1px solid rgb(255, 255, 255);
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dirStyle:hover, .movieStyle:hover, .fileStyle:hover {
|
|
||||||
background-color: rgba(0, 141, 166, 0.56);
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0px 0px 15px rgb(114,184,199);
|
|
||||||
border-radius: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dirStyle:focus, .movieStyle:focus, .fileStyle:focus {
|
|
||||||
background-color: rgba(0, 139, 35, 0.76);
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0px 0px 25px rgb(114, 199, 120);
|
|
||||||
border-radius: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dirTitle, .fileTitle, .movieTitle {
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
border-style: none;
|
|
||||||
font-size: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dirTitle, .fileTitle {
|
|
||||||
width: auto;
|
|
||||||
background-color: #00000000;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.movieTitle {
|
|
||||||
width: 18em;
|
|
||||||
background-color: #ffffff00;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnail {
|
|
||||||
width: 12em;
|
|
||||||
height: 6.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.systemIcon {
|
|
||||||
width: 2em;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconImg {
|
|
||||||
width: 18em;
|
|
||||||
height: 12em;
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popOutBttn, .closeBttn {
|
|
||||||
float: right;
|
|
||||||
z-index: 2;
|
|
||||||
width: 4em;
|
|
||||||
height: 4em;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
line-height: 4em; /* the same as your div height */
|
|
||||||
background-color: rgba(0,0,0, 0.85);
|
|
||||||
color: rgb(255,255,255);
|
|
||||||
border-style:solid;
|
|
||||||
border-color: rgb(255,255,255);
|
|
||||||
}
|
|
||||||
|
|
||||||
.popOutBttnInner {
|
|
||||||
float: right;
|
|
||||||
z-index: 2;
|
|
||||||
width: 2em;
|
|
||||||
height: 2em;
|
|
||||||
text-align: center;
|
|
||||||
background-color: rgba(0,0,0, 0.85);
|
|
||||||
color: rgb(255,255,255);
|
|
||||||
border-style:solid;
|
|
||||||
border-color: rgb(255,255,255);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.completionBar {
|
|
||||||
float:left;
|
|
||||||
clear:left;
|
|
||||||
height: 0.1em;
|
|
||||||
background-color: rgba(25, 125, 10, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hover events */
|
|
||||||
.dirTitle:hover,
|
|
||||||
.iconImg:hover,
|
|
||||||
.closeBttn:hover,
|
|
||||||
.popOutBttnInner:hover,
|
|
||||||
.popOutBttn:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popOutBttnInner:hover,
|
|
||||||
.popOutBttn:hover,
|
|
||||||
.closeBttn:hover {
|
|
||||||
background-color: rgba(255,255,255, 0.85);
|
|
||||||
color: #000000;
|
|
||||||
border-color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Messages coloring */
|
|
||||||
.error, .warnning, .success {
|
|
||||||
float: left;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error { color: rgb(255, 0, 0); }
|
|
||||||
.warning { color: rgb(255, 168, 0); }
|
|
||||||
.success { color: rgb(136, 204, 39); }
|
|
Before Width: | Height: | Size: 7.4 KiB |
|
@ -1 +0,0 @@
|
||||||
Place Holder File
|
|
|
@ -1,54 +0,0 @@
|
||||||
// SSE events if supported
|
|
||||||
if(typeof(EventSource) !== "undefined") {
|
|
||||||
let source = new EventSource("resources/php/sse.php");
|
|
||||||
source.onmessage = (event) => {
|
|
||||||
if (event.data === "updateListing") {
|
|
||||||
getDir("./");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log("SSE Not Supported In Browser...");
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFavesList = () => {
|
|
||||||
doAjax("resources/php/dbController.php", "getTabs=true");
|
|
||||||
}
|
|
||||||
|
|
||||||
const doAjax = async (actionPath, data) => {
|
|
||||||
let xhttp = new XMLHttpRequest();
|
|
||||||
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState === 4 && this.status === 200) {
|
|
||||||
// Send the returned data to further process
|
|
||||||
if (this.responseText != null) {
|
|
||||||
handleJSONReturnData(JSON.parse(this.responseText));
|
|
||||||
} else {
|
|
||||||
document.getElementById('dynUl').innerHTML =
|
|
||||||
"<p class=\"error\" style=\"width:100%;text-align:center;\"> "
|
|
||||||
+ "No content returned. Check the folder path.</p>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhttp.open("POST", actionPath, true);
|
|
||||||
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
||||||
xhttp.overrideMimeType('application/json'); // Force return to be JSON
|
|
||||||
xhttp.send(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileUploader = (data) => {
|
|
||||||
let xhttp = new XMLHttpRequest();
|
|
||||||
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState === 4 && this.status === 200) {
|
|
||||||
// Send the returned data to further process
|
|
||||||
if (this.responseXML != null) {
|
|
||||||
handleXMLReturnData(this.responseXML);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhttp.open("POST", "resources/php/filesystemActions.php", true);
|
|
||||||
xhttp.overrideMimeType('application/xml'); // Force return to be XML
|
|
||||||
xhttp.send(data);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
const getCookie = (cname) => {
|
|
||||||
let decodedCookie = decodeURIComponent(document.cookie);
|
|
||||||
let name = cname + "=";
|
|
||||||
let ca = decodedCookie.split(';');
|
|
||||||
for(let i = 0; i <ca.length; i++) {
|
|
||||||
let c = ca[i];
|
|
||||||
while (c.charAt(0) == ' ') {
|
|
||||||
c = c.substring(1);
|
|
||||||
}
|
|
||||||
if (c.indexOf(name) == 0) {
|
|
||||||
return c.substring(name.length, c.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
const faveManager = (elm) => {
|
|
||||||
let path = document.getElementById("path").innerHTML;
|
|
||||||
let data = "";
|
|
||||||
|
|
||||||
if (elm.style.backgroundColor != "") {
|
|
||||||
elm.style.backgroundColor = "";
|
|
||||||
elm.style.color = "";
|
|
||||||
data = "deleteLink=true";
|
|
||||||
} else {
|
|
||||||
elm.style.backgroundColor = "rgb(255, 255, 255)";
|
|
||||||
elm.style.color = "rgb(0, 0, 0)";
|
|
||||||
data = "deleteLink=false";
|
|
||||||
}
|
|
||||||
|
|
||||||
data += "&linkPath=" + path;
|
|
||||||
doAjax("resources/php/dbController.php", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basically resetting path nodes and setting them up
|
|
||||||
// to the new path and just doing a refresh
|
|
||||||
const loadFave = (elm) => {
|
|
||||||
let path = elm.getAttribute("name");
|
|
||||||
let parts = path.split("/");
|
|
||||||
let size = parts.length;
|
|
||||||
pathNodes = [];
|
|
||||||
|
|
||||||
pathNodes.push(parts[0] + "/");
|
|
||||||
for (let i = 1; i < size - 1; i++) {
|
|
||||||
pathNodes.push(parts[i] + "/");
|
|
||||||
}
|
|
||||||
pathNodes.push(parts[size - 1]);
|
|
||||||
|
|
||||||
getDir("./");
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
let binary = null;
|
|
||||||
let pathNodes = [];
|
|
||||||
|
|
||||||
const lockFolders = () => {
|
|
||||||
const data = "lockFolders=true";
|
|
||||||
doAjax("resources/php/lockedFolders.php", data);
|
|
||||||
getDir("./");
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDir = (query) => {
|
|
||||||
document.getElementById("controls").style.opacity = "1";
|
|
||||||
document.getElementById("dynUl").style.display = "grid";
|
|
||||||
document.getElementById("video").src = "#";
|
|
||||||
document.getElementById("video").style.display = "none";
|
|
||||||
|
|
||||||
let formUlPth = document.getElementById("DIRPATHUL");
|
|
||||||
let mergeType = document.getElementById("MergeType");
|
|
||||||
let passwd = undefined;
|
|
||||||
let data = "";
|
|
||||||
let cookies = "";
|
|
||||||
let dirCookie = "";
|
|
||||||
|
|
||||||
// push or pop to path list
|
|
||||||
if (query === "/") {
|
|
||||||
// Process path from cookie and set to array/list
|
|
||||||
dirCookie = getCookie("dirQuery");
|
|
||||||
if (dirCookie != "" && dirCookie != "./") {
|
|
||||||
dirCookie = dirCookie.split("/");
|
|
||||||
dirCookie.pop(); // account for ending empty slot
|
|
||||||
|
|
||||||
let size = dirCookie.length;
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
pathNodes.push(dirCookie[i] + "/");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pathNodes = [];
|
|
||||||
pathNodes.push("." + query);
|
|
||||||
}
|
|
||||||
} else if (query === "../") {
|
|
||||||
// Only remove while not in root
|
|
||||||
if (pathNodes.length > 1) {
|
|
||||||
pathNodes.pop();
|
|
||||||
}
|
|
||||||
} else if (query === "./") {
|
|
||||||
// Do nothing since re-scanning dir
|
|
||||||
} else {
|
|
||||||
pathNodes.push(query); // Add path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create path from array of items
|
|
||||||
for (pathNode of pathNodes) { data += pathNode; }
|
|
||||||
|
|
||||||
try {
|
|
||||||
passwd = document.getElementById("PASSWD").value;
|
|
||||||
} catch (e) {
|
|
||||||
passwd = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup upload path for form and make a cookie for persistence during browser session....
|
|
||||||
formUlPth.value = data;
|
|
||||||
data = "dirQuery=" + encodeURIComponent(data);
|
|
||||||
document.cookie = data + "; expires=Sun, 31 Dec 2034 12:00:00 UTC";
|
|
||||||
data +="&mergeType=" + mergeType.checked
|
|
||||||
+ "&passwd=" + passwd;
|
|
||||||
|
|
||||||
doAjax("resources/php/getDirList.php", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadFiles = async () => {
|
|
||||||
let toUpload = document.getElementsByName("filesToUpload[]")[0];
|
|
||||||
let path = document.getElementById("path").innerHTML;
|
|
||||||
let reader = new FileReader();
|
|
||||||
let data = new FormData();
|
|
||||||
let size = toUpload.files.length;
|
|
||||||
|
|
||||||
data.append("UploadFiles", "trut");
|
|
||||||
data.append("DIRPATHUL", path);
|
|
||||||
|
|
||||||
// Add files
|
|
||||||
if (size > 0) {
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
data.append("filesToUpload[]", toUpload.files[i]);
|
|
||||||
}
|
|
||||||
fileUploader(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createItem = (type) => {
|
|
||||||
let path = document.getElementById("path").innerHTML;
|
|
||||||
let newItem = document.getElementById("NewItem");
|
|
||||||
let fullPth = path + newItem.value;
|
|
||||||
newItem.value = "";
|
|
||||||
fullPth = encodeURIComponent(fullPth);
|
|
||||||
|
|
||||||
doAjax("resources/php/filesystemActions.php",
|
|
||||||
"createItem=true&item=" + fullPth + "&type=" + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteItem = () => {
|
|
||||||
let path = document.getElementById("path").innerHTML;
|
|
||||||
// Clicked yes to delete and there is an item
|
|
||||||
if (itemObj != undefined && itemObj != null) {
|
|
||||||
let fullPth = path + itemObj;
|
|
||||||
fullPth = encodeURIComponent(fullPth);
|
|
||||||
let answer = confirm("Are you sure you want to delete: " + fullPth);
|
|
||||||
if (answer == true) {
|
|
||||||
doAjax("resources/php/filesystemActions.php",
|
|
||||||
"deleteItem=true&item=" + fullPth);
|
|
||||||
|
|
||||||
console.log("Deleted: " + fullPth);
|
|
||||||
itemObj = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const renameItem = (obj) => {
|
|
||||||
let path = encodeURIComponent(document.getElementById("path").innerHTML);
|
|
||||||
let oldName = encodeURIComponent(formerFileName);
|
|
||||||
let newName = encodeURIComponent(obj.value);
|
|
||||||
let formData = "renameItem=true&oldName=" + oldName + "&newName=" + newName + "&path=" + path;
|
|
||||||
|
|
||||||
console.log("Old name: " + oldName);
|
|
||||||
console.log("New name: " + newName);
|
|
||||||
|
|
||||||
doAjax("resources/php/filesystemActions.php",
|
|
||||||
formData);
|
|
||||||
}
|
|
||||||
|
|
||||||
const openInLocalProg = (media) => {
|
|
||||||
doAjax("resources/php/filesystemActions.php",
|
|
||||||
"media=" + media);
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
const insertArea = document.getElementById('dynUl');
|
|
||||||
|
|
||||||
|
|
||||||
const handleJSONReturnData = (data) => {
|
|
||||||
if (data.message) {
|
|
||||||
if (data.message.type == "locked") {
|
|
||||||
createPassField();
|
|
||||||
} else {
|
|
||||||
const text = document.createTextNode(data.message.text)
|
|
||||||
document.getElementById("serverMsgView").appendChild(text);
|
|
||||||
}
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.list) {
|
|
||||||
updateHTMLDirList(data);
|
|
||||||
} else if (data.FAVES_LIST) {
|
|
||||||
generateFavesList(data.FAVES_LIST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateFavesList = (data) => {
|
|
||||||
let listView = document.getElementById("favesList");
|
|
||||||
listView.innerHTML = "";
|
|
||||||
|
|
||||||
data.forEach(fave => {
|
|
||||||
let liTag = document.createElement("LI");
|
|
||||||
let parts = (fave.includes("/")) ? fave.split("/") : fave.split("\\");
|
|
||||||
let txtNode = document.createTextNode(parts[parts.length - 2]);
|
|
||||||
|
|
||||||
liTag.setAttribute("name", fave);
|
|
||||||
liTag.setAttribute("title", fave);
|
|
||||||
liTag.setAttribute("onclick", "loadFave(this)");
|
|
||||||
liTag.appendChild(txtNode);
|
|
||||||
listView.appendChild(liTag);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateHTMLDirList = async (data) => {
|
|
||||||
var dirTemplate = document.querySelector('#dirTemplate');
|
|
||||||
var vidTemplate = document.querySelector('#vidTemplate');
|
|
||||||
var imgTemplate = document.querySelector('#imgTemplate');
|
|
||||||
var filTemplate = document.querySelector('#filTemplate');
|
|
||||||
let dirPath = data.PATH_HEAD;
|
|
||||||
let isInFaves = data.IN_FAVE;
|
|
||||||
let dirs = (data.list.dirs) ? data.list.dirs : [];
|
|
||||||
let videos = (data.list.vids) ? data.list.vids : [];
|
|
||||||
let images = (data.list.imgs) ? data.list.imgs : [];
|
|
||||||
let files = (data.list.files) ? data.list.files : [];
|
|
||||||
let i = 0;
|
|
||||||
let size = 0;
|
|
||||||
|
|
||||||
document.getElementById("path").innerHTML = dirPath;
|
|
||||||
insertArea.innerHTML = "";
|
|
||||||
|
|
||||||
// Setup background if there is a 000.* in selection
|
|
||||||
let bgImgPth = images[0] ? images[0].image : "";
|
|
||||||
if (bgImgPth.match(/000\.(jpg|png|gif)\b/) != null) {
|
|
||||||
updateBG(dirPath + bgImgPth);
|
|
||||||
} else {
|
|
||||||
updateBG("resources/images/backgrounds/000.jpg");
|
|
||||||
}
|
|
||||||
|
|
||||||
// determin whether to style faves or not
|
|
||||||
let elm = document.getElementById("faves");
|
|
||||||
if (isInFaves == "true") {
|
|
||||||
elm.style.backgroundColor = "rgb(255, 255, 255)";
|
|
||||||
elm.style.color = "rgb(0, 0, 0)";
|
|
||||||
} else {
|
|
||||||
elm.style.backgroundColor = "";
|
|
||||||
elm.style.color = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert dirs
|
|
||||||
let dirClone = document.importNode(dirTemplate.content, true);
|
|
||||||
let dirImg = "resources/images/icons/folder.png";
|
|
||||||
let dir = null;
|
|
||||||
size = dirs.length;
|
|
||||||
for (; i < size; i++) {
|
|
||||||
dir = dirs[i].dir;
|
|
||||||
const clone = dirClone.cloneNode(true);
|
|
||||||
createElmBlock(clone, dirImg, dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert videos
|
|
||||||
let vidClone = document.importNode(vidTemplate.content, true);
|
|
||||||
let thumbnail = "";
|
|
||||||
let title = "";
|
|
||||||
size = videos.length;
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
title = videos[i].video.title;
|
|
||||||
thumbnail = videos[i].video.thumbnail;
|
|
||||||
const clone = vidClone.cloneNode(true);
|
|
||||||
createElmBlock(clone, thumbnail, title, true, dirPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert images
|
|
||||||
let imgClone = document.importNode(imgTemplate.content, true);
|
|
||||||
thumbnail = "";
|
|
||||||
size = images.length;
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
thumbnail = images[i].image;
|
|
||||||
if (thumbnail.match(/000\.(jpg|png|gif)\b/) == null &&
|
|
||||||
!thumbnail.includes("favicon.png")) {
|
|
||||||
const clone = imgClone.cloneNode(true);
|
|
||||||
let imgTag = clone.firstElementChild;
|
|
||||||
imgTag.src = dirPath + '/' + thumbnail;
|
|
||||||
imgTag.alt = thumbnail;
|
|
||||||
insertArea.appendChild(clone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert files
|
|
||||||
let fileClone = document.importNode(filTemplate.content, true);
|
|
||||||
size = files.length;
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
const clone = fileClone.cloneNode(true);
|
|
||||||
let fileName = files[i].file;
|
|
||||||
createElmBlock(clone, setFileIconType(fileName), fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createElmBlock = (elm, imgSrc, fileName, isVideo = null, path = null) => {
|
|
||||||
contnrTag = elm.firstElementChild;
|
|
||||||
let imgTag = null;
|
|
||||||
let inputTag = elm.querySelector("input");
|
|
||||||
|
|
||||||
if (isVideo) {
|
|
||||||
contnrTag.style = "background-image: url('/resources/images/thumbnails/" + imgSrc + "')";
|
|
||||||
inputTag.className = "videoInputField";
|
|
||||||
let fullMedia = path + fileName;
|
|
||||||
elm.querySelector("span").addEventListener("click", function (eve) {
|
|
||||||
openInLocalProg(fullMedia);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
imgTag = elm.querySelector("img");
|
|
||||||
imgTag.src = imgSrc;
|
|
||||||
imgTag.alt = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
contnrTag.title = fileName;
|
|
||||||
inputTag.value = fileName;
|
|
||||||
inputTag.addEventListener("focusout", function (eve) {
|
|
||||||
disableEdits(eve.target);
|
|
||||||
});
|
|
||||||
insertArea.appendChild(elm);
|
|
||||||
}
|
|
||||||
|
|
||||||
const setFileIconType = (fileName) => {
|
|
||||||
if (fileName.match(/\.(doc|docx|xls|xlsx|rtf)\b/) != null) {
|
|
||||||
return "resources/images/icons/doc.png";
|
|
||||||
} else if (fileName.match(/\.(7z|7zip|zip|tar.gz|tar.xz|gz|rar|jar)\b/) != null) {
|
|
||||||
return "resources/images/icons/arc.png";
|
|
||||||
} else if (fileName.match(/\.(pdf)\b/) != null) {
|
|
||||||
return "resources/images/icons/pdf.png";
|
|
||||||
} else if (fileName.match(/\.(html)\b/) != null) {
|
|
||||||
return "resources/images/icons/html.png";
|
|
||||||
} else if (fileName.match(/\.(txt|conf)\b/) != null) {
|
|
||||||
return "resources/images/icons/text.png";
|
|
||||||
} else if (fileName.match(/\.(iso|img)\b/) != null) {
|
|
||||||
return "resources/images/icons/img.png";
|
|
||||||
} else if (fileName.match(/\.(sh|batch|exe)\b/) != null) {
|
|
||||||
return "resources/images/icons/scrip.png";
|
|
||||||
} else {
|
|
||||||
return "resources/images/icons/bin.png";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
const createPassField = () => {
|
|
||||||
let passField = document.createElement("INPUT");
|
|
||||||
let submitBttn = document.createElement("BUTTON");
|
|
||||||
passField.id = "PASSWD";
|
|
||||||
passField.type = "password";
|
|
||||||
passField.placeholder = "Password...";
|
|
||||||
submitBttn.innerHTML = "Submit";
|
|
||||||
insertArea.innerHTML = "";
|
|
||||||
|
|
||||||
passField.onkeyup = (eve) => {
|
|
||||||
if (eve.key == "Enter") {
|
|
||||||
getDir("./");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
submitBttn.onclick = () => {
|
|
||||||
getDir("./");
|
|
||||||
};
|
|
||||||
|
|
||||||
insertArea.appendChild(passField);
|
|
||||||
insertArea.appendChild(submitBttn);
|
|
||||||
}
|
|
|
@ -1,189 +0,0 @@
|
||||||
let formerFileName = "";
|
|
||||||
|
|
||||||
const tgglElmView = (id) => {
|
|
||||||
let elm = document.getElementById(id);
|
|
||||||
if (elm.style.display == "none") {
|
|
||||||
elm.style.display = "block";
|
|
||||||
} else {
|
|
||||||
elm.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchPage = (elm) => {
|
|
||||||
let query = elm.value.toLowerCase();
|
|
||||||
let list = document.getElementById("dynUl").querySelectorAll("[title]");
|
|
||||||
let size = list.length;
|
|
||||||
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
if (!list[i].title.toLowerCase().includes(query)) {
|
|
||||||
list[i].style.display = "none";
|
|
||||||
} else {
|
|
||||||
list[i].style.display = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearSearch = () => {
|
|
||||||
let list = document.getElementById("dynUl").querySelectorAll("[title]");
|
|
||||||
let size = list.length;
|
|
||||||
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
list[i].style.display = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const enableEdit = (obj) => {
|
|
||||||
obj.style.backgroundColor = "#ffffffff";
|
|
||||||
obj.style.color = '#000000ff';
|
|
||||||
obj.readOnly = '';
|
|
||||||
formerFileName = obj.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const disableEdits = (elm) => {
|
|
||||||
elm.style.backgroundColor = "";
|
|
||||||
elm.style.color = '';
|
|
||||||
elm.value = formerFileName;
|
|
||||||
elm.readOnly = "true";
|
|
||||||
}
|
|
||||||
|
|
||||||
const showMedia = async (mediaLoc, type) => {
|
|
||||||
let path = document.getElementById("path").innerHTML;
|
|
||||||
let tempRef = mediaLoc.toLowerCase();
|
|
||||||
let fullMedia = path + mediaLoc;
|
|
||||||
|
|
||||||
if (type === "video") {
|
|
||||||
setupVideo(type, fullMedia, tempRef);
|
|
||||||
} else {
|
|
||||||
createFloatingPane(type, fullMedia);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupVideo = async (type, fullMedia, tempRef) => {
|
|
||||||
try {
|
|
||||||
let video = document.getElementById("video");
|
|
||||||
video.autoplay = true;
|
|
||||||
video.poster = "resources/images/loading.gif";
|
|
||||||
|
|
||||||
if ((/\.(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|mp3|webm|flac|ogg|pdf)$/i).test(tempRef)) {
|
|
||||||
if ((/\.(mkv|avi|wmv)$/i).test(tempRef)) {
|
|
||||||
const params = "remuxVideo=true&mediaPth=" + fullMedia;
|
|
||||||
let response = await fetch("resources/php/filesystemActions.php",
|
|
||||||
{method: "POST", body: new URLSearchParams(params)});
|
|
||||||
let xml = new window.DOMParser().parseFromString(await response.text(), "text/xml");
|
|
||||||
|
|
||||||
if (xml.getElementsByTagName("REMUX_PATH")[0]) {
|
|
||||||
fullMedia = xml.getElementsByTagName("REMUX_PATH")[0].innerHTML;
|
|
||||||
} else {
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
} else if ((/\.(avi|flv|mov|m4v|mpg|wmv)$/i).test(tempRef)) {
|
|
||||||
openInLocalProg(fullMedia);
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is questionable in usage since it loads the full video
|
|
||||||
// before showing; but, seeking doesn't work otherwise...
|
|
||||||
let response = await fetch(fullMedia, {method: "GET"});
|
|
||||||
var vidSrc = URL.createObjectURL(await response.blob()); // IE10+
|
|
||||||
video.src = vidSrc;
|
|
||||||
|
|
||||||
document.getElementById("controls").style.opacity = "0";
|
|
||||||
document.getElementById("video").style.display = "block";
|
|
||||||
document.getElementById("dynUl").style.display = "none";
|
|
||||||
} catch (e) {
|
|
||||||
document.getElementById("controls").style.opacity = "1";
|
|
||||||
document.getElementById("dynUl").style.display = "grid";
|
|
||||||
document.getElementById("video").src = "#";
|
|
||||||
document.getElementById("video").style.display = "none";
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const createFloatingPane = (type, fullMedia) => {
|
|
||||||
let iframe = document.createElement("IFRAME");
|
|
||||||
let outterDiv = document.createElement("DIV");
|
|
||||||
let popOutDiv = document.createElement("DIV");
|
|
||||||
let closeDiv = document.createElement("DIV");
|
|
||||||
let toLocDiv = document.createElement("DIV");
|
|
||||||
let imgDiv = document.createElement("DIV");
|
|
||||||
let aTag = document.createElement("A");
|
|
||||||
let imgTag = document.createElement("IMG");
|
|
||||||
let closeText = document.createTextNode("X");
|
|
||||||
|
|
||||||
closeDiv.className = "closeBttn";
|
|
||||||
closeDiv.title = "Close";
|
|
||||||
closeDiv.setAttribute("onclick", "closeContainer(this)");
|
|
||||||
closeDiv.appendChild(closeText);
|
|
||||||
|
|
||||||
aTag.title = "New Tab";
|
|
||||||
aTag.target = "_blank";
|
|
||||||
aTag.href = fullMedia;
|
|
||||||
|
|
||||||
popOutDiv.className = "popOutBttn";
|
|
||||||
popOutDiv.innerHTML = "↗";
|
|
||||||
aTag.appendChild(popOutDiv);
|
|
||||||
|
|
||||||
toLocDiv.title = "Open In Local Program";
|
|
||||||
toLocDiv.className = "popOutBttn";
|
|
||||||
toLocDiv.innerHTML = "∽";
|
|
||||||
toLocDiv.setAttribute("onclick", "openInLocalProg('" + fullMedia + "')");
|
|
||||||
|
|
||||||
imgDiv.id = "imgArea";
|
|
||||||
imgTag.className = "imgViewImg";
|
|
||||||
imgTag.src = fullMedia;
|
|
||||||
imgDiv.appendChild(imgTag);
|
|
||||||
|
|
||||||
iframe.id = "fileViewInner";
|
|
||||||
iframe.src = fullMedia;
|
|
||||||
|
|
||||||
outterDiv.appendChild(closeDiv);
|
|
||||||
outterDiv.appendChild(aTag);
|
|
||||||
outterDiv.appendChild(toLocDiv);
|
|
||||||
|
|
||||||
if (type === "image") {
|
|
||||||
outterDiv.id = "imgView";
|
|
||||||
outterDiv.appendChild(imgDiv);
|
|
||||||
} else {
|
|
||||||
outterDiv.id = "fileView";
|
|
||||||
outterDiv.appendChild(iframe);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.appendChild(outterDiv);
|
|
||||||
dragContainer(outterDiv); // Set for dragging events
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeContainer = (elm) => {
|
|
||||||
elm.parentElement.parentElement.removeChild(elm.parentElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearDirCookie = () => {
|
|
||||||
let expireDate = "Thu, 01 Jan 1970 00:00:00 UTC";
|
|
||||||
document.cookie = "dirQuery=; expires=" + expireDate;
|
|
||||||
getDir("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadItem = () => {
|
|
||||||
let partialPath = document.getElementById("path").innerHTML;
|
|
||||||
let brTag = document.createElement("BR");
|
|
||||||
let aTag = document.createElement("A");
|
|
||||||
let text = document.createTextNode(itemObj);
|
|
||||||
let fullPath = partialPath + itemObj;
|
|
||||||
aTag.setAttribute("href", fullPath);
|
|
||||||
aTag.setAttribute("target", "_blank");
|
|
||||||
aTag.setAttribute("id", itemObj);
|
|
||||||
aTag.append(text);
|
|
||||||
|
|
||||||
document.getElementById("serverMsgView").append(aTag, brTag);
|
|
||||||
aTag.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearDlList = () => { document.getElementById("CLEARBTTN").click(); }
|
|
||||||
const onloadSetBG = () => { updateBG("resources/images/backgrounds/000.jpg"); }
|
|
||||||
|
|
||||||
const updateBG = (bgImg) => {
|
|
||||||
try {
|
|
||||||
document.getElementById("bg").src = bgImg;
|
|
||||||
} catch (e) { }
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
let itemObj = undefined;
|
|
||||||
let interval = undefined;
|
|
||||||
let cursorX;
|
|
||||||
let cursorY;
|
|
||||||
|
|
||||||
document.getElementById("controls").onmouseover = (eve) => {
|
|
||||||
let source = document.getElementById("video").src;
|
|
||||||
let target = eve.target
|
|
||||||
|
|
||||||
if (interval)
|
|
||||||
clearInterval(interval);
|
|
||||||
|
|
||||||
if (source !== "#") {
|
|
||||||
eve.target.style.opacity = "1";
|
|
||||||
document.getElementById("dynUl").style.display = "grid";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("video").onmouseover = (eve) => {
|
|
||||||
interval = setInterval(function () {
|
|
||||||
elementMouseIsOver = document.elementFromPoint(cursorX, cursorY);
|
|
||||||
if (elementMouseIsOver.tagName == "BODY" ||
|
|
||||||
elementMouseIsOver.id == "video") {
|
|
||||||
let controls = document.getElementById("controls");
|
|
||||||
controls.style.opacity = "0";
|
|
||||||
document.getElementById("dynUl").style.display = "none";
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 2500);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For context menu to have element
|
|
||||||
document.onclick = (event) => {
|
|
||||||
let obj = event.target;
|
|
||||||
let callingID = obj.id;
|
|
||||||
let classNM = obj.className;
|
|
||||||
|
|
||||||
// right-click detect
|
|
||||||
if (event.which == 3) {
|
|
||||||
if (callingID == "imageID") {
|
|
||||||
setSelectedItem(obj.alt);
|
|
||||||
} else if (callingID == "dirID" || callingID == "fileID" ||
|
|
||||||
callingID == "movieID") {
|
|
||||||
let node = obj.parentNode;
|
|
||||||
setSelectedItem(node.children[1].value);
|
|
||||||
} else if (classNM == "fileStyle" || classNM == "dirStyle" ||
|
|
||||||
classNM == "movieStyle") {
|
|
||||||
setSelectedItem(obj.children[1].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Actions for content
|
|
||||||
document.ondblclick = (event) => {
|
|
||||||
let obj = event.target;
|
|
||||||
let callingID = obj.id;
|
|
||||||
let classNM = obj.className;
|
|
||||||
|
|
||||||
// Left click detect
|
|
||||||
if (event.which == 1) {
|
|
||||||
// If clicking on container
|
|
||||||
if (classNM === "fileStyle" || classNM === "movieStyle" ||
|
|
||||||
classNM === "dirStyle") {
|
|
||||||
if (classNM === "dirStyle") {
|
|
||||||
getDir(obj.children[1].value);
|
|
||||||
} else if (classNM === "movieStyle") {
|
|
||||||
showMedia(obj.title, "video");
|
|
||||||
} else {
|
|
||||||
showMedia(obj.children[1].value, "file");
|
|
||||||
}
|
|
||||||
} else if (callingID === "dirID") { // If clicking on dir icon
|
|
||||||
let node = obj.parentNode;
|
|
||||||
getDir(node.children[1].value);
|
|
||||||
} else if (callingID === "movieID") { // If clicking on movie thumbnail
|
|
||||||
let node = obj.parentNode;
|
|
||||||
showMedia(node.children[1].value, "video");
|
|
||||||
} else if (callingID === "imageID") { // If clicking on image
|
|
||||||
showMedia(obj.alt, "image");
|
|
||||||
} else if (callingID === "titleID") { // If clicking on text title
|
|
||||||
enableEdit(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mainly for rename event
|
|
||||||
document.onkeydown = (event) => {
|
|
||||||
let obj = event.target;
|
|
||||||
let callingID = event.target.id;
|
|
||||||
let keyCodeVal = event.keyCode;
|
|
||||||
|
|
||||||
// If keycode == Enter
|
|
||||||
if (keyCodeVal == 13) {
|
|
||||||
if (callingID == "titleID") {
|
|
||||||
renameItem(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setSelectedItem = (item) => { itemObj = item; }
|
|
||||||
|
|
||||||
// Drage event for the poped out image and media container
|
|
||||||
const dragContainer = (elmnt) => {
|
|
||||||
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
|
||||||
elmnt.onmousedown = dragMouseDown;
|
|
||||||
|
|
||||||
function dragMouseDown(e) {
|
|
||||||
e = e || window.event;
|
|
||||||
pauseEvent(e);
|
|
||||||
// get the mouse cursor position at startup:
|
|
||||||
pos3 = e.clientX;
|
|
||||||
pos4 = e.clientY;
|
|
||||||
document.onmouseup = closeDragElement;
|
|
||||||
// call a function whenever the cursor moves:
|
|
||||||
document.onmousemove = elementDrag;
|
|
||||||
}
|
|
||||||
|
|
||||||
function elementDrag(e) {
|
|
||||||
e = e || window.event;
|
|
||||||
pauseEvent(e);
|
|
||||||
// calculate the new cursor position:
|
|
||||||
pos1 = pos3 - e.clientX;
|
|
||||||
pos2 = pos4 - e.clientY;
|
|
||||||
pos3 = e.clientX;
|
|
||||||
pos4 = e.clientY;
|
|
||||||
// set the element's new position:
|
|
||||||
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
|
|
||||||
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDragElement(e) {
|
|
||||||
// stop moving when mouse button is released:
|
|
||||||
document.onmouseup = null;
|
|
||||||
document.onmousemove = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pauseEvent(e) {
|
|
||||||
if(e.stopPropagation) e.stopPropagation();
|
|
||||||
if(e.preventDefault) e.preventDefault();
|
|
||||||
|
|
||||||
e.cancelBubble=true;
|
|
||||||
e.returnValue=false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Mouse position detection for control show/hide setup
|
|
||||||
document.onmousemove = function(e){
|
|
||||||
cursorX = e.pageX;
|
|
||||||
cursorY = e.pageY;
|
|
||||||
}
|
|
||||||
|
|
||||||
setInterval(checkCursor, 2000);
|
|
||||||
function checkCursor() {
|
|
||||||
return "";
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
$MEDIAPLAYER = "mpv ";
|
|
||||||
$MPLAYER_WH = " -xy 1600 -geometry 50%:50% ";
|
|
||||||
$MUSICPLAYER = "/opt/deadbeef/bin/deadbeef";
|
|
||||||
$IMGVIEWER = "mirage";
|
|
||||||
$OFFICEPROG = "libreoffice";
|
|
||||||
$PDFVIEWER = "evince";
|
|
||||||
$TEXTVIEWER = "leafpad";
|
|
||||||
$FILEMANAGER = "spacefm";
|
|
||||||
$LOCKPASSWORD = "1234";
|
|
||||||
$TMPFOLDERSIZE = 8000; // tmp folder size check for cleanup if above 8GB used.
|
|
||||||
// NOTE: Split folders with ::::
|
|
||||||
$LOCKEDFOLDERS = "./dirLockCheck/";
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
include_once 'serverMessenger.php';
|
|
||||||
|
|
||||||
chdir("../../");
|
|
||||||
$db = new SQLite3('resources/db/webfm.db');
|
|
||||||
if($db === false){
|
|
||||||
$message = "Server: [Error] --> Database connection failed!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
include_once 'connection.php';
|
|
||||||
include_once 'serverMessenger.php';
|
|
||||||
|
|
||||||
function getTabLinks() {
|
|
||||||
GLOBAL $db;
|
|
||||||
|
|
||||||
$res = $db->query('Select * FROM faves');
|
|
||||||
$GeneratedJSON = array('FAVES_LIST' => array());
|
|
||||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
|
||||||
$GeneratedJSON['FAVES_LIST'][] = $row['link'];
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode($GeneratedJSON);
|
|
||||||
}
|
|
||||||
|
|
||||||
function manageLink($ACTION, $PATH) {
|
|
||||||
GLOBAL $db;
|
|
||||||
$ACTION_TYPE = "";
|
|
||||||
|
|
||||||
// If action isn't true then we add else we delete or exit.
|
|
||||||
if ($ACTION == "false") {
|
|
||||||
$stmt = $db->prepare('INSERT INTO faves VALUES(:link)');
|
|
||||||
$ACTION_TYPE = "added to";
|
|
||||||
} elseif ($ACTION == "true") {
|
|
||||||
$stmt = $db->prepare('DELETE FROM faves WHERE link = :link');
|
|
||||||
$ACTION_TYPE = "deleted from";
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Action for adding or deleting isn't set properly!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt->bindValue(":link", $PATH, SQLITE3_TEXT);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->close();
|
|
||||||
|
|
||||||
$message = "Server: [Success] --> Fave link: " .
|
|
||||||
$PATH . " " . $ACTION_TYPE . " the database!";
|
|
||||||
serverMessage("success", $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Determin action
|
|
||||||
chdir("../../");
|
|
||||||
if (isset($_POST['getTabs'])) {
|
|
||||||
getTabLinks();
|
|
||||||
} elseif (isset($_POST['deleteLink'],
|
|
||||||
$_POST['linkPath'])) {
|
|
||||||
manageLink($_POST['deleteLink'], $_POST['linkPath']);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Illegal Access Method!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,199 +0,0 @@
|
||||||
<?php
|
|
||||||
session_start();
|
|
||||||
include_once 'serverMessenger.php';
|
|
||||||
|
|
||||||
// Create file or folder
|
|
||||||
function createItem($FILE, $TYPE) {
|
|
||||||
$FILE = trim($FILE);
|
|
||||||
$FILE = preg_replace('/\.*$/','',$FILE); // removing dot . after file extension
|
|
||||||
|
|
||||||
if ($TYPE === "dir"){
|
|
||||||
mkdir($FILE, 0755);
|
|
||||||
} else if ($TYPE === "file") {
|
|
||||||
$myfile = fopen($FILE, "w");
|
|
||||||
fclose($myfile);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Failed to create folder or file!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = "Server: [Success] --> The file " . $FILE . " has been created.";
|
|
||||||
serverMessage("success", $message);
|
|
||||||
$_SESSION["refreshState"] = "updateListing";
|
|
||||||
}
|
|
||||||
|
|
||||||
// File or folder delition
|
|
||||||
function deleteItem($FILE) {
|
|
||||||
if (filetype($FILE) == "dir"){
|
|
||||||
//GLOB_MARK adds a slash to directories returned
|
|
||||||
$files = glob($FILE . '*', GLOB_MARK);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
deleteItem($file);
|
|
||||||
}
|
|
||||||
rmdir($FILE);
|
|
||||||
} else if (filetype($FILE) == "file") {
|
|
||||||
unlink($FILE);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Failed to delete item! Not a folder or file!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = "Server: [Success] --> The file(s) has/have been deleted.";
|
|
||||||
serverMessage("success", $message);
|
|
||||||
$_SESSION["refreshState"] = "updateListing";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename file or folder
|
|
||||||
function renameItem($OLDFILE, $NEWNAME, $PATH) {
|
|
||||||
rename($PATH . $OLDFILE, $PATH . $NEWNAME);
|
|
||||||
$message = "Server: [Success] --> The file " . $OLDFILE . " has been renamed to " . $NEWNAME . " side.";
|
|
||||||
serverMessage("success", $message);
|
|
||||||
$_SESSION["refreshState"] = "updateListing";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uploader
|
|
||||||
function uploadFiles($targetDir) {
|
|
||||||
$numberOfFiles = count($_FILES['filesToUpload']['name']);
|
|
||||||
|
|
||||||
if ($numberOfFiles === 0) {
|
|
||||||
$message = "Server: [Error] --> No files were uploaded!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$type = "";
|
|
||||||
$message = "";
|
|
||||||
for ($i=0; $i < $numberOfFiles; $i++) {
|
|
||||||
$uploadOk = 1;
|
|
||||||
$fileName = $_FILES['filesToUpload']['name'][$i];
|
|
||||||
$fileTmpName = $_FILES['filesToUpload']['tmp_name'][$i];
|
|
||||||
|
|
||||||
// Check if file already exists
|
|
||||||
$targetFile = $targetDir . $fileName;
|
|
||||||
if (file_exists($targetFile)) {
|
|
||||||
if (filetype($targetFile) == "file") {
|
|
||||||
unlink($targetFile);
|
|
||||||
$message = "Server: [Warning] --> This file already exists. Overwriting it.";
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Warning] --> This file might be a directory. Or, no files were submitted for uploading.";
|
|
||||||
$uploadOk = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file size
|
|
||||||
$fileSize = $_FILES['filesToUpload']['size'][$i];
|
|
||||||
if ($fileSize > 500000000000) {
|
|
||||||
$message = "Server: [Warning] --> This file is too large.";
|
|
||||||
$uploadOk = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow certain file formats
|
|
||||||
// $ext = pathinfo($targetFile,PATHINFO_EXTENSION);
|
|
||||||
// if(!preg_match('/^.*\.(rar|iso|img|tar|zip|7z|7zip|jpg|jpeg|png|gif|mpeg|mov|flv|avi|mp4|webm|mpg|mkv|m4a|mp3|ogg|docx|doc|odt|txt|pdf|)$/i', strtolower($ext))) {
|
|
||||||
// $message = "Server: [Warning] --> This file type is not allowed.";
|
|
||||||
// $uploadOk = 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if everything is ok, try to upload file
|
|
||||||
if ($uploadOk !== 0) {
|
|
||||||
if (move_uploaded_file($fileTmpName, $targetFile)) {
|
|
||||||
$type = "success";
|
|
||||||
$message = "Server: [Success] --> The file " . $fileName . " has been uploaded.";
|
|
||||||
$_SESSION["refreshState"] = "updateListing";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$type = "error";
|
|
||||||
$message .= "\nServer: [Error] --> Your file " . $fileName . " was not uploaded.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serverMessage($type, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local program file access
|
|
||||||
function openFile($FILE) {
|
|
||||||
include 'config.php';
|
|
||||||
$EXTNSN = strtolower(pathinfo($FILE, PATHINFO_EXTENSION));
|
|
||||||
|
|
||||||
if (preg_match('(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|webm)', $EXTNSN) === 1) {
|
|
||||||
shell_exec($MEDIAPLAYER . "\"" . $FILE . "\" > /dev/null &");
|
|
||||||
} else if (preg_match('(png|jpg|jpeg|gif)', $EXTNSN) === 1) {
|
|
||||||
shell_exec($IMGVIEWER . ' "' . $FILE . '" > /dev/null &');
|
|
||||||
} else if (preg_match('(psf|mp3|ogg|flac)', $EXTNSN) === 1) {
|
|
||||||
shell_exec($MUSICPLAYER . ' "' . $FILE . '" > /dev/null &');
|
|
||||||
} else if (preg_match('(odt|doc|docx|rtf)', $EXTNSN) === 1) {
|
|
||||||
shell_exec($OFFICEPROG . ' "' . $FILE . '" > /dev/null &');
|
|
||||||
} else if (preg_match('(txt)', $EXTNSN) === 1) {
|
|
||||||
shell_exec($TEXTVIEWER . ' "' . $FILE . '" > /dev/null &');
|
|
||||||
} else if (preg_match('(pdf)', $EXTNSN) === 1) {
|
|
||||||
shell_exec($PDFVIEWER . ' "' . $FILE . '" > /dev/null &');
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = "Server: [Success] --> The file " . $FILE . " has been opened server side.";
|
|
||||||
serverMessage("success", $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
function remuxVideo($FILE) {
|
|
||||||
$FILE = trim($FILE);
|
|
||||||
$PTH = "resources/tmp/";
|
|
||||||
$HASHED_NAME = hash('sha256', $FILE) . '.mp4';
|
|
||||||
$EXTNSN = strtolower(pathinfo($FILE, PATHINFO_EXTENSION));
|
|
||||||
|
|
||||||
|
|
||||||
if (!file_exists($PTH . $HASHED_NAME)) {
|
|
||||||
$io = popen('/usr/bin/du -sm ' . $PTH, 'r');
|
|
||||||
$size = fgets($io, 4096);
|
|
||||||
$size = (int) substr($size, 0, strpos ( $size, "\t" ));
|
|
||||||
pclose ($io);
|
|
||||||
|
|
||||||
include 'config.php';
|
|
||||||
if ($size > $TMPFOLDERSIZE) {
|
|
||||||
$files = glob($PTH . '*');
|
|
||||||
foreach($files as $file){
|
|
||||||
if(is_file($file))
|
|
||||||
unlink($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preg_match('(mkv)', $EXTNSN) === 1)
|
|
||||||
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -codec copy -strict -2 ' . $PTH . $HASHED_NAME;
|
|
||||||
if (preg_match('(avi)', $EXTNSN) === 1)
|
|
||||||
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -c:v libx264 -crf 21 -c:a aac -b:a 192k -ac 2 ' . $PTH . $HASHED_NAME;
|
|
||||||
if (preg_match('(wmv)', $EXTNSN) === 1)
|
|
||||||
$COMMAND = 'ffmpeg -i "' . $FILE . '" -hide_banner -movflags +faststart -c:v libx264 -crf 23 -c:a aac -strict -2 -q:a 100 ' . $PTH . $HASHED_NAME;
|
|
||||||
|
|
||||||
shell_exec($COMMAND . " 2> resources/vdata.txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
$GeneratedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
|
||||||
$GeneratedXML .= "<REMUX_PATH>" . $PTH . $HASHED_NAME ."</REMUX_PATH>";
|
|
||||||
echo $GeneratedXML;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
chdir("../../");
|
|
||||||
if (isset($_POST["remuxVideo"], $_POST["mediaPth"])) {
|
|
||||||
remuxVideo($_POST["mediaPth"]);
|
|
||||||
} else if (isset($_POST["createItem"],
|
|
||||||
$_POST["item"],
|
|
||||||
$_POST["type"])) {
|
|
||||||
createItem($_POST["item"], $_POST["type"]);
|
|
||||||
} else if (isset($_POST["deleteItem"], $_POST["item"])) {
|
|
||||||
deleteItem($_POST["item"]);
|
|
||||||
} else if (isset($_POST["renameItem"],
|
|
||||||
$_POST["oldName"],
|
|
||||||
$_POST["newName"],
|
|
||||||
$_POST["path"])) {
|
|
||||||
renameItem($_POST["oldName"], $_POST["newName"], $_POST["path"]);
|
|
||||||
} else if(isset($_POST["UploadFiles"], $_POST["DIRPATHUL"])) {
|
|
||||||
uploadFiles($_POST["DIRPATHUL"]);
|
|
||||||
} else if (isset($_POST["media"])) {
|
|
||||||
openFile($_POST["media"]);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Incorrect access attempt!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,108 +0,0 @@
|
||||||
<?php
|
|
||||||
session_start();
|
|
||||||
include_once 'serverMessenger.php';
|
|
||||||
|
|
||||||
// Start of retrieving dir data
|
|
||||||
function startListing($NEWPATH, $MERGESEASSONS, $PASSWD) {
|
|
||||||
if (filetype($NEWPATH) == "dir") {
|
|
||||||
include_once 'lockedFolders.php';
|
|
||||||
|
|
||||||
if (checkForLock($NEWPATH, $PASSWD) == false) {
|
|
||||||
$subPath = ""; // This is used for season scanning as a means of properly getting
|
|
||||||
// the video src.... It's left blank when not in a sub dir
|
|
||||||
|
|
||||||
$GeneratedJSON = array('PATH_HEAD' => $NEWPATH,
|
|
||||||
'IN_FAVE' => isInDBCheck($NEWPATH),
|
|
||||||
'list' => array()
|
|
||||||
);
|
|
||||||
|
|
||||||
listDir($GeneratedJSON, $NEWPATH, $MERGESEASSONS, $subPath);
|
|
||||||
echo json_encode($GeneratedJSON);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Folder is locked.";
|
|
||||||
serverMessage("locked", $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function listDir(&$GeneratedJSON, &$NEWPATH, &$MERGESEASSONS, &$subPath) {
|
|
||||||
if ($MERGESEASSONS !== "true") {
|
|
||||||
$files = array_diff(scandir($NEWPATH), array('..', '.', 'resources'));
|
|
||||||
foreach ($files as $fileName) {
|
|
||||||
$fullPath = $NEWPATH . '/' . $fileName;
|
|
||||||
// error_log($fullPath, 4);
|
|
||||||
processItem($GeneratedJSON, $fullPath, $fileName, $subPath);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$files = array_diff(scandir($NEWPATH), array('..', '.', 'resources'));
|
|
||||||
foreach ($files as $fileName) {
|
|
||||||
$fullPath = $NEWPATH . $fileName;
|
|
||||||
// error_log($fullPath, 4);
|
|
||||||
if (filetype($fullPath) == "dir" && strpos(strtolower($fileName),
|
|
||||||
'season') !== false) {
|
|
||||||
$fileName .= "/";
|
|
||||||
listDir($GeneratedJSON, $fullPath, $MERGESEASSONS, $fileName);
|
|
||||||
} else {
|
|
||||||
processItem($GeneratedJSON, $fullPath, $fileName, $subPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign JSON Markup based on file type
|
|
||||||
function processItem(&$GeneratedJSON, &$fullPath, &$fileName, $subPath) {
|
|
||||||
if (preg_match('/^.*\.(mkv|avi|flv|mov|m4v|mpg|wmv|mpeg|mp4|webm)$/i', strtolower($fileName))) {
|
|
||||||
$NAMEHASH = hash('sha256', $fileName);
|
|
||||||
if (!file_exists('resources/images/thumbnails/' . $NAMEHASH . '.jpg')) {
|
|
||||||
shell_exec('resources/ffmpegthumbnailer -t 65% -s 320 -c jpg '
|
|
||||||
. '-i "' . $subPath . $fullPath . '" '
|
|
||||||
. '-o resources/images/thumbnails/' . $NAMEHASH . '.jpg'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$GeneratedJSON['list']['vids'][] = array('video' =>
|
|
||||||
array('title' => $subPath . $fileName,
|
|
||||||
'thumbnail' => $NAMEHASH . '.jpg'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} elseif (preg_match('/^.*\.(png|jpg|gif|jpeg)$/i', strtolower($fileName))) {
|
|
||||||
$GeneratedJSON['list']['imgs'][] = array('image' => $subPath . $fileName);
|
|
||||||
} elseif (filetype($fullPath) == "dir") {
|
|
||||||
$GeneratedJSON['list']['dirs'][] = array('dir' => $fileName . "/");
|
|
||||||
} else {
|
|
||||||
$GeneratedJSON['list']['files'][] = array('file' => $subPath . $fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isInDBCheck($PATH) {
|
|
||||||
$db = new SQLite3('resources/db/webfm.db');
|
|
||||||
|
|
||||||
if($db === false){
|
|
||||||
$message = "Server: [Error] --> Database connection failed!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
die("ERROR: Could not connect to db.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $db->prepare('SELECT 1 FROM faves WHERE link = :link');
|
|
||||||
$stmt->bindValue(":link", $PATH, SQLITE3_TEXT);
|
|
||||||
$result = $stmt->execute() ;
|
|
||||||
$row = $result->fetchArray() ;
|
|
||||||
|
|
||||||
if ($row > 0) {
|
|
||||||
return "true";
|
|
||||||
} else {
|
|
||||||
return "false";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Determin action
|
|
||||||
chdir("../../");
|
|
||||||
if (isset($_POST['dirQuery'])) {
|
|
||||||
startListing(trim($_POST['dirQuery']), $_POST['mergeType'], $_POST['passwd']);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Error] --> Illegal Access Method!";
|
|
||||||
serverMessage("error", $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
// Check if sub folder is in locked folder
|
|
||||||
function checkForLock($NEWPATH, $PASSWD) {
|
|
||||||
include 'config.php';
|
|
||||||
|
|
||||||
$LOCKS = explode("::::", $LOCKEDFOLDERS);
|
|
||||||
$size = sizeof($LOCKS);
|
|
||||||
|
|
||||||
if (isset($_SESSION["unlockState"]) && $_SESSION["unlockState"] == true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
if (strpos($NEWPATH, $LOCKS[$i]) !== false) {
|
|
||||||
if ($PASSWD === $LOCKPASSWORD) {
|
|
||||||
$_SESSION["unlockState"] = true;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lockFolders() {
|
|
||||||
session_start();
|
|
||||||
include 'serverMessenger.php';
|
|
||||||
|
|
||||||
if (isset($_SESSION["unlockState"]) && $_SESSION["unlockState"] == true) {
|
|
||||||
$_SESSION["unlockState"] = false;
|
|
||||||
$message = "Server: [Success] --> Folders unlocked!";
|
|
||||||
serverMessage("success", $message);
|
|
||||||
} else {
|
|
||||||
$message = "Server: [Warning] --> Folders aren't unlocked!"
|
|
||||||
. "\n" . $_SESSION["unlockState"];
|
|
||||||
serverMessage("warning", $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (isset($_POST['lockFolders'])) {
|
|
||||||
lockFolders();
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?php
|
|
||||||
function serverMessage($TYPE, $MESSAGE) {
|
|
||||||
$GeneratedJSON = array( 'message' =>
|
|
||||||
array(
|
|
||||||
'type' => $TYPE,
|
|
||||||
'text' => $MESSAGE
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
echo json_encode($GeneratedJSON);
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
// Start the session
|
|
||||||
session_start();
|
|
||||||
include_once 'config.php';
|
|
||||||
|
|
||||||
if (!isset($_SESSION["refreshState"])) {
|
|
||||||
$_SESSION["refreshState"] = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
header('Content-Type: text/event-stream');
|
|
||||||
header('Cache-Control: no-cache');
|
|
||||||
echo "data:" . $_SESSION["refreshState"] . "\n\n";
|
|
||||||
|
|
||||||
$_SESSION["refreshState"] = "none";
|
|
||||||
flush();
|
|
||||||
?>
|
|
|
@ -1,91 +0,0 @@
|
||||||
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.homeSection {
|
|
||||||
float: left;
|
|
||||||
width: 40%;
|
|
||||||
height: 14em;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
background: rgba(19, 21, 21, .6);
|
|
||||||
}
|
|
||||||
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.header {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
width: 99%;
|
|
||||||
}
|
|
||||||
.section, .lnksNdirs, .header {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
padding-top: 1em;
|
|
||||||
margin: 1em;
|
|
||||||
background: rgba(19, 21, 21, .7);
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
/* border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
.backbutton {
|
|
||||||
top: 3.55em;
|
|
||||||
clear: both;
|
|
||||||
z-index: 2;
|
|
||||||
position: fixed;
|
|
||||||
float: left;
|
|
||||||
width: 5%;
|
|
||||||
height: 50px;
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
iframe {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 2em;
|
|
||||||
left: 25%;
|
|
||||||
width: 50%;
|
|
||||||
height: 360px;
|
|
||||||
background: rgba(0,0,0,.5);
|
|
||||||
transition: 0s;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
iframe:hover {
|
|
||||||
left: 2%;
|
|
||||||
width: 99%;
|
|
||||||
height: 700px;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
.lnkStyl {
|
|
||||||
clear: right;
|
|
||||||
float: left;
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imgsStyl {
|
|
||||||
margin-left: 50%;
|
|
||||||
opacity: 0.699999988079071044921875;
|
|
||||||
width: 32.333%;
|
|
||||||
height: 20em;
|
|
||||||
}
|
|
||||||
.imgsStyl:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
#bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
#bg img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
body {
|
|
||||||
width: 600%;
|
|
||||||
}
|
|
||||||
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.homeSection {
|
|
||||||
float: left;
|
|
||||||
width: 40%;
|
|
||||||
height: 14em;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
background: rgba(19, 21, 21, .6);
|
|
||||||
}
|
|
||||||
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.header {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
width: 99%;
|
|
||||||
}
|
|
||||||
.section, .lnksNdirs, .header {
|
|
||||||
display: block;
|
|
||||||
clear: right;
|
|
||||||
float: left;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
padding-top: 1em;
|
|
||||||
margin: 1em;
|
|
||||||
background: rgba(19, 21, 21, .7);
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
/* border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
.backbutton {
|
|
||||||
top: 3.55em;
|
|
||||||
clear: both;
|
|
||||||
z-index: 2;
|
|
||||||
position: fixed;
|
|
||||||
float: left;
|
|
||||||
width: 5%;
|
|
||||||
height: 50px;
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
iframe {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 2em;
|
|
||||||
left: 25%;
|
|
||||||
width: 50%;
|
|
||||||
height: 360px;
|
|
||||||
background: rgba(0,0,0,.5)
|
|
||||||
transition: 0s;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
.lnkStyl {
|
|
||||||
clear: right;
|
|
||||||
float: left;
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imgsStyl {
|
|
||||||
margin-left: 50%;
|
|
||||||
opacity: 0.699999988079071044921875;
|
|
||||||
width: 32.333%;
|
|
||||||
height: 20em;
|
|
||||||
}
|
|
||||||
.imgsStyl:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
#bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
#bg img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.homeSection {
|
|
||||||
float: left;
|
|
||||||
width: 40%;
|
|
||||||
height: 14em;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
background: rgba(19, 21, 21, .6);
|
|
||||||
}
|
|
||||||
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.header {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
width: 99%;
|
|
||||||
}
|
|
||||||
.section, .lnksNdirs, .header {
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
padding-top: 1em;
|
|
||||||
margin-top: 1em;
|
|
||||||
background: rgba(19, 21, 21, .7);
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
/* border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
.backbutton {
|
|
||||||
top: 3.55em;
|
|
||||||
clear: both;
|
|
||||||
z-index: 2;
|
|
||||||
position: fixed;
|
|
||||||
float: left;
|
|
||||||
width: 5%;
|
|
||||||
height: 50px;
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
iframe {
|
|
||||||
clear: both;
|
|
||||||
z-index: 1;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 5%;
|
|
||||||
width: 40%;
|
|
||||||
height: 500px;
|
|
||||||
background: rgba(0,0,0,.5)
|
|
||||||
transition: 0s;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
iframe:hover {
|
|
||||||
width: 99%;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
.lnkStyl {
|
|
||||||
clear: right;
|
|
||||||
float: left;
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imgsStyl {
|
|
||||||
margin-left: 50%;
|
|
||||||
opacity: 0.699999988079071044921875;
|
|
||||||
width: 32.333%;
|
|
||||||
height: 20em;
|
|
||||||
}
|
|
||||||
.imgsStyl:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
#bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
#bg img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*START OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
.homeSection {
|
|
||||||
float: left;
|
|
||||||
width: 40%;
|
|
||||||
height: 14em;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 100%;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
background: rgba(19, 21, 21, .6);
|
|
||||||
}
|
|
||||||
/*END OF HOMEPAGE BUTTON SETTINGS*/
|
|
||||||
|
|
||||||
.lnkStyl {
|
|
||||||
clear: right;
|
|
||||||
float: left;
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
.imgsStyl {
|
|
||||||
clear: both;
|
|
||||||
float: left;
|
|
||||||
opacity: 0.699999988079071044921875;
|
|
||||||
width: 20%;
|
|
||||||
height: 15em;
|
|
||||||
}
|
|
||||||
.imgsStyl:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.header {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
width: 100%;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.lnksNdirs {
|
|
||||||
width: 20%;
|
|
||||||
float:left;
|
|
||||||
}
|
|
||||||
/* used to format sections of pgs*/
|
|
||||||
.section, .lnksNdirs, .header {
|
|
||||||
clear: both;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 1em;
|
|
||||||
margin-top: 1em;
|
|
||||||
font-size: 100%;
|
|
||||||
background: rgba(19, 21, 21, .6);
|
|
||||||
background: rgba(19, 21, 21, .7);
|
|
||||||
color: rgba(0, 232, 255, .69); /*#00E8FF*/
|
|
||||||
/* border-style: solid;
|
|
||||||
border-width: .2em;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
.backbutton {
|
|
||||||
top: 3em;
|
|
||||||
clear: both;
|
|
||||||
z-index: 2;
|
|
||||||
position: fixed;
|
|
||||||
float: left;
|
|
||||||
width: 5%;
|
|
||||||
height: 55px;
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
.ifrmbutton2 {
|
|
||||||
top: 3em;
|
|
||||||
float: right;
|
|
||||||
right: .5em;
|
|
||||||
z-index:2;
|
|
||||||
position: fixed;
|
|
||||||
width: 5%;
|
|
||||||
height: 30px;
|
|
||||||
opacity: .6;
|
|
||||||
}
|
|
||||||
iframe {
|
|
||||||
margin-left: 2%;
|
|
||||||
z-index: 2;
|
|
||||||
position: fixed;
|
|
||||||
top: 8em;
|
|
||||||
right: 0.534em;
|
|
||||||
bottom: 2%;
|
|
||||||
width: 75%;
|
|
||||||
height: 525px;
|
|
||||||
background: rgba(0,0,0,.5)
|
|
||||||
transition: 0s;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
iframe:hover {
|
|
||||||
height: 775px;
|
|
||||||
transition-delay:2s;
|
|
||||||
}
|
|
||||||
#bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
#bg img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
Place Holder....
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
import builtins
|
||||||
|
import threading
|
||||||
|
import re
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import session
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from core import app
|
||||||
|
from core.utils import Logger
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Threads WILL NOT die with parent's destruction.
|
||||||
|
def threaded_wrapper(fn):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
# NOTE: Threads WILL die with parent's destruction.
|
||||||
|
def daemon_threaded_wrapper(fn):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def sizeof_fmt_def(num, suffix="B"):
|
||||||
|
for unit in ["", "K", "M", "G", "T", "Pi", "Ei", "Zi"]:
|
||||||
|
if abs(num) < 1024.0:
|
||||||
|
return f"{num:3.1f} {unit}{suffix}"
|
||||||
|
num /= 1024.0
|
||||||
|
return f"{num:.1f} Yi{suffix}"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_file_size(file):
|
||||||
|
return "4K" if isdir(file) else sizeof_fmt_def(os.path.getsize(file))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Just reminding myself we can add to builtins two different ways...
|
||||||
|
# __builtins__.update({"event_system": Builtins()})
|
||||||
|
builtins.app_name = "WebFM"
|
||||||
|
builtins.threaded = threaded_wrapper
|
||||||
|
builtins.daemon_threaded = daemon_threaded_wrapper
|
||||||
|
builtins.sizeof_fmt = sizeof_fmt_def
|
||||||
|
builtins.get_file_size = _get_file_size
|
||||||
|
builtins.ROOT_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
builtins.BG_IMGS_PATH = ROOT_FILE_PTH + "/static/imgs/backgrounds/"
|
||||||
|
builtins.BG_FILE_TYPE = (".webm", ".mp4", ".gif", ".jpg", ".png", ".webp")
|
||||||
|
builtins.valid_fname_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]{4,20}")
|
||||||
|
builtins.logger = Logger().get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Need threads defined befor instantiating
|
||||||
|
from core.utils.shellfm.windows.controller import WindowController # Get file manager controller
|
||||||
|
window_controllers = {}
|
||||||
|
def _get_view():
|
||||||
|
controller = None
|
||||||
|
try:
|
||||||
|
controller = window_controllers[ session["win_controller_id"] ].get_window_by_index(0).get_tab_by_index(0)
|
||||||
|
except Exception as e:
|
||||||
|
id = secrets.token_hex(16)
|
||||||
|
controller = WindowController()
|
||||||
|
view = controller.create_window().create_tab()
|
||||||
|
|
||||||
|
try:
|
||||||
|
view.ABS_THUMBS_PTH = app.config['ABS_THUMBS_PTH']
|
||||||
|
except Exception as e:
|
||||||
|
print("No ABS_THUMBS_PTH set by WebFM...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
view.REMUX_FOLDER = app.config['REMUX_FOLDER']
|
||||||
|
except Exception as e:
|
||||||
|
print("No REMUX_FOLDER set by WebFM...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
view.FFMPG_THUMBNLR = app.config['FFMPG_THUMBNLR']
|
||||||
|
except Exception as e:
|
||||||
|
print("No FFMPG_THUMBNLR set by WebFM...")
|
||||||
|
|
||||||
|
view.logger = logger
|
||||||
|
|
||||||
|
session['win_controller_id'] = id
|
||||||
|
window_controllers.update( {id: controller } )
|
||||||
|
controller = window_controllers[ session["win_controller_id"] ].get_window_by_index(0).get_tab_by_index(0)
|
||||||
|
|
||||||
|
return controller
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
builtins.get_view = _get_view
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import Flask
|
||||||
|
#OIDC Login path
|
||||||
|
from flask_oidc import OpenIDConnect
|
||||||
|
# Flask Login Path
|
||||||
|
from flask_bcrypt import Bcrypt
|
||||||
|
from flask_login import current_user, login_user, logout_user, LoginManager
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object("core.config.ProductionConfig")
|
||||||
|
# app.config.from_object("core.config.DevelopmentConfig")
|
||||||
|
|
||||||
|
# Apoplication imports
|
||||||
|
from .__builtins__ import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
oidc = OpenIDConnect(app)
|
||||||
|
login_manager = LoginManager(app)
|
||||||
|
bcrypt = Bcrypt(app)
|
||||||
|
|
||||||
|
def oidc_loggedin():
|
||||||
|
return oidc.user_loggedin
|
||||||
|
|
||||||
|
def oidc_isAdmin():
|
||||||
|
if oidc_loggedin():
|
||||||
|
isAdmin = oidc.user_getfield("isAdmin")
|
||||||
|
if isAdmin == "yes" :
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
app.jinja_env.globals['oidc_loggedin'] = oidc_loggedin
|
||||||
|
app.jinja_env.globals['oidc_isAdmin'] = oidc_isAdmin
|
||||||
|
app.jinja_env.globals['TITLE'] = app.config["TITLE"]
|
||||||
|
|
||||||
|
|
||||||
|
from core.models import db, User, Favorites
|
||||||
|
db.init_app(app)
|
||||||
|
with app.app_context():
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
from core.forms import RegisterForm, LoginForm
|
||||||
|
from core import routes
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"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": "9028c2ac-d6e0-4d96-86bd-02624b91695d",
|
||||||
|
"redirect_uris": [
|
||||||
|
"https%3A%2F%2Fwww.webfm.com%2F"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
# System import
|
||||||
|
import os, secrets
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
|
||||||
|
# Apoplication imports
|
||||||
|
|
||||||
|
|
||||||
|
# Configs
|
||||||
|
APP_NAME = 'WebFM'
|
||||||
|
ROOT_FILE_PTH = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
TITLE = APP_NAME
|
||||||
|
DEBUG = False
|
||||||
|
TESTING = False
|
||||||
|
THREADED = True
|
||||||
|
SECRET_KEY = "2A#GQafbREoblgMSQYomZSxbaPE6dt#"
|
||||||
|
# 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
|
||||||
|
|
||||||
|
LOGIN_PATH = "OIDC" # Value can be OIDC or FLASK_LOGIN
|
||||||
|
OIDC_TOKEN_TYPE_HINT = 'access_token'
|
||||||
|
APP_REDIRECT_URI = "https%3A%2F%2Fwww.webfm.com%2F" # This path is submitted as the redirect URI in certain code flows
|
||||||
|
OIDC_CLIENT_SECRETS = f'{ROOT_FILE_PTH}/client_secrets.json'
|
||||||
|
OIDC_ID_TOKEN_COOKIE_SECURE = True
|
||||||
|
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'
|
||||||
|
]
|
||||||
|
|
||||||
|
STATIC_FPTH = f"{ROOT_FILE_PTH}/static"
|
||||||
|
REL_THUMBS_PTH = "static/imgs/thumbnails" # Used for flask thumbnail return
|
||||||
|
|
||||||
|
# We are overiding some of the the shellmen view settings with these to make it all work with flask.
|
||||||
|
# These are passed along to the shellmen view from the Routes file upon the window controller creation.
|
||||||
|
ABS_THUMBS_PTH = f"{STATIC_FPTH}/imgs/thumbnails" # Used for thumbnail generation
|
||||||
|
REMUX_FOLDER = f"{STATIC_FPTH}/remuxs" # Remuxed files folder
|
||||||
|
FFMPG_THUMBNLR = f"{STATIC_FPTH}/ffmpegthumbnailer" # Thumbnail generator binary
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ProductionConfig(Config):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DevelopmentConfig(Config):
|
||||||
|
DEBUG = True
|
||||||
|
USE_RELOADER = True
|
||||||
|
OIDC_ID_TOKEN_COOKIE_SECURE = False
|
||||||
|
OIDC_REQUIRE_VERIFIED_EMAIL = False
|
||||||
|
|
||||||
|
|
||||||
|
class TestingConfig(Config):
|
||||||
|
TESTING = True
|
|
@ -0,0 +1,24 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, PasswordField, SubmitField
|
||||||
|
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
|
||||||
|
from core import User
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterForm(FlaskForm):
|
||||||
|
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=24)])
|
||||||
|
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||||
|
password = PasswordField('Password', validators=[DataRequired(), Length(min=8)])
|
||||||
|
confirm_password = PasswordField('Confirm Password',
|
||||||
|
validators=[DataRequired(), EqualTo('password', message="Passwords must match!")])
|
||||||
|
submit = SubmitField("Sign Up")
|
||||||
|
|
||||||
|
def validate_username(self, username):
|
||||||
|
user = User.query.filter_by(username=username.data).first()
|
||||||
|
if user:
|
||||||
|
raise ValidationError("User exists already! Please use a different name!")
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=24)])
|
||||||
|
password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=32)])
|
||||||
|
submit = SubmitField("Login")
|
|
@ -0,0 +1,41 @@
|
||||||
|
# System imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from . import app, login_manager
|
||||||
|
from flask_login import UserMixin
|
||||||
|
|
||||||
|
|
||||||
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
|
||||||
|
@login_manager.user_loader
|
||||||
|
def load_user(user_id):
|
||||||
|
return User.query.get(int(user_id))
|
||||||
|
|
||||||
|
|
||||||
|
class User(db.Model, UserMixin):
|
||||||
|
username = db.Column(db.String, unique=True, nullable=False)
|
||||||
|
email = db.Column(db.String, nullable=False)
|
||||||
|
password = db.Column(db.String, nullable=False)
|
||||||
|
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"['{self.username}', '{self.email}', '{self.password}', '{self.id}']"
|
||||||
|
|
||||||
|
class Favorites(db.Model):
|
||||||
|
link = db.Column(db.String, nullable=False, unique=True)
|
||||||
|
id = db.Column(db.Integer, nullable=False, primary_key=True, unique=True, autoincrement=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"['{self.link}', '{self.id}']"
|
||||||
|
|
||||||
|
class Settings(db.Model):
|
||||||
|
key = db.Column(db.String, nullable=False)
|
||||||
|
value = db.Column(db.String, nullable=False)
|
||||||
|
id = db.Column(db.Integer, nullable=False, primary_key=True, unique=True, autoincrement=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"['{self.key}', '{self.value}', '{self.id}']"
|
|
@ -0,0 +1,119 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from flask_uploads import ALL
|
||||||
|
from flask_uploads import configure_uploads
|
||||||
|
from flask_uploads import UploadSet
|
||||||
|
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
# Get from __init__
|
||||||
|
from core import app
|
||||||
|
from core import db
|
||||||
|
from core import Favorites
|
||||||
|
from core import oidc
|
||||||
|
|
||||||
|
from core.utils import MessageHandler # Get simple message processor
|
||||||
|
|
||||||
|
json_message = MessageHandler()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/delete/<_hash>', methods=['GET', 'POST'])
|
||||||
|
def delete_item(_hash = None):
|
||||||
|
if request.method == 'POST':
|
||||||
|
msg = "Log in with an Admin privlidged user to delete files!"
|
||||||
|
if not oidc.user_loggedin:
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
elif oidc.user_loggedin:
|
||||||
|
isAdmin = oidc.user_getfield("isAdmin")
|
||||||
|
if isAdmin != "yes" :
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
view = get_view()
|
||||||
|
folder = view.get_current_directory()
|
||||||
|
file = view.get_path_part_from_hash(_hash)
|
||||||
|
fpath = os.path.join(folder, file)
|
||||||
|
try:
|
||||||
|
msg = f"[Success] Deleted the file/folder -->: {file} !"
|
||||||
|
view.delete_file(fpath)
|
||||||
|
return json_message.create("success", msg)
|
||||||
|
except Exception as e:
|
||||||
|
msg = "[Error] Unable to delete the file/folder...."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/create/<_type>', methods=['GET', 'POST'])
|
||||||
|
def create_item(_type = None):
|
||||||
|
if request.method == 'POST':
|
||||||
|
msg = "Log in with an Admin privlidged user to upload files!"
|
||||||
|
if not oidc.user_loggedin:
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
elif oidc.user_loggedin:
|
||||||
|
isAdmin = oidc.user_getfield("isAdmin")
|
||||||
|
if isAdmin != "yes" :
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
TYPE = _type.strip()
|
||||||
|
if not TYPE in ["dir", "file"]:
|
||||||
|
msg = "Couldn't handle action type for api create..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
FNAME = str(request.values['fname']).strip()
|
||||||
|
if not re.fullmatch(valid_fname_pat, FNAME):
|
||||||
|
msg = "A new item name can only contain alphanumeric, -, _, |, [], (), or spaces and must be minimum of 4 and max of 20 characters..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
view = get_view()
|
||||||
|
folder = view.get_current_directory()
|
||||||
|
new_item = f"{folder}/{FNAME}"
|
||||||
|
view.create_file(new_item, TYPE)
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
msg = "Couldn't create file/folder. An unexpected error occured..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
|
||||||
|
msg = "[Success] created the file/dir..."
|
||||||
|
return json_message.create("success", msg)
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/upload', methods=['GET', 'POST'])
|
||||||
|
def upload():
|
||||||
|
if request.method == 'POST' and len(request.files) > 0:
|
||||||
|
msg = "Log in with an Admin privlidged user to upload files!"
|
||||||
|
if not oidc.user_loggedin:
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
elif oidc.user_loggedin:
|
||||||
|
isAdmin = oidc.user_getfield("isAdmin")
|
||||||
|
if isAdmin != "yes" :
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
view = get_view()
|
||||||
|
folder = view.get_current_directory()
|
||||||
|
UPLOADS_PTH = f'{folder}/'
|
||||||
|
files = UploadSet('files', ALL, default_dest=lambda x: UPLOADS_PTH)
|
||||||
|
configure_uploads(app, files)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for file in request.files:
|
||||||
|
files.save(request.files[file])
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
msg = "[Error] Failed to upload some or all of the file(s)..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
msg = "[Success] Uploaded file(s)..."
|
||||||
|
return json_message.create("success", msg)
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from core import app
|
||||||
|
from core import db
|
||||||
|
from core import Favorites # Get from __init__
|
||||||
|
from core.utils import MessageHandler # Get simple message processor
|
||||||
|
|
||||||
|
|
||||||
|
json_message = MessageHandler()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/list-favorites', methods=['GET', 'POST'])
|
||||||
|
def listFavorites():
|
||||||
|
if request.method == 'POST':
|
||||||
|
list = db.session.query(Favorites).all()
|
||||||
|
faves = []
|
||||||
|
for fave in list:
|
||||||
|
faves.append([fave.link, fave.id])
|
||||||
|
|
||||||
|
return json_message.faves_list(faves)
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
@app.route('/api/load-favorite/<_id>', methods=['GET', 'POST'])
|
||||||
|
def loadFavorite(_id):
|
||||||
|
if request.method == 'POST':
|
||||||
|
try:
|
||||||
|
ID = int(_id)
|
||||||
|
fave = db.session.query(Favorites).filter_by(id = ID).first()
|
||||||
|
view = get_view()
|
||||||
|
view.set_path_with_sub_path(fave.link)
|
||||||
|
return '{"refresh": "true"}'
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
msg = "Incorrect Favorites ID..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/manage-favorites/<_action>', methods=['GET', 'POST'])
|
||||||
|
def manageFavorites(_action):
|
||||||
|
if request.method == 'POST':
|
||||||
|
ACTION = _action.strip()
|
||||||
|
view = get_view()
|
||||||
|
sub_path = view.get_current_sub_path()
|
||||||
|
|
||||||
|
if ACTION == "add":
|
||||||
|
fave = Favorites(link = sub_path)
|
||||||
|
db.session.add(fave)
|
||||||
|
msg = "Added to Favorites successfully..."
|
||||||
|
elif ACTION == "delete":
|
||||||
|
fave = db.session.query(Favorites).filter_by(link = sub_path).first()
|
||||||
|
db.session.delete(fave)
|
||||||
|
msg = "Deleted from Favorites successfully..."
|
||||||
|
else:
|
||||||
|
msg = "Couldn't handle action for favorites item..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
return json_message.create("success", msg)
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
|
@ -0,0 +1,92 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
# Get from __init__
|
||||||
|
from core import app
|
||||||
|
|
||||||
|
from core.utils import MessageHandler # Get simple message processor
|
||||||
|
from core.utils.tmdbscraper import scraper # Get media art scraper
|
||||||
|
|
||||||
|
|
||||||
|
json_message = MessageHandler()
|
||||||
|
tmdb = scraper.get_tmdb_scraper()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-background-poster-trailer', methods=['GET', 'POST'])
|
||||||
|
def getPosterTrailer():
|
||||||
|
if request.method == 'GET':
|
||||||
|
info = {}
|
||||||
|
view = get_view()
|
||||||
|
dot_dots = view.get_dot_dots()
|
||||||
|
|
||||||
|
sub_path = view.get_current_sub_path()
|
||||||
|
file = sub_path.split("/")[-1]
|
||||||
|
trailer = None
|
||||||
|
if "(" in file and ")" in file:
|
||||||
|
title = file.split("(")[0].strip()
|
||||||
|
startIndex = file.index('(') + 1
|
||||||
|
endIndex = file.index(')')
|
||||||
|
date = file[startIndex:endIndex]
|
||||||
|
|
||||||
|
try:
|
||||||
|
video_data = tmdb.search(title, date)[0]
|
||||||
|
video_id = video_data["id"]
|
||||||
|
background_url = video_data["backdrop_path"]
|
||||||
|
background_pth = f"{view.get_current_directory()}/000.jpg"
|
||||||
|
|
||||||
|
tmdb_videos = tmdb.tmdbapi.get_movie(str(video_id), append_to_response="videos")["videos"]["results"]
|
||||||
|
for tmdb_video in tmdb_videos:
|
||||||
|
if "YouTube" in tmdb_video["site"]:
|
||||||
|
trailer_key = tmdb_video["key"]
|
||||||
|
trailer = f"https://www.youtube-nocookie.com/embed/{trailer_key}?start=0&autoplay=1";
|
||||||
|
|
||||||
|
if not trailer:
|
||||||
|
raise Exception("No key found. Defering to none...")
|
||||||
|
except Exception as e:
|
||||||
|
print("No trailer found...")
|
||||||
|
trailer = None
|
||||||
|
|
||||||
|
if not os.path.isfile(background_pth):
|
||||||
|
r = requests.get(background_url, stream = True)
|
||||||
|
|
||||||
|
if r.status_code == 200:
|
||||||
|
r.raw.decode_content = True
|
||||||
|
with open(background_pth,'wb') as f:
|
||||||
|
shutil.copyfileobj(r.raw, f)
|
||||||
|
|
||||||
|
view.load_directory()
|
||||||
|
print('Cover Background Image sucessfully retreived...')
|
||||||
|
else:
|
||||||
|
print('Cover Background Image Couldn\'t be retreived...')
|
||||||
|
|
||||||
|
info.update({'trailer': trailer})
|
||||||
|
info.update({'poster': background_url})
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/backgrounds', methods=['GET', 'POST'])
|
||||||
|
def backgrounds():
|
||||||
|
files = []
|
||||||
|
data = os.listdir(BG_IMGS_PATH)
|
||||||
|
for file in data:
|
||||||
|
if file.lower().endswith(BG_FILE_TYPE):
|
||||||
|
files.append(file)
|
||||||
|
|
||||||
|
return json_message.backgrounds(files)
|
||||||
|
|
||||||
|
@app.route('/api/get-thumbnails', methods=['GET', 'POST'])
|
||||||
|
def getThumbnails():
|
||||||
|
if request.method == 'GET':
|
||||||
|
view = get_view()
|
||||||
|
return json_message.thumbnails( view.get_video_icons() )
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import redirect
|
||||||
|
from flask import request
|
||||||
|
from flask import render_template
|
||||||
|
from flask import send_from_directory
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
# Get from __init__
|
||||||
|
from core import app
|
||||||
|
from core import db
|
||||||
|
from core import Favorites
|
||||||
|
from core import oidc
|
||||||
|
|
||||||
|
from core.utils import MessageHandler # Get simple message processor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
json_message = MessageHandler()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
|
def home():
|
||||||
|
if request.method == 'GET':
|
||||||
|
view = get_view()
|
||||||
|
_dot_dots = view.get_dot_dots()
|
||||||
|
_current_directory = view.get_current_directory()
|
||||||
|
return render_template('pages/index.html', current_directory = _current_directory, dot_dots = _dot_dots)
|
||||||
|
|
||||||
|
return render_template('error.html', title = 'Error!',
|
||||||
|
message = 'Must use GET request type...')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/list-files/<_hash>', methods=['GET', 'POST'])
|
||||||
|
def listFiles(_hash = None):
|
||||||
|
if request.method == 'POST':
|
||||||
|
view = get_view()
|
||||||
|
dot_dots = view.get_dot_dots()
|
||||||
|
|
||||||
|
if dot_dots[0][1] == _hash: # Refresh
|
||||||
|
view.load_directory()
|
||||||
|
elif dot_dots[1][1] == _hash: # Pop from dir
|
||||||
|
view.pop_from_path()
|
||||||
|
|
||||||
|
msg = "Log in with an Admin privlidged user to view the requested path!"
|
||||||
|
is_locked = view.is_folder_locked(_hash)
|
||||||
|
if is_locked and not oidc.user_loggedin:
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
elif is_locked and oidc.user_loggedin:
|
||||||
|
isAdmin = oidc.user_getfield("isAdmin")
|
||||||
|
if isAdmin != "yes" :
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
if dot_dots[0][1] != _hash and dot_dots[1][1] != _hash:
|
||||||
|
path = view.get_path_part_from_hash(_hash)
|
||||||
|
view.push_to_path(path)
|
||||||
|
|
||||||
|
error_msg = view.get_error_message()
|
||||||
|
if error_msg:
|
||||||
|
view.unset_error_message()
|
||||||
|
return json_message.create("danger", error_msg)
|
||||||
|
|
||||||
|
sub_path = view.get_current_sub_path()
|
||||||
|
files = view.get_files_formatted()
|
||||||
|
fave = db.session.query(Favorites).filter_by(link = sub_path).first()
|
||||||
|
in_fave = "true" if fave else "false"
|
||||||
|
files.update({'in_fave': in_fave})
|
||||||
|
return files
|
||||||
|
else:
|
||||||
|
msg = "Can't manage the request type..."
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST'])
|
||||||
|
def fileManagerAction(_type, _hash = None):
|
||||||
|
view = get_view()
|
||||||
|
|
||||||
|
if _type == "reset-path" and _hash == "None":
|
||||||
|
view.set_to_home()
|
||||||
|
msg = "Returning to home directory..."
|
||||||
|
return json_message.create("success", msg)
|
||||||
|
|
||||||
|
folder = view.get_current_directory()
|
||||||
|
file = view.get_path_part_from_hash(_hash)
|
||||||
|
fpath = os.path.join(folder, file)
|
||||||
|
logger.debug(fpath)
|
||||||
|
|
||||||
|
if _type == "files":
|
||||||
|
logger.debug(f"Downloading:\n\tDirectory: {folder}\n\tFile: {file}")
|
||||||
|
return send_from_directory(directory=folder, filename=file)
|
||||||
|
if _type == "remux":
|
||||||
|
# NOTE: Need to actually implimint a websocket to communicate back to client that remux has completed.
|
||||||
|
# As is, the remux thread hangs until completion and client tries waiting until server reaches connection timeout.
|
||||||
|
# I.E....this is stupid but for now works better than nothing
|
||||||
|
good_result = view.remux_video(_hash, fpath)
|
||||||
|
if good_result:
|
||||||
|
return '{"path":"static/remuxs/' + _hash + '.mp4"}'
|
||||||
|
else:
|
||||||
|
msg = "Remuxing: Remux failed or took too long; please, refresh the page and try again..."
|
||||||
|
return json_message.create("success", msg)
|
||||||
|
|
||||||
|
if _type == "remux":
|
||||||
|
stream_target = view.remux_video(_hash, fpath)
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Positionally protecting actions further down that are privlidged
|
||||||
|
# Be aware of ordering!
|
||||||
|
msg = "Log in with an Admin privlidged user to do this action!"
|
||||||
|
if not oidc.user_loggedin:
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
elif oidc.user_loggedin:
|
||||||
|
isAdmin = oidc.user_getfield("isAdmin")
|
||||||
|
if isAdmin != "yes" :
|
||||||
|
return json_message.create("danger", msg)
|
||||||
|
|
||||||
|
|
||||||
|
if _type == "run-locally":
|
||||||
|
msg = "Opened media..."
|
||||||
|
view.open_file_locally(fpath)
|
||||||
|
return json_message.create("success", msg)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from . import Images
|
||||||
|
from . import CRUD
|
||||||
|
from . import Routes
|
||||||
|
from . import Favorites
|
||||||
|
from .pages import Flask_Login
|
||||||
|
from .pages import Flask_Register
|
||||||
|
from .pages import OIDC_Login
|
||||||
|
from .pages import OIDC_Register
|
||||||
|
from .pages import LoginManager
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request, render_template, flash, redirect, url_for
|
||||||
|
from flask_login import current_user, login_user, logout_user
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from core import app, bcrypt, db, User, LoginForm
|
||||||
|
from core.utils import MessageHandler # Get simple message processor
|
||||||
|
|
||||||
|
|
||||||
|
msgHandler = MessageHandler()
|
||||||
|
|
||||||
|
@app.route('/app-login', methods=['GET', 'POST'])
|
||||||
|
def app_login():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for("home"))
|
||||||
|
|
||||||
|
_form = LoginForm()
|
||||||
|
if _form.validate_on_submit():
|
||||||
|
user = db.session.query(User).filter(User.username == _form.username.data).first()
|
||||||
|
|
||||||
|
if user and bcrypt.check_password_hash(user.password, _form.password.data):
|
||||||
|
login_user(user, remember=False)
|
||||||
|
flash("Logged in successfully!", "success")
|
||||||
|
return redirect(url_for("home"))
|
||||||
|
|
||||||
|
flash("Username or password incorrect! Please try again...", "danger")
|
||||||
|
|
||||||
|
return render_template('pages/login.html', form = _form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/app-logout')
|
||||||
|
def app_logout():
|
||||||
|
logout_user()
|
||||||
|
flash("Logged out successfully!", "success")
|
||||||
|
return redirect(url_for("home"))
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request, render_template, url_for, redirect, flash
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from core import app, bcrypt, db, current_user, RegisterForm # Get from __init__
|
||||||
|
from core.models import User
|
||||||
|
from core.utils import MessageHandler # Get simple message processor
|
||||||
|
|
||||||
|
|
||||||
|
msgHandler = MessageHandler()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/app-register', methods=['GET', 'POST'])
|
||||||
|
def app_register():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for("home"))
|
||||||
|
|
||||||
|
_form = RegisterForm()
|
||||||
|
if _form.validate_on_submit():
|
||||||
|
hashed_password = bcrypt.generate_password_hash(_form.password.data).decode("utf-8")
|
||||||
|
user = User(username = _form.username.data, email = _form.email.data, password = hashed_password)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
flash("Account created successfully!", "success")
|
||||||
|
return redirect(url_for("login"))
|
||||||
|
|
||||||
|
return render_template('pages/register.html',
|
||||||
|
form = _form)
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import redirect, url_for, flash
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from core import app
|
||||||
|
|
||||||
|
|
||||||
|
ROUTE = app.config['LOGIN_PATH']
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if ROUTE == "OIDC":
|
||||||
|
return redirect(url_for("oidc_login"))
|
||||||
|
if ROUTE == "FLASK_LOGIN":
|
||||||
|
return redirect(url_for("app_login"))
|
||||||
|
|
||||||
|
flash("No Login Path Accessable! Please contact an Administrator!", "danger")
|
||||||
|
return redirect(url_for("home"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
if ROUTE == "OIDC":
|
||||||
|
return redirect(url_for("oidc_logout"))
|
||||||
|
if ROUTE == "FLASK_LOGIN":
|
||||||
|
return redirect(url_for("app_logout"))
|
||||||
|
|
||||||
|
flash("No Logout Path Accessable! Please contact an Administrator!", "danger")
|
||||||
|
return redirect(url_for("home"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
if ROUTE == "OIDC":
|
||||||
|
return redirect(url_for("oidc_register"))
|
||||||
|
if ROUTE == "FLASK_LOGIN":
|
||||||
|
return redirect(url_for("app_register"))
|
||||||
|
|
||||||
|
flash("No Register Path Accessable! Please contact an Administrator!", "danger")
|
||||||
|
return redirect(url_for("home"))
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request, redirect, flash
|
||||||
|
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from ... import app, oidc
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/oidc-login', methods=['GET', 'POST'])
|
||||||
|
@oidc.require_login
|
||||||
|
def oidc_login():
|
||||||
|
print(request)
|
||||||
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/oidc-logout', methods=['GET', 'POST'])
|
||||||
|
@oidc.require_login
|
||||||
|
def oidc_logout():
|
||||||
|
oidc.logout()
|
||||||
|
flash("Logged out successfully!", "success")
|
||||||
|
# NOTE: Need to redirect to logout on OIDC server to end session there too.
|
||||||
|
# If not, we can hit login url again and get same token until it expires.
|
||||||
|
return redirect( oidc.client_secrets.get('issuer')
|
||||||
|
+ '/protocol/openid-connect/logout?redirect_uri='
|
||||||
|
+ app.config['APP_REDIRECT_URI'])
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from flask import request, render_template, url_for, redirect, flash
|
||||||
|
|
||||||
|
# App imports
|
||||||
|
from ... import app, oidc, db # Get from __init__
|
||||||
|
from ...utils import MessageHandler # Get simple message processor
|
||||||
|
|
||||||
|
|
||||||
|
msgHandler = MessageHandler()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/oidc-register', methods=['GET', 'POST'])
|
||||||
|
def oidc_register():
|
||||||
|
if oidc.user_loggedin:
|
||||||
|
return redirect("/home")
|
||||||
|
|
||||||
|
_form = RegisterForm()
|
||||||
|
if _form.validate_on_submit():
|
||||||
|
# TODO: Create...
|
||||||
|
# NOTE: Do a requests api here maybe??
|
||||||
|
|
||||||
|
# hashed_password = bcrypt.generate_password_hash(_form.password.data).decode("utf-8")
|
||||||
|
# user = User(username=_form.username.data, password=hashed_password)
|
||||||
|
# db.session.add(user)
|
||||||
|
# db.session.commit()
|
||||||
|
flash("Account created successfully!", "success")
|
||||||
|
return redirect("/login")
|
||||||
|
|
||||||
|
return render_template('pages/register.html', form = _form)
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-123" viewBox="0 0 16 16">
|
||||||
|
<path d="M2.873 11.297V4.142H1.699L0 5.379v1.137l1.64-1.18h.06v5.961h1.174Zm3.213-5.09v-.063c0-.618.44-1.169 1.196-1.169.676 0 1.174.44 1.174 1.106 0 .624-.42 1.101-.807 1.526L4.99 10.553v.744h4.78v-.99H6.643v-.069L8.41 8.252c.65-.724 1.237-1.332 1.237-2.27C9.646 4.849 8.723 4 7.308 4c-1.573 0-2.36 1.064-2.36 2.15v.057h1.138Zm6.559 1.883h.786c.823 0 1.374.481 1.379 1.179.01.707-.55 1.216-1.421 1.21-.77-.005-1.326-.419-1.379-.953h-1.095c.042 1.053.938 1.918 2.464 1.918 1.478 0 2.642-.839 2.62-2.144-.02-1.143-.922-1.651-1.551-1.714v-.063c.535-.09 1.347-.66 1.326-1.678-.026-1.053-.933-1.855-2.359-1.845-1.5.005-2.317.88-2.348 1.898h1.116c.032-.498.498-.944 1.206-.944.703 0 1.206.435 1.206 1.07.005.64-.504 1.106-1.2 1.106h-.75v.96Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 870 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-activity" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M6 2a.5.5 0 0 1 .47.33L10 12.036l1.53-4.208A.5.5 0 0 1 12 7.5h3.5a.5.5 0 0 1 0 1h-3.15l-1.88 5.17a.5.5 0 0 1-.94 0L6 3.964 4.47 8.171A.5.5 0 0 1 4 8.5H.5a.5.5 0 0 1 0-1h3.15l1.88-5.17A.5.5 0 0 1 6 2Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 367 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-alarm-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M6 .5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1H9v1.07a7.001 7.001 0 0 1 3.274 12.474l.601.602a.5.5 0 0 1-.707.708l-.746-.746A6.97 6.97 0 0 1 8 16a6.97 6.97 0 0 1-3.422-.892l-.746.746a.5.5 0 0 1-.707-.708l.602-.602A7.001 7.001 0 0 1 7 2.07V1h-.5A.5.5 0 0 1 6 .5zm2.5 5a.5.5 0 0 0-1 0v3.362l-1.429 2.38a.5.5 0 1 0 .858.515l1.5-2.5A.5.5 0 0 0 8.5 9V5.5zM.86 5.387A2.5 2.5 0 1 1 4.387 1.86 8.035 8.035 0 0 0 .86 5.387zM11.613 1.86a2.5 2.5 0 1 1 3.527 3.527 8.035 8.035 0 0 0-3.527-3.527z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 626 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-alarm" viewBox="0 0 16 16">
|
||||||
|
<path d="M8.5 5.5a.5.5 0 0 0-1 0v3.362l-1.429 2.38a.5.5 0 1 0 .858.515l1.5-2.5A.5.5 0 0 0 8.5 9V5.5z"/>
|
||||||
|
<path d="M6.5 0a.5.5 0 0 0 0 1H7v1.07a7.001 7.001 0 0 0-3.273 12.474l-.602.602a.5.5 0 0 0 .707.708l.746-.746A6.97 6.97 0 0 0 8 16a6.97 6.97 0 0 0 3.422-.892l.746.746a.5.5 0 0 0 .707-.708l-.601-.602A7.001 7.001 0 0 0 9 2.07V1h.5a.5.5 0 0 0 0-1h-3zm1.038 3.018a6.093 6.093 0 0 1 .924 0 6 6 0 1 1-.924 0zM0 3.5c0 .753.333 1.429.86 1.887A8.035 8.035 0 0 1 4.387 1.86 2.5 2.5 0 0 0 0 3.5zM13.5 1c-.753 0-1.429.333-1.887.86a8.035 8.035 0 0 1 3.527 3.527A2.5 2.5 0 0 0 13.5 1z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 711 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-bottom" viewBox="0 0 16 16">
|
||||||
|
<rect width="4" height="12" x="6" y="1" rx="1"/>
|
||||||
|
<path d="M1.5 14a.5.5 0 0 0 0 1v-1zm13 1a.5.5 0 0 0 0-1v1zm-13 0h13v-1h-13v1z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 271 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-center" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 1a.5.5 0 0 1 .5.5V6h-1V1.5A.5.5 0 0 1 8 1zm0 14a.5.5 0 0 1-.5-.5V10h1v4.5a.5.5 0 0 1-.5.5zM2 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 315 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-end" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M14.5 1a.5.5 0 0 0-.5.5v13a.5.5 0 0 0 1 0v-13a.5.5 0 0 0-.5-.5z"/>
|
||||||
|
<path d="M13 7a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 318 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-middle" viewBox="0 0 16 16">
|
||||||
|
<path d="M6 13a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v10zM1 8a.5.5 0 0 0 .5.5H6v-1H1.5A.5.5 0 0 0 1 8zm14 0a.5.5 0 0 1-.5.5H10v-1h4.5a.5.5 0 0 1 .5.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 316 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-start" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M1.5 1a.5.5 0 0 1 .5.5v13a.5.5 0 0 1-1 0v-13a.5.5 0 0 1 .5-.5z"/>
|
||||||
|
<path d="M3 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 318 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-align-top" viewBox="0 0 16 16">
|
||||||
|
<rect width="4" height="12" rx="1" transform="matrix(1 0 0 -1 6 15)"/>
|
||||||
|
<path d="M1.5 2a.5.5 0 0 1 0-1v1zm13-1a.5.5 0 0 1 0 1V1zm-13 0h13v1h-13V1z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 287 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-alt" viewBox="0 0 16 16">
|
||||||
|
<path d="M1 13.5a.5.5 0 0 0 .5.5h3.797a.5.5 0 0 0 .439-.26L11 3h3.5a.5.5 0 0 0 0-1h-3.797a.5.5 0 0 0-.439.26L5 13H1.5a.5.5 0 0 0-.5.5zm10 0a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0-.5.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 326 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-app-indicator" viewBox="0 0 16 16">
|
||||||
|
<path d="M5.5 2A3.5 3.5 0 0 0 2 5.5v5A3.5 3.5 0 0 0 5.5 14h5a3.5 3.5 0 0 0 3.5-3.5V8a.5.5 0 0 1 1 0v2.5a4.5 4.5 0 0 1-4.5 4.5h-5A4.5 4.5 0 0 1 1 10.5v-5A4.5 4.5 0 0 1 5.5 1H8a.5.5 0 0 1 0 1H5.5z"/>
|
||||||
|
<path d="M16 3a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 387 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-app" viewBox="0 0 16 16">
|
||||||
|
<path d="M11 2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h6zM5 1a4 4 0 0 0-4 4v6a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V5a4 4 0 0 0-4-4H5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 282 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-apple" viewBox="0 0 16 16">
|
||||||
|
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516.024.034 1.52.087 2.475-1.258.955-1.345.762-2.391.728-2.43zm3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422.212-2.189 1.675-2.789 1.698-2.854.023-.065-.597-.79-1.254-1.157a3.692 3.692 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56.244.729.625 1.924 1.273 2.796.576.984 1.34 1.667 1.659 1.899.319.232 1.219.386 1.843.067.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758.347-.79.505-1.217.473-1.282z"/>
|
||||||
|
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516.024.034 1.52.087 2.475-1.258.955-1.345.762-2.391.728-2.43zm3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422.212-2.189 1.675-2.789 1.698-2.854.023-.065-.597-.79-1.254-1.157a3.692 3.692 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56.244.729.625 1.924 1.273 2.796.576.984 1.34 1.667 1.659 1.899.319.232 1.219.386 1.843.067.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758.347-.79.505-1.217.473-1.282z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-archive-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M12.643 15C13.979 15 15 13.845 15 12.5V5H1v7.5C1 13.845 2.021 15 3.357 15h9.286zM5.5 7h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1 0-1zM.8 1a.8.8 0 0 0-.8.8V3a.8.8 0 0 0 .8.8h14.4A.8.8 0 0 0 16 3V1.8a.8.8 0 0 0-.8-.8H.8z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 359 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-archive" viewBox="0 0 16 16">
|
||||||
|
<path d="M0 2a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1v7.5a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 1 12.5V5a1 1 0 0 1-1-1V2zm2 3v7.5A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V5H2zm13-3H1v2h14V2zM5 7.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 401 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-down" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M4.854 14.854a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L4 13.293V3.5A2.5 2.5 0 0 1 6.5 1h8a.5.5 0 0 1 0 1h-8A1.5 1.5 0 0 0 5 3.5v9.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 350 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M1.146 4.854a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 4H12.5A2.5 2.5 0 0 1 15 6.5v8a.5.5 0 0 1-1 0v-8A1.5 1.5 0 0 0 12.5 5H2.707l3.147 3.146a.5.5 0 1 1-.708.708l-4-4z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 349 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-right" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M14.854 4.854a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 4H3.5A2.5 2.5 0 0 0 1 6.5v8a.5.5 0 0 0 1 0v-8A1.5 1.5 0 0 1 3.5 5h9.793l-3.147 3.146a.5.5 0 0 0 .708.708l4-4z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 350 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-90deg-up" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 349 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-down" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M1 3.5a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13a.5.5 0 0 1-.5-.5zM8 6a.5.5 0 0 1 .5.5v5.793l2.146-2.147a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-3-3a.5.5 0 0 1 .708-.708L7.5 12.293V6.5A.5.5 0 0 1 8 6z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 375 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5zM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 375 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-right" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M6 8a.5.5 0 0 0 .5.5h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L12.293 7.5H6.5A.5.5 0 0 0 6 8zm-2.5 7a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 375 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-up" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 10a.5.5 0 0 0 .5-.5V3.707l2.146 2.147a.5.5 0 0 0 .708-.708l-3-3a.5.5 0 0 0-.708 0l-3 3a.5.5 0 1 0 .708.708L7.5 3.707V9.5a.5.5 0 0 0 .5.5zm-7 2.5a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13a.5.5 0 0 1-.5-.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 376 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
||||||
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 352 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
|
||||||
|
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 359 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-circle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 321 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-circle" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 370 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-left-circle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M16 8A8 8 0 1 0 0 8a8 8 0 0 0 16 0zm-5.904-2.803a.5.5 0 1 1 .707.707L6.707 10h2.768a.5.5 0 0 1 0 1H5.5a.5.5 0 0 1-.5-.5V6.525a.5.5 0 0 1 1 0v2.768l4.096-4.096z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 326 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-left-circle" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-5.904-2.854a.5.5 0 1 1 .707.708L6.707 9.95h2.768a.5.5 0 1 1 0 1H5.5a.5.5 0 0 1-.5-.5V6.475a.5.5 0 1 1 1 0v2.768l4.096-4.097z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 377 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-left-square-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M2 16a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2zm8.096-10.803L6 9.293V6.525a.5.5 0 0 0-1 0V10.5a.5.5 0 0 0 .5.5h3.975a.5.5 0 0 0 0-1H6.707l4.096-4.096a.5.5 0 1 0-.707-.707z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 363 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-left-square" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M15 2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2zM0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm10.096 3.146a.5.5 0 1 1 .707.708L6.707 9.95h2.768a.5.5 0 1 1 0 1H5.5a.5.5 0 0 1-.5-.5V6.475a.5.5 0 1 1 1 0v2.768l4.096-4.097z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 451 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M2 13.5a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 0-1H3.707L13.854 2.854a.5.5 0 0 0-.708-.708L3 12.293V7.5a.5.5 0 0 0-1 0v6z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 286 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-right-circle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm5.904-2.803a.5.5 0 1 0-.707.707L9.293 10H6.525a.5.5 0 0 0 0 1H10.5a.5.5 0 0 0 .5-.5V6.525a.5.5 0 0 0-1 0v2.768L5.904 5.197z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 326 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-right-circle" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.854 5.146a.5.5 0 1 0-.708.708L9.243 9.95H6.475a.5.5 0 1 0 0 1h3.975a.5.5 0 0 0 .5-.5V6.475a.5.5 0 1 0-1 0v2.768L5.854 5.146z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 379 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-right-square-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M14 16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12zM5.904 5.197 10 9.293V6.525a.5.5 0 0 1 1 0V10.5a.5.5 0 0 1-.5.5H6.525a.5.5 0 0 1 0-1h2.768L5.197 5.904a.5.5 0 0 1 .707-.707z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 365 B |