mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
parent
0de87e64b0
commit
7455711b60
@ -4,26 +4,23 @@
|
||||
# This file is part of Supysonic.
|
||||
# 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
|
||||
#
|
||||
# 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 pony.orm import db_session
|
||||
|
||||
from ..db import Artist, Album, Track
|
||||
from ..managers.user import UserManager
|
||||
|
||||
@app.before_request
|
||||
frontend = Blueprint('frontend', __name__)
|
||||
|
||||
@frontend.before_request
|
||||
def login_check():
|
||||
if request.path.startswith('/rest/'):
|
||||
return
|
||||
|
||||
if request.path.startswith('/static/'):
|
||||
return
|
||||
|
||||
request.user = None
|
||||
should_login = True
|
||||
if session.get('userid'):
|
||||
@ -34,11 +31,11 @@ def login_check():
|
||||
request.user = user
|
||||
should_login = False
|
||||
|
||||
if should_login and request.endpoint != 'login':
|
||||
if should_login and request.endpoint != 'frontend.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
|
||||
def index():
|
||||
stats = {
|
||||
@ -52,10 +49,11 @@ def admin_only(f):
|
||||
@wraps(f)
|
||||
def decorated_func(*args, **kwargs):
|
||||
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 decorated_func
|
||||
|
||||
from .user import *
|
||||
from .folder import *
|
||||
from .playlist import *
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is part of Supysonic.
|
||||
#
|
||||
# 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
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@ -21,7 +21,7 @@
|
||||
import os.path
|
||||
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 ..db import Folder
|
||||
@ -29,20 +29,20 @@ from ..managers.user import UserManager
|
||||
from ..managers.folder import FolderManager
|
||||
from ..scanner import Scanner
|
||||
|
||||
from . import admin_only
|
||||
from . import admin_only, frontend
|
||||
|
||||
@app.route('/folder')
|
||||
@frontend.route('/folder')
|
||||
@admin_only
|
||||
@db_session
|
||||
def folder_index():
|
||||
return render_template('folders.html', folders = Folder.select(lambda f: f.root))
|
||||
|
||||
@app.route('/folder/add')
|
||||
@frontend.route('/folder/add')
|
||||
@admin_only
|
||||
def add_folder_form():
|
||||
return render_template('addfolder.html')
|
||||
|
||||
@app.route('/folder/add', methods = [ 'POST' ])
|
||||
@frontend.route('/folder/add', methods = [ 'POST' ])
|
||||
@admin_only
|
||||
def add_folder_post():
|
||||
error = False
|
||||
@ -63,16 +63,16 @@ def add_folder_post():
|
||||
|
||||
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
|
||||
def del_folder(id):
|
||||
try:
|
||||
idid = uuid.UUID(id)
|
||||
except ValueError:
|
||||
flash('Invalid folder id')
|
||||
return redirect(url_for('folder_index'))
|
||||
return redirect(url_for('frontend.folder_index'))
|
||||
|
||||
ret = FolderManager.delete(idid)
|
||||
if ret != FolderManager.SUCCESS:
|
||||
@ -80,14 +80,14 @@ def del_folder(id):
|
||||
else:
|
||||
flash('Deleted folder')
|
||||
|
||||
return redirect(url_for('folder_index'))
|
||||
return redirect(url_for('frontend.folder_index'))
|
||||
|
||||
@app.route('/folder/scan')
|
||||
@app.route('/folder/scan/<id>')
|
||||
@frontend.route('/folder/scan')
|
||||
@frontend.route('/folder/scan/<id>')
|
||||
@admin_only
|
||||
@db_session
|
||||
def scan_folder(id = None):
|
||||
extensions = app.config['BASE']['scanner_extensions']
|
||||
extensions = current_app.config['BASE']['scanner_extensions']
|
||||
if extensions:
|
||||
extensions = extensions.split(' ')
|
||||
|
||||
@ -100,7 +100,7 @@ def scan_folder(id = None):
|
||||
status, folder = FolderManager.get(id)
|
||||
if status != FolderManager.SUCCESS:
|
||||
flash(FolderManager.error_str(status))
|
||||
return redirect(url_for('folder_index'))
|
||||
return redirect(url_for('frontend.folder_index'))
|
||||
scanner.scan(folder)
|
||||
|
||||
scanner.finish()
|
||||
@ -112,5 +112,5 @@ def scan_folder(id = None):
|
||||
flash('Errors in:')
|
||||
for err in stats.errors:
|
||||
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.
|
||||
#
|
||||
# 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
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@ -20,51 +20,53 @@
|
||||
|
||||
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 ObjectNotFound
|
||||
|
||||
from ..db import Playlist
|
||||
from ..managers.user import UserManager
|
||||
|
||||
@app.route('/playlist')
|
||||
from . import frontend
|
||||
|
||||
@frontend.route('/playlist')
|
||||
@db_session
|
||||
def playlist_index():
|
||||
return render_template('playlists.html',
|
||||
mine = Playlist.select(lambda p: p.user == request.user),
|
||||
others = Playlist.select(lambda p: p.user != request.user and p.public))
|
||||
|
||||
@app.route('/playlist/<uid>')
|
||||
@frontend.route('/playlist/<uid>')
|
||||
@db_session
|
||||
def playlist_details(uid):
|
||||
try:
|
||||
uid = uuid.UUID(uid)
|
||||
except ValueError:
|
||||
flash('Invalid playlist id')
|
||||
return redirect(url_for('playlist_index'))
|
||||
return redirect(url_for('frontend.playlist_index'))
|
||||
|
||||
try:
|
||||
playlist = Playlist[uid]
|
||||
except ObjectNotFound:
|
||||
flash('Unknown playlist')
|
||||
return redirect(url_for('playlist_index'))
|
||||
return redirect(url_for('frontend.playlist_index'))
|
||||
|
||||
return render_template('playlist.html', playlist = playlist)
|
||||
|
||||
@app.route('/playlist/<uid>', methods = [ 'POST' ])
|
||||
@frontend.route('/playlist/<uid>', methods = [ 'POST' ])
|
||||
@db_session
|
||||
def playlist_update(uid):
|
||||
try:
|
||||
uid = uuid.UUID(uid)
|
||||
except ValueError:
|
||||
flash('Invalid playlist id')
|
||||
return redirect(url_for('playlist_index'))
|
||||
return redirect(url_for('frontend.playlist_index'))
|
||||
|
||||
try:
|
||||
playlist = Playlist[uid]
|
||||
except ObjectNotFound:
|
||||
flash('Unknown playlist')
|
||||
return redirect(url_for('playlist_index'))
|
||||
return redirect(url_for('frontend.playlist_index'))
|
||||
|
||||
if playlist.user.id != request.user.id:
|
||||
flash("You're not allowed to edit this playlist")
|
||||
@ -77,20 +79,20 @@ def playlist_update(uid):
|
||||
|
||||
return playlist_details(str(uid))
|
||||
|
||||
@app.route('/playlist/del/<uid>')
|
||||
@frontend.route('/playlist/del/<uid>')
|
||||
@db_session
|
||||
def playlist_delete(uid):
|
||||
try:
|
||||
uid = uuid.UUID(uid)
|
||||
except ValueError:
|
||||
flash('Invalid playlist id')
|
||||
return redirect(url_for('playlist_index'))
|
||||
return redirect(url_for('frontend.playlist_index'))
|
||||
|
||||
try:
|
||||
playlist = Playlist[uid]
|
||||
except ObjectNotFound:
|
||||
flash('Unknown playlist')
|
||||
return redirect(url_for('playlist_index'))
|
||||
return redirect(url_for('frontend.playlist_index'))
|
||||
|
||||
if playlist.user.id != request.user.id:
|
||||
flash("You're not allowed to delete this playlist")
|
||||
@ -98,5 +100,5 @@ def playlist_delete(uid):
|
||||
playlist.delete()
|
||||
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
|
||||
# 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 pony.orm import db_session
|
||||
|
||||
@ -27,7 +28,7 @@ from ..lastfm import LastFm
|
||||
from ..managers.user import UserManager
|
||||
from ..py23 import dict
|
||||
|
||||
from . import admin_only
|
||||
from . import admin_only, frontend
|
||||
|
||||
def me_or_uuid(f, arg = 'uid'):
|
||||
@db_session
|
||||
@ -41,12 +42,12 @@ def me_or_uuid(f, arg = 'uid'):
|
||||
if uid == 'me':
|
||||
user = User[request.user.id] # Refetch user from previous transaction
|
||||
elif not request.user.admin:
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for('frontend.index'))
|
||||
else:
|
||||
code, user = UserManager.get(uid)
|
||||
if code != UserManager.SUCCESS:
|
||||
flash(UserManager.error_str(code))
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for('frontend.index'))
|
||||
|
||||
if kwargs:
|
||||
kwargs['user'] = user
|
||||
@ -57,18 +58,18 @@ def me_or_uuid(f, arg = 'uid'):
|
||||
|
||||
return decorated_func
|
||||
|
||||
@app.route('/user')
|
||||
@frontend.route('/user')
|
||||
@admin_only
|
||||
@db_session
|
||||
def user_index():
|
||||
return render_template('users.html', users = User.select())
|
||||
|
||||
@app.route('/user/<uid>')
|
||||
@frontend.route('/user/<uid>')
|
||||
@me_or_uuid
|
||||
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
|
||||
def update_clients(uid, user):
|
||||
clients_opts = dict()
|
||||
@ -86,7 +87,7 @@ def update_clients(uid, user):
|
||||
clients_opts[client] = dict([ (opt, value) ])
|
||||
else:
|
||||
clients_opts[client][opt] = value
|
||||
app.logger.debug(clients_opts)
|
||||
current_app.logger.debug(clients_opts)
|
||||
|
||||
for client, opts in clients_opts.items():
|
||||
prefs = user.clients.select(lambda c: c.client_name == client).first()
|
||||
@ -103,23 +104,23 @@ def update_clients(uid, user):
|
||||
flash('Clients preferences updated.')
|
||||
return user_profile(uid, user)
|
||||
|
||||
@app.route('/user/<uid>/changeusername')
|
||||
@frontend.route('/user/<uid>/changeusername')
|
||||
@admin_only
|
||||
def change_username_form(uid):
|
||||
code, user = UserManager.get(uid)
|
||||
if code != UserManager.SUCCESS:
|
||||
flash(UserManager.error_str(code))
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for('frontend.index'))
|
||||
|
||||
return render_template('change_username.html', user = user)
|
||||
|
||||
@app.route('/user/<uid>/changeusername', methods = [ 'POST' ])
|
||||
@frontend.route('/user/<uid>/changeusername', methods = [ 'POST' ])
|
||||
@admin_only
|
||||
@db_session
|
||||
def change_username_post(uid):
|
||||
code, user = UserManager.get(uid)
|
||||
if code != UserManager.SUCCESS:
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for('frontend.index'))
|
||||
|
||||
username = request.form.get('user')
|
||||
if username in ('', None):
|
||||
@ -141,27 +142,27 @@ def change_username_post(uid):
|
||||
else:
|
||||
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
|
||||
def change_mail_form(uid, 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
|
||||
def change_mail_post(uid, user):
|
||||
mail = request.form.get('mail', '')
|
||||
# No validation, lol.
|
||||
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
|
||||
def change_password_form(uid, 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
|
||||
def change_password_post(uid, user):
|
||||
error = False
|
||||
@ -190,16 +191,16 @@ def change_password_post(uid, user):
|
||||
flash(UserManager.error_str(status))
|
||||
else:
|
||||
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)
|
||||
|
||||
@app.route('/user/add')
|
||||
@frontend.route('/user/add')
|
||||
@admin_only
|
||||
def add_user_form():
|
||||
return render_template('adduser.html')
|
||||
|
||||
@app.route('/user/add', methods = [ 'POST' ])
|
||||
@frontend.route('/user/add', methods = [ 'POST' ])
|
||||
@admin_only
|
||||
def add_user_post():
|
||||
error = False
|
||||
@ -222,13 +223,13 @@ def add_user_post():
|
||||
status = UserManager.add(name, passwd, mail, admin)
|
||||
if status == UserManager.SUCCESS:
|
||||
flash("User '%s' successfully added" % name)
|
||||
return redirect(url_for('user_index'))
|
||||
return redirect(url_for('frontend.user_index'))
|
||||
else:
|
||||
flash(UserManager.error_str(status))
|
||||
|
||||
return add_user_form()
|
||||
|
||||
@app.route('/user/del/<uid>')
|
||||
@frontend.route('/user/del/<uid>')
|
||||
@admin_only
|
||||
def del_user(uid):
|
||||
status = UserManager.delete(uid)
|
||||
@ -237,33 +238,33 @@ def del_user(uid):
|
||||
else:
|
||||
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
|
||||
def lastfm_reg(uid, user):
|
||||
token = request.args.get('token')
|
||||
if token in ('', None):
|
||||
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)
|
||||
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
|
||||
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()
|
||||
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():
|
||||
return_url = request.args.get('returnUrl') or url_for('index')
|
||||
return_url = request.args.get('returnUrl') or url_for('frontend.index')
|
||||
if request.user:
|
||||
flash('Already logged in')
|
||||
return redirect(return_url)
|
||||
@ -291,9 +292,9 @@ def login():
|
||||
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/user/logout')
|
||||
@frontend.route('/user/logout')
|
||||
def logout():
|
||||
session.clear()
|
||||
flash('Logged out!')
|
||||
return redirect(url_for('login'))
|
||||
return redirect(url_for('frontend.login'))
|
||||
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
-#}
|
||||
{% extends "layout.html" %}
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
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
|
||||
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||
class="sr-only">(current)</span></a></li>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
|
@ -2,7 +2,7 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
@ -10,7 +10,7 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block navbar_users %}
|
||||
{% 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>
|
||||
{% else %}
|
||||
{{ super() }}
|
||||
@ -18,7 +18,7 @@
|
||||
{% endblock %}
|
||||
{% block navbar_profile %}
|
||||
{% 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 %}
|
||||
{{ super() }}
|
||||
{% endif %}
|
||||
|
@ -2,7 +2,7 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
@ -10,7 +10,7 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block navbar_users %}
|
||||
{% 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>
|
||||
{% else %}
|
||||
{{ super() }}
|
||||
@ -18,7 +18,7 @@
|
||||
{% endblock %}
|
||||
{% block navbar_profile %}
|
||||
{% 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 %}
|
||||
{{ super() }}
|
||||
{% endif %}
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
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
|
||||
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||
class="sr-only">(current)</span></a></li>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
-#}
|
||||
{% extends "layout.html" %}
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
@ -24,17 +24,17 @@
|
||||
{% for folder in folders %}
|
||||
<tr>
|
||||
<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>
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<a href="{{ url_for('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.add_folder_form') }}" class="btn btn-default">Add</a>
|
||||
<a href="{{ url_for('frontend.scan_folder') }}" class="btn btn-default">Scan all</a>
|
||||
</div>
|
||||
<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
-#}
|
||||
{% extends "layout.html" %}
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
{% 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.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
@ -51,25 +51,25 @@
|
||||
id="supysonic-navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
{% block navbar_index %}
|
||||
<li><a href="{{ url_for('index') }}">Home</a></li>
|
||||
<li><a href="{{ url_for('frontend.index') }}">Home</a></li>
|
||||
{% endblock %}
|
||||
{% 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 %}
|
||||
{% if request.user.admin %}
|
||||
{% 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 %}
|
||||
{% 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 %}
|
||||
{% endif %}
|
||||
{% 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 %}
|
||||
</ul>
|
||||
<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>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
{% endif %}
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
-#}
|
||||
{% extends "layout.html" %}
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
-#}
|
||||
{% extends "layout.html" %}
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
@ -26,13 +26,13 @@
|
||||
<tbody>
|
||||
{% for p in mine %}
|
||||
<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>{% if p.public %}<span class="glyphicon glyphicon-check"
|
||||
aria-label="Public playlist"></span>{% else %}<span
|
||||
class="glyphicon glyphicon-unchecked"
|
||||
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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -50,7 +50,7 @@
|
||||
<tbody>
|
||||
{% for p in others %}
|
||||
<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.get_tracks()|length }}</td>
|
||||
</tr>
|
||||
|
@ -2,7 +2,7 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
Distributed under terms of the GNU AGPLv3 license.
|
||||
@ -10,7 +10,7 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block navbar_users %}
|
||||
{% 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>
|
||||
{% else %}
|
||||
{{ super() }}
|
||||
@ -18,7 +18,7 @@
|
||||
{% endblock %}
|
||||
{% block navbar_profile %}
|
||||
{% 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 %}
|
||||
{{ super() }}
|
||||
{% endif %}
|
||||
@ -39,9 +39,9 @@
|
||||
<input type="text" class="form-control" id="email" placeholder="{{ user.mail }}" readonly>
|
||||
<div class="input-group-btn">
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
</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>
|
||||
<div class="input-group-btn">
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
</div>
|
||||
{% else %}
|
||||
<input type="text" class="form-control" id="lastfm" placeholder="Unlinked" readonly>
|
||||
<div class="input-group-btn">
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -83,10 +83,10 @@
|
||||
</div>
|
||||
</div>
|
||||
{% 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 %}
|
||||
<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('change_password_form', uid = user.id) }}" class="btn btn-default">Change password</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('frontend.change_password_form', uid = user.id) }}" class="btn btn-default">Change password</a></li>
|
||||
{% endif %}
|
||||
{% if clients.count() %}
|
||||
<div class="page-header">
|
||||
|
@ -2,14 +2,14 @@
|
||||
This file is part of Supysonic.
|
||||
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
|
||||
|
||||
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
|
||||
<li class="active"><a href="{{ url_for('frontend.user_index') }}">Users <span
|
||||
class="sr-only">(current)</span></a></li>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
@ -24,15 +24,15 @@
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<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>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<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 class="modal fade" id="confirm-delete" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
|
@ -63,7 +63,8 @@ def create_application(config = None):
|
||||
# Import app sections
|
||||
with app.app_context():
|
||||
if app.config['WEBAPP']['mount_webui']:
|
||||
from . import frontend
|
||||
from .frontend import frontend
|
||||
app.register_blueprint(frontend)
|
||||
if app.config['WEBAPP']['mount_api']:
|
||||
from .api import api
|
||||
app.register_blueprint(api, url_prefix = '/rest')
|
||||
|
Loading…
Reference in New Issue
Block a user