1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-09-19 19:01:03 +00:00

Merge branch 'master' into storm

Conflicts:
	bin/supysonic-cli
	supysonic/api/__init__.py
	supysonic/api/albums_songs.py
	supysonic/api/annotation.py
	supysonic/api/browse.py
	supysonic/api/chat.py
	supysonic/api/media.py
	supysonic/api/playlists.py
	supysonic/api/search.py
	supysonic/api/user.py
	supysonic/db.py
	supysonic/frontend/__init__.py
	supysonic/frontend/folder.py
	supysonic/frontend/playlist.py
	supysonic/frontend/user.py
	supysonic/lastfm.py
	supysonic/managers/folder.py
	supysonic/managers/user.py
	supysonic/scanner.py
	supysonic/web.py
This commit is contained in:
spl0k 2014-06-14 17:55:52 +02:00
commit 2c41e1af64
44 changed files with 117 additions and 77 deletions

4
.gitignore vendored
View File

@ -1,2 +1,6 @@
*.pyc
*.swp
*~
build/
dist/
MANIFEST

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
include README.md

View File

@ -20,12 +20,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys, cmd, argparse, getpass, time
import config
from supysonic import config
from db import get_store, Folder, User
from managers.folder import FolderManager
from managers.user import UserManager
from scanner import Scanner
from supysonic.db import get_store, Folder, User
from supysonic.managers.folder import FolderManager
from supysonic.managers.user import UserManager
from supysonic.scanner import Scanner
class CLIParser(argparse.ArgumentParser):
def error(self, message):

View File

@ -19,12 +19,11 @@
# 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/>.
import sys
from supysonic.web import create_application
app = create_application()
if __name__ == '__main__':
from web import create_application
app = create_application()
if app:
import sys
app.run(host = sys.argv[1] if len(sys.argv) > 1 else None, debug = True)

31
setup.py Executable file
View File

@ -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']}
)

View File

@ -20,7 +20,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from wsgiref.handlers import CGIHandler
from web import create_application
from supysonic.web import create_application
app = create_application()
if app:

View File

@ -20,9 +20,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from flup.server.fcgi import WSGIServer
from web import create_application
from supysonic.web import create_application
app = create_application()
if app:
WSGIServer(app, bindaddress = '/path/to/fcgi.sock').run
WSGIServer(app, bindAddress = '/path/to/fcgi.sock').run()

View File

@ -18,9 +18,7 @@
# 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/>.
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()

0
supysonic/__init__.py Normal file
View File

View File

@ -23,8 +23,8 @@ from xml.etree import ElementTree
import simplejson
import uuid
from web import app, store
from managers.user import UserManager
from supysonic.web import app, store
from supysonic.managers.user import UserManager
@app.before_request
def set_formatter():

View File

@ -24,9 +24,9 @@ from storm.info import ClassAlias
import random
import uuid
from web import app, store
from db import Folder, Artist, Album, Track, RatingFolder, StarredFolder, StarredArtist, StarredAlbum, StarredTrack, User
from db import now
from supysonic.web import app, store
from supysonic.db import Folder, Artist, Album, Track, RatingFolder, StarredFolder, StarredArtist, StarredAlbum, StarredTrack, User
from supysonic.db import now
@app.route('/rest/getRandomSongs.view', methods = [ 'GET', 'POST' ])
def rand_songs():

View File

@ -21,12 +21,12 @@
import time
import uuid
from flask import request
from web import app, store
from supysonic.web import app, store
from . import get_entity
from lastfm import LastFm
from db import Track, Album, Artist, Folder
from db import StarredTrack, StarredAlbum, StarredArtist, StarredFolder
from db import RatingTrack, RatingFolder
from supysonic.lastfm import LastFm
from supysonic.db import Track, Album, Artist, Folder
from supysonic.db import StarredTrack, StarredAlbum, StarredArtist, StarredFolder
from supysonic.db import RatingTrack, RatingFolder
@app.route('/rest/star.view', methods = [ 'GET', 'POST' ])
def star():

View File

@ -19,10 +19,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import request
from web import app, store
from db import Folder, Artist, Album, Track
from supysonic.web import app, store
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():

View File

@ -19,8 +19,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import request
from web import app, store
from db import ChatMessage
from supysonic.web import app, store
from supysonic.db import ChatMessage
@app.route('/rest/getChatMessages.view', methods = [ 'GET', 'POST' ])
def get_chat():

View File

@ -26,9 +26,9 @@ import subprocess
import codecs
from xml.etree import ElementTree
import config, scanner
from web import app, store
from db import Track, Album, Artist, Folder, User, ClientPrefs, now
from supysonic import config, scanner
from supysonic.web import app, store
from supysonic.db import Track, Album, Artist, Folder, User, ClientPrefs, now
from . import get_entity
def prepare_transcoding_cmdline(base_cmdline, input_file, input_format, output_format, output_bitrate):

View File

@ -21,8 +21,8 @@
from flask import request
from storm.expr import Or
import uuid
from web import app, store
from db import Playlist, User, Track
from supysonic.web import app, store
from supysonic.db import Playlist, User, Track
from . import get_entity
@app.route('/rest/getPlaylists.view', methods = [ 'GET', 'POST' ])

View File

@ -20,8 +20,8 @@
from flask import request
from storm.info import ClassAlias
from web import app, store
from db import Folder, Track, Artist, Album
from supysonic.web import app, store
from supysonic.db import Folder, Track, Artist, Album
@app.route('/rest/search.view', methods = [ 'GET', 'POST' ])
def old_search():

View File

@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import request
from web import app
from supysonic.web import app
@app.route('/rest/ping.view', methods = [ 'GET', 'POST' ])
def ping():

View File

@ -19,9 +19,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import request
from web import app, store
from db import User
from managers.user import UserManager
from supysonic.web import app, store
from supysonic.db import User
from supysonic.managers.user import UserManager
@app.route('/rest/getUser.view', methods = [ 'GET', 'POST' ])
def user_info():

View File

@ -19,9 +19,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import session
from web import app, store
from db import Artist, Album, Track
from managers.user import UserManager
from supysonic.web import app, store
from supysonic.db import Artist, Album, Track
from supysonic.managers.user import UserManager
app.add_template_filter(str)

View File

@ -22,11 +22,11 @@ from flask import request, flash, render_template, redirect, url_for, session
import os.path
import uuid
from web import app, store
from db import Folder
from scanner import Scanner
from managers.user import UserManager
from managers.folder import FolderManager
from supysonic.web import app, store
from supysonic.db import Folder
from supysonic.scanner import Scanner
from supysonic.managers.user import UserManager
from supysonic.managers.folder import FolderManager
@app.before_request
def check_admin():

View File

@ -20,8 +20,8 @@
from flask import request, session, flash, render_template, redirect, url_for
import uuid
from web import app, store
from db import Playlist
from supysonic.web import app, store
from supysonic.db import Playlist
@app.route('/playlist')
def playlist_index():

View File

@ -20,12 +20,12 @@
from flask import request, session, flash, render_template, redirect, url_for, make_response
from web import app, store
from managers.user import UserManager
from db import User, ClientPrefs
from supysonic.web import app, store
from supysonic.managers.user import UserManager
from supysonic.db import User, ClientPrefs
import uuid, csv
import config
from lastfm import LastFm
from supysonic import config
from supysonic.lastfm import LastFm
@app.before_request
def check_admin():

View File

@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import requests, hashlib
import config
import supysonic.config
class LastFm:
def __init__(self, user, logger):
@ -34,7 +34,9 @@ class LastFm:
return False, 'No API key set'
res = self.__api_request(False, method = 'auth.getSession', token = token)
if 'error' in res:
if not res:
return False, 'Error connecting to LastFM'
elif 'error' in res:
return False, 'Error %i: %s' % (res['error'], res['message'])
else:
self.__user.lastfm_session = res['session']['key']
@ -49,14 +51,14 @@ class LastFm:
if not self.__enabled:
return
res = self.__api_request(True, method = 'track.updateNowPlaying', artist = track.album.artist.name, track = track.title, album = track.album.name,
self.__api_request(True, method = 'track.updateNowPlaying', artist = track.album.artist.name, track = track.title, album = track.album.name,
trackNumber = track.number, duration = track.duration)
def scrobble(self, track, ts):
if not self.__enabled:
return
res = self.__api_request(True, method = 'track.scrobble', artist = track.album.artist.name, track = track.title, album = track.album.name,
self.__api_request(True, method = 'track.scrobble', artist = track.album.artist.name, track = track.title, album = track.album.name,
timestamp = ts, trackNumber = track.number, duration = track.duration)
def __api_request(self, write, **kwargs):
@ -81,15 +83,20 @@ class LastFm:
kwargs['api_sig'] = sig
kwargs['format'] = 'json'
if write:
r = requests.post('http://ws.audioscrobbler.com/2.0/', data = kwargs)
else:
r = requests.get('http://ws.audioscrobbler.com/2.0/', params = kwargs)
try:
if write:
r = requests.post('http://ws.audioscrobbler.com/2.0/', data = kwargs)
else:
r = requests.get('http://ws.audioscrobbler.com/2.0/', params = kwargs)
except requests.exceptions.RequestException, e:
self.__logger.warn('Error while connecting to LastFM: ' + str(e))
return None
if 'error' in r.json:
if r.json['error'] in (9, '9'):
json = r.json()
if 'error' in json:
if json['error'] in (9, '9'):
self.__user.lastfm_status = False
self.__logger.warn('LastFM error %i: %s' % (r.json['error'], r.json['message']))
self.__logger.warn('LastFM error %i: %s' % (json['error'], json['message']))
return r.json
return json

View File

@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os.path, uuid
from db import Folder, Artist, Album, Track
from supysonic.db import Folder, Artist, Album, Track
class FolderManager:
SUCCESS = 0

View File

@ -21,7 +21,7 @@
import string, random, hashlib
import uuid
from db import User
from supysonic.db import User
class UserManager:
SUCCESS = 0

View File

@ -21,8 +21,8 @@
import os, os.path
import time, mimetypes
import mutagen
import config
from db import Folder, Artist, Album, Track
from supysonic import config
from supysonic.db import Folder, Artist, Album, Track
def get_mime(ext):
return mimetypes.guess_type('dummy.' + ext, False)[0] or config.get('mimetypes', ext) or 'application/octet-stream'
@ -90,7 +90,7 @@ class Scanner:
tr = self.__store.find(Track, Track.path == path).one()
add = False
if tr:
if not os.path.getmtime(path) > tr.last_modification:
if not int(os.path.getmtime(path)) > tr.last_modification:
return
tag = self.__try_load_tag(path)

View File

@ -22,8 +22,8 @@ import os.path
from flask import Flask, g
from werkzeug.local import LocalProxy
import config
from db import get_store
from supysonic import config
from supysonic.db import get_store
def get_db_store():
store = getattr(g, 'store', None)
@ -60,8 +60,8 @@ def create_application():
handler.setLevel(logging.WARNING)
app.logger.addHandler(handler)
import frontend
import api
from supysonic import frontend
from supysonic import api
return app