From dc7f8905ae4d62eeaa08bc935bb9345261d70349 Mon Sep 17 00:00:00 2001 From: Yorick Rommers Date: Mon, 2 Nov 2015 20:32:33 +0100 Subject: [PATCH] Python server added --- yjmpd-c#/config.cfg | 16 ++ yjmpd-c#/requirements.txt | 3 + yjmpd-c#/scan.py | 72 ++++++++ yjmpd-c#/test.py | 72 ++++++++ yjmpd-c#/yjdaemon/API.py | 169 ++++++++++++++++++ yjmpd-c#/yjdaemon/Database.py | 81 +++++++++ yjmpd-c#/yjdaemon/HTTPServer.py | 82 +++++++++ yjmpd-c#/yjdaemon/ServiceSocket.py | 36 ++++ yjmpd-c#/yjdaemon/__init__.py | 0 .../yjdaemon/__pycache__/API.cpython-34.pyc | Bin 0 -> 1448 bytes .../yjdaemon/__pycache__/API.cpython-35.pyc | Bin 0 -> 6328 bytes .../__pycache__/Database.cpython-34.pyc | Bin 0 -> 3480 bytes .../__pycache__/Database.cpython-35.pyc | Bin 0 -> 4190 bytes .../__pycache__/HTTPServer.cpython-34.pyc | Bin 0 -> 2901 bytes .../__pycache__/HTTPServer.cpython-35.pyc | Bin 0 -> 2994 bytes .../__pycache__/__init__.cpython-34.pyc | Bin 0 -> 131 bytes .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 136 bytes .../__pycache__/libraryscanner.cpython-34.pyc | Bin 0 -> 4253 bytes .../__pycache__/libraryscanner.cpython-35.pyc | Bin 0 -> 4335 bytes .../yjdaemon/__pycache__/yjmpd.cpython-34.pyc | Bin 0 -> 3722 bytes .../yjdaemon/__pycache__/yjmpd.cpython-35.pyc | Bin 0 -> 3716 bytes yjmpd-c#/yjdaemon/libraryscanner.py | 78 ++++++++ yjmpd-c#/yjdaemon/maindaemon.py | 60 +++++++ yjmpd-c#/yjdaemon/yjmpd.py | 135 ++++++++++++++ yjmpd-c#/yjmpd.sql | 64 +++++++ 25 files changed, 868 insertions(+) create mode 100644 yjmpd-c#/config.cfg create mode 100644 yjmpd-c#/requirements.txt create mode 100644 yjmpd-c#/scan.py create mode 100644 yjmpd-c#/test.py create mode 100644 yjmpd-c#/yjdaemon/API.py create mode 100644 yjmpd-c#/yjdaemon/Database.py create mode 100644 yjmpd-c#/yjdaemon/HTTPServer.py create mode 100644 yjmpd-c#/yjdaemon/ServiceSocket.py create mode 100644 yjmpd-c#/yjdaemon/__init__.py create mode 100644 yjmpd-c#/yjdaemon/__pycache__/API.cpython-34.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/API.cpython-35.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/Database.cpython-34.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/Database.cpython-35.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/HTTPServer.cpython-34.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/HTTPServer.cpython-35.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/__init__.cpython-34.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/__init__.cpython-35.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/libraryscanner.cpython-34.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/libraryscanner.cpython-35.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/yjmpd.cpython-34.pyc create mode 100644 yjmpd-c#/yjdaemon/__pycache__/yjmpd.cpython-35.pyc create mode 100755 yjmpd-c#/yjdaemon/libraryscanner.py create mode 100644 yjmpd-c#/yjdaemon/maindaemon.py create mode 100644 yjmpd-c#/yjdaemon/yjmpd.py create mode 100644 yjmpd-c#/yjmpd.sql diff --git a/yjmpd-c#/config.cfg b/yjmpd-c#/config.cfg new file mode 100644 index 0000000..d4b4f20 --- /dev/null +++ b/yjmpd-c#/config.cfg @@ -0,0 +1,16 @@ +[HTTP] +port = 8585 + +[Daemon] +port = 17170 + +[Library] +musicdir = /media/USBHDD/shares/Music/English +jancodir = /mnt/Muziek/ + +[Database] +username = yjmpd +password = qLjStxr6xncrfcna +host = imegumii.nl +database = yjmpd +port = 3306 diff --git a/yjmpd-c#/requirements.txt b/yjmpd-c#/requirements.txt new file mode 100644 index 0000000..96bc99c --- /dev/null +++ b/yjmpd-c#/requirements.txt @@ -0,0 +1,3 @@ +configparser +mutagen +PyMySQL \ No newline at end of file diff --git a/yjmpd-c#/scan.py b/yjmpd-c#/scan.py new file mode 100644 index 0000000..778d0dd --- /dev/null +++ b/yjmpd-c#/scan.py @@ -0,0 +1,72 @@ +import sys +import os +import configparser + +from yjdaemon.yjmpd import YJMPD +from yjdaemon.HTTPServer import HTTPServerThread +from yjdaemon.Database import Database +from yjdaemon.libraryscanner import LibraryScanner + +debug = True + +config = configparser.ConfigParser() +try: + config.read("config.cfg") + HTTP_PORT = int(config.get("HTTP", "port")) + DAEMON_PORT = int(config.get("Daemon","port")) + MUSIC_DIR = str(config.get("Library", "musicdir")) + DB_USERNAME = config.get("Database", "username") + DB_PASSWORD = config.get("Database", "password") + DB_HOST = config.get("Database", "host") + DB_DATABASE = config.get("Database", "database") + DB_PORT = config.getint("Database", "port") +except Exception as e: + print(e.with_traceback()) + sys.exit(1) + +class MainDaemon(YJMPD): + def run(self): + HTTP_thread = HTTPServerThread(HTTP_PORT) + HTTP_thread.start() + + +def Test(): + print("Test") + + # socket_thread = ServiceSocket(DAEMON_PORT) + # socket_thread.start() + #HTTP_thread = HTTPServerThread(HTTP_PORT) + #HTTP_thread.start() + db = Database(DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_DATABASE) + LibraryScanner(db, MUSIC_DIR) + + + + + +if debug: + Test() +else: + if __name__ == "__main__": + username = os.getenv('USER') + if None == username: + dir = "/tmp/.pydaemon.pid" + else: + dir = "/home/" + username + "/.pydaemon.pid" + daemon = MainDaemon(dir, MUSIC_DIR) + if len(sys.argv) == 2: + if 'start' == sys.argv[1]: + daemon.start() + elif 'stop' == sys.argv[1]: + daemon.stop() + elif 'restart' == sys.argv[1]: + daemon.restart() + elif 'status' == sys.argv[1]: + daemon.status() + else: + print("Unknown command") + sys.exit(2) + sys.exit(0) + else: + print("usage: %s start|stop|status|restart" % sys.argv[0]) + sys.exit(2) diff --git a/yjmpd-c#/test.py b/yjmpd-c#/test.py new file mode 100644 index 0000000..446975e --- /dev/null +++ b/yjmpd-c#/test.py @@ -0,0 +1,72 @@ +import sys +import os +import configparser + +from yjdaemon.yjmpd import YJMPD +from yjdaemon.HTTPServer import HTTPServerThread +from yjdaemon.Database import Database +from yjdaemon.libraryscanner import LibraryScanner + +debug = True + +config = configparser.ConfigParser() +try: + config.read("config.cfg") + HTTP_PORT = int(config.get("HTTP", "port")) + DAEMON_PORT = int(config.get("Daemon","port")) + MUSIC_DIR = str(config.get("Library", "musicdir")) + DB_USERNAME = config.get("Database", "username") + DB_PASSWORD = config.get("Database", "password") + DB_HOST = config.get("Database", "host") + DB_DATABASE = config.get("Database", "database") + DB_PORT = config.getint("Database", "port") +except Exception as e: + print(e.with_traceback()) + sys.exit(1) + +class MainDaemon(YJMPD): + def run(self): + HTTP_thread = HTTPServerThread(HTTP_PORT) + HTTP_thread.start() + + +def Test(): + print("Test") + + # socket_thread = ServiceSocket(DAEMON_PORT) + # socket_thread.start() + HTTP_thread = HTTPServerThread(HTTP_PORT) + HTTP_thread.start() + #db = Database(DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_DATABASE) + #LibraryScanner(db, MUSIC_DIR) + + + + + +if debug: + Test() +else: + if __name__ == "__main__": + username = os.getenv('USER') + if None == username: + dir = "/tmp/.pydaemon.pid" + else: + dir = "/home/" + username + "/.pydaemon.pid" + daemon = MainDaemon(dir, MUSIC_DIR) + if len(sys.argv) == 2: + if 'start' == sys.argv[1]: + daemon.start() + elif 'stop' == sys.argv[1]: + daemon.stop() + elif 'restart' == sys.argv[1]: + daemon.restart() + elif 'status' == sys.argv[1]: + daemon.status() + else: + print("Unknown command") + sys.exit(2) + sys.exit(0) + else: + print("usage: %s start|stop|status|restart" % sys.argv[0]) + sys.exit(2) diff --git a/yjmpd-c#/yjdaemon/API.py b/yjmpd-c#/yjdaemon/API.py new file mode 100644 index 0000000..1c6d5f1 --- /dev/null +++ b/yjmpd-c#/yjdaemon/API.py @@ -0,0 +1,169 @@ +import json + +from yjdaemon.Database import Database as db +import configparser +""" +Add a key to the validAPIcalls dictionary, with a corresponding function +Function should return jsonified data, so that it can then be passed on to the client. +example: + +Add this to the dictionary +"getsongs": calls.getsongs + +then implement this function +@staticmethod + def getsongs(): + return calls.jsonify({"song": song}) + +And the jsonified data will be returned to the client. + +Every function MUST return jsonified data! + +""" + + +class calls: + @staticmethod + def APIcall(sanitizedpath): + if sanitizedpath in validAPIcalls: + return validAPIcalls[sanitizedpath] + else: + return None + + @staticmethod + def getfromrawjson(data, param): + return calls.dejsonify(data)[param] + + @staticmethod + def jsonify(data): + return json.dumps(data, sort_keys=True, indent=4).encode("utf-8") + + @staticmethod + def dejsonify(rawdata): + return json.loads(rawdata.decode("utf-8")) + + @staticmethod + def getallsongs(args): + return calls.jsonify({"songs": db.executequerystatic("SELECT * FROM tracks;"), "args": args , "result": "OK"}) + + @staticmethod + def getartists(args): + return calls.jsonify({"artists": db.executequerystatic("SELECT artistName FROM tracks GROUP BY artistName;"), "args": args, "result" : "OK"}) + + @staticmethod + def getalbums(args): + return calls.jsonify({"albums": db.executequerystatic("SELECT albumName FROM tracks GROUP BY albumName;"), "args": args, "result":"OK"}) + + @staticmethod + def getgenres(args): + return calls.jsonify({"genres": db.executequerystatic("SELECT genre FROM tracks GROUP BY genre;"), "args": args, "result":"OK"}) + + @staticmethod + def getyears(args): + return calls.jsonify({"years": db.executequerystatic("SELECT year FROM tracks GROUP BY year;"), "args": args, "result":"OK"}) + + @staticmethod + def getalbumnames(args): + return calls.jsonify({"albumnames": db.executequerystatic("SELECT albumName FROM tracks GROUP BY albumName;"), "args": args}) + + @staticmethod + def search(args): + """search? + q(gneeral search term)=iets& + artist=bleh + &genre=neeee""" + data = args.split("&") + songnames = """ + SELECT * FROM tracks WHERE LCASE(trackName) LIKE LCASE(\"%{}%\") GROUP BY trackName; + """ + artists = """ + SELECT artistName FROM tracks WHERE LCASE(artistName) LIKE LCASE(\"%{}%\") GROUP BY artistName; + """ + genres = """ + SELECT genre FROM tracks WHERE LCASE(genre) LIKE LCASE(\"%{}%\") GROUP BY genre; + """ + albums = """ + SELECT albumName FROM tracks WHERE LCASE(albumName) LIKE LCASE(\"%{}%\") GROUP BY albumName; + """ + genquery=""" + SELECT * FROM tracks WHERE LCASE(genre) LIKE LCASE(\"%{}%\") OR LCASE(albumName) LIKE LCASE(\"%{}%\") OR LCASE(trackName) LIKE LCASE(\"%{}%\") OR LCASE(artistName) LIKE LCASE(\"%{}%\"); + """ + genqueryres = "" + artistsres = "" + genresres = "" + songnamesres = "" + albumsres = "" + for entry in data: + values = entry.split("=") + if values[0] == "q": + genqueryres = genquery.format( values[1], values[1], values[1], values[1]) + elif values[0] == "artist": + artistsres = artists.format(values[1]) + elif values[0] == "genre": + genresres = genres.format(values[1]) + elif values[0] == "songname": + songnamesres = songnames.format(values[1]) + elif values[0] == "album": + albumsres = albums.format(values[1]) + return calls.jsonify({"result":"OK", "genres": db.executequerystatic(genresres), "songnames": db.executequerystatic(songnamesres), "artists": db.executequerystatic(artistsres), "albums": db.executequerystatic(albumsres), "query" : db.executequerystatic(genqueryres)}) + return calls.jsonify({"result":"OK"}) + + @staticmethod + def getsongs(args): + """Get song by album, genre, year, artist""" + data = args.partition("?") + splitstring = data[0].partition("=") + name= splitstring[0] + value = splitstring[2].replace("%20"," ") + if name == "album": + songs = db.executequerystatic("SELECT * FROM tracks WHERE albumName = \"" + value + "\"") + elif name == "genre": + songs = db.executequerystatic("SELECT * FROM tracks WHERE genre = \"" + value + "\"") + elif name == "year": + songs = db.executequerystatic("SELECT * FROM tracks WHERE year = \"" + value + "\"") + elif name == "artist": + songs = db.executequerystatic("SELECT * FROM tracks WHERE artistName = \"" + value + "\"") + elif name == "search": + songs = db.executequerystatic("SELECT * FROM tracks WHERE LCASE(trackName) LIKE LCASE(\"%"+ value + "%\") GROUP BY trackName") + else: + return calls.jsonify({"result":"NOK", "errormsg": "Not a valid argument"}) + return calls.jsonify({"result":"OK","songs":songs}) + + @staticmethod + def getsongbyid(args): + config = configparser.ConfigParser() + try: + config.read("config.cfg") + musicdir = config.get("Library","musicdir") + port = config.get("HTTP","port") + except: + return calls.jsonify({"result" : "NOK" , "errormsg" : "I/O error while reading config."}) + data = args.split("&") + splitsting = data[0].split("=") + id = splitsting[1] + file = db.executequerystatic( + "SELECT SUBSTRING_INDEX(trackUrl,'" + musicdir + "',-1) as filedir FROM `tracks` WHERE id = " + id) + try: + url = str(file[0][0]) + except: + return calls.jsonify({"result": "NOK", "errormsg" : "Song ID does not exist in database."}) + return calls.jsonify({"result": "OK", "songurl": "http://imegumii.nl:"+ "/music/English"+ url}) + + @staticmethod + def setsong(args, songname): + global song + song = songname + return calls.jsonify({"result": "OK", "args": args}) + + +validAPIcalls = {"getallsongs": calls.getallsongs, + "setsong": calls.setsong, + "search": calls.search, + "getsongs": calls.getsongs, + "getsongbyid": calls.getsongbyid, + "getartists": calls.getartists, + "getalbums": calls.getalbums, + "getgenres": calls.getgenres, + "getyears": calls.getyears, + "getalbumnames": calls.getalbumnames + } diff --git a/yjmpd-c#/yjdaemon/Database.py b/yjmpd-c#/yjdaemon/Database.py new file mode 100644 index 0000000..69234a5 --- /dev/null +++ b/yjmpd-c#/yjdaemon/Database.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import pymysql +import configparser +from threading import RLock as Lock +from warnings import filterwarnings + +filterwarnings('ignore', category = pymysql.Warning) +class Database: + + buffer = [] + cout = 0 + lock = Lock() + def __init__(self, DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_DATABASE): + """Init class """ + self.cnx = pymysql.connect(user=DB_USERNAME, password=DB_PASSWORD, host=DB_HOST, database=DB_DATABASE, port=DB_PORT, charset='utf8') + self.cursor = self.cnx.cursor() + + def executeQuery(self, query): + try: + self.cursor.execute(query) + self.cnx.commit() + return self.cursor.fetchall() + except pymysql.ProgrammingError as e: + print(e) + print(query) + + @staticmethod + def executequerystatic(query): + config = configparser.ConfigParser() + try: + config.read("config.cfg") + DB_USERNAME = config.get("Database", "username") + DB_PASSWORD = config.get("Database", "password") + DB_HOST = config.get("Database", "host") + DB_DATABASE = config.get("Database", "database") + DB_PORT = config.getint("Database", "port") + except Exception as e: + print(e) + + cnx = pymysql.connect(user=DB_USERNAME, password=DB_PASSWORD, host=DB_HOST, database=DB_DATABASE, port=DB_PORT) + cursor = cnx.cursor() + if query != "": + cursor.execute(query) + cnx.commit() + return cursor.fetchall() + else: + return [] + + + def turnoffautocommit(self): + self.cursor.execute("SET autocommit=0;") + self.cnx.commit() + + def removeSong(self, path): + self.db.executeQuery("DELETE FROM `tracks` WHERE trackUrl = " + path.replace("'", '\\\'')) + + def updateInsertSong(self, genre, trackname, artistname, albumname, albumartist, tracknumber, year, duration, path): #notworking + self.db.executeQuery("INSERT INTO `tracks` (`genre`, `trackUrl`, `trackName`, `artistName`, `albumName`, `albumArtist`, `trackNumber`, `year`, `duration`) VALUES ('" + genre + b"','" + path.replace("'", '\\\'') + "','" + trackname + "','" + artistname + "','" + albumname + "','" + albumartist + "','" + tracknumber + "','" + year + "','0') " + + b" ON DUPLICATE KEY UPDATE `genre`=VALUES(`genre`) , `trackName` = VALUES(`trackName`) , `artistName` = VALUES(`artistName`) ,`albumName` = VALUES(`albumName`) , `albumArtist` = VALUES(`albumArtist`) , `trackNumber` = VALUES(`trackNumber`) , `year` = VALUES(`year`) , `duration` = VALUES(`duration`)") + + def insertMultipleSongs(self, genre, trackname, artistname, albumname, albumartist, tracknumber, year, duration, path): + self.buffer.append([genre, trackname, artistname, albumname, albumartist, tracknumber, year, duration, path]) + # Database.lock.acquire() + if len(self.buffer) > 50: + self.pushbuffer() + # Database.lock.release() + + def pushbuffer(self): + query = ('INSERT INTO `tracks` (`genre`, `trackUrl`, `trackName`, `artistName`, `albumName`, `albumArtist`, `trackNumber`, `year`, `duration`) VALUES ') + for song in self.buffer: + query += "(" + for field in song: + query += "'" + field + "'," + query = query[:-1] + query += ")," + query = query[:-1] + query += " ON DUPLICATE KEY UPDATE `genre`=VALUES(`genre`) , `trackName` = VALUES(`trackName`) , `artistName` = VALUES(`artistName`) ,`albumName` = VALUES(`albumName`) , `albumArtist` = VALUES(`albumArtist`) , `trackNumber` = VALUES(`trackNumber`) , `year` = VALUES(`year`) , `duration` = VALUES(`duration`);" + self.executeQuery(query) + del self.buffer[:] + diff --git a/yjmpd-c#/yjdaemon/HTTPServer.py b/yjmpd-c#/yjdaemon/HTTPServer.py new file mode 100644 index 0000000..8fa28a2 --- /dev/null +++ b/yjmpd-c#/yjdaemon/HTTPServer.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + + +import http.server +import socketserver +import threading +import html + +import yjdaemon.API as API + +""" +HTTP request handler. +""" + + +class HTTPServerThread(threading.Thread): + def __init__(self, PORT): + threading.Thread.__init__(self) + self.PORT = PORT + + def run(self): + Handler = HTTPHandler + socketserver.TCPServer.allow_reuse_address = True + httpd = socketserver.TCPServer(("", self.PORT), Handler) + httpd.serve_forever() + + +""" +REST API +""" + + +class HTTPHandler(http.server.SimpleHTTPRequestHandler): + def send_message(self, status, content_type, data): + self.send_response(status) + self.send_header("Content-type:", content_type) + self.end_headers() + self.wfile.write(data) + self.wfile.write("\n".encode("utf-8")) + + def do_GET(self): + """ Serve a GET request. """ + path = html.unescape(self.path) + path = str(path).lstrip("/").split("?")[0] + retval = API.calls.APIcall(path) + if retval is not None: # if call is valid API function + try: + args = str(html.unescape(self.path)).lstrip("/").split("?")[1] + except IndexError as e: + print(e) + self.send_message(403, "application/json", API.calls.jsonify({"error": "Missing parameters."})) + return + self.send_message(200, "application/json", retval(args)) + else: # else parse as normal HTTP request + f = self.send_head() + if f: + try: + self.copyfile(f, self.wfile) + finally: + f.close() + + def do_POST(self): + """ Serve a POST request. """ + path = html.unescape(self.path) + path = str(path).lstrip("/").split("?")[0] + retval = API.calls.APIcall(path) + if retval is not None: # if call is valid API function + try: + args = str(html.unescape(self.path)).lstrip("/").split("?")[1] #if no args + except IndexError as e: + print(e) + self.send_message(403, "application/json", API.calls.jsonify({"error": "Missing parameters."})) + return + if self.headers["Content-Type"] == 'application/json': # if data is json data + length = int(self.headers["Content-Length"]) + rawdata = self.rfile.read(length) + result = retval(args, API.calls.getfromrawjson(rawdata, "data")) + self.send_message(200, "application/json", result) + else: # if not json data + self.send_message(422, "application/json", API.calls.jsonify({"error": "Not JSON data."})) + else: # if not API function + self.send_message(403, "application/json", API.calls.jsonify({"error": "Not an API function"})) diff --git a/yjmpd-c#/yjdaemon/ServiceSocket.py b/yjmpd-c#/yjdaemon/ServiceSocket.py new file mode 100644 index 0000000..ee848b0 --- /dev/null +++ b/yjmpd-c#/yjdaemon/ServiceSocket.py @@ -0,0 +1,36 @@ +import socket +import threading + +endline = "\r\n" + +class ServiceSocket(threading.Thread): + def __init__(self, PORT): + threading.Thread.__init__(self) + self.PORT = PORT + + def run(self): + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.s.bind(("", self.PORT)) + self.s.listen(1) + while 1: + conn, addr = self.s.accept() + ClientHandler(conn).start() + + + +class ClientHandler(threading.Thread): + def __init__(self,conn): + threading.Thread.__init__(self) + self.conn = conn + def run(self): + while 1: + data = self.conn.recv(1024) + if not data: + break + else: + datastring = data.decode('utf-8', "ignore").rstrip(endline).lower() + print(datastring) + + + diff --git a/yjmpd-c#/yjdaemon/__init__.py b/yjmpd-c#/yjdaemon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yjmpd-c#/yjdaemon/__pycache__/API.cpython-34.pyc b/yjmpd-c#/yjdaemon/__pycache__/API.cpython-34.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6ce791c51d2bcbb5730987e7b1e91532240fc62 GIT binary patch literal 1448 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 b78btHs{NA8(2>!Ui3(L literal 0 HcmV?d00001 diff --git a/yjmpd-c#/yjdaemon/__pycache__/Database.cpython-34.pyc b/yjmpd-c#/yjdaemon/__pycache__/Database.cpython-34.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82b518092ad6935652206b4f6a03c503361573ab GIT binary patch literal 3480 zcmeHK&2!U66yKHQ&p07z3IV1cq8~VdKp5H?4irjg+>&YHgg7C=Q^rvw#c_}&XC)Qv zF}&z=5f~1SR=zY$ui8-StrXHS;pNgb9ClpnakKgjvaB% zzj1QR$+I7!nP*2BmZ$0JX5l5~W;K(`?D}$GKLsyKkRkJ_2WmO2mhX=(`R&NCJu3_ismEAV z7P@FW3xp%}{UD5F!5JDP^==@dnyJftx7X=~hn~#af$zI^Br~@ET;}Xpgn=+~QV(5k zPmV3#Z*JDB>$QcIs%oq))azTT>x(iE$%EDUhGN!M*Ef`SabaWO{zAR_I}_*N;j5eT z`$5N@Z+F~Q+-bMx*F5!GbdHK6mXHhjoHlIXmcd+L0(zBjXCu zn1tsoVWHP=RfPzI%yvcFkIal9%VqX3c12HeSK*eB84{F-6F(&RJLm~%QpPiUl27sT zyu|xwQ)EVUZ905Zs3xQoI+wr@6vs#J4QP~KTCG1-uWlGt90iFhcW&G^b>*tKfGL!l z;vzVcD+f-SJr%{m5BBy>W+3HcH(`1IVrugnis`nJ(OG=T@K3;u8jhVoJS0(!EHXom zDBJZ~|MFsWxw=s`zFuElF?J$h*-yhAW9vb6y=th|rtpkA#w%XwYu{DOjMSW6@gWwF zdEs_F%XUpI*%rNPMf<0HD+!$0@7;Rfw?4q2P#r_wpH5vmk=rFGP@y{Y1$LhHWd@$i z42sGpl>x{nq<|Tm#iY&*YkgQlrsTO{okvYP>J*DYt^dnX4e`5SEY&tvPq3KXX}P{| zcjks&03_vWR!0de5w*i8ZF#$K=e5RylAjEVJG-u+X3w=$?Zm>0+JV1g8jlv1H>-7H zwh~gTR^}>#4kDr7&!E-MgWRZ?Mrh)+8ml#9adT~X>8k~7;pL>zxxOrQ<8VI_4AB#pp)M*!W{H?Yt`NCOWRA!+B5#9ax~;%iiM^=Z z^;C`sKZSCrUIF~7c9!?wOL2LNvW);xMF&kY#&2lA#}}ME-yUML_+*9dBjAM8af>7d zA-4GBJbMAu&<#pS0||q~hZVL(y_l%Le$vo_-GUxhs;JA1c(ECNa4}v!%a)8Y4wO!9Sl`F(&P@vEW>0QWp4v&BmALr*#PbhwR z^aec%%9=SrFG9*z&893ho1MUkJ>tvF=3#7ki9}h}(Cr|Oq)vYyr0#)=^H?m&j|OoR zisCwv&xw#xBZiV`f?i#MqP2i8d>5pwTw5Lr0>+al=7lliUWUyK^~wl!tpi^R3Nq#x4iWdJj&P4j;Mzvx$? literal 0 HcmV?d00001 diff --git a/yjmpd-c#/yjdaemon/__pycache__/Database.cpython-35.pyc b/yjmpd-c#/yjdaemon/__pycache__/Database.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea5bc605bc2b6b95cde0762b3b1a1474dd7e4fb9 GIT binary patch literal 4190 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 literal 0 HcmV?d00001 diff --git a/yjmpd-c#/yjdaemon/__pycache__/HTTPServer.cpython-35.pyc b/yjmpd-c#/yjdaemon/__pycache__/HTTPServer.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85640e59b6bae1d42e63ae6c2427283250331e64 GIT binary patch literal 2994 zcmd5;OOG2x5bpLo{N5+o&5{sILLdg>l~yQ;0EM6^O2Wfg#THrSVvT0nyW<_tINj|e zYwdDb{s9*boRRn^{DwJj;S})?!iBGT?2WYv5*H-K?wXpJ?yByp@2l>$dcE@Jg=^nl zt1|Wno4ai2x6srxh!B6yV#Yi^(+kWi%yf%+)=amVXU}wp4P55A>~4sz7W)BKEG8^A zD6(UWDd7Y9I_87J1ZRUX8&u4?FSFL%|d1?5%;AFi@!2i!tKTN&AoIeHlv}~9S@^u zb6chZ5oon}Fc^-)tHC8Gp)ZDMviWklt z*pKrsCZe(qYjeKHx+I5^@UVnt;$7o4+q?FTM zNQ=xBNsxx3X`5_juF}39E3X#hj(X%MvqN9|bI~@Ltr-tPz{B4a7qMtnfndB2j8_Qf zXXo(Nn(ur9b0XL(nu*&rfIsIig#`iP1bJ;u!-yy$gm_7TF<%j$4eH@iT^f|8{c`-H zhU}M_{EHoIk%?1w$;zwd9GJ7%qhyEKy3F<~O#aUJkadF zjfEu^mRV%xhYHEzzVqliNc>^pdzV%3ThaEahZt=;w zdAUH3G;-0P=MhZe#bC)GXooyCtCO{cDUc1naraKQA;sgdP`cH4!8c#<&n194tz}FGV)j*|5<_IZMIa&QCQVON9G4ds7sD)Im=f7cGb2+p3^f1m!<3y;y zCs2%hy_Z=^%gl`-qfzE4nAMr}#rFNo2~bE>R)kJkR=J;q;@KT?lQ|<9C0ee-a8@M$ zQUAazpCHyN1?lL3P%2lbixb4D5>3}YRK88GTq*PuKhA7l?y8K7jQ3CD(+$%eg3&~G z)qr4y6@HPgpa5BX3-=1Y%-8w(LJdX9;_HRU$~<24!6!(i`a)&QAob_}0I7=q?~qz& zVTEk~s`~{dzv3D|1PbxC$~K6cGgJbK03^L}r>LItVwoL+CN+XBZA`#?&K;P5Zq5?? z@lU1-f>8^vFlup*QOV`m=vSGyG}q7YVR^EC!iVh#ojF3tvq+-cAYn)$--P-Pq>$(E z?X{Gc)MjUkZ+*Ed;xKv3AC6 z$<3NqrE>`O8#*s~C|Y_XYh=bEK|%Ivv!hxIDaa#BJGeP0ch)DB{4NOKTkhCJ~J<~BtBlRpz;=nO>TZlX-=vg K$hcx4W&i*S1|U8F literal 0 HcmV?d00001 diff --git a/yjmpd-c#/yjdaemon/__pycache__/libraryscanner.cpython-34.pyc b/yjmpd-c#/yjdaemon/__pycache__/libraryscanner.cpython-34.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7689ef09ebc74b20319f07cc77015acff19b4da GIT binary patch literal 4253 zcmb_fOLH7a5w4o<(Y#tOdnI9P*0T$&+^{<`Xo)ZpYp}7jwpcSZmY);e7`3Krq#pNl zOVvHHM`54Dks$aP@PoM2l@kZJaMOt|v**#$iW9V^QdL=5)tOmeW@Z1&nXP_y*?Iow zSt0%|a*vJv9kgTs;^QYpC`4b1mK2i41rZiR%i=loZPBv1Z;7xd`ZJ<6lgZg4EQx+u zw8}!>_lv@x5r4q)8F7N;6>;A$3BSy&vQTAlVu@CjcHed@|G=w-D@|$BONLt;>rM2_ zk9HHKkCaA#`RgE5+ruPP{mmm4r4PKw50&oFvhh}pRBw>w@GK?e)3qsK-oqTNGF zo`AeBi1$LY(UII?+AD|?A!KUNzYmspZ;75Q(jrNmWDS@z08Ec?JQ}B0 zvkbkM&LrY$Rr0gj8ttZ6_{IvrANg5VJkKHTZt&7waJ$5dHFu(Ebb@AE~AEfwD zVE03Oh7JK|1enUhUx*jJfTykI@gkpa5bf1R#?+7J>l2TM!$zY4rxy=&5T&{Vg>)Ij zET#h;#oewqNaIf2?*}RTml*r47rrqia)=*j*Vg0#W9v9hO${74G1h@fOr?wSiM+l_ z*y9;lpzSLlOK1tbN64kZto&3SUp?oz{Mk>yIfDh4(Aat3gG=8nN|?OyZAFei2>7ul z{YCN<>`B>;04L!X{)E45G5iZ-6~V*tb0Vco;Xj+!e8~J<61_5^xmOYIIl{0K0zJe! z2N?|~vlZHRI;@@@*3J%{v%}f5!#NuI^Wvl;NK#Yu!rejNR1TEx#=5VxsioTMyom<=T?}mBOO=r~kC!&Jjy0g<`GhXyyL#&F zxt9JuPDo!Rat*|tH^nf1i54{%#Pa z!2wcc9PK5hyr)We%VTirpAhdR5y}fWlrCdP>L;X{kh5|Lf0kU3j{KusmdDE%;1&4qkP-Ayx}p@p zP@M-u!T@fT;K4@*0QRtaDll1yizvTv z!e?Y&AkPrJfX`UHfX|@4fY0d7_;b)}K1*LOqN~D2MD;j|5JCjpDgpOZB*jZkO$*KB zPw>xHb9?j2)B0BP>7)7%<1E>!-`HV*?tDICJ=5W&-$YKKL59}Jh%kF*aF@|MUB>98 z;ZS*+{`&*%r9m9+xb^3EA3odMuHRVI*P&8EXSn+LzbR#}>N+Tgi9JZWxAZ3%>Q9OM zkjRgS{FumRAPL=^t2bBOdg5Yr{ZX^N@$B)#t$TN$Zq~oqd{uw;c!Rnl^KNJF9b4+HpQ_&vlpg9Y$hQp;GozwTjF?^{4u|Z|sP}UaBptek z{%bUwYL-wak=%e_c5fq`9r-uu6cEl=&V}<-eiG4y3ThnDl))%$@k9J_P*at;?aHQG z$dXu9ql7r(mQKx#~?4z4GS#ifSMNrBPT<_GDONfkuk%&sB* zE`7Js8s830w?ai)gf!7^n`*n=kEvip-)XlG2VR)X=tbrti%PGD zJj-?K38DlaBwSt*AH%G?;`b)VFlf{^fMYH>th4!VKzX?}cYEBTk_ zWd838RIzyn^!qE2Ki+G^qc8C!`<9%a mOz>eRnxM_0%x}6#F;o63%O=0FNIS~uR1i8S3l|EP*Z%{h>!P3l literal 0 HcmV?d00001 diff --git a/yjmpd-c#/yjdaemon/__pycache__/libraryscanner.cpython-35.pyc b/yjmpd-c#/yjdaemon/__pycache__/libraryscanner.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8eccc5e1ca07faf4694d68c33790dea8ecea0ba GIT binary patch literal 4335 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 MToG|w$-nl$0rm9)=>Px# literal 0 HcmV?d00001 diff --git a/yjmpd-c#/yjdaemon/__pycache__/yjmpd.cpython-35.pyc b/yjmpd-c#/yjdaemon/__pycache__/yjmpd.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd11cffa23d92b0133fe061250fc2ab8ef5a4c9f GIT binary patch literal 3716 zcmcIn&5j$#5w4yYl1nbR|J1G!N1i}NB4#aatN@9j7`6q=F_gfxBF0{6FU*KDOAeQu zp}L2XIo|a}xdaFh5FbqL6nnb5tE;=d`l|Oz zqfz_slUtva)`cF|@?Cx-I7`7^Y62<>F(Z@jf2&6e7TKvIHB)axlBF1hYQ=N^F(wmC3J28;uq6 ztJ1EJUn752+UTs4-;j2d{5A5|rHug_hifJ!9Innv2h;#v25;T{C+uy>2oLUK;3nk?+P&1Fb_p5WSv7H(Kr_NQSZB z?(-S*hktqNAK)=pAWjvX67|rqq#?)9ZK+Pn$|}>)KMu!1=IA(1T6X4`V0^6EhISzS zMY@ciuWua;<4JH3PJ+Q~5{3u&bvz3Ci8+{$CR6`r|2i7^uJWGtbe^sBdSMhMyNPD5|D^}oB!SiE0;&&4}n71e)8Yy~CbcQ{^Z$&2#=^K<-G(rTSQlt)} z-;$F!bW$Vzl+>9;D&3bMB^n`y7+c}JV=NCZ<^n!E_mMBr$D}VUdKf8s$ZM|fL5-^U zc(iKlgJKQhnw$An@BZ`c-tDMYy@QY6$vf<(PNPQd5MD69cv+Lu$&?K`wQ01;Ci!r-iZ+bMmZ5gesj<2YJ*S2b%h`*zsJCgfBS+cHkMfGFx63Qx z^bu6J$S1wWnT8pE*`u>Eot8On9PcXaN|X?!-#vH;*=^O*H}3`>(kD!VB7@vdJNF*C zemr|T4&38dpCBjtLoW)=q^%iV>5lYXf6G1g0CxYj`^C@BYG1Lcv(VkTpv#m_EpI=y zp6zF<*1DQmvB?~^lG$dCX7G8KWR*|4A8H-ztZWh=iJ6t2=`abhG6qh(BrElYeyAmp zFeFG!=x3$b#4{%{Bc5T%@pxv2nG;WgD69Cv(`YsxXO2Ic-hvr88^u`(*25?(dx@;N zqJu$bk^sy4!59e)mlC`Tf&?#;yRC{YVRM-+T6lJ3nGjEmC&<*XBy}!0?Xqf~yExKK zyqen(WZSB)>ei;(QdgC0Z>XATywulF4QTPTURU_q8`hR;s&qZi@TJZEBE|W4NliO= z$@7~LAnB=mLWDs~wyr~)l{I)0jA!GerDRJld9Gck-o<0sE)u_9tStw#T$ssc1J4I| z%s(OKZ}Zx`u&@e4?{&c=C{i)E0HGsifm4eP!6HE5VYCOTDGAj)Af*(#K^>2nH^4CZ z&s>%O(_PL!ptvGbg5;_eYXQv+Xx5=xl;nlISmmV@x9ZY@-EOO#Zg&$;Cl`uATi4)! zC;dUe+ze&c8|%RH=PvMJlD3=f-OVe^q92ta3Zub$HC@HzRzqLKj~r`ehfxCd$8^S- z4yRe=-Y0@;jVex>0im~8Y_nKr!8D!OFxql*oLSRjeVscilfZB#dnrt88XLKTA!ZMP z99uQ5>wwRyRaUmDsMi3lno3vm@X6?9^tb~35RXLf*8tJ?6Fu|%|BN07XTDFLzd=cj zLx;#GIVUq?uF(h_oQL+wpXl%~`YSNAA(**?0^k6pPbk?GR^1XFEYT@wb6c1jc&td3 zTUTs-hepj8*wF{ldF4fqid;%|FPK{ZEGN~x$lqfU_rOgL{B*n#G;R>gj ztn~rJFt<^>W)6BEiL?IQ4%#if67F#Tr_CGImo*L{rZiZ{mX!}X3)yd{uDez-%=Ko3*C z@HXnO?>?T(KL%_o>6npoa_|N~=Y%XHlnB=X-hNO@dx8 ztMz)5*q@ELUGMdt%)D`-sq}h&-0$@?AD-~NR9eAVj<{d3;vE*e);YeHNbs#fGmv<` z;b_(EvQ>BLH|uu2S-)1V)K^50^pmJcxPQ*GuCaKF1<$|CaJKSJ?)JMJH^cQ1H 0: + # exit first parent + sys.exit(0) + except OSError as err: + sys.stderr.write('fork #1 failed: {0}\n'.format(err)) + sys.exit(1) + + # decouple from parent environment + os.chdir(self.root) + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError as err: + sys.stderr.write('fork #2 failed: {0}\n'.format(err)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = open(os.devnull, 'r') + so = open(os.devnull, 'a+') + se = open(os.devnull, 'a+') + + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # write pidfile + atexit.register(self.delpid) + + pid = str(os.getpid()) + with open(self.pidfile, 'w+') as f: + f.write(pid + '\n') + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """Start the yjdaemon.""" + + # Check for a pidfile to see if the yjdaemon already runs + try: + with open(self.pidfile, 'r') as pf: + + pid = int(pf.read().strip()) + except IOError: + pid = None + + if pid: + message = "pidfile {0} already exist. " + \ + "Daemon already running?\n" + sys.stderr.write(message.format(self.pidfile)) + sys.exit(1) + + # Start the yjdaemon + self.daemonize() + self.run() + + def stop(self): + """Stop the yjdaemon.""" + + # Get the pid from the pidfile + try: + with open(self.pidfile, 'r') as pf: + pid = int(pf.read().strip()) + except IOError: + pid = None + + if not pid: + message = "pidfile {0} does not exist. " + \ + "Daemon not running?\n" + sys.stderr.write(message.format(self.pidfile)) + return # not an error in a restart + + # Try killing the yjdaemon process + try: + while 1: + os.kill(pid, signal.SIGTERM) + time.sleep(0.1) + except OSError as err: + e = str(err.args) + if e.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print(str(err.args)) + sys.exit(1) + + def restart(self): + """Restart the yjdaemon.""" + self.stop() + self.start() + + + + def status(self): + """Print out status.""" + if os.path.exists(self.pidfile): + message = "Daemon running.\n" + else: + message = "Daemon not running.\n" + sys.stdout.write(message) + + def run(self): + """You should override this method when you subclass Daemon. + + It will be called after the process has been daemonized by + start() or restart().""" diff --git a/yjmpd-c#/yjmpd.sql b/yjmpd-c#/yjmpd.sql new file mode 100644 index 0000000..e0e60be --- /dev/null +++ b/yjmpd-c#/yjmpd.sql @@ -0,0 +1,64 @@ +-- phpMyAdmin SQL Dump +-- version 4.3.8deb0.1 +-- http://www.phpmyadmin.net +-- +-- Host: localhost +-- Gegenereerd op: 27 sep 2015 om 22:19 +-- Serverversie: 5.6.19-0ubuntu0.14.04.1 +-- PHP-versie: 5.5.9-1ubuntu4.11 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + +-- +-- Database: `yjmpd` +-- + +-- -------------------------------------------------------- + +-- +-- Tabelstructuur voor tabel `tracks` +-- + +CREATE TABLE IF NOT EXISTS `tracks` ( + `id` int(11) NOT NULL, + `genre` varchar(100) DEFAULT NULL, + `trackUrl` varchar(200) NOT NULL, + `trackName` varchar(100) DEFAULT NULL, + `artistName` varchar(100) DEFAULT NULL, + `albumName` varchar(100) DEFAULT NULL, + `albumArtist` varchar(100) DEFAULT NULL, + `trackNumber` int(11) DEFAULT NULL, + `year` int(11) DEFAULT NULL, + `duration` time DEFAULT NULL, + `playCount` int(11) NOT NULL DEFAULT '0' +) ENGINE=InnoDB AUTO_INCREMENT=8135 DEFAULT CHARSET=latin1; + +-- +-- Indexen voor geƫxporteerde tabellen +-- + +-- +-- Indexen voor tabel `tracks` +-- +ALTER TABLE `tracks` + ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `trackUrl` (`trackUrl`); + +-- +-- AUTO_INCREMENT voor geƫxporteerde tabellen +-- + +-- +-- AUTO_INCREMENT voor een tabel `tracks` +-- +ALTER TABLE `tracks` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=8135; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;