make slightly less awful

This commit is contained in:
dave 2020-02-06 18:47:34 -08:00
parent 20e4a5e0b6
commit bbb67ab425
4 changed files with 119 additions and 116 deletions

89
app.py
View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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
} }