spl0k / supysonic Goto Github PK
View Code? Open in Web Editor NEWSupysonic is a Python implementation of the Subsonic server API.
Home Page: https://supysonic.readthedocs.io
License: GNU Affero General Public License v3.0
Supysonic is a Python implementation of the Subsonic server API.
Home Page: https://supysonic.readthedocs.io
License: GNU Affero General Public License v3.0
I'd like to be able to create playlists from .m3u files. I've written a very rough patch to do this on my own repo of supysonic here: c13b3ae. I just added a route to upload playlists to the frontend to implement it. The path for the uploads and the upload form are both hardcoded and ugly and the error handling is pretty awful, so it should probably be cleaned up before being pulled in. I can do that if there's interest in it.
One other issue related to error handling - due to the way the database is organized, multiple copies of the same song cannot be in the same playlist. I'm not sure how subsonic itself handles it, maybe that's consistent, but to me it's annoying. I'd like the option to have multiple of the same songs. I may end up changing this as well.
With the move from SQLAlchemy to Storm, supysonic errors out like this. I dropped the db and created it anew, so I can't provide further info. Backtrace:
Exception on /user/login [POST]
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "./supysonic/frontend/user.py", line 232, in login
status, user = UserManager.try_auth(store, name, password)
File "./supysonic/managers/user.py", line 85, in try_auth
user = store.find(User, User.name == name).one()
File "/usr/lib/python2.7/site-packages/storm/store.py", line 1163, in one
return self._load_objects(result, values)
File "/usr/lib/python2.7/site-packages/storm/store.py", line 989, in _load_objects
return self._find_spec.load_objects(self._store, result, values)
File "/usr/lib/python2.7/site-packages/storm/store.py", line 1734, in load_objects
values[values_start:values_end])
File "/usr/lib/python2.7/site-packages/storm/store.py", line 699, in _load_object
variable = columns[i].variable_factory(value=value, from_db=True)
File "/usr/lib/python2.7/site-packages/storm/variables.py", line 506, in parse_set
value = uuid.UUID(value)
File "/usr/lib/python2.7/uuid.py", line 134, in __init__
raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string
Hello reddit!
http://www.reddit.com/r/raspberry_pi/comments/1x0asp/
Hey,
nice to be have an feature to set privileges for folder to an group/user to allow or restrict access.
greetings
Sam
Whole database tables are loaded into python objects by getRandomSongs
or getAlbumList?type=random
calls. It causes the memory usage to skyrocket.
It should only load objects we are really going to use.
When I try to run a folder scan in the CLI, I get the following exception:
supysonic> folder scan owncloud-martin
Traceback (most recent call last):
File "bin/supysonic-cli", line 234, in <module>
cli.cmdloop()
File "/usr/lib/python2.7/cmd.py", line 142, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "bin/supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "bin/supysonic-cli", line 150, in folder_scan
scanner.scan(folder, TimedProgressDisplay(folder.name))
File "/opt/supysonic/supysonic/scanner.py", line 60, in scan
self.scan_file(path)
File "/opt/supysonic/supysonic/scanner.py", line 140, in scan_file
trroot = self.__find_root_folder(path)
File "/opt/supysonic/supysonic/scanner.py", line 212, in __find_root_folder
count = folders.count()
File "/usr/local/lib/python2.7/dist-packages/storm/store.py", line 1264, in count
return int(self._aggregate(lambda expr: Count(expr, distinct), expr))
File "/usr/local/lib/python2.7/dist-packages/storm/store.py", line 1253, in _aggregate
result = self._store._connection.execute(select)
File "/usr/local/lib/python2.7/dist-packages/storm/databases/mysql.py", line 106, in execute
return Connection.execute(self, statement, params, noresult)
File "/usr/local/lib/python2.7/dist-packages/storm/database.py", line 241, in execute
raw_cursor = self.raw_execute(statement, params)
File "/usr/local/lib/python2.7/dist-packages/storm/database.py", line 374, in raw_execute
self._run_execution(raw_cursor, args, params, statement)
File "/usr/local/lib/python2.7/dist-packages/storm/database.py", line 388, in _run_execution
self._check_disconnect(raw_cursor.execute, *args)
File "/usr/local/lib/python2.7/dist-packages/storm/database.py", line 454, in _check_disconnect
return function(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 159, in execute
query = query % db.literal(args)
ValueError: unsupported format character ''' (0x27) at index 58
Currently, collection scan is only done in a request context, so the user's browser will keep "loading" until Supysonic is done scanning.
Move the scanning process to its own thread so the request can return as soon as it starts, and not when it's done.
It might also be a good idea to create a command-line script to scan. It could then be used by cron or such.
I have set up supysonic for testing -- what a fascinating project! -- and it's working with DSub. I'm not terribly experienced but I have successfully run supysonic with uwsgi and with gunicorn, with bits and pieces of gathered info. Apache didn't work for me but I'm sure I was doing it wrong.
With DSub, it's working both directly (IP:port) in my LAN, and through nginx reverse proxy via domain name --
as I've always run Subsonic --but with jamstash it won't work with either. The artist list loads up, and I can browse music, but when I try to play something, nothing happens.
I'm not sure how to generate appropriate logs, or where to look. The supysonic log is empty.
Any chance of bumping the API to v1.9 and supporting genres?
I'm using the cgi script. When I try to link my lastfm account I get the following in the log:
Exception on /user/lastfm/link [GET]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/usr/lib/cgi-bin/supysonic/frontend/user.py", line 188, in lastfm_reg
status, error = lfm.link_account(token)
File "/usr/lib/cgi-bin/supysonic/lastfm.py", line 37, in link_account
res = self.__api_request(False, method = 'auth.getSession', token = token)
File "/usr/lib/cgi-bin/supysonic/lastfm.py", line 92, in __api_request
if 'error' in r.json:
TypeError: argument of type 'instancemethod' is not iterable
I think that tracks with an empty album tag cause problems...
E.g.: From one artist I have just one track. In the clients (Ultra Sonic on android, Jamstash in Chrome), the artist is shown but the track cannot be displayed when clicking the artist (Jamstash: no reaction, Ultra Sonic: Error - Didn't understand the reply. Please check the server adress.). However, everything works fine when I change the album tag from empty to e.g. "none".
For another artist I have single tracks (empty album tag) and a couple of albums. When I click on the Artist in the clients, the same happens as described above. Neither the tracks nor the albums can be shown.
Please consider adding a middleware like http://flask.pocoo.org/snippets/35/
so supysonic can run below some path other than / .
Thank you.
Currently, the search method finds only for strings that matches exactly, which means that the user has to type the query with exact capitalization.
It would be great if this could be worked out.
PolySonic is a decent, clean-looking client software for Subsonic API. It runs as a Google Chrome extension.
When I try to connect it to an instance of supysonic, it would return an error without proper explanation. On the server side, I only get a line of log (when debugging):
10.8.8.1 - - [13/Aug/2015 02:05:05] "GET /rest/ping.view?u=test&p=test&v=1.11.0&c=PolySonic&f=json HTTP/1.1" 200 -
and nothing afterwards.
Since other clients such as Jamstash, Perisonic, DSub and Subsonic on Android works, it would not be a major problem. However, I assume a slightest chance of some compatibility issue since PolySonic works with the official server software.
I could not determine if it is a problem on the server side or client side. Some differences between supysonic and Subsonic that I was able to find out was:
application/json; charset=utf-8
while supysonic was missing the charset part.You may refer to the code at: https://github.com/dough10/PolySonic.
What license is supysonic under? It'd be great if you can ship a license file.
Python2 is slowly being deprecated everywhere and I think migrating to python3 would be a very nice thing for this program.
If you think this would be nice, I can help port some part of it.
Hi.
First of all - thanks! Great software!
I found only one big problem up to this moment:
When stream fails (connection closed by the client) server shows exception, but transcoder is working. So it hangs (because output stream already closed).
Maybe we can add some exception handler for closed stream and send SIGTERM to the transcoder, or even redirect it's output to /dev/null (if it can't be closed by SIGTERM)
Would be great to have FCGI support to use in nginx like envs.
OK, we use SQLAlchemy, so we should be able to use almost any DBMS.
But for now it has only been tested on SQLite, and I know there are some SQLite-specific queries (example).
I'm working on the unit tests and discover a bug caused by __decode_password
in user manager. I think that is old code that can be deleted without problem, but at this moment cause one issue that is what your password cannot start with enc:
.
The second bug is that you cannot use non ascii characters in passwords. This is caused by the construction hashlib.sha1(salt + password).hexdigest()
in the return. The problem is that the password is unicode but salt not and yu gives an UnicodeEncodeError like this.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "supysonic/managers/user.py", line 153, in __encrypt_password
return hashlib.sha1(salt + password).hexdigest(), salt
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf1' in position 6: ordinal not in range(128)
The safest method that I foud to fix this is change the return to manage password and salt as utf8:
return hashlib.sha1(salt.encode('utf-8') + password.encode('utf-8')).hexdigest(), salt
While lots of linux distribution supported the classic init.d, systemd is becoming more and more popular among many distributions. Starting from jessie/15.04 builds of Debian and Ubuntu, systemd is fully supported, and even more systems are populated with systemd based daemon administration.
The thing is that systemd works the best with undaemonized processes where the main process won't fork but instead emit the output to stdout/stderr. It is difficult to utilize the current implementation to fit into a systemd service.
Thus, I suggest providing a simpler interface -- where python watcher.py
or some simple script would persist with the terminal -- along with current one.
Hi!
While trying to run the watcher, I got his dependency error:
ImportError: No module named daemon.runner
This is of course the result of me not installing supysonic via pip but with Debian packages.
Sadly, is does not seem like daemon-runner is packaged for Debian. python-daemon, the library under the daemon-runner wrapper is though.
I was thus wondering how hard it would be to drop the wrapper and use the base library instead.
I'm not closed to the idea of doing the PR myself, I've just never touched that python lib before and was wondering if you could explain you design choice.
Unhandled exceptions starting the fcgi socket
[supysonic@alarm ~]$ source/supysonic.fcgi
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
$ mv some-album-played-last/ ../music_backup/
$ supysonic-cli folder scan
Scanning 'Music': 0% (1/116)
Scanning 'Music': 100% (116/116)
Traceback (most recent call last):
File "virtualenv/bin/supysonic-cli", line 6, in <module>
exec(compile(open(__file__).read(), __file__, 'exec'))
File "app/bin/supysonic-cli", line 232, in <module>
cli.onecmd(' '.join(sys.argv[1:]))
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "app/bin/supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "app/bin/supysonic-cli", line 153, in folder_scan
scanner.scan(folder, TimedProgressDisplay(folder.name))
File "app/supysonic/scanner.py", line 98, in scan
folders += f.children
File "virtualenv/lib/python2.7/site-packages/storm/references.py", line 288, in __iter__
return self.find().__iter__()
File "virtualenv/lib/python2.7/site-packages/storm/references.py", line 282, in find
result = store.find(self._target_cls, where, *args, **kwargs)
File "virtualenv/lib/python2.7/site-packages/storm/store.py", line 221, in find
self.flush()
File "virtualenv/lib/python2.7/site-packages/storm/store.py", line 501, in flush
self._flush_one(obj_info)
File "virtualenv/lib/python2.7/site-packages/storm/store.py", line 517, in _flush_one
self._connection.execute(expr, noresult=True)
File "virtualenv/lib/python2.7/site-packages/storm/databases/postgres.py", line 266, in execute
return Connection.execute(self, statement, params, noresult)
File "virtualenv/lib/python2.7/site-packages/storm/database.py", line 241, in execute
raw_cursor = self.raw_execute(statement, params)
File "virtualenv/lib/python2.7/site-packages/storm/databases/postgres.py", line 276, in raw_execute
return Connection.raw_execute(self, statement, params)
File "virtualenv/lib/python2.7/site-packages/storm/database.py", line 374, in raw_execute
self._run_execution(raw_cursor, args, params, statement)
File "virtualenv/lib/python2.7/site-packages/storm/database.py", line 388, in _run_execution
self._check_disconnect(raw_cursor.execute, *args)
File "virtualenv/lib/python2.7/site-packages/storm/database.py", line 454, in _check_disconnect
return function(*args, **kwargs)
psycopg2.IntegrityError: update or delete on table "track" violates foreign key constraint "user_last_play_id_fkey" on table "user"
DETAIL: Key (id)=(79f79da0-5aa0-4090-af09-e464a66a7d7f) is still referenced from table "user".
Exception Exception: Exception("There's still something to check. Did you run Scanner.finish()?",) in <bound method Scanner.__del__ of <supysonic.scanner.Scanner instance at 0xb604ffd0>> ignored
happens on folder sync after removing album containing last played song.
I use PostgreSQL for my database backend, but this maybe can happen also on other backends although I haven't tested.
In web.py (or in any other python module, I really don't mind) please consider adding
"application = create_application()"
so it's possible to use supysonic with gunicorn with something like
$ gunicorn web:application
Thank you.
Traceback (most recent call last):
File "/usr/lib/python2.7/pdb.py", line 1314, in main
pdb._runscript(mainpyfile)
File "/usr/lib/python2.7/pdb.py", line 1233, in _runscript
self.run(statement)
File "/usr/lib/python2.7/bdb.py", line 400, in run
exec cmd in globals, locals
File "<string>", line 1, in <module>
File "cli.py", line 3, in <module>
import sys, cmd, argparse, getpass
File "/usr/lib/python2.7/cmd.py", line 142, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "cli.py", line 27, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "cli.py", line 110, in folder_scan
FolderManager.scan(folder.id, s)
File "managers/folder.py", line 94, in scan
scanner.scan(folder)
File "scanner.py", line 28, in scan
self.__scan_file(os.path.join(root, f), folder)
File "scanner.py", line 77, in __scan_file
tr.album = self.__find_album(self.__try_read_tag(tag, 'artist'), self.__try_read_tag(tag, 'album'))
File "scanner.py", line 83, in __find_album
ar = self.__find_artist(artist)
File "scanner.py", line 94, in __find_artist
ar = filter(lambda a: a.name.lower() == artist.lower(), self.__artists)
File "scanner.py", line 94, in <lambda>
ar = filter(lambda a: a.name.lower() == artist.lower(), self.__artists)
AttributeError: 'NoneType' object has no attribute 'lower'
This happens because artist
is None
in this context, because back in __scan_file
, self.__try_read_tag(tag, 'artist')
returns None
. (I guess I have a broken MP3 file without an artist tag...) Adding a default, i.e. changing to self.__try_read_tag(tag, 'artist', '')
seems to help, but I don't know what the right solution is.
BTW: I'm really psyched to have found something I can run on my Sheevaplug that doesn't require something as heavy as Java!
Just an idea for a new feature.
How about monitoring the music folders for changes using inotify? So that new files or updated files would be added instantly without re-scanning the whole library?
There is pyinotify that could be used for that.
I realize this feature would be Linux specific and wouldn't work on any other operating system. But it would really be a great improvement and I would guess most people run it on Linux. If not, it could be optional.
Was following instructions in #52 (comment) and the following error took place:
$ PYTHONPATH=.. ./supysonic-cli --help
Traceback (most recent call last):
File "./supysonic-cli", line 26, in <module>
from supysonic.managers.folder import FolderManager
File "<supysonic-location>/managers/folder.py", line 23, in <module>
from supysonic.scanner import Scanner
File "<supysonic-location>/scanner.py", line 206
self.__store.find(User, User.last_play_id == tr.id).set(User.last_play_id = None)
SyntaxError: keyword can't be an expression
This is caused by commit 1de9e2b (currently the latest commit); reverting to the commit before it (783e9b5) fixes this for now.
I get sqlite3.DatabaseError: file is encrypted or is not a database
when trying to add a new user. The database exists, is not encrypted, is correctly referenced in the supysonic config file.
Here is the output:
pi@pi ~/supysonic/bin $ ./supysonic-cli
supysonic> user add johndoe -p password
Traceback (most recent call last):
File "./supysonic-cli", line 233, in <module>
cli.cmdloop()
File "/usr/lib/python2.7/cmd.py", line 142, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "./supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "./supysonic-cli", line 190, in user_add
status = UserManager.add(self.__store, name, password, email, admin)
File "/usr/local/lib/python2.7/dist-packages/supysonic/managers/user.py", line 53, in add
if store.find(User, User.name == name).one():
File "/usr/lib/python2.7/dist-packages/storm/store.py", line 1142, in one
result = self._store._connection.execute(select)
File "/usr/lib/python2.7/dist-packages/storm/database.py", line 238, in execute
raw_cursor = self.raw_execute(statement, params)
File "/usr/lib/python2.7/dist-packages/storm/databases/sqlite.py", line 154, in raw_execute
return Connection.raw_execute(self, statement, params)
File "/usr/lib/python2.7/dist-packages/storm/database.py", line 322, in raw_execute
self._check_disconnect(raw_cursor.execute, *args)
File "/usr/lib/python2.7/dist-packages/storm/database.py", line 371, in _check_disconnect
return function(*args, **kwargs)
sqlite3.DatabaseError: file is encrypted or is not a database
Possibly relevant:
pi@pi ~/supysonic/bin $ sqlite -version
2.8.17
pi@pi ~/supysonic/bin $ python -V
Python 2.7.3
OS: Debian Jessie
Supysonic version: commit 2b3155c
python-storm
crashes when I try to scan my music folder.
foo@bar:~# supysonic-cli folder scan Musique
Scanning 'Musique': 0% (1/10797)
Scanning 'Musique': 2% (315/10797)
Scanning 'Musique': 5% (636/10797)
Scanning 'Musique': 8% (949/10797)
Scanning 'Musique': 11% (1272/10797)
Scanning 'Musique': 14% (1563/10797)
Scanning 'Musique': 16% (1831/10797)
Scanning 'Musique': 19% (2131/10797)
Scanning 'Musique': 22% (2455/10797)
Scanning 'Musique': 25% (2772/10797)
Scanning 'Musique': 28% (3091/10797)
Scanning 'Musique': 31% (3403/10797)
Scanning 'Musique': 34% (3717/10797)
Scanning 'Musique': 37% (4043/10797)
Scanning 'Musique': 40% (4359/10797)
Scanning 'Musique': 43% (4688/10797)
Scanning 'Musique': 46% (5000/10797)
Scanning 'Musique': 49% (5313/10797)
Scanning 'Musique': 52% (5631/10797)
Scanning 'Musique': 55% (5955/10797)
Scanning 'Musique': 58% (6276/10797)
Scanning 'Musique': 61% (6594/10797)
Scanning 'Musique': 64% (6911/10797)
Scanning 'Musique': 66% (7230/10797)
Scanning 'Musique': 69% (7547/10797)
Scanning 'Musique': 72% (7864/10797)
Scanning 'Musique': 75% (8172/10797)
Scanning 'Musique': 78% (8484/10797)
Scanning 'Musique': 81% (8811/10797)
Scanning 'Musique': 84% (9129/10797)
Scanning 'Musique': 87% (9440/10797)
Scanning 'Musique': 90% (9767/10797)
Scanning 'Musique': 93% (10098/10797)
Scanning 'Musique': 96% (10415/10797)
Scanning 'Musique': 99% (10729/10797)
Scanning 'Musique': 100% (10797/10797)
Traceback (most recent call last):
File "/usr/local/bin/supysonic-cli", line 233, in <module>
cli.onecmd(' '.join(sys.argv[1:]))
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "/usr/local/bin/supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "/usr/local/bin/supysonic-cli", line 151, in folder_scan
scanner.scan(folder, TimedProgressDisplay(folder.name))
File "/usr/local/lib/python2.7/dist-packages/supysonic/scanner.py", line 94, in scan
self.remove_file(track.path)
File "/usr/local/lib/python2.7/dist-packages/supysonic/scanner.py", line 201, in remove_file
tr = self.__store.find(Track, Track.path == path).one()
File "/usr/lib/python2.7/dist-packages/storm/store.py", line 1145, in one
raise NotOneError("one() used with more than one result available")
storm.exceptions.NotOneError: one() used with more than one result available
Exception Exception: Exception("There's still something to check. Did you run Scanner.finish()?",) in <bound method Scanner.__del__ of <supysonic.scanner.Scanner instance at 0x7f7efcaa27e8>> ignored
i've tried to delete the folder to try to fix the problem but it seems the fact that the scan does not finish correctly makes it impossible to do.
foo@bar:~# supysonic-cli folder delete Musique
Traceback (most recent call last):
File "/usr/local/bin/supysonic-cli", line 233, in <module>
cli.onecmd(' '.join(sys.argv[1:]))
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "/usr/local/bin/supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "/usr/local/bin/supysonic-cli", line 126, in folder_delete
ret = FolderManager.delete_by_name(self.__store, name)
File "/usr/local/lib/python2.7/dist-packages/supysonic/managers/folder.py", line 104, in delete_by_name
return FolderManager.delete(store, folder.id)
File "/usr/local/lib/python2.7/dist-packages/supysonic/managers/folder.py", line 88, in delete
scanner.remove_file(track.path)
File "/usr/local/lib/python2.7/dist-packages/supysonic/scanner.py", line 201, in remove_file
tr = self.__store.find(Track, Track.path == path).one()
File "/usr/lib/python2.7/dist-packages/storm/store.py", line 1145, in one
raise NotOneError("one() used with more than one result available")
storm.exceptions.NotOneError: one() used with more than one result available
Exception Exception: Exception("There's still something to check. Did you run Scanner.finish()?",) in <bound method Scanner.__del__ of <supysonic.scanner.Scanner instance at 0x7f5f7b4f8d88>> ignored
I "fixed" the problem by deleting the DB and recreating it from scratch, but I made a dump before so I can test things out if you want.
Fun fact: if that can help, Dsub was still working but one artist (I had only one album from her and I replaced it from a mp3 source to a FLAC source a few days ago) was shown multiple times.
I've been doing that a lot lately (replacing mp3 albums by FLAC ones) and it seems that it is what triggered that bug for me.
Traceback (most recent call last):
File "/usr/share/pyshared/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/share/pyshared/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/usr/share/pyshared/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/share/pyshared/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/share/pyshared/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/share/pyshared/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/share/pyshared/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/share/pyshared/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/jelmer/supysonic/api/albums_songs.py", line 64, in album_list
x = random.choice(xrange(count))
File "/usr/lib/python2.7/random.py", line 273, in choice
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
IndexError: xrange object index out of range
flask.json.jsonify()
after_request
handlersTranscoding flac
to mp3
does not change the file extension. I'm downloading the files with DSub and even though they are transcoded fine and end up being mp3
files, they are still named *.flac
:
$ file "01-Pints of Guinness Make You Strong.complete.flac"
> 01-Pints of Guinness Make You Strong.complete.flac: MPEG ADTS, layer III, v1, 320 kbps, 44.1 kHz, JntStereo
I'm using the recommended transcoding options on the wiki:
transcoder_mp3_mp3 = lame --quiet --mp3input -b %outrate %srcpath -
transcoder = ffmpeg -i %srcpath -ab %outratek -v 0 -f %outfmt -
decoder_mp3 = mpg123 --quiet -w - %srcpath
decoder_ogg = oggdec -o %srcpath
decoder_flac = flac -d -c -s %srcpath
encoder_mp3 = lame --quiet -b %outrate - -
encoder_ogg = oggenc2 -q -M %outrate -
This is not a critical bug since most players won't care and will play the files anyway, but I wondered for a while why the transcoding was not working even though I could see flac
and lame
running on the server.
As with Issue #33, MySQL Database does not work correctly with supysonic. Neither PostgreSQL does work correctly, but with another error.
Commands input (almost) as exactly:
git clone https://github.com/spl0k/supysonic.git app
virtualenv2 virtualenv
virtualenv/bin/pip install flask storm psycopg2==2.4 pillow simplejson requests mutagen watchdog
virtualenv/bin/pip install -e app/
sudo -upostgres createuser supysonic
sudo -upostgres createdb -E utf8 -T postgres -O supysonic supysonic
psql -dsupysonic -Usupysonic -f app/schema/postgresql.sql
virtualenv/bin/supysonic-cli folder add music /data/files/music
virtualenv/bin/supysonic-cli folder scan
Turns out to be:
$ virtualenv/bin/supysonic-cli folder scan
Traceback (most recent call last):
File "virtualenv/bin/supysonic-cli", line 6, in <module>
exec(compile(open(__file__).read(), __file__, 'exec'))
File "/home/tki/app/bin/supysonic-cli", line 232, in <module>
cli.onecmd(' '.join(sys.argv[1:]))
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "/home/tki/app/bin/supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "/home/tki/app/bin/supysonic-cli", line 153, in folder_scan
scanner.scan(folder, TimedProgressDisplay(folder.name))
File "/home/tki/app/supysonic/scanner.py", line 60, in scan
self.scan_file(path)
File "/home/tki/app/supysonic/scanner.py", line 140, in scan_file
trroot = self.__find_root_folder(path)
File "/home/tki/app/supysonic/scanner.py", line 211, in __find_root_folder
count = folders.count()
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/store.py", line 1264, in count
return int(self._aggregate(lambda expr: Count(expr, distinct), expr))
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/store.py", line 1253, in _aggregate
result = self._store._connection.execute(select)
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/databases/postgres.py", line 266, in execute
return Connection.execute(self, statement, params, noresult)
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/database.py", line 241, in execute
raw_cursor = self.raw_execute(statement, params)
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/databases/postgres.py", line 276, in raw_execute
return Connection.raw_execute(self, statement, params)
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/database.py", line 374, in raw_execute
self._run_execution(raw_cursor, args, params, statement)
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/database.py", line 388, in _run_execution
self._check_disconnect(raw_cursor.execute, *args)
File "/home/tki/virtualenv/lib/python2.7/site-packages/storm/database.py", line 454, in _check_disconnect
return function(*args, **kwargs)
IndexError: tuple index out of range
My /etc/supysonic:
[base]
database_uri = postgres://supysonic@localhost/supysonic
scanner_extensions = mp3 ogg oga aac m4a flac wav wma aif aiff ape mpc shn
[webapp]
log_level = WARNING
[transcoding]
transcoder = ffmpeg -i %srcpath -ab %outratek -v 0 -f %outfmt -
[mimetypes]
mp3 = audio/mpeg
ogg = audio/ogg
[daemon]
log_level = WARNING
Please notice that I am using psycopg2==2.4, thus the issue has nothing to do with this.
When I tried with a local SQLite DB, the procedure worked wonderfully.
Cheers to you and collaborators for this interesting project. I am fairly excited to the fact that I can use Subsonic API implementation without using JVM.
I created a configuration file with the appropriate values, but my file editor added an additional newline to the end of the file. This caused the config parser to error out with the error below:
$ python main.py
Traceback (most recent call last):
File "main.py", line 3, in <module>
import config
File "/home/william/Documents/supysonic/config.py", line 20, in <module>
config_dict[spl[0].strip()] = eval(spl[1])
IndexError: list index out of range
If you print out the line after the split, it looked like this:
['DATABASE_URI', '"sqlite:////var/supysonic/supysonic.db"\n']
['CACHE_DIR', '"/tmp/supysonic"\n']
['LOG_FILE', '"/var/log/supysonic"\n']
['\n']
There should be a way to handle or ignore these additional newlines. Maybe add:
if len(spl) == 2:
or something similar.
I've noticed that unlike Subsonic, Supysonic doesn't allow per-folder browsing. On my Subsonic 6.1.1 server, I have an Audiobooks folder, a Podcasts folder (I use manolomartinez/greg to download & tag podcasts), and a Music folder. I'll switch between them depending on what I want.
I tend to think of the "Folders" each as a Library in their own right.
Supysonic lets me add folders but seems to treat them all as one big library. DSub shows everything all jumbled together.
With Subsonic, DSub lets me pick an individual "Folder" to operate in which is much more convenient. (Additionally, although I don't have Jamstash playback working as per #58 , it does list the supysonic "folders" in its drop-down, but it also doesn't work to switch between them; it does nothing to change what I'm seeing.)
Would it be possible to add this capability to Supysonic?
This is a solved problem, and it's really not a supysonic issue (ie webserver configuration) but I spent a bit of time hunting it down and thought it'd be useful to document.
The solution is already noted in the README.md - see the green in this commit
Using the DSub android client, browsing to a song titled "Après Moi" (notice the UTF-8 character), the client would fail with the message:
Error: A problem occurred communication with the server. ...
ie, the server returned a 500.
Then looking in the subsonic.log
the error message in the title of this git issue was logged.
I noticed this issue didn't occur when running supysonic as stand alone, only from my Apache2 WSGI endpoint.
Similar to what's mentioned in the README.md, for me it was solved by adding:
export LANG="en_US.utf8"
export LC_ALL="en_US.UTF-8"
before the export LANG
line in my /etc/apache2/envvars
file and issuing a sudo service apache2 restart
.
Currently, playlist items are automatically sorted by ids, and may not have more than one entry with the same song ID.
It would be wonderful to support having playlists with arbitrarily ordered items and the same item occuring possibly more than once.
The current status of uniquified and id-sorted playlists seems to be coded in two levels: the API, and the DB schema.
In the API, upon creating and updating the song UUIDs to put/add are put in a python set
, which sorts them and uniquifies them.
In the schema, playlist items are also unordered and do not have a unique key to differentiate one entry from another with the same song.
Perhaps an ever-increasing integer column can be added to the playlist items table, as the playlist item ID, and this ID can be used as the playlist sort order too, since the subsonic API only appends items to the end of a playlist? I am not a DBA though, so take this idea with a grain of salt if you'd like :-)
As far as the API code, it seems on a quick glance that only the updatePlaylist
method needs to be changed, to remove an item not only by song ID but by playlist item ID too.
When launching, it fails to start with error:
$ python cgi-bin/server.py
Traceback (most recent call last):
File "cgi-bin/server.py", line 22, in
from supysonic.web import create_application
ImportError: No module named supysonic.web
right now the file scan runs mutagen on all filetypes, really there should be an option in the configuration file to either whitelist or blacklist filetypes.
Most subsonic clients does not specify the destination format of the transcoded format.
When using flac files, they do not transcode with designated bitrate (since they are not lossy) and does meaninglessly run the transcoder process.
Furthermore, most browsers do not support flac audio, so they cannot be played on HTML5 players such as Jamstash.
Whereas this can be detoured out by setting a specific format with transcoder settings, it seems to be a good practice to be able to specify a default target format.
Also, a option to always transcode a specific format into another specified format when requested by some user-agent or application name may also be lovely.
EDIT: Nevermind: I just noticed the options on /user/me. Sorry for that.
When a folder and one of its subfolders are both added as supysonic folders, scanning the folders causes a 500 error.
The link to the perisonic Android app is dead.
Hi spl0k,
I was very happy when I found your project and I'm using it on my raspberry pi now.
I'm playing around with it and started to work on support for .ogg and .flac files and on transcoding using ffmpeg/libav. I've got it partially working on archlinux but it needs testing. Are you interested in a patch/pull request? Maybe we can work together?
And another question: what license do you want to use for supysonic?
GPL, Apache, MIT, ... ?
The transcoding options present are useful, but I have suggestions to give more versatility to the features.
First, some formats such as Opus has bitrate up to 510 and are not bound to powers of 2, so max bitrate input would be better if it was a textbox rather than a combobox.
Also, there are instances when a specific format does not work on a client (flac on HTML5), and want other formats sent out raw. Currently on the transcoding configuration, only the destination format is specified and all audio format are transcoded into such. Being able to specify a original format to be transcoded is desirable, and if there is no definition for a specific format, it can maybe fallback to a definition with no original format input (if available) or sent out raw.
Problem:
Multiple folder entries (with empty name) are created if two folders share a first word.
In the example below, both "Album I" and "Album II" start with the same name.
What's happening:
Reproduction scenario:
Python 2.7, centos 7, sqlite
# setup
mkdir /tmp/test
mkdir /tmp/test/Artist
mkdir "/tmp/test/Artist/Album I"
mkdir "/tmp/test/Artist/Album II"
cp sometaggedmp3.mp3 "/tmp/test/Artist/Album I/test.mp3"
cp sometaggedmp3.mp3 "/tmp/test/Artist/Album I/test2.mp3"
cp sometaggedmp3.mp3 "/tmp/test/Artist/Album II/test.mp3"
cp sometaggedmp3.mp3 "/tmp/test/Artist/Album II/test2.mp3"
# first scan
python supysonic-cli
folder add TestLibrary /tmp/test
folder scan TestLibrary
exit
# See erroneously inserted folders @ db
sqlite3 /var/supysonic/supysonic.db
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM folder;
6f28fba3-1d75-4b62-be32-013678e28367|1|test|/tmp/test|2016-02-12 01:57:03|0|1455238626|
9f16c505-4da6-4b91-8610-f63b0658d81a|0|Artist|/tmp/test/Artist|2016-02-12 01:57:06|0|0|6f28fba3-1d75-4b62-be32-013678e28367
958eee7e-324e-4d4b-ac74-aacf99a84d27|0|Album I|/tmp/test/Artist/Album I|2016-02-12 01:57:06|1|0|9f16c505-4da6-4b91-8610-f63b0658d81a
0d937f59-8234-47a7-98e0-cc0486553933|0||/tmp/test/Artist/Album I/|2016-02-12 01:57:06|1|0|958eee7e-324e-4d4b-ac74-aacf99a84d27
016f276a-323f-48dc-82e9-0208e81729a4|0||/tmp/test/Artist/Album I/|2016-02-12 01:57:06|1|0|958eee7e-324e-4d4b-ac74-aacf99a84d27
sqlite>
# Add extra file to "Album II"
cp sometaggedmp3.mp3 "/tmp/test/Artist/Album II/test3.mp3"
python supysonic-cli
folder scan TestLibrary
# An extra wrong record added
sqlite3 /var/supysonic/supysonic.db
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM folder;
5dc83780-cadf-4512-aff7-bfb2a761d7cf|1|test|/tmp/test|2016-02-12 02:04:53|0|1455239102|
85520cd1-8e69-4a2e-84db-4d7ab458c436|0|Artist|/tmp/test/Artist|2016-02-12 02:04:56|0|0|5dc83780-cadf-4512-aff7-bfb2a761d7cf
82ede624-5826-47ae-9369-6d5ab9f322c3|0|Album I|/tmp/test/Artist/Album I|2016-02-12 02:04:56|0|0|85520cd1-8e69-4a2e-84db-4d7ab458c436
14d57814-db9b-40dd-bb88-c8dd5261f674|0||/tmp/test/Artist/Album I/|2016-02-12 02:04:56|0|0|82ede624-5826-47ae-9369-6d5ab9f322c3
2f1f9019-55a1-4c4a-a5f6-c91fa3befce4|0||/tmp/test/Artist/Album I/|2016-02-12 02:04:56|0|0|82ede624-5826-47ae-9369-6d5ab9f322c3
3cbdd51d-bf10-44ca-b1e8-93cd6faf8b8d|0||/tmp/test/Artist/Album I/|2016-02-12 02:05:02|0|0|82ede624-5826-47ae-9369-6d5ab9f322c
Hi I installed supysonic on my QNAP TS-412 (ARM),
but if I try to add an user I get the following error. There are no special characters in the password.
[/opt] # /opt/supysonic/bin/supysonic-cli user add --admin pingumania
Password:
Confirm password:
Traceback (most recent call last):
File "/opt/supysonic/bin/supysonic-cli", line 231, in <module>
cli.onecmd(' '.join(sys.argv[1:]))
File "/share/MD0_DATA/.qpkg/Python/lib/python2.7/cmd.py", line 219, in onecmd
return func(arg)
File "/opt/supysonic/bin/supysonic-cli", line 51, in method
return func(** { key: vars(args)[key] for key in vars(args) if key != 'action' })
File "/opt/supysonic/bin/supysonic-cli", line 190, in user_add
status = UserManager.add(self.__store, name, password, email, admin)
File "/share/MD0_DATA/.qpkg/Python/lib/python2.7/site-packages/supysonic/managers/user.py", line 53, in add
if store.find(User, User.name == name).one():
File "/opt/local/lib/python2.7/site-packages/storm/store.py", line 1158, in one
result = self._store._connection.execute(select)
File "/opt/local/lib/python2.7/site-packages/storm/database.py", line 238, in execute
statement = self.compile(statement, state)
File "/opt/local/lib/python2.7/site-packages/storm/databases/sqlite.py", line 57, in compile_select_sqlite
statement = compile_select(compile, select, state)
File "/opt/local/lib/python2.7/site-packages/storm/expr.py", line 666, in compile_select
tokens.append(compile(select.columns, state))
LookupError: unknown encoding:
It seems that supysonic returns multiple album entries when some tracks have different artists. In this case, we get one album per track artist, all with the same name. In this case, it would be nice if supysonic supported the album artist metadata field, in order to group albums by album artists.
SubWiji (and maybe other clients) tests the server by sending a ping request without any credentials.
Since supysonic requires credentials, it prevents the use of that particular client.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.