X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/f5eb2affdc1c88c71b4d66598dfc14216b22730f..c3be4f19f3d6465ff4c1110a1a7e7a0686f8dc37:/python/disorder.py.in?ds=sidebyside diff --git a/python/disorder.py.in b/python/disorder.py.in index 9585438..9a6b385 100644 --- a/python/disorder.py.in +++ b/python/disorder.py.in @@ -1,5 +1,5 @@ # -# Copyright (C) 2004, 2005 Richard Kettlewell +# Copyright (C) 2004, 2005, 2007 Richard Kettlewell # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -39,6 +39,11 @@ Example 2: for path in sys.argv[1:]: d.play(path) +See disorder_protocol(5) for details of the communication protocol. + +NB that this code only supports servers configured to use SHA1-based +authentication. If the server demands another hash then it will not be +possible to use this module. """ import re @@ -271,7 +276,7 @@ class client: debug_proto = 0x0001 debug_body = 0x0002 - def __init__(self): + def __init__(self, user=None, password=None): """Constructor for DisOrder client class. The constructor reads the configuration file, but does not connect @@ -289,6 +294,8 @@ class client: self.config = { 'collections': [], 'username': pw.pw_name, 'home': _dbhome } + self.user = user + self.password = password home = os.getenv("HOME") if not home: home = pw.pw_dir @@ -322,8 +329,10 @@ class client: sys.stderr.write("\n") sys.stderr.flush() - def connect(self): - """Connect to the DisOrder server and authenticate. + def connect(self, cookie=None): + """c.connect(cookie=None) + + Connect to the DisOrder server and authenticate. Raises communicationError if connection fails and operationError if authentication fails (in which case disconnection is automatic). @@ -334,6 +343,9 @@ class client: Other operations automatically connect if we're not already connected, so it is not strictly necessary to call this method. + + If COOKIE is specified then that is used to log in instead of + the username/password. """ if self.state == 'disconnected': try: @@ -364,10 +376,21 @@ class client: self.w = s.makefile("wb") self.r = s.makefile("rb") (res, challenge) = self._simple() - h = sha.sha() - h.update(self.config['password']) - h.update(binascii.unhexlify(challenge)) - self._simple("user", self.config['username'], h.hexdigest()) + if cookie is None: + if self.user is None: + user = self.config['username'] + else: + user = self.user + if self.password is None: + password = self.config['password'] + else: + password = self.password + h = sha.sha() + h.update(password) + h.update(binascii.unhexlify(challenge)) + self._simple("user", user, h.hexdigest()) + else: + self._simple("cookie", cookie) self.state = 'connected' except socket.error, e: self._disconnect() @@ -403,8 +426,14 @@ class client: Arguments: track -- the path of the track to play. + + Returns the ID of the new queue entry. + + Note that queue IDs are unicode strings (because all track information + values are unicode strings). """ - self._simple("play", track) + res, details = self._simple("play", track) + return unicode(details) # because it's unicode in queue() output def remove(self, track): """Remove a track from the queue. @@ -472,7 +501,10 @@ class client: def playing(self): """Return the currently playing track. - If a track is playing then it is returned as a dictionary. + If a track is playing then it is returned as a dictionary. See + disorder_protocol(5) for the meanings of the keys. All keys are + plain strings but the values will be unicode strings. + If no track is playing then None is returned.""" res, details = self._simple("playing") if res % 10 != 9: @@ -494,14 +526,20 @@ class client: """Return a list of recently played tracks. The return value is a list of dictionaries corresponding to - recently played tracks. The oldest track comes first.""" + recently played tracks. The oldest track comes first. + + See disorder_protocol(5) for the meanings of the keys. All keys are + plain strings but the values will be unicode strings.""" return self._somequeue("recent") def queue(self): """Return the current queue. The return value is a list of dictionaries corresponding to - recently played tracks. The next track to be played comes first.""" + recently played tracks. The next track to be played comes first. + + See disorder_protocol(5) for the meanings of the keys. All keys are + plain strings but the values will be unicode strings.""" return self._somequeue("queue") def _somedir(self, command, dir, re): @@ -583,7 +621,7 @@ class client: track -- the track to query key -- the preference to remove - The return value is the preference + The return value is the preference. """ ret, details = self._simple("get", track, key) if ret == 555: @@ -711,6 +749,21 @@ class client: ret, details = self._simple("move", track, str(delta)) return int(details) + def moveafter(self, target, tracks): + """Move a track in the queue + + Arguments: + target -- target ID or None + tracks -- a list of IDs to move + + If target is '' or is not in the queue then the tracks are moved to + the head of the queue. + + Otherwise the tracks are moved to just after the target.""" + if target is None: + target = '' + self._simple("moveafter", target, *tracks) + def log(self, callback): """Read event log entries as they happen. @@ -798,6 +851,41 @@ class client: else: return details + def make_cookie(self): + """Create a login cookie""" + ret, details = self._simple("make-cookie") + return details + + def revoke(self): + """Revoke a login cookie""" + self._simple("revoke") + + def adduser(self, user, password): + """Create a user""" + self._simple("adduser", user, password) + + def deluser(self, user): + """Delete a user""" + self._simple("deluser", user) + + def userinfo(self, user, key): + """Get user information""" + res, details = self._simple("userinfo", user, key) + if res == 555: + return None + return _split(details)[0] + + def edituser(self, user, key, value): + """Set user information""" + self._simple("edituser", user, key, value) + + def users(self): + """List all users + + The return value is a list of all users.""" + self._simple("users") + return self._body() + ######################################################################## # I/O infrastructure