First rough draft

This commit is contained in:
Tyler Vigario
2020-05-19 20:45:22 -07:00
parent 3b104a6f3e
commit 1c9498e340
26 changed files with 6875 additions and 593 deletions

6135
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
web/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "botamusique",
"private": true,
"scripts": {
"dev": "npm run development",
"development": "webpack --mode=development --config webpack.config.js --progress",
"prod": "npm run production",
"production": "webpack --mode=production --config webpack.config.js --progress",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/azlux/botamusique.git"
},
"author": "azlux",
"license": "MIT",
"bugs": {
"url": "https://github.com/azlux/botamusique/issues"
},
"homepage": "https://github.com/azlux/botamusique#readme",
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"babel-loader": "^8.1.0",
"core-js": "^3.6.5",
"css-loader": "^3.5.3",
"html-webpack-plugin": "^4.3.0",
"mini-css-extract-plugin": "^0.9.0",
"regenerator-runtime": "^0.13.5",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
},
"dependencies": {
"bootstrap": "^4.5.0",
"jquery": "^3.5.1",
"jquery-migrate": "^3.3.0",
"popper.js": "^1.16.1"
}
}

21
web/scripts/build-css.js Normal file
View File

@ -0,0 +1,21 @@
const sass = require('node-sass');
const fs = require('fs');
sass.render({
file: './src/sass/main.scss',
outFile: './build/assets/css/main.min.css',
outputStyle: 'compressed',
}, function (error, result) {
if (error) {
console.error(error);
}
// No errors during compilation, write the result to disk
fs.writeFile('./build/assets/css/main.min.css', result.css, function (err) {
if (err) {
console.error(err);
}
console.info('CSS compiled.');
});
});

12
web/src/js/app.js Normal file
View File

@ -0,0 +1,12 @@
// jQuery 3.x
window.$ = window.jQuery = require('jquery/dist/jquery');
require('jquery-migrate/dist/jquery-migrate');
// Popper 1.x
require('popper.js/dist/popper');
// Boostrap 4.x
require('bootstrap/js/src/index');
// Application code
require('./main.js');

7
web/src/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

5
web/src/js/fontawesome.all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1112
web/src/js/main.js Normal file

File diff suppressed because it is too large Load Diff

5
web/src/js/popper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

3
web/src/sass/app.scss Normal file
View File

@ -0,0 +1,3 @@
@import '~bootstrap/scss/bootstrap';
@import './main';

12
web/src/sass/bootstrap.darkly.min.css vendored Normal file

File diff suppressed because one or more lines are too long

214
web/src/sass/main.scss Normal file
View File

@ -0,0 +1,214 @@
.btn-space {margin-right: 5px;}
/* Playlist */
.playlist-item {transition: all 0.2s ease-in-out;}
.playlist-artwork {
float: left;
margin-left: 10px;
white-space: nowrap;
overflow: hidden;
}
.tag-space {
margin-right: 3px;
}
.tag-click {
cursor: pointer;
transition: 400ms;
}
.tag-unclicked {
opacity: 0.6;
}
.tag-clicked {
box-shadow: 2px 4px 10px #777777;
transform: scale(1.2);
opacity: 1;
margin: 5px;
}
.library-item {
display: flex;
padding: .5rem .5rem .5rem 0;
height: 72px;
transition: ease-in-out 200ms;
}
.library-thumb-img {
width: 70px;
height: 70px;
border-radius: 5px;
}
.library-thumb-col {
position: relative;
padding-left: 0;
overflow: hidden;
margin: -0.5rem 1rem -0.5rem 0;
}
.library-thumb-grp {
position: absolute;
top: 0;
left: -95px;
width: 70px;
margin-left: 15px;
transition: left 300ms;
border-radius: 5px;
opacity: 0.7;
font-weight: 300;
}
.library-thumb-grp-hover {
left: -15px;
}
.library-thumb-btn-up {
position: absolute !important;
top: 0;
height: 70px;
font-size: 2em;
padding-top: 10px;
}
.library-btn-svg {
width: 1rem;
fill: currentColor;
}
.library-info-col {
margin-right: 1rem;
padding: 3px 0;
display: flex;
flex-direction: column;
justify-content: center;
white-space: nowrap;
overflow: hidden;
}
.library-info-col .small {
font-weight: 300;
}
.library-action {
margin-left: auto;
}
.library-info-col .path {
font-style: italic !important;
font-weight: 300;
}
/* Theme changer and player button */
.floating-button {
width: 50px;
height: 50px;
background-color: #aaaaaa40;
border-radius: 50%;
box-shadow: 0 6px 10px 0 #66666647;
transition: all 0.1s ease-in-out;
font-size: 25px;
color: #9896967a;
text-align: center;
line-height: 52px;
position: fixed;
right: 50px;
}
.floating-button:hover {
background-color: hsl(0, 0%, 43%);
color: white;
}
#volume-slider {
margin-top: 4px;
margin-right: 5px;
}
.dropdown {
display: inline-block;
}
#volume-popover {
position: relative;
background: #333;
color: white;
font-weight: bold;
padding: 4px 8px;
font-size: 20px;
border-radius: 4px;
display: none;
}
#volume-popover[data-show] {
display: flex;
}
#volume-popover a {
cursor: pointer;
}
#volume-popover-arrow,
#volume-popover-arrow::before {
position: absolute;
width: 10px;
height: 10px;
z-index: -1;
top: 16px;
left: 46px;
}
#volume-popover-arrow::before {
content: '';
transform: rotate(45deg);
background: #333;
}
#volume-popover[data-popper-placement^='top'] > #volume-popover-arrow {
bottom: -4px;
}
#playerToast {
position: fixed;
right: 20px;
top: 20px;
max-width: 800px;
}
#playerContainer {
display: flex;
height: 105px;
}
#playerArtwork {
width: 80px;
height: 80px;
border-radius: 5px;
}
#playerArtworkIdle {
width: 80px;
height: 80px;
border-radius: 5px;
margin: auto;
padding: 15px;
}
#playerInfo {
position: relative;
padding-top: 6px;
margin-left: 10px;
height: 80px;
font-size: 15px;
}
#playerTitle {
display: block;
white-space: nowrap;
}
#playerArtist {
display: block;
white-space: nowrap;
min-height: 20px;
}
#playerActionBox {
margin-top: 5px;
display: flex;
float: right;
}
#playerBarBox {
margin-top: 5px;
height: 15px;
width: 400px;
cursor: pointer;
}
.scrolling {
animation: scrolling 8s linear infinite;
}
@keyframes scrolling {
0% {
transform: translateX(100%);
opacity: 1;
}
95%{
transform: translateX(-90%);
opacity: 1;
}
100% {
transform: translateX(-100%);
opacity: 0;
}
}

View File

@ -0,0 +1,581 @@
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta charset="UTF-8">
<title>botamusique web interface</title>
<link rel="icon" href="../assets/image/favicon.ico" />
<link rel="stylesheet" href="../assets/css/main.css">
</head>
<body>
<div class="container page-header mb-5" id="banner">
<div class="row">
<div class="col-auto">
<img src="../assets/image/logo.png" height="200px">
</div>
<div class="col my-auto">
<h1>botamusique Web Interface</h1>
</div>
</div>
</div>
<div id="playlist" class="container mb-5">
<div class="btn-toolbar mb-2" role="toolbar" aria-label="Playlist controls">
<button type="button" id="play-pause-btn" class="btn btn-info mb-2 btn-space" onclick="togglePlayPause()">
<i class="fas fa-play"></i>
</button>
<button type="button" id="fast-forward-btn" class="btn btn-info mb-2"
onclick="request('post', {action : 'next'})">
<i class="fas fa-fast-forward"></i>
</button>
<div class="ml-auto">
<div class="dropdown mr-2">
<button class="btn btn-secondary dropdown-toggle" type="button" id="play-mode"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-tasks mr-2" aria-hidden="true" id="modeIndicator"></i>
</button>
<div class="dropdown-menu" aria-labelledby="play-mode">
<a class="dropdown-item" href="#" id="one-shot-mode-btn" onclick="changePlayMode('one-shot')">
<i class="fas fa-tasks mr-2" aria-hidden="true"></i>One-shot
</a>
<a class="dropdown-item" href="#" id="random-mode-btn" onclick="changePlayMode('randomize')">
<i class="fas fa-random mr-2" aria-hidden="true"></i>Random
</a>
<a class="dropdown-item" href="#" id="repeat-mode-btn" onclick="changePlayMode('repeat')">
<i class="fas fa-redo mr-2" aria-hidden="true"></i>Repeat
</a>
<a class="dropdown-item" href="#" id="autoplay-mode-btn" onclick="changePlayMode('autoplay')">
<i class="fas fa-robot mr-2" aria-hidden="true"></i>Autoplay
</a>
</div>
</div>
<button type="button" id="volume-popover-btn" class="btn btn-warning ml-1"
onclick="toggleVolumePopover()">
<i class="fa fa-volume-up" aria-hidden="true"></i>
</button>
<div id="volume-popover">
<a onclick="request('post', {action : 'volume_down'})">
<i class="fa fa-volume-down" aria-hidden="true"></i>
</a>
<input type="range" class="custom-range ml-1" id="volume-slider" min="0" max="1" step="0.01"
value="0.5" onchange="setVolumeDelayed(this.value)" />
<a onclick="request('post', {action : 'volume_up'})">
<i class="fa fa-volume-up" aria-hidden="true"></i>
</a>
<div id="volume-popover-arrow"></div>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col" class="d-none d-md-table-cell">#</th>
<th scope="col" class="w-50">Title</th>
<th scope="col" class="d-none d-md-table-cell">Url/Path</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody id="playlist-table" class="playlist-table">
<tr id="playlist-loading">
<td colspan="4" class="text-center">
<img style="margin: auto; width: 35px;" src="../assets/image/loading.svg" />
</td>
</tr>
<tr id="playlist-empty" class="d-none">
<td colspan="4" class="text-center">
<img style="margin: auto; width: 35px;" src="../assets/image/empty_box.svg" />
</td>
</tr>
<tr class="playlist-expand table-dark d-none">
<td colspan="4" class="text-center">
<a class="text-muted" href="javascript:">See item <span
class="playlist-expand-item-range"></span> on the playlist.
</a>
</td>
</tr>
<tr class="playlist-item-template d-none">
<th scope="row" class="playlist-item-index d-none d-md-table-cell"></th>
<td>
<input hidden type="text" class="playlist-item-id" value="" />
<div class="float-left">
<img width="80" class="playlist-item-thumbnail" src="../assets/image/unknown-album.png" />
</div>
<div class="playlist-artwork">
<b class="playlist-item-title"></b>
<span class="playlist-item-type badge badge-secondary"></span>
<br />
<span class="playlist-item-artist"></span>
<br />
<div class="playlist-item-tags">
<a class="playlist-item-edit tag-space tag-click">
<i class="fas fa-edit" style="color: #AAAAAA"></i>
</a>
</div>
</div>
</td>
<td class="d-none d-md-table-cell">
<small class="playlist-item-path"></small>
</td>
<td>
<div class="btn-group">
<button type="button" class="playlist-item-play btn btn-info btn-sm">
<i class="fas fa-play" aria-hidden="true"></i>
</button>
<button type="button" class="playlist-item-trash btn btn-danger btn-sm ml-1">
<i class="fas fa-trash-alt" aria-hidden="true"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="btn-group">
<button type="button" class="btn btn-danger mr-1" onclick="request('post', {action : 'clear'})">
<i class="fas fa-trash-alt" aria-hidden="true"></i> Clear Playlist
</button>
</div>
</div>
<div class="container mb-3">
<h2 id="forms">Music Library</h2>
<div class="card mb-3">
<div class="card-header">
<h5 class="card-title">Filters</h5>
<hr>
<div class="row">
<div class="col">
<label>Type</label>
<div id="filter-type" class="input-group mb-2">
<div class="btn-group btn-group-sm btn-group-toggle">
<label id="filter-type-file" class="btn btn-secondary"
onclick="setFilterType(event, 'file')">
<input type="checkbox" name="options"> File
</label>
<label id="filter-type-url" class="btn btn-secondary"
onclick="setFilterType(event, 'url')">
<input type="checkbox" name="options"> URL
</label>
<label id="filter-type-radio" class="btn btn-secondary"
onclick="setFilterType(event, 'radio')">
<input type="checkbox" name="options"> Radio
</label>
</div>
</div>
<label for="filter-dir">Directory</label>
<div id="filter-path" class="input-group mb-2">
<select class="form-control form-control-sm" id="filter-dir" disabled>
<option value="">.</option>
{% for dir in dirs %}
<option value="{{ dir }}">{{ dir }}</option>
{% endfor %}
</select>
</div>
<label for="filter-keywords">Keywords</label>
<div id="filter-path" class="input-group mb-2">
<input class="form-control form-control-sm" id="filter-keywords" name="keywords"
placeholder="Keywords..." style="margin-top:5px;" />
</div>
</div>
<div class="col">
<label for="filter-tag">Tags</label>
<div id="filter-type mb-2">
{% for tag in tags_color_lookup.keys() %}
<span id="filter-tag"
class="filter-tag tag-unclicked tag-click badge badge-{{ tags_color_lookup[tag] }}">{{ tag }}</span>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="card-body">
<div id="library-group" class="list-group library-group" style="overflow: auto;">
<div id="library-item-loading" class="list-group-item library-item">
<img style="margin: auto; width: 35px;" src="../assets/image/loading.svg" />
</div>
<div id="library-item-empty" style="display: none" class="list-group-item library-item">
<img style="margin: auto; width: 35px;" src="../assets/image/empty_box.svg" />
</div>
<div id="library-item" style="display: none;" class="list-group-item library-item">
<input hidden type="text" class="library-item-id" value="" />
<div class="library-thumb-col">
<div class="library-thumb-img">
<img class="library-item-thumb library-thumb-img"
src="../assets/image/unknown-album.png" />
</div>
<div class="btn-group-vertical library-thumb-grp">
<div class="library-item-play btn btn-secondary library-thumb-btn-up" title="Play">
<i class="fas fa-play" aria-hidden="true"></i>
</div>
</div>
</div>
<label for="filter-keywords">Keywords</label>
<div id="filter-path" class="input-group mb-2">
<input class="form-control form-control-sm" id="filter-keywords" name="keywords"
placeholder="Keywords..." style="margin-top:5px;" />
</div>
</div>
<div class="library-info-col col-4 d-none d-md-flex" style="padding: 3px;">
<span class="library-item-path text-muted path">Path/to/the/file</span>
<div class="library-item-tags">
<a class="tag-space tag-click library-item-edit"><i class="fas fa-edit"
style="color: #AAAAAA"></i></a>
<span class="library-item-notag badge badge-light text-muted font-italic">No
tag</span>
<span class="library-item-tag tag-space badge">Tag</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="library-group" class="list-group library-group" style="overflow: auto;">
<div id="library-item-loading" class="list-group-item library-item">
<img style="margin: auto; width: 35px;" src="../assets/image/loading.svg" />
</div>
<div id="library-item-empty" style="display: none" class="list-group-item library-item">
<img style="margin: auto; width: 35px;" src="../assets/image/empty_box.svg" />
</div>
<div id="library-item" style="display: none;" class="list-group-item library-item">
<input hidden type="text" class="library-item-id" value="" />
<div class="btn-group library-action">
<button class="library-item-add-next btn btn-info btn-sm btn-space" type="button" title="Next to play">
<svg class="library-btn-svg" style="width: 1rem; fill: currentColor;" viewBox="5 5 17 17">
<path d="m5.700245,3.92964l0,14.150376l11.451127,-7.075188l-11.451127,-7.075188z"></path>
<path
d="m20.942859,18.221072l-3.323292,0l0,3.323292l-1.107764,0l0,-3.323292l-3.323292,0l0,-1.107764l3.323292,0l0,-3.323292l1.107764,0l0,3.323292l3.323292,0l0,1.107764z">
</path>
</svg>
</button>
<button class="library-item-add-bottom library-btn btn btn-info btn-sm btn-space" type="button"
title="Add to bottom">
<svg class="library-btn-svg" style="width: 1rem; fill: currentColor;" viewBox="2 2 20 20">
<path d="M2,16H10V14H2M18,14V10H16V14H12V16H16V20H18V16H22V14M14,6H2V8H14M14,10H2V12H14V10Z">
</path>
</svg>
</button>
<button class="library-item-download btn btn-primary btn-sm btn-space" type="button">
<i class="fas fa-download" aria-hidden="true"></i>
</button>
<button class="library-item-trash btn btn-danger btn-sm btn-space" type="button">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
<div class="library-info-col library-info-title col-5" style="padding: 12px 0;">
<div>
<span class="library-item-type lead text-muted btn-space">[File]</span>
<span class="library-item-title lead btn-space">This is my title</span>
<span class="library-item-artist text-muted"> - Artist</span>
</div>
</div>
<div class="library-info-col col-4 d-none d-md-flex" style="padding: 3px;">
<span class="library-item-path text-muted path">Path/to/the/file</span>
<div class="library-item-tags">
<a class="tag-space tag-click library-item-edit"><i class="fas fa-edit" style="color: #AAAAAA"></i></a>
<span class="library-item-notag badge badge-light text-muted font-italic">No
tag</span>
<span class="library-item-tag tag-space badge">Tag</span>
</div>
</div>
<div class="btn-group" role="group" class="mb-2">
<button type="submit" class="btn btn-secondary mr-1" onclick="addAllResults()"><i class="fas fa-plus"
aria-hidden="true"></i> Add All
</button>
<button type="submit" class="btn btn-secondary mr-1"
onclick="request('post', {action : 'rescan'}); updateResults()">
<i class="fas fa-sync-alt" aria-hidden="true"></i> Rescan Files
</button>
<button type="submit" class="btn btn-secondary mr-1" onclick="downloadAllResults()"><i class="fas fa-download"
aria-hidden="true"></i> Download All
</button>
<button type="button" class="btn btn-danger mr-1" data-toggle="modal" data-target="#deleteWarningModal"><i
class="fas fa-trash-alt" aria-hidden="true"></i>
Delete All
</button>
</div>
</div>
<div class="modal fade" id="deleteWarningModal" tabindex="-1" role="dialog" aria-labelledby="Warning-Delete-File"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteWarningModalLabel">Are you really sure?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
All files listed here, include files on other pages, will be deleted from your
hard-drive.
Is that what you want?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" data-dismiss="modal"
onclick="deleteAllResults()">Delete All Listed Files</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% if upload_enabled %}
<div id="upload" class="container mb-3">
{% else %}
<div id="upload" class="container mb-3" style="display: none;">
<input type="hidden" id="uploadDisabled" value="true" />
{% endif %}
<div class="card">
<div class="card-header">
<h5 class="card-title">Upload File</h5>
</div>
<div class="card-body">
<form action="./upload" method="post" enctype="multipart/form-data">
<div class="row">
<div id="uploadBox" class="col-lg-7">
<div class="input-group mb-3">
<div id="uploadField" style="display: flex; width: 100%">
<div class="custom-file">
<input type="file" name="file[]" class="custom-file-input"
id="uploadSelectFile" aria-describedby="uploadSubmit"
value="Browse Music file" multiple />
<label class="custom-file-label" for="uploadSelectFile">Choose file</label>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="row">
<div class="col">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Upload To</span>
</div>
<input class="form-control" list="targetdirs" id="uploadTargetDir"
name="targetdir" placeholder="uploads" />
<datalist id="targetdirs">
{% for dir in dirs %}
<option value="{{ dir }}">
{% endfor %}
</datalist>
</div>
</div>
<div class="col-auto">
<button class="btn btn-primary" type="button" id="uploadSubmit"><i
class="fas fa-upload mr-1"></i>Upload!</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="container mb-5">
<div class="card-deck">
<div class="card">
<div class="card-header">
<h5 class="card-title">Add URL</h5>
</div>
<div class="card-body">
<label for="add_url_input">Add Youtube or Soundcloud URL</label>
<div class="input-group mb-2">
<input class="form-control" type="text" id="add_url_input" placeholder="URL...">
</div>
<button type="submit" class="btn btn-primary"
onclick="var $i = $('#add_url_input')[0]; request('post', {add_url : $i.value }); $i.value = ''; ">Add
URL
</button>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title">Add Radio</h5>
</div>
<div class="card-body">
<label for="add_radio_input">Add Radio URL</label>
<div class="input-group mb-2">
<input class="form-control" type="text" id="add_radio_input" placeholder="Radio Address...">
</div>
<button type="submit" class="btn btn-primary"
onclick="var $i = $('#add_radio_input')[0]; request('post', {add_radio : $i.value }); $i.value = '';">Add
Radio
</button>
</div>
</div>
</div>
</div>
<div class="floating-button" style="bottom: 120px;" onclick="togglePlayer()">
<i class="fas fa-play" aria-hidden="true"></i>
</div>
<div class="floating-button" style="bottom: 50px;" onclick="switchTheme()">
<i class="fas fa-lightbulb" aria-hidden="true"></i>
</div>
<div id="playerToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
<div class="toast-header">
<i class="fas fa-play-circle mr-2 text-primary"></i>
<strong class="mr-auto">Now Playing...</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body" id="playerContainer">
<img id="playerArtworkIdle" src="../assets/image/empty_box.svg" />
<img id="playerArtwork" src="../assets/image/unknown-album.png" style="display: none;" />
<div id="playerInfo">
<div id="playerActionBox">
<button id="playerPlayBtn" class="btn btn-primary btn-sm btn-space" style="display: none"
onclick="request('post', {action: 'resume'})">
<i class="fas fa-play"></i>
</button>
<button id="playerPauseBtn" class="btn btn-primary btn-sm btn-space" style="display: none"
onclick="request('post', {action: 'pause'})">
<i class="fas fa-pause"></i>
</button>
<button id="playerSkipBtn" class="btn btn-primary btn-sm"
onclick="request('post', {action : 'next'})">
<i class="fas fa-fast-forward"></i>
</button>
</div>
<div style="overflow: hidden; max-width: 320px;">
<strong id="playerTitle">Song Title</strong>
</div>
<span id="playerArtist">Artist</span>
<div id="playerBarBox" class="progress">
<div id="playerBar" class="progress-bar" role="progressbar" aria-valuenow="50" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
<div id="footer" style="height:50px; width: 100%; margin-top: 100px;"></div>
<form id="download-form" action="download" method="GET" target="_blank">
<input hidden type="text" name="id" value="">
<input hidden type="text" name="type" value="">
<input hidden type="text" name="dir" value="">
<input hidden type="text" name="tags" value="">
<input hidden type="text" name="keywords" value="">
</form>
<!-- Add tags modal -->
<div class="modal fade" id="addTagModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addTagModalTitle">Edit tags for ?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="addTagModalBody" class="modal-body">
<input hidden type="text" id="addTagModalItemId" name="id" value="">
<div class="modal-tag" style="display: none; width: 100%;">
<span class="modal-tag-text tag-space badge badge-pill badge-dark">Tag</span>
<a class="modal-tag-remove tag-click small"><i
class="fas fa-times-circle btn-outline-danger"></i></a>
</div>
<div id="addTagModalTags" style="margin-left: 5px; margin-bottom: 10px;">
</div>
<div class="input-group">
<input class="form-control form-control-sm btn-space" type="text" id="addTagModalInput"
placeholder="tag1,tag2,...">
<button id="addTagModalAddBtn" type="button" class="btn btn-primary btn-sm"
onclick="addTagModalAdd()">
<i class="fas fa-plus" aria-hidden="true"></i>
Add
</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button id="addTagModalSubmit" type="button" class="btn btn-success" data-dismiss="modal"
onclick="addTagModalSubmit()">Edit!</button>
</div>
</div>
</div>
</div>
<!-- Upload files modal -->
<div class="modal fade" id="uploadModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="uploadTitle"><i class="fas fa-upload mr-1"></i>Uploading files...
</h5>
</div>
<div id="uploadModalBody" class="modal-body">
<div id="uploadSuccessAlert" class="alert alert-success" role="alert" style="display: none">
<i class="fas fa-check mr-1"></i>
Uploaded finished!
</div>
<div id="uploadModalList" style="margin-left: 5px; margin-bottom: 10px;">
<div class="uploadItem" style="display: none; width: 100%; padding-bottom: 8px;">
<i class="far fa-file-alt mr-1"></i>
<span class="uploadItemTitle mr-3"></span>
<span class="uploadItemError text-danger"></span>
<div class="progress" style="margin-top: 5px; height: 10px;">
<div class="uploadProgress progress-bar" role="progressbar" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" id="uploadClose" class="btn btn-success" data-dismiss="modal">
<i class="fas fa-times mr-1"></i> Close</button>
<button type="button" id="uploadCancel" class="btn btn-danger" data-toggle="tooltip"
data-html="true"
title="<strong>Are you really sure?</strong> <br /> Click again to abort uploading.">
<i class="fas fa-trash-alt mr-1" aria-hidden="true"></i> Cancel</button>
</div>
</div>
</div>
</div>
<input type="hidden" id="maxUploadFileSize" value="{{ max_upload_file_size }}" />
<script src="../assets/js/main.js"></script>
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta charset="UTF-8">
<title>botamusique web interface</title>
<link rel="icon" href="../assets/image/favicon.ico" />
<link id="pagestyle" rel="stylesheet" href="../assets/css/bootstrap.min.css">
<link rel="stylesheet" href="../assets/css/main.css">
</head>
<body>
<div class="container" style="max-width: 800px">
<div class="col-8" style="margin: auto; padding-top: 50px;">
<div class="card">
<div class="card-header">
Token Required
</div>
<div class="card-body">
<h3>Token Required!</h3>
You are accessing the web interface of {{ name }}.
A token is needed to grant you access.<br />
Please send "{{ command }}" to the bot in mumble to acquire one.
<form action="." method="get">
<div class="form-group mt-3">
<label for="token_input">Token</label>
<div class="input-group">
<input type="password" class="form-control btn-space" id="token_input" name="token"
placeholder="xxxxxxx">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="../assets/js/jquery-3.5.1.min.js"></script>
<script src="../assets/js/popper.min.js"></script>
<script src="../assets/js/bootstrap.min.js"></script>
<script src="../assets/js/fontawesome.all.min.js"></script>
</body>
</html>

67
web/webpack.config.js Normal file
View File

@ -0,0 +1,67 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
main: [
'./src/js/app.js',
'./src/sass/app.scss',
],
},
output: {
filename: 'assets/js/[name].js',
path: path.resolve(__dirname, 'build/'),
//ecmaVersion: 5,
},
plugins: [
new MiniCssExtractPlugin({
filename: 'assets/css/[name].css',
}),
new HtmlWebpackPlugin({
title: 'botamusique',
filename: 'templates/index.html',
template: './src/templates/index.html',
inject: false,
}),
new HtmlWebpackPlugin({
title: 'botamusique - Needs Auth',
filename: 'templates/need_token.html',
template: './src/templates/need_token.html',
inject: false,
}),
],
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
],
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
'corejs': '3.6',
'useBuiltIns': 'usage',
},
],
],
},
},
},
],
},
/*experiments: {
mjs: true,
},*/
};