catpic/catpic.py

107 lines
3.4 KiB
Python

#!/usr/bin/env python3
from threading import Thread
import traceback
import os
import cherrypy
import subprocess
from base64 import b64encode
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
from ws4py.websocket import EchoWebSocket
from time import sleep, time
INTERVAL = 2
cherrypy.config.update({'server.socket_port': 3000,
'server.socket_host': "0.0.0.0"})
WebSocketPlugin(cherrypy.engine).subscribe()
cherrypy.tools.websocket = WebSocketTool()
class CatHandler(Thread):
def __init__(self, master):
"""
Run the image creator subprocess. Every X seconds, delete all images but the newest one
"""
super().__init__()
self.master = master
self.proc = None
self.start()
def run(self):
compressed = ""
self.startproc()
while True:
if time() - self.proctime > 15 * 60: # restart proc every 15 minutes
self.stopproc()
self.startproc()
start = time()
images = sorted([i.name for i in os.scandir("images")]) # newer images towards end
if images:
for imname in images[0:-1]:
os.unlink(os.path.join("images", imname))
newest = os.path.join("images", images[-1])
if newest != compressed:
subprocess.check_call(["convert", "-strip", "-interlace", "Plane", "-gaussian-blur", "0.05",
"-quality", "50%", newest, newest])
compressed = newest
with open(newest, "rb") as f:
self.master.latest = f.read()
cherrypy.engine.publish('websocket-broadcast',
"data:image/jpeg;base64,{}".format(b64encode(self.master.latest).decode("ascii")))
spent = time() - start
sleep(max(0.2, INTERVAL - spent))
def stopproc(self):
if not self.proc:
raise Exception("No proc to kill")
self.proc.kill()
self.proc.wait()
self.proc = None
def startproc(self):
if self.proc:
raise Exception("Proc already running!")
# initial cleanout
for f in os.scandir("images"):
os.unlink(f)
self.proc = subprocess.Popen(["imagesnap", "-t", str(INTERVAL)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=os.path.join(os.path.dirname(__file__), "images"))
self.proctime = time()
class Root(object):
def __init__(self):
self.latest = None
self.cathandler = CatHandler(self)
@cherrypy.expose
def index(self):
with open("page.htm") as f:
yield f.read()
@cherrypy.expose
def latest_jpg(self):
if self.latest:
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
yield self.latest
else:
raise cherrypy.HTTPError(404)
@cherrypy.expose
def ws(self):
# you can access the class instance through
handler = cherrypy.request.ws_handler
def main():
cherrypy.quickstart(Root(), '/', config={'/ws': {'tools.websocket.on': True,
'tools.websocket.handler_cls': EchoWebSocket}})
if __name__ == '__main__':
main()