diff --git a/.gitignore b/.gitignore
index c9b568f..ce11010 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,6 @@
*.pyc
*.swp
+*~
+build/
+dist/
+MANIFEST
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..bb3ec5f
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include README.md
diff --git a/cli.py b/bin/supysonic-cli
similarity index 97%
rename from cli.py
rename to bin/supysonic-cli
index 4b4f069..817cf7c 100755
--- a/cli.py
+++ b/bin/supysonic-cli
@@ -20,7 +20,7 @@
# along with this program. If not, see .
import sys, cmd, argparse, getpass, time
-import config
+from supysonic import config
class CLIParser(argparse.ArgumentParser):
def error(self, message):
@@ -219,12 +219,12 @@ if __name__ == "__main__":
if not config.check():
sys.exit(1)
- import db
+ from supysonic import db
db.init_db()
- from managers.folder import FolderManager
- from managers.user import UserManager
- from scanner import Scanner
+ from supysonic.managers.folder import FolderManager
+ from supysonic.managers.user import UserManager
+ from supysonic.scanner import Scanner
if len(sys.argv) > 1:
CLI().onecmd(' '.join(sys.argv[1:]))
diff --git a/debug_server.py b/debug_server.py
index 955c7e3..283218d 100755
--- a/debug_server.py
+++ b/debug_server.py
@@ -22,7 +22,7 @@
import sys
if __name__ == '__main__':
- from web import create_application
+ from supysonic.web import create_application
app = create_application()
if app:
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..e8d85ea
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+# encoding: utf-8
+
+from distutils.core import setup
+
+setup(name='supysonic',
+ description='Python implementation of the Subsonic server API.',
+ keywords='subsonic music',
+ version='0.1',
+ url='https://github.com/spl0k/supysonic',
+ license='AGPLv3',
+ author='Alban FĂ©ron',
+ author_email='alban.feron@gmail.com',
+ long_description="""
+ Supysonic is a Python implementation of the Subsonic server API.
+
+ Current supported features are:
+
+ * browsing (by folders or tags)
+ * streaming of various audio file formats
+ * transcoding
+ * user or random playlists
+ * cover arts (cover.jpg files in the same folder as music files)
+ * starred tracks/albums and ratings
+ * Last.FM scrobbling
+ """,
+ packages=['supysonic', 'supysonic.api', 'supysonic.frontend',
+ 'supysonic.managers'],
+ scripts=['bin/supysonic-cli'],
+ package_data={'supysonic': ['templates/*.html']}
+ )
diff --git a/supysonic.cgi b/supysonic.cgi
index f7e7549..9cadacc 100755
--- a/supysonic.cgi
+++ b/supysonic.cgi
@@ -20,7 +20,7 @@
# along with this program. If not, see .
from wsgiref.handlers import CGIHandler
-from web import create_application
+from supysonic.web import create_application
app = create_application()
if app:
diff --git a/supysonic.fcgi b/supysonic.fcgi
index 47cf802..81fd551 100755
--- a/supysonic.fcgi
+++ b/supysonic.fcgi
@@ -20,7 +20,7 @@
# along with this program. If not, see .
from flup.server.fcgi import WSGIServer
-from web import create_application
+from supysonic.web import create_application
app = create_application()
if app:
diff --git a/supysonic.wsgi b/supysonic.wsgi
index 5437096..17b12b0 100755
--- a/supysonic.wsgi
+++ b/supysonic.wsgi
@@ -21,6 +21,6 @@
import sys
sys.path.insert(0, '/path/to/the/supysonic/app')
-from web import create_application
+from supysonic.web import create_application
application = create_application()
diff --git a/supysonic/__init__.py b/supysonic/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/api/__init__.py b/supysonic/api/__init__.py
similarity index 98%
rename from api/__init__.py
rename to supysonic/api/__init__.py
index d4bdc90..e7fb0d2 100644
--- a/api/__init__.py
+++ b/supysonic/api/__init__.py
@@ -23,8 +23,8 @@ from xml.etree import ElementTree
import simplejson
import uuid
-from web import app
-from managers.user import UserManager
+from supysonic.web import app
+from supysonic.managers.user import UserManager
@app.before_request
def set_formatter():
diff --git a/api/albums_songs.py b/supysonic/api/albums_songs.py
similarity index 99%
rename from api/albums_songs.py
rename to supysonic/api/albums_songs.py
index d6ac599..db34579 100644
--- a/api/albums_songs.py
+++ b/supysonic/api/albums_songs.py
@@ -24,8 +24,8 @@ from sqlalchemy.orm import aliased
import random
import uuid
-from web import app
-from db import *
+from supysonic.web import app
+from supysonic.db import *
@app.route('/rest/getRandomSongs.view', methods = [ 'GET', 'POST' ])
def rand_songs():
diff --git a/api/annotation.py b/supysonic/api/annotation.py
similarity index 97%
rename from api/annotation.py
rename to supysonic/api/annotation.py
index 39943cb..1185e76 100644
--- a/api/annotation.py
+++ b/supysonic/api/annotation.py
@@ -21,10 +21,10 @@
import time
import uuid
from flask import request
-from web import app
+from supysonic.web import app
from . import get_entity
-from lastfm import LastFm
-from db import *
+from supysonic.lastfm import LastFm
+from supysonic.db import *
@app.route('/rest/star.view', methods = [ 'GET', 'POST' ])
def star():
diff --git a/api/browse.py b/supysonic/api/browse.py
similarity index 98%
rename from api/browse.py
rename to supysonic/api/browse.py
index c5ffd5c..4874a8b 100644
--- a/api/browse.py
+++ b/supysonic/api/browse.py
@@ -19,10 +19,10 @@
# along with this program. If not, see .
from flask import request
-from web import app
-from db import Folder, Artist, Album, Track
+from supysonic.web import app
+from supysonic.db import Folder, Artist, Album, Track
from . import get_entity
-import uuid, time, string
+import uuid, string
@app.route('/rest/getMusicFolders.view', methods = [ 'GET', 'POST' ])
def list_folders():
diff --git a/api/chat.py b/supysonic/api/chat.py
similarity index 95%
rename from api/chat.py
rename to supysonic/api/chat.py
index 56fdee5..420e584 100644
--- a/api/chat.py
+++ b/supysonic/api/chat.py
@@ -19,8 +19,8 @@
# along with this program. If not, see .
from flask import request
-from web import app
-from db import ChatMessage, session
+from supysonic.web import app
+from supysonic.db import ChatMessage, session
@app.route('/rest/getChatMessages.view', methods = [ 'GET', 'POST' ])
def get_chat():
diff --git a/api/media.py b/supysonic/api/media.py
similarity index 98%
rename from api/media.py
rename to supysonic/api/media.py
index 3ff1be2..70a1fde 100644
--- a/api/media.py
+++ b/supysonic/api/media.py
@@ -26,9 +26,9 @@ import subprocess
import codecs
from xml.etree import ElementTree
-import config, scanner
-from web import app
-from db import Track, Album, Artist, Folder, User, ClientPrefs, now, session
+from supysonic import config, scanner
+from supysonic.web import app
+from supysonic.db import Track, Album, Artist, Folder, ClientPrefs, now, session
from . import get_entity
from sqlalchemy import func
diff --git a/api/playlists.py b/supysonic/api/playlists.py
similarity index 98%
rename from api/playlists.py
rename to supysonic/api/playlists.py
index f31da82..a5ffcdb 100644
--- a/api/playlists.py
+++ b/supysonic/api/playlists.py
@@ -21,8 +21,8 @@
from flask import request
from sqlalchemy import or_, func
import uuid
-from web import app
-from db import Playlist, User, Track, session
+from supysonic.web import app
+from supysonic.db import Playlist, User, Track, session
from . import get_entity
@app.route('/rest/getPlaylists.view', methods = [ 'GET', 'POST' ])
diff --git a/api/search.py b/supysonic/api/search.py
similarity index 98%
rename from api/search.py
rename to supysonic/api/search.py
index 4cdd052..e790d85 100644
--- a/api/search.py
+++ b/supysonic/api/search.py
@@ -19,8 +19,8 @@
# along with this program. If not, see .
from flask import request
-from web import app
-from db import Folder, Track, Artist, Album
+from supysonic.web import app
+from supysonic.db import Folder, Track, Artist, Album
@app.route('/rest/search.view', methods = [ 'GET', 'POST' ])
def old_search():
diff --git a/api/system.py b/supysonic/api/system.py
similarity index 97%
rename from api/system.py
rename to supysonic/api/system.py
index 7d83e69..3f8871c 100644
--- a/api/system.py
+++ b/supysonic/api/system.py
@@ -19,7 +19,7 @@
# along with this program. If not, see .
from flask import request
-from web import app
+from supysonic.web import app
@app.route('/rest/ping.view', methods = [ 'GET', 'POST' ])
def ping():
diff --git a/api/user.py b/supysonic/api/user.py
similarity index 96%
rename from api/user.py
rename to supysonic/api/user.py
index 3c4db73..a0dd10c 100644
--- a/api/user.py
+++ b/supysonic/api/user.py
@@ -19,9 +19,9 @@
# along with this program. If not, see .
from flask import request
-from web import app
-from db import User
-from managers.user import UserManager
+from supysonic.web import app
+from supysonic.db import User
+from supysonic.managers.user import UserManager
@app.route('/rest/getUser.view', methods = [ 'GET', 'POST' ])
def user_info():
diff --git a/config.py b/supysonic/config.py
similarity index 100%
rename from config.py
rename to supysonic/config.py
diff --git a/db.py b/supysonic/db.py
similarity index 99%
rename from db.py
rename to supysonic/db.py
index c7897d3..fdf70a0 100644
--- a/db.py
+++ b/supysonic/db.py
@@ -18,7 +18,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-import config
+from supysonic import config
from sqlalchemy import create_engine, Table, Column, ForeignKey, func
from sqlalchemy import Integer, String, Boolean, DateTime
@@ -75,6 +75,7 @@ class UUID(TypeDecorator):
def now():
return datetime.datetime.now().replace(microsecond = 0)
+config.check()
engine = create_engine(config.get('base', 'database_uri'), convert_unicode = True)
session = scoped_session(sessionmaker(autocommit = False, autoflush = False, bind = engine))
diff --git a/frontend/__init__.py b/supysonic/frontend/__init__.py
similarity index 95%
rename from frontend/__init__.py
rename to supysonic/frontend/__init__.py
index ea1a21f..13a3f7f 100644
--- a/frontend/__init__.py
+++ b/supysonic/frontend/__init__.py
@@ -18,8 +18,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from web import app
-from managers.user import UserManager
+from supysonic.web import app
+from supysonic.managers.user import UserManager
app.add_template_filter(str)
diff --git a/frontend/folder.py b/supysonic/frontend/folder.py
similarity index 93%
rename from frontend/folder.py
rename to supysonic/frontend/folder.py
index da13830..63cf619 100644
--- a/frontend/folder.py
+++ b/supysonic/frontend/folder.py
@@ -19,14 +19,13 @@
# along with this program. If not, see .
from flask import request, flash, render_template, redirect, url_for, session as fl_sess
-import os.path
import uuid
-from web import app
-from db import session, Folder
-from scanner import Scanner
-from managers.user import UserManager
-from managers.folder import FolderManager
+from supysonic.web import app
+from supysonic.db import session, Folder
+from supysonic.scanner import Scanner
+from supysonic.managers.user import UserManager
+from supysonic.managers.folder import FolderManager
@app.before_request
def check_admin():
diff --git a/frontend/playlist.py b/supysonic/frontend/playlist.py
similarity index 98%
rename from frontend/playlist.py
rename to supysonic/frontend/playlist.py
index 8c77718..2017b29 100644
--- a/frontend/playlist.py
+++ b/supysonic/frontend/playlist.py
@@ -20,8 +20,8 @@
from flask import request, session, flash, render_template, redirect, url_for
import uuid
-from web import app
-import db
+from supysonic.web import app
+from supysonic import db
@app.route('/playlist')
def playlist_index():
diff --git a/frontend/user.py b/supysonic/frontend/user.py
similarity index 97%
rename from frontend/user.py
rename to supysonic/frontend/user.py
index ed8c937..f3acd7c 100644
--- a/frontend/user.py
+++ b/supysonic/frontend/user.py
@@ -20,12 +20,12 @@
from flask import request, session, flash, render_template, redirect, url_for, make_response
-from web import app
-from managers.user import UserManager
-from db import User, ClientPrefs, session as db_sess
+from supysonic.web import app
+from supysonic.managers.user import UserManager
+from supysonic.db import User, ClientPrefs, session as db_sess
import uuid, csv
-import config
-from lastfm import LastFm
+from supysonic import config
+from supysonic.lastfm import LastFm
@app.before_request
def check_admin():
diff --git a/lastfm.py b/supysonic/lastfm.py
similarity index 98%
rename from lastfm.py
rename to supysonic/lastfm.py
index 45844b8..ba62330 100644
--- a/lastfm.py
+++ b/supysonic/lastfm.py
@@ -19,8 +19,8 @@
# along with this program. If not, see .
import requests, hashlib
-import config
-from db import session
+from supysonic import config
+from supysonic.db import session
class LastFm:
def __init__(self, user, logger):
diff --git a/managers/__init__.py b/supysonic/managers/__init__.py
similarity index 100%
rename from managers/__init__.py
rename to supysonic/managers/__init__.py
diff --git a/managers/folder.py b/supysonic/managers/folder.py
similarity index 98%
rename from managers/folder.py
rename to supysonic/managers/folder.py
index 3235361..2f8192d 100644
--- a/managers/folder.py
+++ b/supysonic/managers/folder.py
@@ -19,7 +19,7 @@
# along with this program. If not, see .
import os.path, uuid
-from db import Folder, Artist, session
+from supysonic.db import Folder, Artist, session
class FolderManager:
SUCCESS = 0
diff --git a/managers/user.py b/supysonic/managers/user.py
similarity index 99%
rename from managers/user.py
rename to supysonic/managers/user.py
index c6cc3b9..60ba75f 100644
--- a/managers/user.py
+++ b/supysonic/managers/user.py
@@ -21,7 +21,7 @@
import string, random, hashlib
import uuid
-from db import User, session
+from supysonic.db import User, session
class UserManager:
SUCCESS = 0
diff --git a/scanner.py b/supysonic/scanner.py
similarity index 99%
rename from scanner.py
rename to supysonic/scanner.py
index 4c9cf90..ed925a3 100644
--- a/scanner.py
+++ b/supysonic/scanner.py
@@ -21,7 +21,7 @@
import os, os.path
import time, mimetypes
import mutagen
-import config, db
+from supysonic import config, db
def get_mime(ext):
return mimetypes.guess_type('dummy.' + ext, False)[0] or config.get('mimetypes', ext) or 'application/octet-stream'
diff --git a/web.py b/supysonic/web.py
similarity index 91%
rename from web.py
rename to supysonic/web.py
index 1ce3beb..15ae154 100644
--- a/web.py
+++ b/supysonic/web.py
@@ -19,9 +19,9 @@
# along with this program. If not, see .
import os.path
-from flask import Flask, request, session, flash, render_template, redirect, url_for
+from flask import Flask
-import config
+from supysonic import config
def teardown(exception):
db.session.remove()
@@ -35,7 +35,7 @@ def create_application():
if not os.path.exists(config.get('base', 'cache_dir')):
os.makedirs(config.get('base', 'cache_dir'))
- import db
+ from supysonic import db
db.init_db()
app = Flask(__name__)
@@ -50,8 +50,8 @@ def create_application():
app.teardown_request(teardown)
- import frontend
- import api
+ from supysonic import frontend
+ from supysonic import api
return app