3 Commits
Author SHA1 Message Date
Yorick Rommers 35ded0f592 fixed advanced search 2015-11-03 11:59:56 +01:00
Yorick Rommers e1de890f33 fixed search partially 2015-11-02 23:20:59 +01:00
Yorick Rommers dc7f8905ae Python server added 2015-11-02 20:32:33 +01:00
25 changed files with 868 additions and 0 deletions
+16
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
configparser
mutagen
PyMySQL
+72
View File
@@ -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)
+72
View File
@@ -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)
+169
View File
@@ -0,0 +1,169 @@
import json
from yjdaemon.Database import Database as db
import configparser
import string
"""
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):
data = args.split("&")
genquery="""
SELECT $ident FROM tracks WHERE LCASE(trackName) LIKE LCASE(\"%$trackname%\") $orgenre LCASE(genre) LIKE LCASE(\"%$genre%\") $oralbum LCASE(albumName) LIKE LCASE(\"%$album%\") $orartist LCASE(artistName) LIKE LCASE(\"%$artistname%\") $end;
"""
query="""
SELECT $ident FROM tracks WHERE ($or1 OR $or2 OR $or3 ) $and1 $and2 $and3 $end;
"""
test = string.Template(query)
genqueryres = ""
arguments = []
general = ""
for entry in data:
values = entry.split("=")
values[1] = values[1].replace("%20"," ")
if values[0] == "q":
general = values[1]
continue
if values[1]:
arguments.append(values)
print(arguments)
finalstring = "" + genquery
for arg in arguments:
if arg[0] == "artist":
finalstring = string.Template(finalstring).safe_substitute(artistname=arg[1], orartist="AND")
test = string.Template(test.safe_substitute(and1="AND LCASE(artistName) LIKE LCASE(\"%"+ arg[1] +"%\")"))
elif arg[0] == "genre":
finalstring = string.Template(finalstring).safe_substitute(genre=arg[1],orgenre="AND")
test = string.Template(test.safe_substitute(and2="AND LCASE(genre) LIKE LCASE(\"%"+ arg[1] +"%\")"))
elif arg[0] == "album":
finalstring = string.Template(finalstring).safe_substitute(album=arg[1],oralbum="AND")
test = string.Template(test.safe_substitute(and3="AND LCASE(albumName) LIKE LCASE(\"%"+ arg[1] +"%\")"))
print(finalstring)
finalstring = string.Template(finalstring).safe_substitute(genre=general,artistname=general,album=general,trackname=general)
print(finalstring)
test = test.safe_substitute( and1="", and2="",and3="",or1="LCASE(artistName) LIKE LCASE(\"%"+general+"%\")",or2="LCASE(genre) LIKE LCASE(\"%"+general+"%\")",or3="LCASE(albumName) LIKE LCASE(\"%"+general+"%\")")
print(test)
print(string.Template(test).safe_substitute(ident="*",end=""))
return calls.jsonify({"result":"OK", "genres": db.executequerystatic(string.Template(test).safe_substitute(ident="genre",end="GROUP BY genre")),"artists": db.executequerystatic(string.Template(test).safe_substitute(ident="artistName",end="GROUP BY artistName")), "albums": db.executequerystatic(string.Template(test).safe_substitute(ident="albumName",end="GROUP BY albumName")), "songs" : db.executequerystatic(string.Template(test).safe_substitute(ident="*",end=""))})
@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
}
+81
View File
@@ -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[:]
+82
View File
@@ -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"}))
+36
View File
@@ -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)
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from mutagen.easyid3 import EasyID3
from mutagen.mp3 import MP3
import mutagen._util
import os
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class LibraryScanner:
def __init__(self, Database, librarypath):
"""Init class """
self.url = librarypath
self.db = Database
self.scanRecursif()
ob = Observer()
ob.schedule(Filehandler(self), self.url, recursive=True)
ob.start()
def scanRecursif(self):
print("Scanning library "+self.url+" recursively...")
# musicdirs = [os.path.join(self.url,o) for o in os.listdir(self.url) if os.path.isdir(os.path.join(self.url,o))]
self.db.turnoffautocommit()
for root, directories, filenames in os.walk(self.url):
self.scandir(filenames,root)
def scandir(self,filenames, root):
for filename in filenames:
if filename.lower().endswith(('.mp3')):
path = os.path.join(root,filename)
try:
print(path, end='\r')
id3 = EasyID3(path)
audio = MP3(path)
print(audio.info.length)
self.db.insertMultipleSongs(self.getValue(id3, "genre"),path.replace("'", '\\\''),self.getValue(id3, "title"),self.getValue(id3, "artist"),self.getValue(id3, "album"),self.getValue(id3, "performer"),self.getValue(id3, "tracknumber"),self.getValue(id3, "date"),str(audio.info.length))
except (mutagen.id3._util.ID3NoHeaderError):
print("Error reading ID3 tag", end='\r')
def insertSong(self, path):
try:
id3 = EasyID3(path)
self.db.executeQuery(b"INSERT INTO `tracks` (`genre`, `trackUrl`, `trackName`, `artistName`, `albumName`, `albumArtist`, `trackNumber`, `year`, `duration`) VALUES ('" + self.getValue(id3,"genre") + b"'," + b"'" + path.replace("'", '\\\'').encode('utf8') + b"'," + b"'" + self.getValue(id3, "title") + b"'," + b"'" + self.getValue(id3, "artist") + b"'," + b"'" + self.getValue(id3, "album") + b"'," + b"'" + self.getValue(id3, "performer") + b"'," + b"'" + self.getValue(id3, "tracknumber") + b"'," + b"'" + self.getValue(id3, "date") + b"'," + b"'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`)")
except (mutagen.id3._util.ID3NoHeaderError):
pass
def removeSong(self,path):
self.db.removeSong(path)
def getValue(self, id3, value):
try:
return id3[value][0].replace("'", "\\'")
except (KeyError, IndexError, ValueError):
print("Error reading value of ID3 tag", end='\r')
return ""
class Filehandler(FileSystemEventHandler):
def __init__(self, LibraryScanner):
self.libscanner = LibraryScanner
def process(self, event):
if not(event.is_directory):
if os.path.isfile(event.src_path) and event.src_path.lower().endswith(('.mp3','.flac', 'm4a')):
self.libscanner.insertSong(event.src_path)
else:
self.libscanner.removeSong(event.src_path)
def on_modified(self, event):
self.process(event)
def on_created(self, event):
self.process(event)
+60
View File
@@ -0,0 +1,60 @@
import sys
import os
import configparser
from yjdaemon.Database import Database
from yjdaemon.libraryscanner import LibraryScanner
from yjdaemon.yjmpd import YJMPD
from yjdaemon.HTTPServer import HTTPServerThread
debug = False
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", "jancodir"))
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()
db = Database(DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_DATABASE)
LibraryScanner(db, MUSIC_DIR)
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)
+135
View File
@@ -0,0 +1,135 @@
import sys
import os
import time
import atexit
import signal
class YJMPD:
"""A generic yjdaemon class.
Usage: subclass the yjdaemon class and override the run() method."""
def __init__(self, pidfile, root):
self.pidfile = pidfile
self.root = root
def daemonize(self):
"""Deamonize class. UNIX double fork mechanism."""
try:
pid = os.fork()
if pid > 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()."""
+64
View File
@@ -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 */;