web interface add ajax feature
This commit is contained in:
parent
0b55c21cb7
commit
f5ca09716b
57
interface.py
57
interface.py
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask import Flask, render_template, request, redirect, send_file, Response
|
from flask import Flask, render_template, request, redirect, send_file, Response, jsonify
|
||||||
import variables as var
|
import variables as var
|
||||||
import util
|
import util
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -90,7 +90,7 @@ def requires_auth(f):
|
|||||||
return decorated
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
@web.route("/", methods=['GET', 'POST'])
|
@web.route("/", methods=['GET'])
|
||||||
@requires_auth
|
@requires_auth
|
||||||
def index():
|
def index():
|
||||||
folder_path = var.music_folder
|
folder_path = var.music_folder
|
||||||
@ -99,6 +99,40 @@ def index():
|
|||||||
for file in files:
|
for file in files:
|
||||||
music_library.add_file(file)
|
music_library.add_file(file)
|
||||||
|
|
||||||
|
|
||||||
|
return render_template('index.html',
|
||||||
|
all_files=files,
|
||||||
|
music_library=music_library,
|
||||||
|
os=os,
|
||||||
|
playlist=var.playlist,
|
||||||
|
user=var.user
|
||||||
|
)
|
||||||
|
|
||||||
|
@web.route("/playlist", methods=['GET'])
|
||||||
|
@requires_auth
|
||||||
|
def playlist():
|
||||||
|
if var.playlist.length() == 0:
|
||||||
|
return jsonify([render_template('playlist.html',
|
||||||
|
m=False,
|
||||||
|
index=-1
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for index, item in enumerate(var.playlist.playlist):
|
||||||
|
data.append(render_template('playlist.html',
|
||||||
|
index=index,
|
||||||
|
m=item,
|
||||||
|
playlist=var.playlist
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify(data)
|
||||||
|
|
||||||
|
@web.route("/post", methods=['POST'])
|
||||||
|
@requires_auth
|
||||||
|
def post():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
logging.debug("Post request: "+ str(request.form))
|
logging.debug("Post request: "+ str(request.form))
|
||||||
if 'add_file_bottom' in request.form and ".." not in request.form['add_file_bottom']:
|
if 'add_file_bottom' in request.form and ".." not in request.form['add_file_bottom']:
|
||||||
@ -194,7 +228,7 @@ def index():
|
|||||||
elif 'action' in request.form:
|
elif 'action' in request.form:
|
||||||
action = request.form['action']
|
action = request.form['action']
|
||||||
if action == "randomize":
|
if action == "randomize":
|
||||||
random.shuffle(var.playlist.playlist)
|
var.playlist.randomize()
|
||||||
elif action == "stop":
|
elif action == "stop":
|
||||||
var.botamusique.pause()
|
var.botamusique.pause()
|
||||||
elif action == "clear":
|
elif action == "clear":
|
||||||
@ -212,14 +246,7 @@ def index():
|
|||||||
var.botamusique.volume = 0
|
var.botamusique.volume = 0
|
||||||
logging.info("web: volume down to %d" % (var.botamusique.volume * 100))
|
logging.info("web: volume down to %d" % (var.botamusique.volume * 100))
|
||||||
|
|
||||||
return render_template('index.html',
|
return jsonify({'ver': var.playlist.version})
|
||||||
all_files=files,
|
|
||||||
music_library=music_library,
|
|
||||||
os=os,
|
|
||||||
playlist=var.playlist,
|
|
||||||
user=var.user
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@web.route('/upload', methods=["POST"])
|
@web.route('/upload', methods=["POST"])
|
||||||
def upload():
|
def upload():
|
||||||
@ -239,9 +266,9 @@ def upload():
|
|||||||
return redirect("./", code=406)
|
return redirect("./", code=406)
|
||||||
|
|
||||||
logging.info('Uploading file:')
|
logging.info('Uploading file:')
|
||||||
logging.info(' - filename:', filename)
|
logging.info(' - filename: ' + filename)
|
||||||
logging.info(' - targetdir:', targetdir)
|
logging.info(' - targetdir: ' + targetdir)
|
||||||
logging.info(' - mimetype:', file.mimetype)
|
logging.info(' - mimetype: ' + file.mimetype)
|
||||||
|
|
||||||
if "audio" in file.mimetype:
|
if "audio" in file.mimetype:
|
||||||
storagepath = os.path.abspath(os.path.join(var.music_folder, targetdir))
|
storagepath = os.path.abspath(os.path.join(var.music_folder, targetdir))
|
||||||
@ -256,7 +283,7 @@ def upload():
|
|||||||
return redirect("./", code=500)
|
return redirect("./", code=500)
|
||||||
|
|
||||||
filepath = os.path.join(storagepath, filename)
|
filepath = os.path.join(storagepath, filename)
|
||||||
logging.info(' - filepath: ', filepath)
|
logging.info(' - filepath: ' + filepath)
|
||||||
if os.path.exists(filepath):
|
if os.path.exists(filepath):
|
||||||
return redirect("./", code=406)
|
return redirect("./", code=406)
|
||||||
|
|
||||||
|
@ -4,11 +4,15 @@ import variables as var
|
|||||||
class PlayList:
|
class PlayList:
|
||||||
playlist = []
|
playlist = []
|
||||||
current_index = 0
|
current_index = 0
|
||||||
|
version = 0 # increase by one after each change
|
||||||
|
|
||||||
def append(self, item):
|
def append(self, item):
|
||||||
|
self.version += 1
|
||||||
self.playlist.append(item)
|
self.playlist.append(item)
|
||||||
|
|
||||||
def insert(self, index, item):
|
def insert(self, index, item):
|
||||||
|
self.version += 1
|
||||||
|
|
||||||
if index == -1:
|
if index == -1:
|
||||||
index = self.current_index
|
index = self.current_index
|
||||||
|
|
||||||
@ -21,9 +25,11 @@ class PlayList:
|
|||||||
return len(self.playlist)
|
return len(self.playlist)
|
||||||
|
|
||||||
def extend(self, items):
|
def extend(self, items):
|
||||||
|
self.version += 1
|
||||||
self.playlist.extend(items)
|
self.playlist.extend(items)
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
|
self.version += 1
|
||||||
if len(self.playlist) == 0:
|
if len(self.playlist) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -32,11 +38,13 @@ class PlayList:
|
|||||||
return self.playlist[self.current_index]
|
return self.playlist[self.current_index]
|
||||||
|
|
||||||
def update(self, item, index=-1):
|
def update(self, item, index=-1):
|
||||||
|
self.version += 1
|
||||||
if index == -1:
|
if index == -1:
|
||||||
index = self.current_index
|
index = self.current_index
|
||||||
self.playlist[index] = item
|
self.playlist[index] = item
|
||||||
|
|
||||||
def remove(self, index=-1):
|
def remove(self, index=-1):
|
||||||
|
self.version += 1
|
||||||
if index > len(self.playlist) - 1:
|
if index > len(self.playlist) - 1:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -66,10 +74,16 @@ class PlayList:
|
|||||||
return self.playlist[self.next_index()]
|
return self.playlist[self.next_index()]
|
||||||
|
|
||||||
def jump(self, index):
|
def jump(self, index):
|
||||||
|
self.version += 1
|
||||||
self.current_index = index
|
self.current_index = index
|
||||||
return self.playlist[index]
|
return self.playlist[index]
|
||||||
|
|
||||||
|
def randomize(self):
|
||||||
|
random.shuffle(self.playlist)
|
||||||
|
self.version += 1
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
self.version += 1
|
||||||
self.playlist = []
|
self.playlist = []
|
||||||
self.current_index = 0
|
self.current_index = 0
|
||||||
|
|
||||||
|
@ -6,21 +6,21 @@
|
|||||||
<li class="directory list-group-item list-group-item-primary">
|
<li class="directory list-group-item list-group-item-primary">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<form method="post" class="directory">
|
<button type="button" class="btn btn-success btn-sm"
|
||||||
<input type="text" value="{{ subdirpath }}" name="add_folder" hidden>
|
onclick="request('/post', {add_folder : '{{ subdirpath }}'})">
|
||||||
<button type="button" class="btn btn-success btn-sm btn-submit"><i class="fa fa-plus" aria-hidden="true"></i></button>
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</form>
|
</button>
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button id="btnGroupDrop2" type="button" class="btn btn-success btn-sm dropdown-toggle btn-space" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
<button id="btnGroupDrop2" type="button" class="btn btn-success btn-sm dropdown-toggle btn-space" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop2" style="">
|
<div class="dropdown-menu" aria-labelledby="btnGroupDrop2" style="">
|
||||||
<form method="post" class="directory">
|
<a class="dropdown-item"
|
||||||
<input type="text" value="{{ subdirpath }}" name="add_folder" hidden>
|
onclick="request('/post', {add_folder : '{{ subdirpath }}'})">
|
||||||
<a href="#" class="dropdown-item a-submit"><i class="fa fa-folder" aria-hidden="true"></i> Entire folder</a>
|
<i class="fa fa-folder" aria-hidden="true"></i> Entire folder
|
||||||
</form>
|
</a>
|
||||||
<form method="post" class="directory">
|
<a class="dropdown-item"
|
||||||
<input type="text" value="{{ subdirpath }}" name="add_folder_recursively" hidden>
|
onclick="request('/post', {add_folder_recursively : '{{ subdirpath }}'})">
|
||||||
<a href="#" class="dropdown-item a-submit"><i class="fa fa-folder" aria-hidden="true"></i> Entire folder and sub-folders</a>
|
<i class="fa fa-folder" aria-hidden="true"></i> Entire folder and sub-folders
|
||||||
</form>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -54,21 +54,21 @@
|
|||||||
<li class="file list-group-item">
|
<li class="file list-group-item">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<form method="post" class="file file_add">
|
<button type="button" class="btn btn-success btn-sm"
|
||||||
<input type="text" value="{{ filepath }}" name="add_file_bottom" hidden>
|
onclick="request('/post', {add_file_bottom : '{{ filepath }}'})">
|
||||||
<button type="button" class="btn btn-success btn-sm btn-submit"><i class="fa fa-plus" aria-hidden="true"></i></button>
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</form>
|
</button>
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button id="btnGroupDrop2" type="button" class="btn btn-success btn-sm dropdown-toggle btn-space" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
<button id="btnGroupDrop2" type="button" class="btn btn-success btn-sm dropdown-toggle btn-space" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop2" style="">
|
<div class="dropdown-menu" aria-labelledby="btnGroupDrop2" style="">
|
||||||
<form method="post" class="file file_add">
|
<a class="dropdown-item"
|
||||||
<input type="text" value="{{ filepath }}" name="add_file_bottom" hidden>
|
onclick="request('/post', {add_file_bottom : '{{ filepath }}'})">
|
||||||
<a href="#" class="dropdown-item a-submit"><i class="fa fa-angle-down" aria-hidden="true"></i> To bottom of play list</a>
|
<i class="fa fa-angle-down" aria-hidden="true"></i> To bottom of play list
|
||||||
</form>
|
</a>
|
||||||
<form method="post" class="file file_add">
|
<a class="dropdown-item"
|
||||||
<input type="text" value="{{ filepath }}" name="add_file_next" hidden>
|
onclick="request('/post', {add_file_next : '{{ filepath }}'})">
|
||||||
<a href="#" class="dropdown-item a-submit"><i class="fa fa-angle-right" aria-hidden="true"></i> After current song</a>
|
<i class="fa fa-angle-right" aria-hidden="true"></i> After current song
|
||||||
</form>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -120,25 +120,25 @@
|
|||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="btn-group" style="margin-bottom: 5px;">
|
<div class="btn-group" style="margin-bottom: 5px;">
|
||||||
<form method="post">
|
<button type="button" class="btn btn-primary btn-space"
|
||||||
<input type="text" value="randomize" name="action" hidden>
|
onclick="request('/post', {action : 'randomize'})">
|
||||||
<button type="submit" class="btn btn-primary btn-space"><i class="fas fa-random"></i></button>
|
<i class="fas fa-random" aria-hidden="true"></i>
|
||||||
</form>
|
</button>
|
||||||
<form method="post">
|
<button type="button" class="btn btn-danger btn-space"
|
||||||
<input type="text" value="stop" name="action" hidden>
|
onclick="request('/post', {action : 'stop'})">
|
||||||
<button type="submit" class="btn btn-danger btn-space"><i class="fas fa-stop"></i></button>
|
<i class="fas fa-stop" aria-hidden="true"></i>
|
||||||
</form>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group" style="float: right;">
|
<div class="btn-group" style="float: right;">
|
||||||
<form method="post">
|
<button type="button" class="btn btn-warning btn-space"
|
||||||
<input type="text" value="volume_down" name="action" hidden>
|
onclick="request('/post', {action : 'volume_down'})">
|
||||||
<button type="submit" class="btn btn-warning btn-space"><i class="fa fa-volume-down" aria-hidden="true"></i></button>
|
<i class="fa fa-volume-down" aria-hidden="true"></i>
|
||||||
</form>
|
</button>
|
||||||
<form method="post">
|
<button type="button" class="btn btn-warning btn-space"
|
||||||
<input type="text" value="volume_up" name="action" hidden>
|
onclick="request('/post', {action : 'volume_up'})">
|
||||||
<button type="submit" class="btn btn-warning btn-space"><i class="fa fa-volume-up" aria-hidden="true"></i></button>
|
<i class="fa fa-volume-up" aria-hidden="true"></i>
|
||||||
</form>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@ -150,72 +150,18 @@
|
|||||||
<th scope="col">Action</th>
|
<th scope="col">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="playlist-table">
|
||||||
{% if playlist.length() == 0 %}
|
|
||||||
<tr class="table-dark">
|
<tr class="table-dark">
|
||||||
<td colspan="4" class="text-muted" style="text-align:center;"> Play list is empty. </td>
|
<td colspan="4" class="text-muted" style="text-align:center;"> Play list is empty. </td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
|
||||||
{% for m in playlist.playlist %}
|
|
||||||
{% if loop.index0 == playlist.current_index %}
|
|
||||||
<tr class="table-active">
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
{% endif %}
|
|
||||||
<th scope="row">{{ loop.index }}</th>
|
|
||||||
<td>
|
|
||||||
<div class="playlist-title">
|
|
||||||
{% if 'thumbnail' in m %}
|
|
||||||
<img width="80" src="data:image/PNG;base64,{{ m['thumbnail'] }}"/>
|
|
||||||
{% else %}
|
|
||||||
<img width="80" src="/static/image/unknown-album.png"/>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="playlist-artwork">
|
|
||||||
{% if 'title' in m and m['title'].strip() %}
|
|
||||||
<b>{{ m['title'] }}</b>
|
|
||||||
{% else %}
|
|
||||||
<b>{{ m['url'] }}</b>
|
|
||||||
{% endif %}
|
|
||||||
<span class="badge badge-secondary">{{ m['type'].capitalize() }}</span>
|
|
||||||
<br>
|
|
||||||
{% if 'artist' in m %}
|
|
||||||
{{ m['artist'] }}
|
|
||||||
{% else %}
|
|
||||||
Unknown Artist
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if 'url' in m %}
|
|
||||||
<small><a href="{{ m['url'] }}"><i>{{ m['url']|truncate(50) }}</i></a></small>
|
|
||||||
{% elif 'path' in m %}
|
|
||||||
<small>{{ m['path']|truncate(50) }}</small>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="btn-group">
|
|
||||||
<form method="post">
|
|
||||||
<input type="text" value="{{ loop.index0 }}" name="play_music" hidden>
|
|
||||||
<button type="submit" class="btn btn-success btn-sm btn-space"><i class="fas fa-play"></i></button>
|
|
||||||
</form>
|
|
||||||
<form method="post">
|
|
||||||
<input type="text" value="{{ loop.index0 }}" name="delete_music" hidden>
|
|
||||||
<button type="submit" class="btn btn-danger btn-sm btn-space"><i class="fas fa-trash-alt"></i></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<form method="post">
|
<button type="button" class="btn btn-danger btn-space"
|
||||||
<input type="text" value="clear" name="action" hidden>
|
onclick="request('/post', {action : 'clear'})">
|
||||||
<button type="submit" class="btn btn-danger btn-space"><i class="fas fa-trash-alt"></i> Clear Playlist</button>
|
<i class="fas fa-trash-alt" aria-hidden="true"></i> Clear Playlist
|
||||||
</form>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -341,6 +287,59 @@
|
|||||||
$('a.a-submit, button.btn-submit').on('click', function (event) {
|
$('a.a-submit, button.btn-submit').on('click', function (event) {
|
||||||
$(event.target).closest('form').submit();
|
$(event.target).closest('form').submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var playlist_ver = 0;
|
||||||
|
|
||||||
|
function request(url, _data){
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/post',
|
||||||
|
data : _data,
|
||||||
|
statusCode : {
|
||||||
|
200 : function(data) {
|
||||||
|
if (data.ver > playlist_ver) {
|
||||||
|
updatePlaylist();
|
||||||
|
playlist_ver = data.ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayPlaylist(data){
|
||||||
|
$("#playlist-table tr").remove();
|
||||||
|
$.each(data, function(index, item){
|
||||||
|
$("#playlist-table").append(item);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlaylist(){
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: '/playlist',
|
||||||
|
statusCode : {
|
||||||
|
200 : displayPlaylist
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check the version of playlist to see if update is needed.
|
||||||
|
setInterval(function(){
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url : '/post',
|
||||||
|
statusCode : {
|
||||||
|
200 : function(data){
|
||||||
|
if(data.ver > playlist_ver){
|
||||||
|
updatePlaylist();
|
||||||
|
playlist_ver = data.ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} , 3000);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user