Merge pull request #159 from TylerVigario/node-build-flow
Implement Node build flow
This commit is contained in:
commit
fa246553a9
2
.gitignore
vendored
2
.gitignore
vendored
@ -117,4 +117,4 @@ tmp/
|
||||
*.db
|
||||
|
||||
# Pycharm
|
||||
.idea/
|
||||
.idea/
|
2
static/.gitignore
vendored
Normal file
2
static/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
css/
|
||||
js/
|
12
static/css/bootstrap.darkly.min.css
vendored
12
static/css/bootstrap.darkly.min.css
vendored
File diff suppressed because one or more lines are too long
6
static/css/bootstrap.min.css
vendored
6
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
7
static/js/bootstrap.min.js
vendored
7
static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
1116
static/js/custom.js
1116
static/js/custom.js
File diff suppressed because it is too large
Load Diff
5
static/js/fontawesome.all.min.js
vendored
5
static/js/fontawesome.all.min.js
vendored
File diff suppressed because one or more lines are too long
2
static/js/jquery-3.5.1.min.js
vendored
2
static/js/jquery-3.5.1.min.js
vendored
File diff suppressed because one or more lines are too long
5
static/js/popper.min.js
vendored
5
static/js/popper.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,49 +1 @@
|
||||
<!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="static/image/favicon.ico" />
|
||||
|
||||
<link id="pagestyle" rel="stylesheet" href="static/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="static/css/custom.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="static/js/jquery-3.5.1.min.js"></script>
|
||||
<script src="static/js/popper.min.js"></script>
|
||||
<script src="static/js/bootstrap.min.js"></script>
|
||||
<script src="static/js/fontawesome.all.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<!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="static/image/favicon.ico"/><link rel="stylesheet" href="static/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="."><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="static/js/main.js"></script></body>
|
12
web/.editorconfig
Normal file
12
web/.editorconfig
Normal file
@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
quote_type = single
|
||||
|
||||
[*.json]
|
||||
quote_type = double
|
38
web/.eslintrc.json
Normal file
38
web/.eslintrc.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"es2017": true,
|
||||
"es2020": true,
|
||||
"jquery": true
|
||||
},
|
||||
"plugins": [
|
||||
"node",
|
||||
"import",
|
||||
"jsdoc"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"google",
|
||||
"plugin:node/recommended-module",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:jsdoc/recommended"
|
||||
],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"require-jsdoc": "off",
|
||||
"valid-jsdoc": "off",
|
||||
"jsdoc/require-jsdoc": "off",
|
||||
"max-len": ["warn", {
|
||||
"code": 120
|
||||
}],
|
||||
"import/no-commonjs": "error",
|
||||
"import/no-amd": "error",
|
||||
"linebreak-style": "off"
|
||||
}
|
||||
}
|
1
web/.gitignore
vendored
Normal file
1
web/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules/
|
7757
web/package-lock.json
generated
Normal file
7757
web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
55
web/package.json
Normal file
55
web/package.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "botamusique",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"lint": "eslint --config .eslintrc.json src/js/ --ext .mjs",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"build": "webpack --config webpack.config.cjs --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.10.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
||||
"@babel/preset-env": "^7.10.3",
|
||||
"autoprefixer": "^9.8.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"core-js": "^3.6.5",
|
||||
"css-loader": "^3.6.0",
|
||||
"eslint": "^7.3.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-import": "^2.21.2",
|
||||
"eslint-plugin-jsdoc": "^27.1.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"regenerator-runtime": "^0.13.5",
|
||||
"sass": "^1.26.8",
|
||||
"sass-loader": "^8.0.2",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.29",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.13.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.13.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.1",
|
||||
"bootstrap": "^4.5.0",
|
||||
"bootswatch": "^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
21
web/scripts/build-css.js
Normal 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.');
|
||||
});
|
||||
});
|
23
web/src/js/app.mjs
Normal file
23
web/src/js/app.mjs
Normal file
@ -0,0 +1,23 @@
|
||||
import {library, dom} from '@fortawesome/fontawesome-svg-core/index.es';
|
||||
import {fas} from '@fortawesome/free-solid-svg-icons/index.es';
|
||||
import {far} from '@fortawesome/free-regular-svg-icons/index.es';
|
||||
library.add(fas, far);
|
||||
|
||||
// Old application code
|
||||
import './main.mjs';
|
||||
|
||||
// New application code
|
||||
import Theme from './theme.mjs';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
Theme.init();
|
||||
|
||||
// Replace any existing <i> tags with <svg> and set up a MutationObserver to
|
||||
// continue doing this as the DOM changes.
|
||||
dom.watch();
|
||||
|
||||
document.getElementById('theme-switch-btn').addEventListener('click', () => {
|
||||
Theme.swap();
|
||||
});
|
||||
});
|
||||
|
1210
web/src/js/main.mjs
Normal file
1210
web/src/js/main.mjs
Normal file
File diff suppressed because it is too large
Load Diff
42
web/src/js/theme.mjs
Normal file
42
web/src/js/theme.mjs
Normal file
@ -0,0 +1,42 @@
|
||||
export default class {
|
||||
/**
|
||||
* @property {boolean} #dark Interal state for dark theme activation.
|
||||
* @private
|
||||
*/
|
||||
static #dark = false;
|
||||
|
||||
/**
|
||||
* Inialize the theme class.
|
||||
*/
|
||||
static init() {
|
||||
// Check LocalStorage for dark theme selection
|
||||
if (localStorage.getItem('darkTheme') === 'true') {
|
||||
// Update page theme
|
||||
this.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set page theme and update local storage variable.
|
||||
*
|
||||
* @param {boolean} dark Whether to activate dark theme.
|
||||
*/
|
||||
static set(dark = false) {
|
||||
// Swap CSS to selected theme
|
||||
document.getElementById('pagestyle')
|
||||
.setAttribute('href', 'static/css/' + (dark ? 'dark' : 'main') + '.css');
|
||||
|
||||
// Update local storage
|
||||
localStorage.setItem('darkTheme', dark);
|
||||
|
||||
// Update internal state
|
||||
this.#dark = dark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap page theme.
|
||||
*/
|
||||
static swap() {
|
||||
this.set(!this.#dark);
|
||||
}
|
||||
}
|
18
web/src/js/util.js
Normal file
18
web/src/js/util.js
Normal file
@ -0,0 +1,18 @@
|
||||
export function isOverflown(element) {
|
||||
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
|
||||
}
|
||||
|
||||
export function setProgressBar(bar, progress, text = '') {
|
||||
const progPos = (-1 * (1 - progress) * bar.scrollWidth).toString();
|
||||
const progStr = (progress * 100).toString();
|
||||
bar.setAttribute('aria-valuenow', progStr);
|
||||
bar.style.transform = "translateX(" + progPos + "px)";
|
||||
bar.textContent = text;
|
||||
}
|
||||
|
||||
export function secondsToStr(seconds) {
|
||||
seconds = Math.floor(seconds);
|
||||
const mins = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return ('00' + mins).slice(-2) + ':' + ('00' + secs).slice(-2);
|
||||
}
|
5
web/src/sass/app-dark.scss
Normal file
5
web/src/sass/app-dark.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@import '~bootswatch/dist/darkly/variables';
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
@import '~bootswatch/dist/darkly/bootswatch';
|
||||
|
||||
@import './main';
|
3
web/src/sass/app.scss
Normal file
3
web/src/sass/app.scss
Normal file
@ -0,0 +1,3 @@
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
|
||||
@import './main';
|
516
web/src/templates/index.html
Normal file
516
web/src/templates/index.html
Normal file
@ -0,0 +1,516 @@
|
||||
<!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="static/image/favicon.ico" />
|
||||
|
||||
<link id="pagestyle" rel="stylesheet" href="static/css/main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container page-header mb-5" id="banner">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<img src="static/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">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button type="button" id="fast-forward-btn" class="btn btn-info mb-2">
|
||||
<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">
|
||||
<i class="fas fa-tasks mr-2" aria-hidden="true"></i>One-shot
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" id="random-mode-btn">
|
||||
<i class="fas fa-random mr-2" aria-hidden="true"></i>Random
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" id="repeat-mode-btn">
|
||||
<i class="fas fa-redo mr-2" aria-hidden="true"></i>Repeat
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" id="autoplay-mode-btn">
|
||||
<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">
|
||||
<i class="fa fa-volume-up" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<div id="volume-popover">
|
||||
<a id="volume-down-btn">
|
||||
<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" />
|
||||
|
||||
<a id="volume-up-btn">
|
||||
<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="static/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="static/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="static/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" id="clear-playlist-btn" class="btn btn-danger mr-1">
|
||||
<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">
|
||||
<input type="checkbox" name="options"> File
|
||||
</label>
|
||||
<label id="filter-type-url" class="btn btn-secondary">
|
||||
<input type="checkbox" name="options"> URL
|
||||
</label>
|
||||
<label id="filter-type-radio" class="btn btn-secondary">
|
||||
<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="static/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="static/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="static/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>
|
||||
|
||||
<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 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="list-group">
|
||||
<div id="library-pagination" style="margin-left: auto; margin-top: 10px;">
|
||||
<ul id="library-page-ul" class="pagination pagination">
|
||||
<li class="library-page-li page-item active">
|
||||
<a class="library-page-no page-link">1</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group mb-2" role="group">
|
||||
<button id="add-to-playlist-btn" type="button" class="btn btn-secondary mr-1">
|
||||
<i class="fas fa-plus" aria-hidden="true"></i> Add All
|
||||
</button>
|
||||
<button id="library-rescan-btn" type="button" class="btn btn-secondary mr-1">
|
||||
<i class="fas fa-sync-alt" aria-hidden="true"></i> Rescan Files
|
||||
</button>
|
||||
<button id="library-download-btn" type="button" class="btn btn-secondary mr-1">
|
||||
<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 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">×</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 id="library-delete-btn" type="button" class="btn btn-danger" data-dismiss="modal">Delete All Listed Files</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- beautify ignore:start -->
|
||||
{% 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>
|
||||
<!-- beautify ignore:end -->
|
||||
|
||||
<div class="container mb-5">
|
||||
<div class="card-deck">
|
||||
<div id="add-music-url" class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Add URL</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<label for="music-url-input">Add Youtube or Soundcloud URL</label>
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" type="text" id="music-url-input" placeholder="URL...">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Add URL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="add-radio-url" class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Add Radio</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<label for="radio-url-input">Add Radio URL</label>
|
||||
<div class="input-group mb-2">
|
||||
<input id="radio-url-input" class="form-control" type="text" placeholder="Radio Address...">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Add Radio
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="player-toast" class="floating-button" style="bottom: 120px;">
|
||||
<i class="fas fa-play" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
<div id="theme-switch-btn" class="floating-button" style="bottom: 50px;">
|
||||
<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">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="toast-body" id="playerContainer">
|
||||
<img id="playerArtworkIdle" src="static/image/empty_box.svg" />
|
||||
<img id="playerArtwork" src="static/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">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button id="playerPauseBtn" class="btn btn-primary btn-sm btn-space" style="display: none">
|
||||
<i class="fas fa-pause"></i>
|
||||
</button>
|
||||
<button id="playerSkipBtn" class="btn btn-primary btn-sm">
|
||||
<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 pr-2" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 100%; text-align: right; transform: translateX(-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">×</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">
|
||||
<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">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 pr-2" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 100%; text-align: right; transform: translateX(-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="static/js/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
43
web/src/templates/need_token.html
Normal file
43
web/src/templates/need_token.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!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="static/image/favicon.ico" />
|
||||
|
||||
<link rel="stylesheet" href="static/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="static/js/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
87
web/webpack.config.cjs
Normal file
87
web/webpack.config.cjs
Normal file
@ -0,0 +1,87 @@
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
main: [
|
||||
'./src/js/app.mjs',
|
||||
'./src/sass/app.scss',
|
||||
],
|
||||
dark: [
|
||||
'./src/sass/app-dark.scss',
|
||||
],
|
||||
},
|
||||
devtool: 'source-map',
|
||||
/*optimization: {
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
},
|
||||
},*/
|
||||
output: {
|
||||
filename: 'static/js/[name].js',
|
||||
path: path.resolve(__dirname, '../'),
|
||||
// ecmaVersion: 5,
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'static/css/[name].css',
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'templates/index.html',
|
||||
template: './src/templates/index.html',
|
||||
inject: false,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
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', // translates CSS into CommonJS modules
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
plugins: function() {
|
||||
return [
|
||||
require('autoprefixer'),
|
||||
];
|
||||
},
|
||||
},
|
||||
},
|
||||
'sass-loader', // compiles Sass to CSS
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.m?js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
'corejs': '3.6',
|
||||
'useBuiltIns': 'usage',
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
/* experiments: {
|
||||
mjs: true,
|
||||
},*/
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user