mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-23 01:16:18 +00:00
Web UI now scans in background
This commit is contained in:
parent
7bbbdac41c
commit
e210f25bb3
@ -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 = {
|
||||||
|
@ -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']
|
|
||||||
if extensions:
|
|
||||||
extensions = extensions.split(' ')
|
|
||||||
|
|
||||||
scanner = Scanner(extensions = extensions)
|
|
||||||
|
|
||||||
if id is None:
|
|
||||||
for folder in Folder.select(lambda f: f.root):
|
|
||||||
scanner.scan(folder)
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
folder = FolderManager.get(id)
|
if id is not None:
|
||||||
|
folders = [ FolderManager.get(id).name ]
|
||||||
|
else:
|
||||||
|
folders = []
|
||||||
|
DaemonClient(current_app.config['DAEMON']['socket']).scan(folders)
|
||||||
|
flash('Scanning started')
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
flash(str(e), 'error')
|
flash(str(e), 'error')
|
||||||
return redirect(url_for('frontend.folder_index'))
|
|
||||||
except ObjectNotFound:
|
except ObjectNotFound:
|
||||||
flash('No such folder', 'error')
|
flash('No such folder', 'error')
|
||||||
|
except DaemonUnavailableError:
|
||||||
|
flash("Can't start scan", 'error')
|
||||||
|
|
||||||
return redirect(url_for('frontend.folder_index'))
|
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'))
|
|
||||||
|
|
||||||
|
@ -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">
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user