web interface add ajax feature

This commit is contained in:
Terry Geng 2020-02-05 15:25:38 +08:00
parent 0b55c21cb7
commit f5ca09716b
3 changed files with 161 additions and 121 deletions

View File

@ -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']:
@ -149,17 +183,17 @@ def index():
elif 'add_url' in request.form: elif 'add_url' in request.form:
var.playlist.append({'type':'url', var.playlist.append({'type':'url',
'url': request.form['add_url'], 'url': request.form['add_url'],
'user': 'Web', 'user': 'Web',
'ready': 'validation'}) 'ready': 'validation'})
logging.info("web: add to playlist: " + request.form['add_url']) logging.info("web: add to playlist: " + request.form['add_url'])
media.url.get_url_info() media.url.get_url_info()
var.playlist.playlist[-1]['ready'] = "no" var.playlist.playlist[-1]['ready'] = "no"
elif 'add_radio' in request.form: elif 'add_radio' in request.form:
var.playlist.append({'type': 'radio', var.playlist.append({'type': 'radio',
'path': request.form['add_radio'], 'path': request.form['add_radio'],
'user': "Web"}) 'user': "Web"})
logging.info("web: add to playlist: " + request.form['add_radio']) logging.info("web: add to playlist: " + request.form['add_radio'])
elif 'delete_music' in request.form: elif 'delete_music' in request.form:
@ -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)

View File

@ -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

View File

@ -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>
{% 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> </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>