OctoPrint/octoprint/timelapse.py

141 lines
3.7 KiB
Python
Raw Normal View History

2013-01-03 20:38:46 +00:00
# coding=utf-8
__author__ = "Gina Häußge <osd@foosel.net>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
from octoprint.settings import settings
import octoprint.util as util
2013-01-03 20:38:46 +00:00
import os
import threading
import urllib
import time
2013-01-03 21:58:47 +00:00
import subprocess
2013-01-04 12:11:00 +00:00
import fnmatch
import datetime
2013-01-04 12:11:00 +00:00
def getFinishedTimelapses():
files = []
basedir = settings().getBaseFolder("timelapse")
for osFile in os.listdir(basedir):
if not fnmatch.fnmatch(osFile, "*.mpg"):
continue
statResult = os.stat(os.path.join(basedir, osFile))
2013-01-04 12:11:00 +00:00
files.append({
"name": osFile,
"size": util.getFormattedSize(statResult.st_size),
"bytes": statResult.st_size,
"date": util.getFormattedDateTime(datetime.datetime.fromtimestamp(statResult.st_ctime))
2013-01-04 12:11:00 +00:00
})
return files
2013-01-03 20:38:46 +00:00
class Timelapse(object):
def __init__(self):
self._imageNumber = None
self._inTimelapse = False
self._gcodeFile = None
2013-01-03 20:38:46 +00:00
self._captureDir = settings().getBaseFolder("timelapse_tmp")
self._movieDir = settings().getBaseFolder("timelapse")
self._snapshotUrl = settings().get(["webcam", "snapshot"])
self._renderThread = None
self._captureMutex = threading.Lock()
2013-01-03 20:38:46 +00:00
def onPrintjobStarted(self, gcodeFile):
self.startTimelapse(gcodeFile)
def onPrintjobStopped(self):
self.stopTimelapse()
def onPrintjobProgress(self, oldPos, newPos, percentage):
pass
def onZChange(self, oldZ, newZ):
pass
def startTimelapse(self, gcodeFile):
2013-01-03 21:58:47 +00:00
self.cleanCaptureDir()
self._imageNumber = 0
self._inTimelapse = True
self._gcodeFile = os.path.basename(gcodeFile)
2013-01-03 20:38:46 +00:00
def stopTimelapse(self):
self._renderThread = threading.Thread(target=self._createMovie)
self._renderThread.daemon = True
self._renderThread.start()
2013-01-03 21:58:47 +00:00
self._imageNumber = None
self._inTimelapse = False
2013-01-03 20:38:46 +00:00
def captureImage(self):
if self._captureDir is None:
2013-01-03 20:38:46 +00:00
return
with self._captureMutex:
filename = os.path.join(self._captureDir, "tmp_%05d.jpg" % (self._imageNumber))
self._imageNumber += 1;
2013-01-03 20:38:46 +00:00
captureThread = threading.Thread(target=self._captureWorker, kwargs={"filename": filename})
captureThread.daemon = True
2013-01-03 20:38:46 +00:00
captureThread.start()
def _captureWorker(self, filename):
urllib.urlretrieve(self._snapshotUrl, filename)
2013-01-03 20:38:46 +00:00
def _createMovie(self):
ffmpeg = settings().get(["webcam", "ffmpeg"])
bitrate = settings().get(["webcam", "bitrate"])
if ffmpeg is None or bitrate is None:
2013-01-03 21:58:47 +00:00
return
input = os.path.join(self._captureDir, "tmp_%05d.jpg")
output = os.path.join(self._movieDir, "%s_%s.mpg" % (os.path.splitext(self._gcodeFile)[0], time.strftime("%Y%m%d%H%M%S")))
2013-01-03 21:58:47 +00:00
subprocess.call([
ffmpeg, '-i', input, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y',
'-b:v', bitrate, '-f', 'vob', output
2013-01-03 21:58:47 +00:00
])
def cleanCaptureDir(self):
if not os.path.isdir(self._captureDir):
2013-01-03 21:58:47 +00:00
return
for filename in os.listdir(self._captureDir):
2013-01-04 12:11:00 +00:00
if not fnmatch.fnmatch(filename, "*.jpg"):
continue
os.remove(os.path.join(self._captureDir, filename))
2013-01-03 21:58:47 +00:00
2013-01-03 20:38:46 +00:00
class ZTimelapse(Timelapse):
def __init__(self):
Timelapse.__init__(self)
def onZChange(self, oldZ, newZ):
self.captureImage()
class TimedTimelapse(Timelapse):
def __init__(self, interval=1):
Timelapse.__init__(self)
self._interval = interval
if self._interval < 1:
self._interval = 1 # force minimum interval of 1s
2013-01-03 20:38:46 +00:00
self._timerThread = None
2013-01-03 20:38:46 +00:00
def interval(self):
return self._interval
2013-01-04 10:08:19 +00:00
def onPrintjobStarted(self, filename):
Timelapse.onPrintjobStarted(self, filename)
if self._timerThread is not None:
2013-01-03 20:38:46 +00:00
return
self._timerThread = threading.Thread(target=self.timerWorker)
self._timerThread.daemon = True
self._timerThread.start()
2013-01-03 20:38:46 +00:00
def timerWorker(self):
while self._inTimelapse:
2013-01-03 20:38:46 +00:00
self.captureImage()
time.sleep(self._interval)