#!/usr/bin/python import sys import os import datetime import traceback from SimpleXMLRPCServer import * import crypt import Queue import SocketServer import logging import logging.handlers sys.path.append(os.path.join("..")) import mjukconf import dbLayer import location import statemachine import playqueue import track import search import serverArtist import serverAlbum import serverTrack import serverGenre SIDseed=1; ### users currently logged in loggedInUsers={} ### active location locations={} def flatten(seq): return [x for subseq in seq for x in subseq] def mjukLogger(): logger=logging.getLogger("mjuk") logger.setLevel(logging.DEBUG) ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") ch.setFormatter(formatter) logger.addHandler(ch) syslogh = logging.handlers.SysLogHandler('/dev/log') syslogh.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s - %(name)s - %(process)d - %(levelname)s - %(message)s") syslogh.setFormatter(formatter) logger.addHandler(syslogh) return logger class MjukServerXMLRPC: def __init__(self): global loggedInUsers global locations # Init config. mjukconfig=mjukconf.serverClass(); databaseuser = mjukconfig.databaseuser() databasepwd = mjukconfig.databasepassword() database = mjukconfig.database() databasehost = mjukconfig.databasehost() self.db=dbLayer.DBLayer() self.db.connect(user=databaseuser,passwd=databasepwd,db=database,host=databasehost) self.serverArtist=serverArtist.serverArtist(self.db,loggedInUsers,locations) self.serverAlbum=serverAlbum.serverAlbum(self.db,loggedInUsers,locations) self.serverTrack=serverTrack.serverTrack(self.db,loggedInUsers,locations) self.serverGenre=serverGenre.serverGenre(self.db,loggedInUsers,locations) self.setupAvahi() ### ******************************************************************************* def setupAvahi(self): try: import avahi except ImportError: print "avahi not found, continuing without announcement" return try: import dbus, dbus.glib except ImportError: print "dbus not found, continuing without announcement" return bus = dbus.SystemBus() server = dbus.Interface(bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) g = dbus.Interface(bus.get_object(avahi.DBUS_NAME, server.EntryGroupNew()), avahi.DBUS_INTERFACE_ENTRY_GROUP) g.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, 0, "mjuk-server", "_mjuk._tcp", "", "", dbus.UInt16(mjukconfig.port()),"") g.Commit() def login(self, user,password): global SIDseed global loggedInUsers #print user,password c=self.db.cursor() numUsers=c.execute("""SELECT userId,password,name,defaultLocation FROM Users WHERE username=%s""", (user,)) if (numUsers<>1): c.close() return ("error","auth error") dbuserid,dbpassword,dbname,dblocation=c.fetchone() c.close() if (crypt.crypt(password,dbpassword[:2])!=dbpassword): return ("error","auth error") if (dblocation==None): return ("error","No default location") sid=SIDseed ### increse SID for next user SIDseed+=1 if not locations.has_key(dblocation): locations[dblocation]=location.Location(self.db, dblocation) locations[dblocation].statemachine.start() loggedInUsers[sid]=(dbuserid,user,dblocation) return ("ok",sid) ### ******************************************************************************* def login_with_location(self, user,password,locationname): global SIDseed global loggedInUsers c=self.db.cursor() numUsers=c.execute("""SELECT userId,password,name FROM Users WHERE username=%s""", (user,)) if (numUsers<>1): c.close() return ("error","auth error") dbuserId,dbpassword,dbname=c.fetchone() if (crypt.crypt(password,dbpassword[:2])!=dbpassword): c.close() return ("error","auth error") status=c.execute("""SELECT locationId,access FROM Location WHERE Location.name=%s""",(locationname,)) print status if (status<>1): c.close() return ("error","unknown location") locationId,access=c.fetchone() print locationId,access if (access=='private'): status=c.execute("""SELECT userId FROM UserLocation WHERE userId=%s and locationId=%s""",(dbuserId,locationId)) print status if (status<>1): c.close() return ("error","unknown location") c.close() sid=SIDseed ### increse SID for next user SIDseed+=1 if not locations.has_key(locationId): locations[locationId]=location.Location(self.db, locationId) locations[locationId].statemachine.start() loggedInUsers[sid]=(dbuserId,user,locationId) return ("ok",sid) ### ******************************************************************************* def logout(self, sid): global loggedInUsers print "before del: ",loggedInUsers try: del loggedInUsers[int(sid)] print "after del: ",loggedInUsers except KeyError: return ("error","no such sid") return ("ok",) ### ******************************************************************************* def set(self, sid, command, arg1=None,arg2=None): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] userId=loggedInUsers[int(sid)][0] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") if (command=="random"): if (arg1==None): return ("error","too few arguments") if (arg2!=None): return ("error","too many argumets") if (arg1=="on"): machine.playQueue.randomOn() return ("ok",) elif (arg1=="off"): machine.playQueue.randomOff() return ("ok",) else: return ("error","invalid argument (on/off)") else: return ("error","no such command") ### ******************************************************************************* def get(self, sid, command, arg1=None,arg2=None): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] userId=loggedInUsers[int(sid)][0] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") if (command=="status"): if (arg1!=None): return ("error","too many argumets") status=machine.statemachine.getStatus() if status[0]!="stopped": trackRate=self.getTrackRate(sid,status[1]) albumRate=self.getAlbumRate(sid,status[3]) artistRates=[] for i in status[5]: artistRates.append(self.getArtistRate(sid,i[0])[1]) status.append(trackRate[1]) status.append(albumRate[1]) status.append(artistRates) return ("ok",status) else: return ("error","no such command") ### ******************************************************************************* def play(self, sid): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") state=machine.statemachine.getState() if (state==machine.statemachine.STOP or state==machine.statemachine.PAUSE): machine.commandQueue.put("play") return ("ok",) else: return ("ok","wrong state") return ("ok",) ### ******************************************************************************* def stop(self, sid): global locations global loggedInUsers print "stop" try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") state=machine.statemachine.getState() if (state==machine.statemachine.PLAY or state==machine.statemachine.PAUSE): machine.commandQueue.put("stop") return ("ok",) ### ******************************************************************************* def pause(self,sid): global locations global loggedInUsers print "pause" try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") state=machine.statemachine.getState() if (state==machine.statemachine.PLAY): machine.commandQueue.put("pause") return ("ok",) ### ******************************************************************************* def next(self,sid): global locations global loggedInUsers print "next" try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") state=machine.statemachine.getState() if (state==machine.statemachine.PLAY): machine.commandQueue.put("next") return ("ok",) ### ******************************************************************************* def enqueue(self, sid, trackIds): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") errorTrackIds=[] tracksToPlay=[] for trackId in trackIds: if not int(trackId)==0: c=self.db.cursor() trackExist=c.execute("""SELECT trackId FROM Track WHERE trackId=%s""", (trackId,)) if (trackExist<>1): errorTrackIds.append(trackId) else: result=c.fetchone() tracksToPlay.append(track.Track(self.db,result[0])) c.close() for playme in tracksToPlay: machine.playQueue.addToQueue(playme) if len(errorTrackIds)>0: return ("error","trackId problem",errorTrackIds) return ("ok",) ### ******************************************************************************* def getQueue(self, sid): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") return ("ok",machine.playQueue.getQueue()) ### ******************************************************************************* def deleteFromQueue(self, sid, queue, trackids): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") return machine.playQueue.deleteFromQueue(queue,trackids) ### ******************************************************************************* def randomizeQueued(self,sid): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") try: machine=locations[locationId] except KeyError: return ("error","no such location") return machine.playQueue.randomizeQueued() ### ******************************************************************************* def search(self, sid, resultType, casesensitive, sortOrder, queryTree): global loggedInUsers global locations try: dummy=loggedInUsers[int(sid)] except KeyError: return ("error","no such sid") try: query=search.Query() query.setResult(resultType) query.setCasesensitive(casesensitive) query.setOrder(sortOrder) print "queryTree:",queryTree tree=self.buildTree(queryTree) print "tree:",tree query.setTree(tree) #Do search and return result. selectQuery=query.getSelect() print selectQuery c=self.db.cursor() numLocations=c.execute(selectQuery) resultArray=c.fetchall() c.close() except: print>>sys.stderr, datetime.datetime.now() traceback.print_exc() raise; return ("ok",flatten(resultArray)) def buildTree(self,elements): print "element:",elements[0] if (elements[0]=="and" or elements[0]=="or"): tree=search.QueryBinaryOperator(elements[0]) for elem in elements[1:]: tree.Add(self.buildTree(elem)) return tree elif (elements[0]=="not"): tree=search.QueryUnaryOperator(elements[0]) tree.Add(self.buildTree(elements[1])) return tree elif (elements[0]=="leaf"): row=search.QueryRow() row.setRow(elements[1], elements[2], elements[3]) return row else: print "report error" ### ******************************************************************************* def getPublicLocations(self): c=self.db.cursor() numLocations=c.execute("""SELECT name,description FROM Location WHERE access='public'""") locs=c.fetchall() c.close() return ("ok",locs) ### ******************************************************************************* def getLocations(self, sid): global loggedInUsers global locations try: userId=loggedInUsers[int(sid)][0] except KeyError: return ("error","no such sid") c=self.db.cursor() numLocations=c.execute("""SELECT name,description FROM Location where Location.access='public' UNION SELECT Location.name,Location.description FROM Location,UserLocation WHERE Location.locationId=UserLocation.locationId AND UserLocation.userId=%s""",(userId,)) locs=c.fetchall() c.close() return ("ok",locs) ### ******************************************************************************* def getLocation(self, sid): global loggedInUsers global locations try: locationId=loggedInUsers[int(sid)][2] except KeyError: return ("error","no such sid") c=self.db.cursor() numLocations=c.execute("""SELECT name,description FROM Location where locationId=%s""",(locationId,)) loc=c.fetchone() c.close() return ("ok",loc) ### ******************************************************************************* ### Tracks ### ******************************************************************************* def getTrackShortInfo(self, sid, trackIds): return self.serverTrack.getTrackShortInfo(sid, trackIds) def getTrackLongInfo(self, sid, trackId): return self.serverTrack.getTrackLongInfo(sid, trackId) def setTrackRate(self, sid, trackid, rate): return self.serverTrack.setTrackRate(sid, trackid, rate) def getTrackRate(self, sid, trackid): return self.serverTrack.getTrackRate(sid, trackid) def setTrackGenre(self, sid, trackId,genreId): return self.serverTrack.setTrackGenre(sid,trackId,genreId) def getTrackGenre(self, sid, trackId): return self.serverTrack.getTrackGenre(sid,trackId) def deleteTrackGenre(self, sid, genreMappingId): return self.serverTrack.deleteTrackGenre(sid,genreMappingId) ### ******************************************************************************* ### Albums ### ******************************************************************************* def getAlbumShortInfo(self, sid, albumIds): return self.serverAlbum.getAlbumShortInfo(sid, albumIds) def getAlbumLongInfo(self, sid, albumIds): return self.serverAlbum.getAlbumLongInfo(sid, albumIds) def setAlbumRate(self, sid, albumid, rate): return self.serverAlbum.setAlbumRate(sid, albumid, rate); def getAlbumRate(self, sid, albumid): return self.serverAlbum.getAlbumRate(sid, albumid); def setAlbumGenre(self, sid, albumId,genreId): return self.serverAlbum.setAlbumGenre(sid,albumId,genreId) def getAlbumGenre(self, sid, albumId): return self.serverAlbum.getAlbumGenre(sid,albumId) def deleteAlbumGenre(self, sid, genreMappingId): return self.serverAlbum.deleteAlbumGenre(sid,genreMappingId) ### ******************************************************************************* ### Artists ### ******************************************************************************* def getArtistShortInfo(self, sid, artistIds): return self.serverArtist.getArtistShortInfo(sid, artistIds) def setArtistRate(self, sid, artistid, rate): return self.serverArtist.setArtistRate(sid, artistid, rate); def getArtistRate(self, sid, artistid): return self.serverArtist.getArtistRate(sid, artistid); def setArtistGenre(self, sid, artistId,genreId): return self.serverArtist.setArtistGenre(sid,artistId,genreId) def getArtistGenre(self, sid, artistId): return self.serverArtist.getArtistGenre(sid,artistId) def deleteArtistGenre(self, sid, genreMappingId): return self.serverArtist.deleteArtistGenre(sid,genreMappingId) ### ******************************************************************************* ### Genre/Mood ### ******************************************************************************* def addGenre(self,sid,name,description): return self.serverGenre.addGenre(sid,name,description) def getGenreShortInfo(self,sid,genreIds): return self.serverGenre.getGenreShortInfo(sid,genreIds) def getGenreLongInfo(self,sid,genreIds): return self.serverGenre.getGenreLongInfo(sid,genreIds) ### ******************************************************************************* ### ******************************************************************************* ### ******************************************************************************* ### ******************************************************************************* ### ******************************************************************************* ### ******************************************************************************* ### ******************************************************************************* ### ******************************************************************************* class mjukXMLRPCDispatcher(SimpleXMLRPCDispatcher): def __init__(self, allow_none, encoding): SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) def _dispatch(self,method,params): try: status=SimpleXMLRPCDispatcher._dispatch(self,method,params) except Exception,v: print>>sys.stderr, "exception!!!!: " traceback.print_exc() return status class mjukXMLRPCServer(SocketServer.TCPServer,mjukXMLRPCDispatcher): def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, logRequests=1, allow_none=False, encoding=None): self.logRequests = logRequests mjukXMLRPCDispatcher.__init__(self, allow_none, encoding) SocketServer.TCPServer.__init__(self, addr, requestHandler) mjukconfig=mjukconf.serverClass() server = mjukXMLRPCServer( (mjukconfig.host(),mjukconfig.port())) server.logRequests=0 server.register_instance(MjukServerXMLRPC()) pid=os.getpid() try: filename="/var/run/mjuk-server.pid" file(filename,"w").write("%s\n" % pid) except IOError: sys.stderr.write("Can't create pid-file: "+filename+"\n") logger = mjukLogger() logger.info("Server has started!") try: server.serve_forever() except: print "eject" for i in locations: print i locations[i].commandQueue.put( ("die",) ) for i in locations: locations[i].playQueue.addToQueue(-1) server.server_close() logging.shutdown() raise