mirror of
https://github.com/spl0k/supysonic.git
synced 2024-11-09 19:52:16 +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:
commit
2c41e1af64
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,6 @@
|
||||
*.pyc
|
||||
*.swp
|
||||
*~
|
||||
build/
|
||||
dist/
|
||||
MANIFEST
|
||||
|
1
MANIFEST.in
Normal file
1
MANIFEST.in
Normal file
@ -0,0 +1 @@
|
||||
include README.md
|
@ -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):
|
@ -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
31
setup.py
Executable 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']}
|
||||
)
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
0
supysonic/__init__.py
Normal 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():
|
@ -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():
|
@ -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():
|
@ -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():
|
@ -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():
|
@ -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):
|
@ -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' ])
|
@ -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():
|
@ -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():
|
@ -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():
|
@ -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)
|
||||
|
@ -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():
|
@ -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():
|
@ -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():
|
@ -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'
|
||||
|
||||
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
|
||||
|
@ -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
|
@ -21,7 +21,7 @@
|
||||
import string, random, hashlib
|
||||
import uuid
|
||||
|
||||
from db import User
|
||||
from supysonic.db import User
|
||||
|
||||
class UserManager:
|
||||
SUCCESS = 0
|
@ -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)
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user