mirror of
https://github.com/spl0k/supysonic.git
synced 2024-11-10 04:02:17 +00:00
parent
0de87e64b0
commit
7455711b60
@ -4,26 +4,23 @@
|
|||||||
# 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-2017 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2018 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 session, request, redirect, url_for, current_app as app
|
from flask import redirect, request, session, url_for
|
||||||
|
from flask import Blueprint
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
from ..db import Artist, Album, Track
|
from ..db import Artist, Album, Track
|
||||||
from ..managers.user import UserManager
|
from ..managers.user import UserManager
|
||||||
|
|
||||||
@app.before_request
|
frontend = Blueprint('frontend', __name__)
|
||||||
|
|
||||||
|
@frontend.before_request
|
||||||
def login_check():
|
def login_check():
|
||||||
if request.path.startswith('/rest/'):
|
|
||||||
return
|
|
||||||
|
|
||||||
if request.path.startswith('/static/'):
|
|
||||||
return
|
|
||||||
|
|
||||||
request.user = None
|
request.user = None
|
||||||
should_login = True
|
should_login = True
|
||||||
if session.get('userid'):
|
if session.get('userid'):
|
||||||
@ -34,11 +31,11 @@ def login_check():
|
|||||||
request.user = user
|
request.user = user
|
||||||
should_login = False
|
should_login = False
|
||||||
|
|
||||||
if should_login and request.endpoint != 'login':
|
if should_login and request.endpoint != 'frontend.login':
|
||||||
flash('Please login')
|
flash('Please login')
|
||||||
return redirect(url_for('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:]))
|
||||||
|
|
||||||
@app.route('/')
|
@frontend.route('/')
|
||||||
@db_session
|
@db_session
|
||||||
def index():
|
def index():
|
||||||
stats = {
|
stats = {
|
||||||
@ -52,10 +49,11 @@ def admin_only(f):
|
|||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_func(*args, **kwargs):
|
def decorated_func(*args, **kwargs):
|
||||||
if not request.user or not request.user.admin:
|
if not request.user or not request.user.admin:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('frontend.index'))
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_func
|
return decorated_func
|
||||||
|
|
||||||
from .user import *
|
from .user import *
|
||||||
from .folder import *
|
from .folder import *
|
||||||
from .playlist import *
|
from .playlist import *
|
||||||
|
|
||||||
|
@ -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-2017 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2018 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -21,7 +21,7 @@
|
|||||||
import os.path
|
import os.path
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from flask import request, flash, render_template, redirect, url_for, current_app as app
|
from flask import current_app, flash, redirect, render_template, request, url_for
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
from ..db import Folder
|
from ..db import Folder
|
||||||
@ -29,20 +29,20 @@ from ..managers.user import UserManager
|
|||||||
from ..managers.folder import FolderManager
|
from ..managers.folder import FolderManager
|
||||||
from ..scanner import Scanner
|
from ..scanner import Scanner
|
||||||
|
|
||||||
from . import admin_only
|
from . import admin_only, frontend
|
||||||
|
|
||||||
@app.route('/folder')
|
@frontend.route('/folder')
|
||||||
@admin_only
|
@admin_only
|
||||||
@db_session
|
@db_session
|
||||||
def folder_index():
|
def folder_index():
|
||||||
return render_template('folders.html', folders = Folder.select(lambda f: f.root))
|
return render_template('folders.html', folders = Folder.select(lambda f: f.root))
|
||||||
|
|
||||||
@app.route('/folder/add')
|
@frontend.route('/folder/add')
|
||||||
@admin_only
|
@admin_only
|
||||||
def add_folder_form():
|
def add_folder_form():
|
||||||
return render_template('addfolder.html')
|
return render_template('addfolder.html')
|
||||||
|
|
||||||
@app.route('/folder/add', methods = [ 'POST' ])
|
@frontend.route('/folder/add', methods = [ 'POST' ])
|
||||||
@admin_only
|
@admin_only
|
||||||
def add_folder_post():
|
def add_folder_post():
|
||||||
error = False
|
error = False
|
||||||
@ -63,16 +63,16 @@ def add_folder_post():
|
|||||||
|
|
||||||
flash("Folder '%s' created. You should now run a scan" % name)
|
flash("Folder '%s' created. You should now run a scan" % name)
|
||||||
|
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('frontend.folder_index'))
|
||||||
|
|
||||||
@app.route('/folder/del/<id>')
|
@frontend.route('/folder/del/<id>')
|
||||||
@admin_only
|
@admin_only
|
||||||
def del_folder(id):
|
def del_folder(id):
|
||||||
try:
|
try:
|
||||||
idid = uuid.UUID(id)
|
idid = uuid.UUID(id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
flash('Invalid folder id')
|
flash('Invalid folder id')
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('frontend.folder_index'))
|
||||||
|
|
||||||
ret = FolderManager.delete(idid)
|
ret = FolderManager.delete(idid)
|
||||||
if ret != FolderManager.SUCCESS:
|
if ret != FolderManager.SUCCESS:
|
||||||
@ -80,14 +80,14 @@ def del_folder(id):
|
|||||||
else:
|
else:
|
||||||
flash('Deleted folder')
|
flash('Deleted folder')
|
||||||
|
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('frontend.folder_index'))
|
||||||
|
|
||||||
@app.route('/folder/scan')
|
@frontend.route('/folder/scan')
|
||||||
@app.route('/folder/scan/<id>')
|
@frontend.route('/folder/scan/<id>')
|
||||||
@admin_only
|
@admin_only
|
||||||
@db_session
|
@db_session
|
||||||
def scan_folder(id = None):
|
def scan_folder(id = None):
|
||||||
extensions = app.config['BASE']['scanner_extensions']
|
extensions = current_app.config['BASE']['scanner_extensions']
|
||||||
if extensions:
|
if extensions:
|
||||||
extensions = extensions.split(' ')
|
extensions = extensions.split(' ')
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ def scan_folder(id = None):
|
|||||||
status, folder = FolderManager.get(id)
|
status, folder = FolderManager.get(id)
|
||||||
if status != FolderManager.SUCCESS:
|
if status != FolderManager.SUCCESS:
|
||||||
flash(FolderManager.error_str(status))
|
flash(FolderManager.error_str(status))
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('frontend.folder_index'))
|
||||||
scanner.scan(folder)
|
scanner.scan(folder)
|
||||||
|
|
||||||
scanner.finish()
|
scanner.finish()
|
||||||
@ -112,5 +112,5 @@ def scan_folder(id = None):
|
|||||||
flash('Errors in:')
|
flash('Errors in:')
|
||||||
for err in stats.errors:
|
for err in stats.errors:
|
||||||
flash('- ' + err)
|
flash('- ' + err)
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('frontend.folder_index'))
|
||||||
|
|
||||||
|
@ -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-2017 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2018 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -20,51 +20,53 @@
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from flask import request, flash, render_template, redirect, url_for, current_app as app
|
from flask import flash, redirect, render_template, request, url_for
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
from pony.orm import ObjectNotFound
|
from pony.orm import ObjectNotFound
|
||||||
|
|
||||||
from ..db import Playlist
|
from ..db import Playlist
|
||||||
from ..managers.user import UserManager
|
from ..managers.user import UserManager
|
||||||
|
|
||||||
@app.route('/playlist')
|
from . import frontend
|
||||||
|
|
||||||
|
@frontend.route('/playlist')
|
||||||
@db_session
|
@db_session
|
||||||
def playlist_index():
|
def playlist_index():
|
||||||
return render_template('playlists.html',
|
return render_template('playlists.html',
|
||||||
mine = Playlist.select(lambda p: p.user == request.user),
|
mine = Playlist.select(lambda p: p.user == request.user),
|
||||||
others = Playlist.select(lambda p: p.user != request.user and p.public))
|
others = Playlist.select(lambda p: p.user != request.user and p.public))
|
||||||
|
|
||||||
@app.route('/playlist/<uid>')
|
@frontend.route('/playlist/<uid>')
|
||||||
@db_session
|
@db_session
|
||||||
def playlist_details(uid):
|
def playlist_details(uid):
|
||||||
try:
|
try:
|
||||||
uid = uuid.UUID(uid)
|
uid = uuid.UUID(uid)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
flash('Invalid playlist id')
|
flash('Invalid playlist id')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
playlist = Playlist[uid]
|
playlist = Playlist[uid]
|
||||||
except ObjectNotFound:
|
except ObjectNotFound:
|
||||||
flash('Unknown playlist')
|
flash('Unknown playlist')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
return render_template('playlist.html', playlist = playlist)
|
return render_template('playlist.html', playlist = playlist)
|
||||||
|
|
||||||
@app.route('/playlist/<uid>', methods = [ 'POST' ])
|
@frontend.route('/playlist/<uid>', methods = [ 'POST' ])
|
||||||
@db_session
|
@db_session
|
||||||
def playlist_update(uid):
|
def playlist_update(uid):
|
||||||
try:
|
try:
|
||||||
uid = uuid.UUID(uid)
|
uid = uuid.UUID(uid)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
flash('Invalid playlist id')
|
flash('Invalid playlist id')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
playlist = Playlist[uid]
|
playlist = Playlist[uid]
|
||||||
except ObjectNotFound:
|
except ObjectNotFound:
|
||||||
flash('Unknown playlist')
|
flash('Unknown playlist')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
if playlist.user.id != request.user.id:
|
if playlist.user.id != request.user.id:
|
||||||
flash("You're not allowed to edit this playlist")
|
flash("You're not allowed to edit this playlist")
|
||||||
@ -77,20 +79,20 @@ def playlist_update(uid):
|
|||||||
|
|
||||||
return playlist_details(str(uid))
|
return playlist_details(str(uid))
|
||||||
|
|
||||||
@app.route('/playlist/del/<uid>')
|
@frontend.route('/playlist/del/<uid>')
|
||||||
@db_session
|
@db_session
|
||||||
def playlist_delete(uid):
|
def playlist_delete(uid):
|
||||||
try:
|
try:
|
||||||
uid = uuid.UUID(uid)
|
uid = uuid.UUID(uid)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
flash('Invalid playlist id')
|
flash('Invalid playlist id')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
playlist = Playlist[uid]
|
playlist = Playlist[uid]
|
||||||
except ObjectNotFound:
|
except ObjectNotFound:
|
||||||
flash('Unknown playlist')
|
flash('Unknown playlist')
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
if playlist.user.id != request.user.id:
|
if playlist.user.id != request.user.id:
|
||||||
flash("You're not allowed to delete this playlist")
|
flash("You're not allowed to delete this playlist")
|
||||||
@ -98,5 +100,5 @@ def playlist_delete(uid):
|
|||||||
playlist.delete()
|
playlist.delete()
|
||||||
flash('Playlist deleted')
|
flash('Playlist deleted')
|
||||||
|
|
||||||
return redirect(url_for('playlist_index'))
|
return redirect(url_for('frontend.playlist_index'))
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from flask import request, session, flash, render_template, redirect, url_for, current_app as app
|
from flask import flash, redirect, render_template, request, session, url_for
|
||||||
|
from flask import current_app
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ from ..lastfm import LastFm
|
|||||||
from ..managers.user import UserManager
|
from ..managers.user import UserManager
|
||||||
from ..py23 import dict
|
from ..py23 import dict
|
||||||
|
|
||||||
from . import admin_only
|
from . import admin_only, frontend
|
||||||
|
|
||||||
def me_or_uuid(f, arg = 'uid'):
|
def me_or_uuid(f, arg = 'uid'):
|
||||||
@db_session
|
@db_session
|
||||||
@ -41,12 +42,12 @@ def me_or_uuid(f, arg = 'uid'):
|
|||||||
if uid == 'me':
|
if uid == 'me':
|
||||||
user = User[request.user.id] # Refetch user from previous transaction
|
user = User[request.user.id] # Refetch user from previous transaction
|
||||||
elif not request.user.admin:
|
elif not request.user.admin:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('frontend.index'))
|
||||||
else:
|
else:
|
||||||
code, user = UserManager.get(uid)
|
code, user = UserManager.get(uid)
|
||||||
if code != UserManager.SUCCESS:
|
if code != UserManager.SUCCESS:
|
||||||
flash(UserManager.error_str(code))
|
flash(UserManager.error_str(code))
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('frontend.index'))
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
kwargs['user'] = user
|
kwargs['user'] = user
|
||||||
@ -57,18 +58,18 @@ def me_or_uuid(f, arg = 'uid'):
|
|||||||
|
|
||||||
return decorated_func
|
return decorated_func
|
||||||
|
|
||||||
@app.route('/user')
|
@frontend.route('/user')
|
||||||
@admin_only
|
@admin_only
|
||||||
@db_session
|
@db_session
|
||||||
def user_index():
|
def user_index():
|
||||||
return render_template('users.html', users = User.select())
|
return render_template('users.html', users = User.select())
|
||||||
|
|
||||||
@app.route('/user/<uid>')
|
@frontend.route('/user/<uid>')
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def user_profile(uid, user):
|
def user_profile(uid, user):
|
||||||
return render_template('profile.html', user = user, has_lastfm = app.config['LASTFM']['api_key'] != None, clients = user.clients)
|
return render_template('profile.html', user = user, has_lastfm = current_app.config['LASTFM']['api_key'] != None, clients = user.clients)
|
||||||
|
|
||||||
@app.route('/user/<uid>', methods = [ 'POST' ])
|
@frontend.route('/user/<uid>', methods = [ 'POST' ])
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def update_clients(uid, user):
|
def update_clients(uid, user):
|
||||||
clients_opts = dict()
|
clients_opts = dict()
|
||||||
@ -86,7 +87,7 @@ def update_clients(uid, user):
|
|||||||
clients_opts[client] = dict([ (opt, value) ])
|
clients_opts[client] = dict([ (opt, value) ])
|
||||||
else:
|
else:
|
||||||
clients_opts[client][opt] = value
|
clients_opts[client][opt] = value
|
||||||
app.logger.debug(clients_opts)
|
current_app.logger.debug(clients_opts)
|
||||||
|
|
||||||
for client, opts in clients_opts.items():
|
for client, opts in clients_opts.items():
|
||||||
prefs = user.clients.select(lambda c: c.client_name == client).first()
|
prefs = user.clients.select(lambda c: c.client_name == client).first()
|
||||||
@ -103,23 +104,23 @@ def update_clients(uid, user):
|
|||||||
flash('Clients preferences updated.')
|
flash('Clients preferences updated.')
|
||||||
return user_profile(uid, user)
|
return user_profile(uid, user)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changeusername')
|
@frontend.route('/user/<uid>/changeusername')
|
||||||
@admin_only
|
@admin_only
|
||||||
def change_username_form(uid):
|
def change_username_form(uid):
|
||||||
code, user = UserManager.get(uid)
|
code, user = UserManager.get(uid)
|
||||||
if code != UserManager.SUCCESS:
|
if code != UserManager.SUCCESS:
|
||||||
flash(UserManager.error_str(code))
|
flash(UserManager.error_str(code))
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('frontend.index'))
|
||||||
|
|
||||||
return render_template('change_username.html', user = user)
|
return render_template('change_username.html', user = user)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changeusername', methods = [ 'POST' ])
|
@frontend.route('/user/<uid>/changeusername', methods = [ 'POST' ])
|
||||||
@admin_only
|
@admin_only
|
||||||
@db_session
|
@db_session
|
||||||
def change_username_post(uid):
|
def change_username_post(uid):
|
||||||
code, user = UserManager.get(uid)
|
code, user = UserManager.get(uid)
|
||||||
if code != UserManager.SUCCESS:
|
if code != UserManager.SUCCESS:
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('frontend.index'))
|
||||||
|
|
||||||
username = request.form.get('user')
|
username = request.form.get('user')
|
||||||
if username in ('', None):
|
if username in ('', None):
|
||||||
@ -141,27 +142,27 @@ def change_username_post(uid):
|
|||||||
else:
|
else:
|
||||||
flash("No changes for '%s'." % username)
|
flash("No changes for '%s'." % username)
|
||||||
|
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('frontend.user_profile', uid = uid))
|
||||||
|
|
||||||
@app.route('/user/<uid>/changemail')
|
@frontend.route('/user/<uid>/changemail')
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def change_mail_form(uid, user):
|
def change_mail_form(uid, user):
|
||||||
return render_template('change_mail.html', user = user)
|
return render_template('change_mail.html', user = user)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changemail', methods = [ 'POST' ])
|
@frontend.route('/user/<uid>/changemail', methods = [ 'POST' ])
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def change_mail_post(uid, user):
|
def change_mail_post(uid, user):
|
||||||
mail = request.form.get('mail', '')
|
mail = request.form.get('mail', '')
|
||||||
# No validation, lol.
|
# No validation, lol.
|
||||||
user.mail = mail
|
user.mail = mail
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('frontend.user_profile', uid = uid))
|
||||||
|
|
||||||
@app.route('/user/<uid>/changepass')
|
@frontend.route('/user/<uid>/changepass')
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def change_password_form(uid, user):
|
def change_password_form(uid, user):
|
||||||
return render_template('change_pass.html', user = user)
|
return render_template('change_pass.html', user = user)
|
||||||
|
|
||||||
@app.route('/user/<uid>/changepass', methods = [ 'POST' ])
|
@frontend.route('/user/<uid>/changepass', methods = [ 'POST' ])
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def change_password_post(uid, user):
|
def change_password_post(uid, user):
|
||||||
error = False
|
error = False
|
||||||
@ -190,16 +191,16 @@ def change_password_post(uid, user):
|
|||||||
flash(UserManager.error_str(status))
|
flash(UserManager.error_str(status))
|
||||||
else:
|
else:
|
||||||
flash('Password changed')
|
flash('Password changed')
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('frontend.user_profile', uid = uid))
|
||||||
|
|
||||||
return change_password_form(uid, user)
|
return change_password_form(uid, user)
|
||||||
|
|
||||||
@app.route('/user/add')
|
@frontend.route('/user/add')
|
||||||
@admin_only
|
@admin_only
|
||||||
def add_user_form():
|
def add_user_form():
|
||||||
return render_template('adduser.html')
|
return render_template('adduser.html')
|
||||||
|
|
||||||
@app.route('/user/add', methods = [ 'POST' ])
|
@frontend.route('/user/add', methods = [ 'POST' ])
|
||||||
@admin_only
|
@admin_only
|
||||||
def add_user_post():
|
def add_user_post():
|
||||||
error = False
|
error = False
|
||||||
@ -222,13 +223,13 @@ def add_user_post():
|
|||||||
status = UserManager.add(name, passwd, mail, admin)
|
status = UserManager.add(name, passwd, mail, admin)
|
||||||
if status == UserManager.SUCCESS:
|
if status == UserManager.SUCCESS:
|
||||||
flash("User '%s' successfully added" % name)
|
flash("User '%s' successfully added" % name)
|
||||||
return redirect(url_for('user_index'))
|
return redirect(url_for('frontend.user_index'))
|
||||||
else:
|
else:
|
||||||
flash(UserManager.error_str(status))
|
flash(UserManager.error_str(status))
|
||||||
|
|
||||||
return add_user_form()
|
return add_user_form()
|
||||||
|
|
||||||
@app.route('/user/del/<uid>')
|
@frontend.route('/user/del/<uid>')
|
||||||
@admin_only
|
@admin_only
|
||||||
def del_user(uid):
|
def del_user(uid):
|
||||||
status = UserManager.delete(uid)
|
status = UserManager.delete(uid)
|
||||||
@ -237,33 +238,33 @@ def del_user(uid):
|
|||||||
else:
|
else:
|
||||||
flash(UserManager.error_str(status))
|
flash(UserManager.error_str(status))
|
||||||
|
|
||||||
return redirect(url_for('user_index'))
|
return redirect(url_for('frontend.user_index'))
|
||||||
|
|
||||||
@app.route('/user/<uid>/lastfm/link')
|
@frontend.route('/user/<uid>/lastfm/link')
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def lastfm_reg(uid, user):
|
def lastfm_reg(uid, user):
|
||||||
token = request.args.get('token')
|
token = request.args.get('token')
|
||||||
if token in ('', None):
|
if token in ('', None):
|
||||||
flash('Missing LastFM auth token')
|
flash('Missing LastFM auth token')
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('frontend.user_profile', uid = uid))
|
||||||
|
|
||||||
lfm = LastFm(app.config['LASTFM'], user, app.logger)
|
lfm = LastFm(current_app.config['LASTFM'], user, current_app.logger)
|
||||||
status, error = lfm.link_account(token)
|
status, error = lfm.link_account(token)
|
||||||
flash(error if not status else 'Successfully linked LastFM account')
|
flash(error if not status else 'Successfully linked LastFM account')
|
||||||
|
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('frontend.user_profile', uid = uid))
|
||||||
|
|
||||||
@app.route('/user/<uid>/lastfm/unlink')
|
@frontend.route('/user/<uid>/lastfm/unlink')
|
||||||
@me_or_uuid
|
@me_or_uuid
|
||||||
def lastfm_unreg(uid, user):
|
def lastfm_unreg(uid, user):
|
||||||
lfm = LastFm(app.config['LASTFM'], user, app.logger)
|
lfm = LastFm(current_app.config['LASTFM'], user, current_app.logger)
|
||||||
lfm.unlink_account()
|
lfm.unlink_account()
|
||||||
flash('Unlinked LastFM account')
|
flash('Unlinked LastFM account')
|
||||||
return redirect(url_for('user_profile', uid = uid))
|
return redirect(url_for('frontend.user_profile', uid = uid))
|
||||||
|
|
||||||
@app.route('/user/login', methods = [ 'GET', 'POST'])
|
@frontend.route('/user/login', methods = [ 'GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
return_url = request.args.get('returnUrl') or url_for('index')
|
return_url = request.args.get('returnUrl') or url_for('frontend.index')
|
||||||
if request.user:
|
if request.user:
|
||||||
flash('Already logged in')
|
flash('Already logged in')
|
||||||
return redirect(return_url)
|
return redirect(return_url)
|
||||||
@ -291,9 +292,9 @@ def login():
|
|||||||
|
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
@app.route('/user/logout')
|
@frontend.route('/user/logout')
|
||||||
def logout():
|
def logout():
|
||||||
session.clear()
|
session.clear()
|
||||||
flash('Logged out!')
|
flash('Logged out!')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('frontend.login'))
|
||||||
|
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_folders %}
|
{% block navbar_folders %}
|
||||||
<li class="active"><a href="{{ url_for('folder_index') }}">Folders <span
|
<li class="active"><a href="{{ url_for('frontend.folder_index') }}">Folders <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
@ -10,7 +10,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
{% if request.user.id != user.id %}
|
{% if request.user.id != user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
@ -18,7 +18,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="{{ url_for('frontend.user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
@ -10,7 +10,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
{% if request.user.id != user.id %}
|
{% if request.user.id != user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
@ -18,7 +18,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="{{ url_for('frontend.user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_folders %}
|
{% block navbar_folders %}
|
||||||
<li class="active"><a href="{{ url_for('folder_index') }}">Folders <span
|
<li class="active"><a href="{{ url_for('frontend.folder_index') }}">Folders <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
@ -24,17 +24,17 @@
|
|||||||
{% for folder in folders %}
|
{% for folder in folders %}
|
||||||
<tr>
|
<tr>
|
||||||
<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('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('scan_folder', id = folder.id) }}" aria-label="Scan folder">
|
<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>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a href="{{ url_for('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('scan_folder') }}" class="btn btn-default">Scan all</a>
|
<a href="{{ url_for('frontend.scan_folder') }}" class="btn btn-default">Scan all</a>
|
||||||
</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">
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_index %}
|
{% block navbar_index %}
|
||||||
<li class="active"><a href="{{ url_for('index') }}">Home <span
|
<li class="active"><a href="{{ url_for('frontend.index') }}">Home <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
{#-
|
|
||||||
This file is part of Supysonic.
|
|
||||||
Supysonic is a Python implementation of the Subsonic server API.
|
|
||||||
|
|
||||||
Copyright (C) 2013-2017 Alban 'spl0k' Féron
|
|
||||||
2017 Óscar García Amor
|
|
||||||
|
|
||||||
Distributed under terms of the GNU AGPLv3 license.
|
|
||||||
-#}
|
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% block navbar_users %}
|
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
|
||||||
class="sr-only">(current)</span></a></li>
|
|
||||||
{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<div class="page-header first-header">
|
|
||||||
<h2>Import users</h2>
|
|
||||||
</div>
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
<strong>Warning!</strong>
|
|
||||||
this will overwrite any existing users, even you!
|
|
||||||
</div>
|
|
||||||
<form enctype="multipart/form-data" method="post">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="file">Users file</label>
|
|
||||||
<input type="file" name="file" id="file" />
|
|
||||||
</div>
|
|
||||||
<input class="btn btn-default" type="submit" />
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
@ -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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
@ -51,25 +51,25 @@
|
|||||||
id="supysonic-navbar-collapse">
|
id="supysonic-navbar-collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
{% block navbar_index %}
|
{% block navbar_index %}
|
||||||
<li><a href="{{ url_for('index') }}">Home</a></li>
|
<li><a href="{{ url_for('frontend.index') }}">Home</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_playlists %}
|
{% block navbar_playlists %}
|
||||||
<li><a href="{{ url_for('playlist_index') }}">Playlists</a></li>
|
<li><a href="{{ url_for('frontend.playlist_index') }}">Playlists</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% if request.user.admin %}
|
{% if request.user.admin %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
<li><a href="{{ url_for('user_index') }}">Users</a></li>
|
<li><a href="{{ url_for('frontend.user_index') }}">Users</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_folders %}
|
{% block navbar_folders %}
|
||||||
<li><a href="{{ url_for('folder_index') }}">Folders</a></li>
|
<li><a href="{{ url_for('frontend.folder_index') }}">Folders</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
<li><a href="{{ url_for('user_profile', uid = 'me')}}">{{ request.user.name }}</a></li>
|
<li><a href="{{ url_for('frontend.user_profile', uid = 'me')}}">{{ request.user.name }}</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li><a href="{{ url_for('logout') }}">Log out</a></li>
|
<li><a href="{{ url_for('frontend.logout') }}">Log out</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- /.navbar-collapse -->
|
</div><!-- /.navbar-collapse -->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_playlists %}
|
{% block navbar_playlists %}
|
||||||
<li class="active"><a href="{{ url_for('playlist_index') }}">Playlists <span
|
<li class="active"><a href="{{ url_for('frontend.playlist_index') }}">Playlists <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_playlists %}
|
{% block navbar_playlists %}
|
||||||
<li class="active"><a href="{{ url_for('playlist_index') }}">Playlists <span
|
<li class="active"><a href="{{ url_for('frontend.playlist_index') }}">Playlists <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
@ -26,13 +26,13 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for p in mine %}
|
{% for p in mine %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ url_for('playlist_details', uid = p.id) }}">{{ p.name }}</a></td>
|
<td><a href="{{ url_for('frontend.playlist_details', uid = p.id) }}">{{ p.name }}</a></td>
|
||||||
<td>{{ p.get_tracks()|length }}</td>
|
<td>{{ p.get_tracks()|length }}</td>
|
||||||
<td>{% if p.public %}<span class="glyphicon glyphicon-check"
|
<td>{% if p.public %}<span class="glyphicon glyphicon-check"
|
||||||
aria-label="Public playlist"></span>{% else %}<span
|
aria-label="Public playlist"></span>{% else %}<span
|
||||||
class="glyphicon glyphicon-unchecked"
|
class="glyphicon glyphicon-unchecked"
|
||||||
aria-label="Private playlist"></span>{% endif %}</td>
|
aria-label="Private playlist"></span>{% endif %}</td>
|
||||||
<td><button class="btn btn-danger btn-xs" data-href="{{ url_for('playlist_delete', uid = p.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete playlist">
|
<td><button class="btn btn-danger btn-xs" data-href="{{ url_for('frontend.playlist_delete', uid = p.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete playlist">
|
||||||
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete playlist"></span></button></td>
|
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete playlist"></span></button></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -50,7 +50,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for p in others %}
|
{% for p in others %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ url_for('playlist_details', uid = p.id) }}">{{ p.name }}</a></td>
|
<td><a href="{{ url_for('frontend.playlist_details', uid = p.id) }}">{{ p.name }}</a></td>
|
||||||
<td>{{ p.user.name }}</td>
|
<td>{{ p.user.name }}</td>
|
||||||
<td>{{ p.get_tracks()|length }}</td>
|
<td>{{ p.get_tracks()|length }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
@ -10,7 +10,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
{% if request.user.id != user.id %}
|
{% if request.user.id != user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
@ -18,7 +18,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block navbar_profile %}
|
{% block navbar_profile %}
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<li class="active"><a href="{{ url_for('user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="{{ url_for('frontend.user_profile', uid = 'me') }}">{{ request.user.name }} <span class="sr-only">(current)</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -39,9 +39,9 @@
|
|||||||
<input type="text" class="form-control" id="email" placeholder="{{ user.mail }}" readonly>
|
<input type="text" class="form-control" id="email" placeholder="{{ user.mail }}" readonly>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="{{ url_for('change_mail_form', uid = 'me') }}" class="btn btn-default">Change eMail</a>
|
<a href="{{ url_for('frontend.change_mail_form', uid = 'me') }}" class="btn btn-default">Change eMail</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('change_mail_form', uid = user.id) }}" class="btn btn-default">Change eMail</a>
|
<a href="{{ url_for('frontend.change_mail_form', uid = user.id) }}" class="btn btn-default">Change eMail</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -59,18 +59,18 @@
|
|||||||
<input type="text" class="form-control" id="lastfm" placeholder="{% if user.lastfm_status %}Linked{% else %}Invalid session{% endif %}" readonly>
|
<input type="text" class="form-control" id="lastfm" placeholder="{% if user.lastfm_status %}Linked{% else %}Invalid session{% endif %}" readonly>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="{{ url_for('lastfm_unreg', uid = 'me') }}" class="btn btn-default">Unlink</a>
|
<a href="{{ url_for('frontend.lastfm_unreg', uid = 'me') }}" class="btn btn-default">Unlink</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('lastfm_unreg', uid = user.id) }}" class="btn btn-default">Unlink</a>
|
<a href="{{ url_for('frontend.lastfm_unreg', uid = user.id) }}" class="btn btn-default">Unlink</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="text" class="form-control" id="lastfm" placeholder="Unlinked" readonly>
|
<input type="text" class="form-control" id="lastfm" placeholder="Unlinked" readonly>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('lastfm_reg', uid = 'me') }}" class="btn btn-default">Link</a>
|
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('frontend.lastfm_reg', uid = 'me') }}" class="btn btn-default">Link</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('lastfm_reg', uid = user.id) }}" class="btn btn-default">Link</a>
|
<a href="http://www.last.fm/api/auth/?api_key={{ api_key }}&cb={{ request.url_root[:-(request.script_root|length+1)] + url_for('frontend.lastfm_reg', uid = user.id) }}" class="btn btn-default">Link</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -83,10 +83,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if request.user.id == user.id %}
|
{% if request.user.id == user.id %}
|
||||||
<a href="{{ url_for('change_password_form', uid = 'me') }}" class="btn btn-default">Change password</a></li>
|
<a href="{{ url_for('frontend.change_password_form', uid = 'me') }}" class="btn btn-default">Change password</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('change_username_form', uid = user.id) }}" class="btn btn-default">Change username or admin status</a></li>
|
<a href="{{ url_for('frontend.change_username_form', uid = user.id) }}" class="btn btn-default">Change username or admin status</a></li>
|
||||||
<a href="{{ url_for('change_password_form', uid = user.id) }}" class="btn btn-default">Change password</a></li>
|
<a href="{{ url_for('frontend.change_password_form', uid = user.id) }}" class="btn btn-default">Change password</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if clients.count() %}
|
{% if clients.count() %}
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
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-2017 Alban 'spl0k' Féron
|
Copyright (C) 2013-2018 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.
|
||||||
-#}
|
-#}
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block navbar_users %}
|
{% block navbar_users %}
|
||||||
<li class="active"><a href="{{ url_for('user_index') }}">Users <span
|
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||||
class="sr-only">(current)</span></a></li>
|
class="sr-only">(current)</span></a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
@ -24,15 +24,15 @@
|
|||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% if request.user.id == user.id %}{{ user.name }}{% else %}
|
<td>{% if request.user.id == user.id %}{{ user.name }}{% else %}
|
||||||
<a href="{{ url_for('user_profile', uid = user.id) }}">{{ user.name }}</a>{% endif %}</td>
|
<a href="{{ url_for('frontend.user_profile', uid = user.id) }}">{{ user.name }}</a>{% endif %}</td>
|
||||||
<td>{{ user.mail }}</td><td>{{ user.admin }}</td><td>{{ user.last_play_date }}</td><td>
|
<td>{{ user.mail }}</td><td>{{ user.admin }}</td><td>{{ user.last_play_date }}</td><td>
|
||||||
{% if request.user.id != user.id %}<button class="btn btn-danger btn-xs" data-href="{{ url_for('del_user', uid = user.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete user">
|
{% if request.user.id != user.id %}<button class="btn btn-danger btn-xs" data-href="{{ url_for('frontend.del_user', uid = user.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete user">
|
||||||
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete user"></span></button>{% endif %}</td></tr>
|
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete user"></span></button>{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a href="{{ url_for('add_user_form') }}" class="btn btn-default">Add</a>
|
<a href="{{ url_for('frontend.add_user_form') }}" class="btn btn-default">Add</a>
|
||||||
</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">
|
||||||
|
@ -63,7 +63,8 @@ def create_application(config = None):
|
|||||||
# Import app sections
|
# Import app sections
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
if app.config['WEBAPP']['mount_webui']:
|
if app.config['WEBAPP']['mount_webui']:
|
||||||
from . import frontend
|
from .frontend import frontend
|
||||||
|
app.register_blueprint(frontend)
|
||||||
if app.config['WEBAPP']['mount_api']:
|
if app.config['WEBAPP']['mount_api']:
|
||||||
from .api import api
|
from .api import api
|
||||||
app.register_blueprint(api, url_prefix = '/rest')
|
app.register_blueprint(api, url_prefix = '/rest')
|
||||||
|
Loading…
Reference in New Issue
Block a user