diff --git a/octoprint/server.py b/octoprint/server.py index 03665a7..60c7374 100644 --- a/octoprint/server.py +++ b/octoprint/server.py @@ -349,7 +349,8 @@ def getSettings(): "streamUrl": s.get(["webcam", "stream"]), "snapshotUrl": s.get(["webcam", "snapshot"]), "ffmpegPath": s.get(["webcam", "ffmpeg"]), - "bitrate": s.get(["webcam", "bitrate"]) + "bitrate": s.get(["webcam", "bitrate"]), + "watermark": s.getBoolean(["webcam", "watermark"]) }, "feature": { "gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]), @@ -387,6 +388,7 @@ def setSettings(): if "snapshotUrl" in data["webcam"].keys(): s.set(["webcam", "snapshot"], data["webcam"]["snapshotUrl"]) if "ffmpeg" in data["webcam"].keys(): s.set(["webcam", "ffmpeg"], data["webcam"]["ffmpeg"]) if "bitrate" in data["webcam"].keys(): s.set(["webcam", "bitrate"], data["webcam"]["bitrate"]) + if "watermark" in data["webcam"].keys(): s.setBoolean(["webcam", "watermark"], data["webcam"]["watermark"]) if "feature" in data.keys(): if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["feature", "gCodeVisualizer"], data["feature"]["gcodeViewer"]) diff --git a/octoprint/settings.py b/octoprint/settings.py index 8bb4480..2b096bc 100644 --- a/octoprint/settings.py +++ b/octoprint/settings.py @@ -32,7 +32,8 @@ old_default_settings = { "stream": None, "snapshot": None, "ffmpeg": None, - "bitrate": "5000k" + "bitrate": "5000k", + "watermark": True }, "folder": { "uploads": None, diff --git a/octoprint/static/img/watermark.png b/octoprint/static/img/watermark.png new file mode 100644 index 0000000..18d559f Binary files /dev/null and b/octoprint/static/img/watermark.png differ diff --git a/octoprint/static/js/ui.js b/octoprint/static/js/ui.js index eb7f54c..70a49f9 100644 --- a/octoprint/static/js/ui.js +++ b/octoprint/static/js/ui.js @@ -977,6 +977,7 @@ function SettingsViewModel() { self.webcam_snapshotUrl = ko.observable(undefined); self.webcam_ffmpegPath = ko.observable(undefined); self.webcam_bitrate = ko.observable(undefined); + self.webcam_watermark = ko.observable(undefined); self.feature_gcodeViewer = ko.observable(undefined); self.feature_waitForStart = ko.observable(undefined); @@ -1018,6 +1019,7 @@ function SettingsViewModel() { self.webcam_snapshotUrl(response.webcam.snapshotUrl); self.webcam_ffmpegPath(response.webcam.ffmpegPath); self.webcam_bitrate(response.webcam.bitrate); + self.webcam_watermark(response.webcam.watermark); self.feature_gcodeViewer(response.feature.gcodeViewer); self.feature_waitForStart(response.feature.waitForStart); @@ -1046,7 +1048,8 @@ function SettingsViewModel() { "streamUrl": self.webcam_streamUrl(), "snapshotUrl": self.webcam_snapshotUrl(), "ffmpegPath": self.webcam_ffmpegPath(), - "bitrate": self.webcam_bitrate() + "bitrate": self.webcam_bitrate(), + "watermark": self.webcam_watermark() }, "feature": { "gcodeViewer": self.feature_gcodeViewer(), diff --git a/octoprint/templates/settings.html b/octoprint/templates/settings.html index 883cc59..9005fce 100644 --- a/octoprint/templates/settings.html +++ b/octoprint/templates/settings.html @@ -81,6 +81,13 @@ +
+
+ +
+
diff --git a/octoprint/timelapse.py b/octoprint/timelapse.py index 1c85254..d8dcdce 100644 --- a/octoprint/timelapse.py +++ b/octoprint/timelapse.py @@ -13,6 +13,8 @@ import subprocess import fnmatch import datetime +import sys + def getFinishedTimelapses(): files = [] basedir = settings().getBaseFolder("timelapse") @@ -91,10 +93,24 @@ class Timelapse(object): 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"))) - subprocess.call([ - ffmpeg, '-i', input, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', - '-b:v', bitrate, '-f', 'vob', output - ]) + + # prepare ffmpeg command + command = [ + ffmpeg, '-i', input, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', '-b:v', bitrate, + '-f', 'vob'] + + # add watermark if configured + if settings().getBoolean(["webcam", "watermark"]): + watermark = os.path.join(os.path.dirname(__file__), "static", "img", "watermark.png") + if sys.platform == "win32": + # Because ffmpeg hiccups on windows' drive letters and backslashes we have to give the watermark + # path a special treatment. Yeah, I couldn't believe it either... + watermark = watermark.replace("\\", "/").replace(":", "\\\\:") + command.extend(['-vf', 'movie=%s [wm]; [in][wm] overlay=10:main_h-overlay_h-10 [out]' % (watermark)]) + + # finalize command with output file + command.append(output) + subprocess.call(command) def cleanCaptureDir(self): if not os.path.isdir(self._captureDir):