1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-11-13 21:52:18 +00:00

Web UI now scans in background

This commit is contained in:
spl0k 2019-04-22 17:48:44 +02:00
parent 7bbbdac41c
commit e210f25bb3
4 changed files with 47 additions and 45 deletions

View File

@ -3,16 +3,18 @@
# This file is part of Supysonic. # This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API. # Supysonic is a Python implementation of the Subsonic server API.
# #
# Copyright (C) 2013-2018 Alban 'spl0k' Féron # Copyright (C) 2013-2019 Alban 'spl0k' Féron
# 2017 Óscar García Amor # 2017 Óscar García Amor
# #
# Distributed under terms of the GNU AGPLv3 license. # Distributed under terms of the GNU AGPLv3 license.
from flask import redirect, request, session, url_for from flask import current_app, redirect, request, session, url_for
from flask import Blueprint from flask import Blueprint
from functools import wraps from functools import wraps
from pony.orm import ObjectNotFound from pony.orm import ObjectNotFound
from ..daemon.client import DaemonClient
from ..daemon.exceptions import DaemonUnavailableError
from ..db import Artist, Album, Track from ..db import Artist, Album, Track
from ..managers.user import UserManager from ..managers.user import UserManager
@ -34,6 +36,18 @@ def login_check():
flash('Please login') flash('Please login')
return redirect(url_for('frontend.login', returnUrl = request.script_root + request.url[len(request.url_root)-1:])) return redirect(url_for('frontend.login', returnUrl = request.script_root + request.url[len(request.url_root)-1:]))
@frontend.before_request
def scan_status():
if not request.user or not request.user.admin:
return
try:
scanned = DaemonClient(current_app.config['DAEMON']['socket']).get_scanning_progress()
if scanned is not None:
flash('Scanning in progress, {} files scanned.'.format(scanned))
except DaemonUnavailableError:
pass
@frontend.route('/') @frontend.route('/')
def index(): def index():
stats = { stats = {

View File

@ -3,7 +3,7 @@
# This file is part of Supysonic. # This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API. # Supysonic is a Python implementation of the Subsonic server API.
# #
# Copyright (C) 2013-2018 Alban 'spl0k' Féron # Copyright (C) 2013-2019 Alban 'spl0k' Féron
# #
# Distributed under terms of the GNU AGPLv3 license. # Distributed under terms of the GNU AGPLv3 license.
@ -13,6 +13,8 @@ import uuid
from flask import current_app, flash, redirect, render_template, request, url_for from flask import current_app, flash, redirect, render_template, request, url_for
from pony.orm import ObjectNotFound from pony.orm import ObjectNotFound
from ..daemon.client import DaemonClient
from ..daemon.exceptions import DaemonUnavailableError
from ..db import Folder from ..db import Folder
from ..managers.folder import FolderManager from ..managers.folder import FolderManager
from ..scanner import Scanner from ..scanner import Scanner
@ -22,7 +24,13 @@ from . import admin_only, frontend
@frontend.route('/folder') @frontend.route('/folder')
@admin_only @admin_only
def folder_index(): def folder_index():
return render_template('folders.html', folders = Folder.select(lambda f: f.root)) try:
DaemonClient(current_app.config['DAEMON']['socket']).get_scanning_progress()
allow_scan = True
except DaemonUnavailableError:
allow_scan = False
flash("The daemon is unavailable, can't scan from the web interface, use the CLI to do so.", 'warning')
return render_template('folders.html', folders = Folder.select(lambda f: f.root), allow_scan = allow_scan)
@frontend.route('/folder/add') @frontend.route('/folder/add')
@admin_only @admin_only
@ -69,35 +77,18 @@ def del_folder(id):
@frontend.route('/folder/scan/<id>') @frontend.route('/folder/scan/<id>')
@admin_only @admin_only
def scan_folder(id = None): def scan_folder(id = None):
extensions = current_app.config['BASE']['scanner_extensions'] try:
if extensions: if id is not None:
extensions = extensions.split(' ') folders = [ FolderManager.get(id).name ]
else:
folders = []
DaemonClient(current_app.config['DAEMON']['socket']).scan(folders)
flash('Scanning started')
except ValueError as e:
flash(str(e), 'error')
except ObjectNotFound:
flash('No such folder', 'error')
except DaemonUnavailableError:
flash("Can't start scan", 'error')
scanner = Scanner(extensions = extensions)
if id is None:
for folder in Folder.select(lambda f: f.root):
scanner.scan(folder)
else:
try:
folder = FolderManager.get(id)
except ValueError as e:
flash(str(e), 'error')
return redirect(url_for('frontend.folder_index'))
except ObjectNotFound:
flash('No such folder', 'error')
return redirect(url_for('frontend.folder_index'))
scanner.scan(folder)
scanner.finish()
stats = scanner.stats()
flash('Added: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.added))
flash('Deleted: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.deleted))
if stats.errors:
flash('Errors in:')
for err in stats.errors:
flash('- ' + err)
return redirect(url_for('frontend.folder_index')) return redirect(url_for('frontend.folder_index'))

View File

@ -2,7 +2,7 @@
This file is part of Supysonic. This file is part of Supysonic.
Supysonic is a Python implementation of the Subsonic server API. Supysonic is a Python implementation of the Subsonic server API.
Copyright (C) 2013-2018 Alban 'spl0k' Féron Copyright (C) 2013-2019 Alban 'spl0k' Féron
2017 Óscar García Amor 2017 Óscar García Amor
Distributed under terms of the GNU AGPLv3 license. Distributed under terms of the GNU AGPLv3 license.
@ -18,7 +18,7 @@
</div> </div>
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr><th>Name</th><th>Path</th><th></th><th></th></tr> <tr><th>Name</th><th>Path</th><th></th>{% if allow_scan %}<th></th>{% endif %}</tr>
</thead> </thead>
<tbody> <tbody>
{% for folder in folders %} {% for folder in folders %}
@ -26,15 +26,15 @@
<td>{{ folder.name }}</td><td>{{ folder.path }}</td><td> <td>{{ folder.name }}</td><td>{{ folder.path }}</td><td>
<button class="btn btn-danger btn-xs" data-href="{{ url_for('frontend.del_folder', id = folder.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete folder"> <button class="btn btn-danger btn-xs" data-href="{{ url_for('frontend.del_folder', id = folder.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete folder">
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete folder"></span></button></td> <span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete folder"></span></button></td>
<td><a class="btn btn-default btn-xs" href="{{ url_for('frontend.scan_folder', id = folder.id) }}" aria-label="Scan folder"> {%if allow_scan %}<td><a class="btn btn-default btn-xs" href="{{ url_for('frontend.scan_folder', id = folder.id) }}" aria-label="Scan folder">
<span class="glyphicon glyphicon-search" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Scan folder"></span></a></td> <span class="glyphicon glyphicon-search" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Scan folder"></span></a></td>{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<div class="btn-toolbar" role="toolbar"> <div class="btn-toolbar" role="toolbar">
<a href="{{ url_for('frontend.add_folder_form') }}" class="btn btn-default">Add</a> <a href="{{ url_for('frontend.add_folder_form') }}" class="btn btn-default">Add</a>
<a href="{{ url_for('frontend.scan_folder') }}" class="btn btn-default">Scan all</a> {% if allow_scan %}<a href="{{ url_for('frontend.scan_folder') }}" class="btn btn-default">Scan all</a>{% endif %}
</div> </div>
<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog"> <div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">

View File

@ -4,7 +4,7 @@
# This file is part of Supysonic. # This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API. # Supysonic is a Python implementation of the Subsonic server API.
# #
# Copyright (C) 2017-2018 Alban 'spl0k' Féron # Copyright (C) 2017-2019 Alban 'spl0k' Féron
# #
# Distributed under terms of the GNU AGPLv3 license. # Distributed under terms of the GNU AGPLv3 license.
@ -94,12 +94,9 @@ class FolderTestCase(FrontendTestBase):
rv = self.client.get('/folder/scan/' + str(uuid.uuid4()), follow_redirects = True) rv = self.client.get('/folder/scan/' + str(uuid.uuid4()), follow_redirects = True)
self.assertIn('No such folder', rv.data) self.assertIn('No such folder', rv.data)
rv = self.client.get('/folder/scan/' + str(folder.id), follow_redirects = True) rv = self.client.get('/folder/scan/' + str(folder.id), follow_redirects = True)
self.assertIn('Added', rv.data) self.assertIn('start', rv.data)
self.assertIn('Deleted', rv.data)
rv = self.client.get('/folder/scan', follow_redirects = True) rv = self.client.get('/folder/scan', follow_redirects = True)
self.assertIn('Added', rv.data) self.assertIn('start', rv.data)
self.assertIn('Deleted', rv.data)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()