mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
Merge branch 'master' into db-setup
This commit is contained in:
commit
5a065c75ae
@ -152,8 +152,7 @@ example of what it looks like:
|
|||||||
<Directory /path/to/supysonic/cgi-bin>
|
<Directory /path/to/supysonic/cgi-bin>
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
WSGIApplicationGroup %{GLOBAL}
|
||||||
WSGIPassAuthorization On
|
WSGIPassAuthorization On
|
||||||
Order deny,allow
|
Require all granted
|
||||||
Allow from all
|
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
You might also need to run _Apache_ using the system default locale, as the one
|
You might also need to run _Apache_ using the system default locale, as the one
|
||||||
|
2
setup.py
2
setup.py
@ -37,7 +37,7 @@ setup(
|
|||||||
author_email=project.AUTHOR_EMAIL,
|
author_email=project.AUTHOR_EMAIL,
|
||||||
url=project.URL,
|
url=project.URL,
|
||||||
license=project.LICENSE,
|
license=project.LICENSE,
|
||||||
packages=find_packages(),
|
packages=find_packages(exclude=['tests*']),
|
||||||
install_requires = reqs,
|
install_requires = reqs,
|
||||||
extras_require = extras,
|
extras_require = extras,
|
||||||
scripts=['bin/supysonic-cli', 'bin/supysonic-watcher'],
|
scripts=['bin/supysonic-cli', 'bin/supysonic-watcher'],
|
||||||
|
@ -18,7 +18,6 @@ import tempfile
|
|||||||
|
|
||||||
class DefaultConfig(object):
|
class DefaultConfig(object):
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
SECRET_KEY = os.urandom(128)
|
|
||||||
|
|
||||||
tempdir = os.path.join(tempfile.gettempdir(), 'supysonic')
|
tempdir = os.path.join(tempfile.gettempdir(), 'supysonic')
|
||||||
BASE = {
|
BASE = {
|
||||||
|
@ -93,10 +93,18 @@
|
|||||||
<h2>Clients</h2>
|
<h2>Clients</h2>
|
||||||
</div>
|
</div>
|
||||||
<p>Here's a list of clients you used to stream music. If you want to use
|
<p>Here's a list of clients you used to stream music. If you want to use
|
||||||
transcoding or downsampling with one of them (for instance using a low
|
transcoding or downsampling with one of them (for instance using a low bitrate
|
||||||
bitrate on mobile connections to reduce used bandwidth), but the client
|
on mobile connections to reduce used bandwidth), but the client doesn't provide
|
||||||
doesn't provide options to do so, you can set default values here. They'll
|
options to do so, you can set default values here. They'll only be used if no
|
||||||
only be used if no transcoding/downsampling is requested by the client.</p>
|
transcoding/downsampling is requested by the client.<br/>
|
||||||
|
The first field is used to specify the format all files will be converted to. It
|
||||||
|
means if you enter <em>mp3</em>, all media will be served as mp3, wether the
|
||||||
|
original file is a mp3, ogg, FLAC or whatever. The bitrate option specify the
|
||||||
|
maximum streaming bitrate. If a file has a higher bitrate it will be transcoded
|
||||||
|
to match this value. Note that either the format or bitrate field can be left
|
||||||
|
out, for instance you can only enter a bitrate so files will be streamed using
|
||||||
|
their original format, only transcoded if their bitrate exceed the selected
|
||||||
|
one.</p>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<table id="clients" class="table table-striped">
|
<table id="clients" class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@ -106,7 +114,9 @@ only be used if no transcoding/downsampling is requested by the client.</p>
|
|||||||
{% for client in clients %}
|
{% for client in clients %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><label>{{ client.client_name }}</label></td>
|
<td><label>{{ client.client_name }}</label></td>
|
||||||
<td><input type="text" class="form-control" name="{{ client.client_name }}_format" value="{{ client.format if client.format else '' }}" /></td>
|
<td><input type="text" class="form-control" name="{{ client.client_name }}_format"
|
||||||
|
value="{{ client.format if client.format else '' }}"
|
||||||
|
pattern="[a-z0-9]{0,8}" title="Up to 8 letters and numbers" /></td>
|
||||||
<td><select class="form-control" name="{{ client.client_name }}_bitrate">
|
<td><select class="form-control" name="{{ client.client_name }}_bitrate">
|
||||||
<option />
|
<option />
|
||||||
<option {{ 'selected="selected"' if client.bitrate == 64 else '' }}>64</option>
|
<option {{ 'selected="selected"' if client.bitrate == 64 else '' }}>64</option>
|
||||||
|
@ -8,10 +8,11 @@
|
|||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
|
import io
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from os import makedirs, path
|
from os import makedirs, path, urandom
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
from .config import IniConfig
|
from .config import IniConfig
|
||||||
@ -24,13 +25,13 @@ def create_application(config = None):
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_object('supysonic.config.DefaultConfig')
|
app.config.from_object('supysonic.config.DefaultConfig')
|
||||||
|
|
||||||
if not config:
|
if not config: # pragma: nocover
|
||||||
config = IniConfig.from_common_locations()
|
config = IniConfig.from_common_locations()
|
||||||
app.config.from_object(config)
|
app.config.from_object(config)
|
||||||
|
|
||||||
# Set loglevel
|
# Set loglevel
|
||||||
logfile = app.config['WEBAPP']['log_file']
|
logfile = app.config['WEBAPP']['log_file']
|
||||||
if logfile:
|
if logfile: # pragma: nocover
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
handler = TimedRotatingFileHandler(logfile, when = 'midnight')
|
handler = TimedRotatingFileHandler(logfile, when = 'midnight')
|
||||||
@ -59,7 +60,18 @@ def create_application(config = None):
|
|||||||
# Test for the cache directory
|
# Test for the cache directory
|
||||||
cache_path = app.config['WEBAPP']['cache_dir']
|
cache_path = app.config['WEBAPP']['cache_dir']
|
||||||
if not path.exists(cache_path):
|
if not path.exists(cache_path):
|
||||||
makedirs(cache_path)
|
makedirs(cache_path) # pragma: nocover
|
||||||
|
|
||||||
|
# Read or create secret key
|
||||||
|
secret_path = path.join(cache_path, 'secret')
|
||||||
|
if path.exists(secret_path):
|
||||||
|
with io.open(secret_path, 'rb') as f:
|
||||||
|
app.secret_key = f.read()
|
||||||
|
else:
|
||||||
|
secret = urandom(128)
|
||||||
|
with io.open(secret_path, 'wb') as f:
|
||||||
|
f.write(secret)
|
||||||
|
app.secret_key = secret
|
||||||
|
|
||||||
# Import app sections
|
# Import app sections
|
||||||
if app.config['WEBAPP']['mount_webui']:
|
if app.config['WEBAPP']['mount_webui']:
|
||||||
|
@ -14,6 +14,7 @@ from .test_config import ConfigTestCase
|
|||||||
from .test_db import DbTestCase
|
from .test_db import DbTestCase
|
||||||
from .test_lastfm import LastFmTestCase
|
from .test_lastfm import LastFmTestCase
|
||||||
from .test_scanner import ScannerTestCase
|
from .test_scanner import ScannerTestCase
|
||||||
|
from .test_secret import SecretTestCase
|
||||||
from .test_watcher import suite as watcher_suite
|
from .test_watcher import suite as watcher_suite
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
@ -25,6 +26,7 @@ def suite():
|
|||||||
suite.addTest(watcher_suite())
|
suite.addTest(watcher_suite())
|
||||||
suite.addTest(unittest.makeSuite(CLITestCase))
|
suite.addTest(unittest.makeSuite(CLITestCase))
|
||||||
suite.addTest(unittest.makeSuite(LastFmTestCase))
|
suite.addTest(unittest.makeSuite(LastFmTestCase))
|
||||||
|
suite.addTest(unittest.makeSuite(SecretTestCase))
|
||||||
|
|
||||||
return suite
|
return suite
|
||||||
|
|
||||||
|
48
tests/base/test_secret.py
Normal file
48
tests/base/test_secret.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
# This file is part of Supysonic.
|
||||||
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018 Alban 'spl0k' Féron
|
||||||
|
#
|
||||||
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from supysonic.db import init_database, release_database
|
||||||
|
from supysonic.web import create_application
|
||||||
|
|
||||||
|
from ..testbase import TestConfig
|
||||||
|
|
||||||
|
class SecretTestCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.__dbfile = tempfile.mkstemp()[1]
|
||||||
|
self.__dir = tempfile.mkdtemp()
|
||||||
|
self.config = TestConfig(False, False)
|
||||||
|
self.config.BASE['database_uri'] = 'sqlite:///' + self.__dbfile
|
||||||
|
self.config.WEBAPP['cache_dir'] = self.__dir
|
||||||
|
|
||||||
|
init_database(self.config.BASE['database_uri'], True)
|
||||||
|
release_database()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.__dir)
|
||||||
|
os.remove(self.__dbfile)
|
||||||
|
|
||||||
|
|
||||||
|
def test_key(self):
|
||||||
|
app1 = create_application(self.config)
|
||||||
|
release_database()
|
||||||
|
|
||||||
|
app2 = create_application(self.config)
|
||||||
|
release_database()
|
||||||
|
|
||||||
|
self.assertEqual(app1.secret_key, app2.secret_key)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
|
@ -8,7 +8,6 @@
|
|||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import unittest
|
import unittest
|
||||||
|
Loading…
Reference in New Issue
Block a user