make slightly less awful
This commit is contained in:
parent
20e4a5e0b6
commit
bbb67ab425
89
app.py
89
app.py
|
@ -15,8 +15,9 @@ from libs import database
|
||||||
from libs import recordTick
|
from libs import recordTick
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__' or 'uwsgi' in __name__:
|
if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
appdir = "/home/streamrecord/app"
|
appdir = os.path.abspath(os.path.normpath(os.path.dirname(__file__)))
|
||||||
appconf = {
|
appconf = {
|
||||||
'/': {
|
'/': {
|
||||||
#'tools.proxy.on':True,
|
#'tools.proxy.on':True,
|
||||||
|
@ -27,12 +28,12 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
'tools.sessions.timeout':525600,
|
'tools.sessions.timeout':525600,
|
||||||
'request.show_tracebacks': True
|
'request.show_tracebacks': True
|
||||||
},
|
},
|
||||||
'/media': {
|
'/static': {
|
||||||
'tools.staticdir.on': True,
|
'tools.staticdir.on': True,
|
||||||
'tools.staticdir.dir': appdir+"/static/"
|
'tools.staticdir.dir': appdir+"/static/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cherrypy.config.update({
|
cherrypy.config.update({
|
||||||
'server.socket_port':3000,
|
'server.socket_port':3000,
|
||||||
'server.thread_pool':1,
|
'server.thread_pool':1,
|
||||||
|
@ -40,32 +41,32 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
'sessionFilter.on':True,
|
'sessionFilter.on':True,
|
||||||
'server.show.tracebacks': True
|
'server.show.tracebacks': True
|
||||||
})
|
})
|
||||||
|
|
||||||
cherrypy.server.socket_timeout = 5
|
cherrypy.server.socket_timeout = 5
|
||||||
|
|
||||||
# env - jinja2 template renderer
|
# env - jinja2 template renderer
|
||||||
env = Environment(loader=FileSystemLoader("/home/streamrecord/app/templates"))
|
env = Environment(loader=FileSystemLoader(os.path.join(appdir, "templates")))
|
||||||
# db - slightly custom sqlite3 object. rows = db.execute(query, args)
|
# db - slightly custom sqlite3 object. rows = db.execute(query, args)
|
||||||
db = database()
|
db = database()
|
||||||
# REC - recorder thread - see recordTick.py
|
# REC - recorder thread - see recordTick.py
|
||||||
#REC = recordTick(db)
|
#REC = recordTick(db)
|
||||||
|
|
||||||
def render(template, args):
|
def render(template, args):
|
||||||
templatesCache = pysite.cacheTemplates()
|
templatesCache = pysite.cacheTemplates()
|
||||||
defaults = {"templates":templatesCache}
|
defaults = {"templates":templatesCache}
|
||||||
for item in args:
|
for item in args:
|
||||||
defaults[item] = args[item]
|
defaults[item] = args[item]
|
||||||
return quickRender(template, defaults)
|
return quickRender(template, defaults)
|
||||||
|
|
||||||
def quickRender(template, args):
|
def quickRender(template, args):
|
||||||
template = env.get_template(template)
|
template = env.get_template(template)
|
||||||
return template.render(args)
|
return template.render(args)
|
||||||
|
|
||||||
class siteRoot(object):
|
class siteRoot(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
print("Siteroot init !")
|
print("Siteroot init !")
|
||||||
self.templateCache = self.cacheTemplates()
|
self.templateCache = self.cacheTemplates()
|
||||||
|
|
||||||
def cacheTemplates(self):
|
def cacheTemplates(self):
|
||||||
templateFiles = os.listdir("jstemplates/")
|
templateFiles = os.listdir("jstemplates/")
|
||||||
templateList = []
|
templateList = []
|
||||||
|
@ -75,66 +76,66 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
templateList.append({"name":name[0],"content":open("jstemplates/"+item,"r").read().replace("\t", "").replace("\n","")})
|
templateList.append({"name":name[0],"content":open("jstemplates/"+item,"r").read().replace("\t", "").replace("\n","")})
|
||||||
nameList.append(name[0])
|
nameList.append(name[0])
|
||||||
return quickRender("templates.html", {"names":json.dumps(nameList), "templates":templateList})
|
return quickRender("templates.html", {"names":json.dumps(nameList), "templates":templateList})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def index(self):
|
def index(self):
|
||||||
return render("html.html", {})
|
return render("html.html", {})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def htmltest(self):
|
def htmltest(self):
|
||||||
return render("html.tpl", {})
|
return render("html.tpl", {})
|
||||||
#index.exposed = True
|
#index.exposed = True
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def templates(self):
|
def templates(self):
|
||||||
return self.templateCache
|
return self.templateCache
|
||||||
|
|
||||||
class api(object):
|
class api(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.REC = recordTick(db)
|
self.REC = recordTick(db)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def tick(self):
|
def tick(self):
|
||||||
self.REC.tick()
|
self.REC.tick()
|
||||||
return "OK"
|
return "OK"
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def getStreams(self):
|
def getStreams(self):
|
||||||
streamList = db.execute('SELECT * FROM "streams" ORDER BY "name" ASC;')
|
streamList = db.execute('SELECT * FROM "streams" ORDER BY "name" ASC;')
|
||||||
|
|
||||||
for stream in streamList:
|
for stream in streamList:
|
||||||
stream["time"] = db.execute('SELECT * FROM "times" WHERE streamid=?', [stream["id"]])[0]
|
stream["time"] = db.execute('SELECT * FROM "times" WHERE streamid=?', [stream["id"]])[0]
|
||||||
stream["files"] = self._getFiles(stream["id"])
|
stream["files"] = self._getFiles(stream["id"])
|
||||||
stream["recorder_status"] = self.REC.streamStatus(stream["id"])
|
stream["recorder_status"] = self.REC.streamStatus(stream["id"])
|
||||||
stream["is_running"] = stream["recorder_status"] not in [0, -1] # idle states
|
stream["is_running"] = stream["recorder_status"] not in [0, -1] # idle states
|
||||||
|
|
||||||
return json.dumps(streamList)
|
return json.dumps(streamList)
|
||||||
|
|
||||||
def _getStream(self,id):
|
def _getStream(self,id):
|
||||||
streamList = db.execute('SELECT * FROM "streams" WHERE "id"=?', [int(id)])
|
streamList = db.execute('SELECT * FROM "streams" WHERE "id"=?', [int(id)])
|
||||||
|
|
||||||
for stream in streamList:
|
for stream in streamList:
|
||||||
stream["time"] = db.execute('SELECT * FROM "times" WHERE streamid=?', [stream["id"]])[0]
|
stream["time"] = db.execute('SELECT * FROM "times" WHERE streamid=?', [stream["id"]])[0]
|
||||||
stream["files"]=self._getFiles(id)
|
stream["files"]=self._getFiles(id)
|
||||||
stream["recorder_status"] = self.REC.streamStatus(stream["id"])
|
stream["recorder_status"] = self.REC.streamStatus(stream["id"])
|
||||||
stream["is_running"] = stream["recorder_status"] not in [0, -1] # idle states
|
stream["is_running"] = stream["recorder_status"] not in [0, -1] # idle states
|
||||||
return streamList[0]
|
return streamList[0]
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def getStream(self, id):
|
def getStream(self, id):
|
||||||
return json.dumps(self._getStream(id))
|
return json.dumps(self._getStream(id))
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def changeStatus(self, streamid, status):
|
def changeStatus(self, streamid, status):
|
||||||
streamid = int(streamid)
|
streamid = int(streamid)
|
||||||
db.execute('UPDATE "streams" SET "status"=? WHERE "id"=? ;', [status, streamid])
|
db.execute('UPDATE "streams" SET "status"=? WHERE "id"=? ;', [status, streamid])
|
||||||
return json.dumps({"result":True})
|
return json.dumps({"result":True})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def changeTimeDay(self, streamid, day, value):
|
def changeTimeDay(self, streamid, day, value):
|
||||||
streamid = int(streamid)
|
streamid = int(streamid)
|
||||||
value = value == "true"
|
value = value == "true"
|
||||||
|
|
||||||
col = ""
|
col = ""
|
||||||
if day == "daysu":
|
if day == "daysu":
|
||||||
col="su"
|
col="su"
|
||||||
|
@ -152,11 +153,11 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
col="sa"
|
col="sa"
|
||||||
else:
|
else:
|
||||||
raise cherrypy.HTTPError(500, message="Day not found")
|
raise cherrypy.HTTPError(500, message="Day not found")
|
||||||
|
|
||||||
db.execute('UPDATE "times" SET "'+col+'"=? WHERE "streamid"=? ;', [1 if value else 0,streamid])
|
db.execute('UPDATE "times" SET "'+col+'"=? WHERE "streamid"=? ;', [1 if value else 0,streamid])
|
||||||
|
|
||||||
return json.dumps({"result":True})
|
return json.dumps({"result":True})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def changeName(self, streamid, value):
|
def changeName(self, streamid, value):
|
||||||
streamid = int(streamid)
|
streamid = int(streamid)
|
||||||
|
@ -177,10 +178,10 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
assert endHour>=0 and endHour<=23
|
assert endHour>=0 and endHour<=23
|
||||||
endMin=int(endMin)
|
endMin=int(endMin)
|
||||||
assert endMin>=0 and endMin<=59
|
assert endMin>=0 and endMin<=59
|
||||||
|
|
||||||
db.execute('UPDATE "times" SET "starthour"=?, "startmin"=?, "endhour"=?, "endmin"=? WHERE "streamid"=? ;', [startHour, startMin, endHour, endMin, streamid])
|
db.execute('UPDATE "times" SET "starthour"=?, "startmin"=?, "endhour"=?, "endmin"=? WHERE "streamid"=? ;', [startHour, startMin, endHour, endMin, streamid])
|
||||||
return json.dumps({"result":True})
|
return json.dumps({"result":True})
|
||||||
|
|
||||||
def _filterName(self, input):
|
def _filterName(self, input):
|
||||||
allowed="abcdefghijklmnopqrstuvwxyz123456789-"
|
allowed="abcdefghijklmnopqrstuvwxyz123456789-"
|
||||||
input = input.replace(" ", "-").lower()
|
input = input.replace(" ", "-").lower()
|
||||||
|
@ -189,17 +190,17 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
if input[i:i+1] in allowed:
|
if input[i:i+1] in allowed:
|
||||||
output.append(input[i:i+1])
|
output.append(input[i:i+1])
|
||||||
return ''.join(output)
|
return ''.join(output)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def createStream(self, data):
|
def createStream(self, data):
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
|
|
||||||
assert not data["name"] == ""
|
assert not data["name"] == ""
|
||||||
assert not data["url"] == ""
|
assert not data["url"] == ""
|
||||||
assert data["time"]["su"] or data["time"]["m"] or data["time"]["t"] or data["time"]["w"] or data["time"]["r"] or data["time"]["f"] or data["time"]["sa"]
|
assert data["time"]["su"] or data["time"]["m"] or data["time"]["t"] or data["time"]["w"] or data["time"]["r"] or data["time"]["f"] or data["time"]["sa"]
|
||||||
|
|
||||||
dirName = self._filterName(data["name"])
|
dirName = self._filterName(data["name"])
|
||||||
|
|
||||||
rowid = db.execute('INSERT INTO "streams" ("user", "name", "url", "directory", "status", "message") VALUES (?, ?, ?, ?, ?, ?);', [0, data["name"], data["url"], dirName, data["status"], ""])
|
rowid = db.execute('INSERT INTO "streams" ("user", "name", "url", "directory", "status", "message") VALUES (?, ?, ?, ?, ?, ?);', [0, data["name"], data["url"], dirName, data["status"], ""])
|
||||||
db.execute('INSERT INTO "times" ("streamid", "su", "m", "t", "w", "r", "f", "sa", "starthour", "startmin", "endhour", "endmin") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);', [
|
db.execute('INSERT INTO "times" ("streamid", "su", "m", "t", "w", "r", "f", "sa", "starthour", "startmin", "endhour", "endmin") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);', [
|
||||||
rowid,
|
rowid,
|
||||||
|
@ -215,9 +216,9 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
data["time"]["endHour"],
|
data["time"]["endHour"],
|
||||||
data["time"]["endMin"]
|
data["time"]["endMin"]
|
||||||
])
|
])
|
||||||
|
|
||||||
return json.dumps({"result":rowid})
|
return json.dumps({"result":rowid})
|
||||||
|
|
||||||
def _getFiles(self, id):
|
def _getFiles(self, id):
|
||||||
stream = db.execute('SELECT * FROM "streams" WHERE "id"=?', [int(id)])[0]
|
stream = db.execute('SELECT * FROM "streams" WHERE "id"=?', [int(id)])[0]
|
||||||
recordingsDir = "files/output/"+stream["directory"]+"/"
|
recordingsDir = "files/output/"+stream["directory"]+"/"
|
||||||
|
@ -241,24 +242,24 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
"date":os.path.getctime(recordingsDir+item)
|
"date":os.path.getctime(recordingsDir+item)
|
||||||
})
|
})
|
||||||
return allFiles
|
return allFiles
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def getFiles(self, id):
|
def getFiles(self, id):
|
||||||
files = self._getFiles(id)
|
files = self._getFiles(id)
|
||||||
return json.dumps({"data":files})
|
return json.dumps({"data":files})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def download(self, id, fn):
|
def download(self, id, fn):
|
||||||
files = self._getFiles(id)
|
files = self._getFiles(id)
|
||||||
item = files[int(fn)]
|
item = files[int(fn)]
|
||||||
raise cherrypy.HTTPRedirect("/static/output/"+item["streamdir"]+"/"+item["filename"], 302)
|
raise cherrypy.HTTPRedirect("/static/output/"+item["streamdir"]+"/"+item["filename"], 302)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def getUrl(self, id, fn):
|
def getUrl(self, id, fn):
|
||||||
files = self._getFiles(id)
|
files = self._getFiles(id)
|
||||||
item = files[int(fn)]
|
item = files[int(fn)]
|
||||||
return json.dumps({"result":"/static/output/"+item["streamdir"]+"/"+item["filename"]})
|
return json.dumps({"result":"/static/output/"+item["streamdir"]+"/"+item["filename"]})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.response_headers(headers=[('Content-Type', 'application/rss+xml')])
|
@cherrypy.tools.response_headers(headers=[('Content-Type', 'application/rss+xml')])
|
||||||
def getPodcast(self, id):
|
def getPodcast(self, id):
|
||||||
|
@ -270,17 +271,17 @@ if __name__ == '__main__' or 'uwsgi' in __name__:
|
||||||
"stream":stream,
|
"stream":stream,
|
||||||
"builddate": datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0800")#Thu, 31 Jul 2014 07:13:48 +0000
|
"builddate": datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0800")#Thu, 31 Jul 2014 07:13:48 +0000
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def getRecStatus(self, id):
|
def getRecStatus(self, id):
|
||||||
return json.dumps({"data":self.REC.streamStatus(int(id))})
|
return json.dumps({"data":self.REC.streamStatus(int(id))})
|
||||||
|
|
||||||
pysite = siteRoot()
|
pysite = siteRoot()
|
||||||
pysite.api = api()
|
pysite.api = api()
|
||||||
|
|
||||||
print( "Ready to start application" )
|
print( "Ready to start application" )
|
||||||
|
|
||||||
if(len(sys.argv)>1 and sys.argv[1]=="test"):
|
if(len(sys.argv)>1 and sys.argv[1]=="test"):
|
||||||
print("test!")
|
print("test!")
|
||||||
application = cherrypy.quickstart(pysite, '/', appconf)
|
application = cherrypy.quickstart(pysite, '/', appconf)
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import os.path
|
import os.path
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
|
||||||
class database:
|
class database:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.createDatabase()
|
self.createDatabase()
|
||||||
self.db = self.openDB()
|
self.db = self.openDB()
|
||||||
|
|
||||||
def createDatabase(self):
|
def createDatabase(self):
|
||||||
queries = [
|
queries = [
|
||||||
"CREATE TABLE 'streams' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'user' INTEGER, 'name' TEXT, 'url' TEXT, 'directory' TEXT, 'status' INTEGER, 'message' TEXT);",
|
"CREATE TABLE 'streams' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'user' INTEGER, 'name' TEXT, 'url' TEXT, 'directory' TEXT, 'status' INTEGER, 'message' TEXT);",
|
||||||
|
@ -21,27 +23,27 @@ class database:
|
||||||
print("Executing: %s" % query)
|
print("Executing: %s" % query)
|
||||||
cursor.execute(query)
|
cursor.execute(query)
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
def openDB(self):
|
def openDB(self):
|
||||||
db = sqlite3.connect("db.sqlite", check_same_thread=False, cached_statements=0, isolation_level=None)
|
db = sqlite3.connect("db.sqlite", check_same_thread=False, cached_statements=0, isolation_level=None)
|
||||||
db.row_factory = self.dict_factory
|
db.row_factory = self.dict_factory
|
||||||
return db
|
return db
|
||||||
|
|
||||||
def dict_factory(self, cursor, row):
|
def dict_factory(self, cursor, row):
|
||||||
d = {}
|
d = {}
|
||||||
for idx, col in enumerate(cursor.description):
|
for idx, col in enumerate(cursor.description):
|
||||||
d[col[0]] = row[idx]
|
d[col[0]] = row[idx]
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def execute(self, sql, params=None):
|
def execute(self, sql, params=None):
|
||||||
db = self.db
|
db = self.db
|
||||||
cursor = db.cursor()
|
with closing(db.cursor()) as cursor:
|
||||||
if params:
|
if params:
|
||||||
cursor.execute(sql, params)
|
cursor.execute(sql, params)
|
||||||
else:
|
else:
|
||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
data = cursor.fetchall()
|
data = cursor.fetchall()
|
||||||
if not cursor.lastrowid==None:
|
if sql.lower().startswith("insert "):
|
||||||
return cursor.lastrowid
|
return cursor.lastrowid
|
||||||
cursor.close()
|
cursor.close()
|
||||||
return data
|
return data
|
|
@ -16,52 +16,52 @@ class recordTick:
|
||||||
self.db = database
|
self.db = database
|
||||||
# list of downloader threads
|
# list of downloader threads
|
||||||
self.threads = {}
|
self.threads = {}
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
now=datetime.datetime.now()
|
now=datetime.datetime.now()
|
||||||
#print("Tick start: %s" % now)
|
#print("Tick start: %s" % now)
|
||||||
|
|
||||||
# Look for starting times set to now
|
# Look for starting times set to now
|
||||||
days = ["m", "t", "w", "r", "f", "sa", "su"]
|
days = ["m", "t", "w", "r", "f", "sa", "su"]
|
||||||
day = days[datetime.datetime.now().weekday()]
|
day = days[datetime.datetime.now().weekday()]
|
||||||
|
|
||||||
startTimes = self.db.execute('SELECT * FROM "times" JOIN "streams" ON "streams"."id" = "times"."streamid" where "starthour"=? AND "startmin"=? AND "'+day+'"=1 AND "status"=0', (now.hour, now.minute))
|
startTimes = self.db.execute('SELECT * FROM "times" JOIN "streams" ON "streams"."id" = "times"."streamid" where "starthour"=? AND "startmin"=? AND "'+day+'"=1 AND "status"=0', (now.hour, now.minute))
|
||||||
for startTime in startTimes:
|
for startTime in startTimes:
|
||||||
# Start each downloader
|
# Start each downloader
|
||||||
self.startStream(startTime["streamid"])
|
self.startStream(startTime["streamid"])
|
||||||
|
|
||||||
# Look for end times set to now
|
# Look for end times set to now
|
||||||
endTimes = self.db.execute('SELECT * FROM "times" where "endhour"=? AND "endmin"=?', (now.hour, now.minute))
|
endTimes = self.db.execute('SELECT * FROM "times" where "endhour"=? AND "endmin"=?', (now.hour, now.minute))
|
||||||
for endTime in endTimes:
|
for endTime in endTimes:
|
||||||
# terminate each downloader
|
# terminate each downloader
|
||||||
self.endStream(endTime["streamid"])
|
self.endStream(endTime["streamid"])
|
||||||
|
|
||||||
#print("Tick end: %s" % now)
|
#print("Tick end: %s" % now)
|
||||||
|
|
||||||
def startStream(self, id):
|
def startStream(self, id):
|
||||||
# Find stream information
|
# Find stream information
|
||||||
stream = self.db.execute('SELECT * FROM "streams" WHERE "id"=? ;', (id,))[0]
|
stream = self.db.execute('SELECT * FROM "streams" WHERE "id"=? ;', (id,))[0]
|
||||||
|
|
||||||
# if the downloader isnt running already:
|
# if the downloader isnt running already:
|
||||||
if not stream["id"] in self.threads:
|
if not stream["id"] in self.threads:
|
||||||
# Create the recording thread
|
# Create the recording thread
|
||||||
self.threads[stream["id"]] = recordThread(stream["url"], stream["directory"])
|
self.threads[stream["id"]] = recordThread(stream["url"], stream["directory"])
|
||||||
|
|
||||||
def endStream(self, id):
|
def endStream(self, id):
|
||||||
if id in self.threads:
|
if id in self.threads:
|
||||||
# tell the downloader to finish
|
# tell the downloader to finish
|
||||||
self.threads[id].cancel()
|
self.threads[id].cancel()
|
||||||
del self.threads[id]
|
del self.threads[id]
|
||||||
|
|
||||||
def streamStatus(self, id):
|
def streamStatus(self, id):
|
||||||
if not id in self.threads:
|
if not id in self.threads:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
return self.threads[id].status
|
return self.threads[id].status
|
||||||
|
|
||||||
def getSelf(self):
|
def getSelf(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def timeToNextMinute(self):
|
def timeToNextMinute(self):
|
||||||
# calculate time to the milliscond until the next minute rolls over
|
# calculate time to the milliscond until the next minute rolls over
|
||||||
# Find the next minute
|
# Find the next minute
|
||||||
|
@ -86,13 +86,14 @@ class recordThread(Thread):
|
||||||
self.running = True
|
self.running = True
|
||||||
# Start time of the recording
|
# Start time of the recording
|
||||||
self.startdate = None
|
self.startdate = None
|
||||||
|
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print("%s starting downloader for %s" % (datetime.datetime.now(), self.url))
|
print("%s starting downloader for %s" % (datetime.datetime.now(), self.url))
|
||||||
# Download the stream to temp file(s)
|
# Download the stream to temp file(s)
|
||||||
self.status = 2
|
self.status = 2
|
||||||
|
self.clearTempDir()
|
||||||
self.downloadStream()
|
self.downloadStream()
|
||||||
# Combine files into 1 audio file
|
# Combine files into 1 audio file
|
||||||
self.status = 3
|
self.status = 3
|
||||||
|
@ -105,33 +106,30 @@ class recordThread(Thread):
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
print("%s finished downloader for %s" % (datetime.datetime.now(), self.url))
|
print("%s finished downloader for %s" % (datetime.datetime.now(), self.url))
|
||||||
self.status = 0
|
self.status = 0
|
||||||
|
|
||||||
|
def clearTempDir(self):
|
||||||
|
# Delete temp files
|
||||||
|
files = os.listdir("files/temp/%s"%self.directory)
|
||||||
|
for f in files:
|
||||||
|
os.unlink("files/temp/%s/%s"%(self.directory,f))
|
||||||
|
|
||||||
def downloadStream(self):
|
def downloadStream(self):
|
||||||
self.startdate = datetime.datetime.now()
|
self.startdate = datetime.datetime.now()
|
||||||
|
recdate = str(int(time.time()))
|
||||||
# As long as we're supposed to keep retrying
|
# As long as we're supposed to keep retrying
|
||||||
while self.running:
|
while self.running:
|
||||||
# Create the temp dir for this stream
|
# Create the temp dir for this stream
|
||||||
if not os.path.exists("files/temp/"+self.directory):
|
os.makedirs("files/temp/"+self.directory, exist_ok=True)
|
||||||
os.mkdir("files/temp/"+self.directory)
|
|
||||||
|
|
||||||
# If there are already files, we're resuming. take the next available number
|
# If there are already files, we're resuming. take the next available number
|
||||||
recNum = 0
|
recNum = 0
|
||||||
while os.path.exists("files/temp/%s/recdate%s.mp3" % (self.directory, ".%s"%recNum)):
|
while os.path.exists("files/temp/%s/%s_%s.mp3" % (self.directory, recdate, recNum)):
|
||||||
recNum = recNum + 1
|
recNum = recNum + 1
|
||||||
# Filename is something like files/temp/stream-name/rec-y-m-d_h-m-s.0.mp3
|
# Filename is something like files/temp/stream-name/rec-y-m-d_h-m-s.0.mp3
|
||||||
fileName = "files/temp/%s/recdate%s.mp3" % (self.directory, "" if recNum == None else ".%s"%recNum)
|
fileName = "files/temp/%s/%s_%s.mp3" % (self.directory, recdate, recNum)
|
||||||
|
|
||||||
# Args if we download with curl (bad)
|
|
||||||
args_curl = [
|
|
||||||
'/usr/bin/curl',
|
|
||||||
#'-s',
|
|
||||||
'-A',
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36',
|
|
||||||
'--output', fileName, self.url]
|
|
||||||
|
|
||||||
# args if we download/transcode with avconv (HERO!)
|
|
||||||
args_libav = [
|
args_libav = [
|
||||||
'/usr/bin/avconv',
|
os.environ.get("CONVERT_PROGRAM") or "/usr/bin/ffmpeg",
|
||||||
'-loglevel',
|
'-loglevel',
|
||||||
'error',
|
'error',
|
||||||
'-i',
|
'-i',
|
||||||
|
@ -140,12 +138,12 @@ class recordThread(Thread):
|
||||||
'128k',
|
'128k',
|
||||||
fileName
|
fileName
|
||||||
]
|
]
|
||||||
|
|
||||||
self.proc = subprocess.Popen(args_libav, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
self.proc = subprocess.Popen(args_libav, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
output = self.proc.communicate()
|
output = self.proc.communicate()
|
||||||
print("LibAV output for %s:\n%s" % (self.url, output))
|
print("LibAV output for %s:\n%s" % (self.url, output))
|
||||||
self.proc = None
|
self.proc = None
|
||||||
|
|
||||||
|
|
||||||
def mergeStream(self):
|
def mergeStream(self):
|
||||||
# Get an ordered list of the piece files
|
# Get an ordered list of the piece files
|
||||||
files = os.listdir("files/temp/%s"%self.directory)
|
files = os.listdir("files/temp/%s"%self.directory)
|
||||||
|
@ -154,37 +152,40 @@ class recordThread(Thread):
|
||||||
command = ['/usr/bin/mkvmerge', '-o', "files/temp/%s/temp.mka"%self.directory, "files/temp/%s/%s"%(self.directory,files.pop(0))]
|
command = ['/usr/bin/mkvmerge', '-o', "files/temp/%s/temp.mka"%self.directory, "files/temp/%s/%s"%(self.directory,files.pop(0))]
|
||||||
for fname in files:
|
for fname in files:
|
||||||
command.append("+files/temp/%s/%s"%(self.directory,fname))
|
command.append("+files/temp/%s/%s"%(self.directory,fname))
|
||||||
|
|
||||||
self.mergeproc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
self.mergeproc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
# Wait for the merge to finish
|
# Wait for the merge to finish
|
||||||
output = self.mergeproc.communicate()
|
output = self.mergeproc.communicate()
|
||||||
|
|
||||||
def transcodeStream(self):
|
def transcodeStream(self):
|
||||||
# Delete the existing output file
|
# Delete the existing output file
|
||||||
if os.path.exists("files/temp/%s/out.mp3"%self.directory):
|
if os.path.exists("files/temp/%s/out.mp3"%self.directory):
|
||||||
os.unlink("files/temp/%s/out.mp3"%self.directory)
|
os.unlink("files/temp/%s/out.mp3"%self.directory)
|
||||||
|
|
||||||
# Convert the matroska file to mp3
|
# Convert the matroska file to mp3
|
||||||
command = ['/usr/bin/avconv', '-i', "files/temp/%s/temp.mka"%self.directory, '-q:a', '0', '-ab', '128k', "files/temp/%s/out.mp3"%self.directory]
|
command = [
|
||||||
|
os.environ.get("CONVERT_PROGRAM") or '/usr/bin/ffmpeg',
|
||||||
|
'-i', "files/temp/%s/temp.mka"%self.directory,
|
||||||
|
'-q:a', '0',
|
||||||
|
'-ab', '128k',
|
||||||
|
"files/temp/%s/out.mp3"%self.directory
|
||||||
|
]
|
||||||
self.transcodeproc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
self.transcodeproc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
# wait for the trancode to finish
|
# wait for the trancode to finish
|
||||||
output = self.transcodeproc.communicate()
|
output = self.transcodeproc.communicate()
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
# create a dated name for the file
|
# create a dated name for the file
|
||||||
newname = self.startdate.strftime("%Y-%m-%d_%H-%M-%S")+".mp3"
|
newname = self.startdate.strftime("%Y-%m-%d_%H-%M-%S")+".mp3"
|
||||||
|
|
||||||
# make it's finished storage location
|
# make it's finished storage location
|
||||||
if not os.path.exists("files/output/"+self.directory):
|
os.makedirs("files/output/"+self.directory, exist_ok=True)
|
||||||
os.mkdir("files/output/"+self.directory)
|
|
||||||
|
|
||||||
# copy final recording to output dir
|
# copy final recording to output dir
|
||||||
os.rename("files/temp/%s/out.mp3"%(self.directory), "files/output/%s/%s"%(self.directory,newname))
|
os.rename("files/temp/%s/out.mp3"%(self.directory), "files/output/%s/%s"%(self.directory,newname))
|
||||||
|
|
||||||
# Delete temp files
|
self.clearTempDir()
|
||||||
files = os.listdir("files/temp/%s"%self.directory)
|
|
||||||
for f in files:
|
|
||||||
os.unlink("files/temp/%s/%s"%(self.directory,f))
|
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
print("Closing %s" % self.url)
|
print("Closing %s" % self.url)
|
||||||
# turn off keep-alive dow the downloader
|
# turn off keep-alive dow the downloader
|
||||||
|
@ -192,7 +193,7 @@ class recordThread(Thread):
|
||||||
# Kill the download process
|
# Kill the download process
|
||||||
self.proc.terminate()
|
self.proc.terminate()
|
||||||
Thread(target=self.kill).start()
|
Thread(target=self.kill).start()
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
print("Starting kill thread for %s" % self.url)
|
print("Starting kill thread for %s" % self.url)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
@ -206,4 +207,3 @@ class recordThread(Thread):
|
||||||
if not self.proc == None:
|
if not self.proc == None:
|
||||||
# Kill it
|
# Kill it
|
||||||
self.proc.kill()
|
self.proc.kill()
|
||||||
|
|
|
@ -10,7 +10,7 @@ var ui = {
|
||||||
modal.find(".modal-body").html(content)
|
modal.find(".modal-body").html(content)
|
||||||
modal.modal()
|
modal.modal()
|
||||||
},
|
},
|
||||||
|
|
||||||
__streamstatus:function() {
|
__streamstatus:function() {
|
||||||
$(".btn-group-status button").click(function() {
|
$(".btn-group-status button").click(function() {
|
||||||
$(this).parent().find(".active").removeClass("active")
|
$(this).parent().find(".active").removeClass("active")
|
||||||
|
@ -124,8 +124,8 @@ var validators = {
|
||||||
startMin = parseInt(startMin)
|
startMin = parseInt(startMin)
|
||||||
endHour = parseInt(endHour)
|
endHour = parseInt(endHour)
|
||||||
endMin = parseInt(endMin)
|
endMin = parseInt(endMin)
|
||||||
|
|
||||||
|
|
||||||
if(startHour<0 || startHour>=24 || isNaN(startHour)) {
|
if(startHour<0 || startHour>=24 || isNaN(startHour)) {
|
||||||
messages.push("Start hour must be 0-23")
|
messages.push("Start hour must be 0-23")
|
||||||
}
|
}
|
||||||
|
@ -135,17 +135,17 @@ var validators = {
|
||||||
if(endHour<0 || endHour>=24 || isNaN(endHour)) {
|
if(endHour<0 || endHour>=24 || isNaN(endHour)) {
|
||||||
messages.push("End hour must be 0-23")
|
messages.push("End hour must be 0-23")
|
||||||
}
|
}
|
||||||
if(endMin<0 || endMin>50 || isNaN(endMin)) {
|
if(endMin<0 || endMin>59 || isNaN(endMin)) {
|
||||||
messages.push("End minute must be 0-59")
|
messages.push("End minute must be 0-59")
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
messages.push("Time values must be numeric")
|
messages.push("Time values must be numeric")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(messages.length==0) {
|
if(messages.length==0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue