From 2b1977892e2f269aa29bb40ed4826d978bebe454 Mon Sep 17 00:00:00 2001
From: spl0k
Date: Sun, 1 Apr 2018 12:32:36 +0200
Subject: [PATCH 1/4] Generate a secret key only once
---
supysonic/config.py | 1 -
supysonic/web.py | 20 ++++++++++++----
tests/base/__init__.py | 2 ++
tests/base/test_secret.py | 48 +++++++++++++++++++++++++++++++++++++++
tests/testbase.py | 1 -
5 files changed, 66 insertions(+), 6 deletions(-)
create mode 100644 tests/base/test_secret.py
diff --git a/supysonic/config.py b/supysonic/config.py
index 0e602b8..f82487c 100644
--- a/supysonic/config.py
+++ b/supysonic/config.py
@@ -18,7 +18,6 @@ import tempfile
class DefaultConfig(object):
DEBUG = False
- SECRET_KEY = os.urandom(128)
tempdir = os.path.join(tempfile.gettempdir(), 'supysonic')
BASE = {
diff --git a/supysonic/web.py b/supysonic/web.py
index ac5259d..1af92d8 100644
--- a/supysonic/web.py
+++ b/supysonic/web.py
@@ -8,10 +8,11 @@
#
# Distributed under terms of the GNU AGPLv3 license.
+import io
import mimetypes
from flask import Flask
-from os import makedirs, path
+from os import makedirs, path, urandom
from pony.orm import db_session
from .config import IniConfig
@@ -24,13 +25,13 @@ def create_application(config = None):
app = Flask(__name__)
app.config.from_object('supysonic.config.DefaultConfig')
- if not config:
+ if not config: # pragma: nocover
config = IniConfig.from_common_locations()
app.config.from_object(config)
# Set loglevel
logfile = app.config['WEBAPP']['log_file']
- if logfile:
+ if logfile: # pragma: nocover
import logging
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(logfile, when = 'midnight')
@@ -59,7 +60,18 @@ def create_application(config = None):
# Test for the cache directory
cache_path = app.config['WEBAPP']['cache_dir']
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
if app.config['WEBAPP']['mount_webui']:
diff --git a/tests/base/__init__.py b/tests/base/__init__.py
index 30098d4..2f4630e 100644
--- a/tests/base/__init__.py
+++ b/tests/base/__init__.py
@@ -14,6 +14,7 @@ from .test_config import ConfigTestCase
from .test_db import DbTestCase
from .test_lastfm import LastFmTestCase
from .test_scanner import ScannerTestCase
+from .test_secret import SecretTestCase
from .test_watcher import suite as watcher_suite
def suite():
@@ -25,6 +26,7 @@ def suite():
suite.addTest(watcher_suite())
suite.addTest(unittest.makeSuite(CLITestCase))
suite.addTest(unittest.makeSuite(LastFmTestCase))
+ suite.addTest(unittest.makeSuite(SecretTestCase))
return suite
diff --git a/tests/base/test_secret.py b/tests/base/test_secret.py
new file mode 100644
index 0000000..8f186cc
--- /dev/null
+++ b/tests/base/test_secret.py
@@ -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()
+
diff --git a/tests/testbase.py b/tests/testbase.py
index d93b477..8d3dad8 100644
--- a/tests/testbase.py
+++ b/tests/testbase.py
@@ -8,7 +8,6 @@
# Distributed under terms of the GNU AGPLv3 license.
import inspect
-import io
import os
import shutil
import unittest
From 66bc65a46c9bd58fefc82243a8fbda9cccb8b286 Mon Sep 17 00:00:00 2001
From: spl0k
Date: Sat, 1 Sep 2018 17:52:00 +0200
Subject: [PATCH 2/4] Fixed setup installing tests
---
setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index ca97a79..3b46027 100755
--- a/setup.py
+++ b/setup.py
@@ -37,7 +37,7 @@ setup(
author_email=project.AUTHOR_EMAIL,
url=project.URL,
license=project.LICENSE,
- packages=find_packages(),
+ packages=find_packages(exclude=['tests*']),
install_requires = reqs,
extras_require = extras,
scripts=['bin/supysonic-cli', 'bin/supysonic-watcher'],
From 6f6521786a90ce669752f7edf79990067f864730 Mon Sep 17 00:00:00 2001
From: spl0k
Date: Sat, 8 Sep 2018 15:22:28 +0200
Subject: [PATCH 3/4] Updated README for Apache2
---
README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 715f48c..eefbacc 100644
--- a/README.md
+++ b/README.md
@@ -166,8 +166,7 @@ example of what it looks like:
WSGIApplicationGroup %{GLOBAL}
WSGIPassAuthorization On
- Order deny,allow
- Allow from all
+ Require all granted
You might also need to run _Apache_ using the system default locale, as the one
From 59f7fd62b04e5cc911790602f0cc3d168c5cdfe4 Mon Sep 17 00:00:00 2001
From: spl0k
Date: Sat, 15 Sep 2018 16:02:36 +0200
Subject: [PATCH 4/4] Improved transcoding description and added client-side
validation
Closes #115
---
supysonic/templates/profile.html | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/supysonic/templates/profile.html b/supysonic/templates/profile.html
index 4dc3cab..0713853 100644
--- a/supysonic/templates/profile.html
+++ b/supysonic/templates/profile.html
@@ -93,10 +93,18 @@
Clients
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
-bitrate on mobile connections to reduce used bandwidth), but the client
-doesn't provide options to do so, you can set default values here. They'll
-only be used if no transcoding/downsampling is requested by the client.
+transcoding or downsampling with one of them (for instance using a low bitrate
+on mobile connections to reduce used bandwidth), but the client doesn't provide
+options to do so, you can set default values here. They'll only be used if no
+transcoding/downsampling is requested by the client.
+The first field is used to specify the format all files will be converted to. It
+means if you enter mp3, 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.