DVR for internet radio
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

292 lines
8.6 KiB

  1. #!/usr/bin/env python3
  2. STREAM_STATUS_ACTIVE = 0
  3. STREAM_STATUS_PAUSED = 1
  4. STREAM_STATUS_ERROR = 2
  5. import sys
  6. import os
  7. import os.path
  8. import cherrypy
  9. import json
  10. import signal
  11. from jinja2 import Environment, FileSystemLoader
  12. from libs import database
  13. from libs import recordTick
  14. from datetime import datetime
  15. if __name__ == '__main__' or 'uwsgi' in __name__:
  16. appdir = os.path.abspath(os.path.normpath(os.path.dirname(__file__)))
  17. appconf = {
  18. '/': {
  19. #'tools.proxy.on':True,
  20. #'tools.proxy.base': conf["base"]["url"],
  21. 'tools.sessions.on':True,
  22. 'tools.sessions.storage_type':'file',
  23. 'tools.sessions.storage_path':appdir+'/sessions/',
  24. 'tools.sessions.timeout':525600,
  25. 'request.show_tracebacks': True
  26. },
  27. '/static': {
  28. 'tools.staticdir.on': True,
  29. 'tools.staticdir.dir': appdir+"/static/"
  30. }
  31. }
  32. cherrypy.config.update({
  33. 'server.socket_port':3000,
  34. 'server.thread_pool':1,
  35. 'server.socket_host': '0.0.0.0',
  36. 'sessionFilter.on':True,
  37. 'server.show.tracebacks': True
  38. })
  39. cherrypy.server.socket_timeout = 5
  40. # env - jinja2 template renderer
  41. env = Environment(loader=FileSystemLoader(os.path.join(appdir, "templates")))
  42. # db - slightly custom sqlite3 object. rows = db.execute(query, args)
  43. db = database()
  44. # REC - recorder thread - see recordTick.py
  45. #REC = recordTick(db)
  46. def render(template, args):
  47. templatesCache = pysite.cacheTemplates()
  48. defaults = {"templates":templatesCache}
  49. for item in args:
  50. defaults[item] = args[item]
  51. return quickRender(template, defaults)
  52. def quickRender(template, args):
  53. template = env.get_template(template)
  54. return template.render(args)
  55. class siteRoot(object):
  56. def __init__(self):
  57. print("Siteroot init !")
  58. self.templateCache = self.cacheTemplates()
  59. def cacheTemplates(self):
  60. templateFiles = os.listdir("jstemplates/")
  61. templateList = []
  62. nameList = []
  63. for item in templateFiles:
  64. name = item.split(".")
  65. templateList.append({"name":name[0],"content":open("jstemplates/"+item,"r").read().replace("\t", "").replace("\n","")})
  66. nameList.append(name[0])
  67. return quickRender("templates.html", {"names":json.dumps(nameList), "templates":templateList})
  68. @cherrypy.expose
  69. def index(self):
  70. return render("html.html", {})
  71. @cherrypy.expose
  72. def htmltest(self):
  73. return render("html.tpl", {})
  74. #index.exposed = True
  75. @cherrypy.expose
  76. def templates(self):
  77. return self.templateCache
  78. class api(object):
  79. def __init__(self):
  80. self.REC = recordTick(db)
  81. @cherrypy.expose
  82. def tick(self):
  83. self.REC.tick()
  84. return "OK"
  85. @cherrypy.expose
  86. def getStreams(self):
  87. streamList = db.execute('SELECT * FROM "streams" ORDER BY "name" ASC;')
  88. for stream in streamList:
  89. stream["time"] = db.execute('SELECT * FROM "times" WHERE streamid=?', [stream["id"]])[0]
  90. stream["files"] = self._getFiles(stream["id"])
  91. stream["recorder_status"] = self.REC.streamStatus(stream["id"])
  92. stream["is_running"] = stream["recorder_status"] not in [0, -1] # idle states
  93. return json.dumps(streamList)
  94. def _getStream(self,id):
  95. streamList = db.execute('SELECT * FROM "streams" WHERE "id"=?', [int(id)])
  96. for stream in streamList:
  97. stream["time"] = db.execute('SELECT * FROM "times" WHERE streamid=?', [stream["id"]])[0]
  98. stream["files"]=self._getFiles(id)
  99. stream["recorder_status"] = self.REC.streamStatus(stream["id"])
  100. stream["is_running"] = stream["recorder_status"] not in [0, -1] # idle states
  101. return streamList[0]
  102. @cherrypy.expose
  103. def getStream(self, id):
  104. return json.dumps(self._getStream(id))
  105. @cherrypy.expose
  106. def changeStatus(self, streamid, status):
  107. streamid = int(streamid)
  108. db.execute('UPDATE "streams" SET "status"=? WHERE "id"=? ;', [status, streamid])
  109. return json.dumps({"result":True})
  110. @cherrypy.expose
  111. def changeTimeDay(self, streamid, day, value):
  112. streamid = int(streamid)
  113. value = value == "true"
  114. col = ""
  115. if day == "daysu":
  116. col="su"
  117. elif day == "daym":
  118. col="m"
  119. elif day == "dayt":
  120. col="t"
  121. elif day == "dayw":
  122. col="w"
  123. elif day == "dayr":
  124. col="r"
  125. elif day == "dayf":
  126. col="f"
  127. elif day == "daysa":
  128. col="sa"
  129. else:
  130. raise cherrypy.HTTPError(500, message="Day not found")
  131. db.execute('UPDATE "times" SET "'+col+'"=? WHERE "streamid"=? ;', [1 if value else 0,streamid])
  132. return json.dumps({"result":True})
  133. @cherrypy.expose
  134. def changeName(self, streamid, value):
  135. streamid = int(streamid)
  136. db.execute('UPDATE "streams" SET "name"=? WHERE "id"=?', [value,streamid])
  137. return json.dumps({"result":True})
  138. @cherrypy.expose
  139. def changeUrl(self, streamid, value):
  140. streamid = int(streamid)
  141. db.execute('UPDATE "streams" SET "url"=? WHERE "id"=?', [value,streamid])
  142. return json.dumps({"result":True})
  143. @cherrypy.expose
  144. def changeTime(self, streamid, startHour, startMin, endHour, endMin):
  145. startHour=int(startHour)
  146. assert startHour>=0 and startHour<=23
  147. startMin=int(startMin)
  148. assert startMin>=0 and startMin<=59
  149. endHour=int(endHour)
  150. assert endHour>=0 and endHour<=23
  151. endMin=int(endMin)
  152. assert endMin>=0 and endMin<=59
  153. db.execute('UPDATE "times" SET "starthour"=?, "startmin"=?, "endhour"=?, "endmin"=? WHERE "streamid"=? ;', [startHour, startMin, endHour, endMin, streamid])
  154. return json.dumps({"result":True})
  155. def _filterName(self, input):
  156. allowed="abcdefghijklmnopqrstuvwxyz123456789-"
  157. input = input.replace(" ", "-").lower()
  158. output=[]
  159. for i in range(0, len(allowed)):
  160. if input[i:i+1] in allowed:
  161. output.append(input[i:i+1])
  162. return ''.join(output)
  163. @cherrypy.expose
  164. def createStream(self, data):
  165. data = json.loads(data)
  166. assert not data["name"] == ""
  167. assert not data["url"] == ""
  168. 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"]
  169. dirName = self._filterName(data["name"])
  170. rowid = db.execute('INSERT INTO "streams" ("user", "name", "url", "directory", "status", "message") VALUES (?, ?, ?, ?, ?, ?);', [0, data["name"], data["url"], dirName, data["status"], ""])
  171. db.execute('INSERT INTO "times" ("streamid", "su", "m", "t", "w", "r", "f", "sa", "starthour", "startmin", "endhour", "endmin") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);', [
  172. rowid,
  173. data["time"]["su"],
  174. data["time"]["m"],
  175. data["time"]["t"],
  176. data["time"]["w"],
  177. data["time"]["r"],
  178. data["time"]["f"],
  179. data["time"]["sa"],
  180. data["time"]["startHour"],
  181. data["time"]["startMin"],
  182. data["time"]["endHour"],
  183. data["time"]["endMin"]
  184. ])
  185. return json.dumps({"result":rowid})
  186. def _getFiles(self, id):
  187. stream = db.execute('SELECT * FROM "streams" WHERE "id"=?', [int(id)])[0]
  188. recordingsDir = "files/output/"+stream["directory"]+"/"
  189. files = []
  190. if os.path.exists(recordingsDir):
  191. files = os.listdir(recordingsDir)
  192. files.sort()
  193. allFiles = []
  194. for i in range(0, len(files)):
  195. item = files[i]
  196. if item[0:1]==".":
  197. continue;
  198. size = os.path.getsize(recordingsDir+item)
  199. allFiles.append({
  200. "filename":item,
  201. "directory":recordingsDir,
  202. "streamdir":stream["directory"],
  203. "filenum":i,
  204. "bytes":size,
  205. "mbytes":round(size/1024.0/1024.0, 2),
  206. "date":os.path.getctime(recordingsDir+item)
  207. })
  208. return allFiles
  209. @cherrypy.expose
  210. def getFiles(self, id):
  211. files = self._getFiles(id)
  212. return json.dumps({"data":files})
  213. @cherrypy.expose
  214. def download(self, id, fn):
  215. files = self._getFiles(id)
  216. item = files[int(fn)]
  217. raise cherrypy.HTTPRedirect("/static/output/"+item["streamdir"]+"/"+item["filename"], 302)
  218. @cherrypy.expose
  219. def getUrl(self, id, fn):
  220. files = self._getFiles(id)
  221. item = files[int(fn)]
  222. return json.dumps({"result":"/static/output/"+item["streamdir"]+"/"+item["filename"]})
  223. @cherrypy.expose
  224. @cherrypy.tools.response_headers(headers=[('Content-Type', 'application/rss+xml')])
  225. def getPodcast(self, id):
  226. stream = self._getStream(id)
  227. # Thu, 31 Jul 2014 07:13:48 +0000
  228. for f in stream["files"]:
  229. f["date"]=datetime.fromtimestamp(f["date"]).strftime("%a, %d %b %Y %H:%M:%S +0800")
  230. return str.encode(render("podcast.html", {
  231. "stream":stream,
  232. "builddate": datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0800")#Thu, 31 Jul 2014 07:13:48 +0000
  233. }))
  234. @cherrypy.expose
  235. def getRecStatus(self, id):
  236. return json.dumps({"data":self.REC.streamStatus(int(id))})
  237. pysite = siteRoot()
  238. pysite.api = api()
  239. print( "Ready to start application" )
  240. if(len(sys.argv)>1 and sys.argv[1]=="test"):
  241. print("test!")
  242. application = cherrypy.quickstart(pysite, '/', appconf)
  243. else:
  244. sys.stdout = sys.stderr
  245. cherrypy.config.update({'environment': 'embedded'})
  246. application = cherrypy.tree.mount(pysite, "/", appconf)