From f802de88ce9cba3a2a4b1507e2a5aa2dfab4ec49 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Wed, 2 Oct 2024 20:33:57 -0700 Subject: [PATCH] Watermark QR code over webcam feeds to deter screen recording --- package-lock.json | 6 +++++ package.json | 1 + src/App.vue | 43 ++++++++++++++++++++++++++++-------- src/components/VideoFeed.vue | 34 +++++++++++++++++++++++++++- src/lib/watermark.js | 27 ++++++++++++++++++++++ 5 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 src/lib/watermark.js diff --git a/package-lock.json b/package-lock.json index 8e93a32..4d6a398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "floating-vue": "^2.0.0-beta.24", "interactjs": "^1.10.18", + "qrcodejs": "github:danielgjackson/qrcodejs", "vue": "^3.3.4", "vue-mention": "^2.0.0-alpha.3", "vue3-emoji-picker": "^1.1.7", @@ -1680,6 +1681,11 @@ "node": ">=6" } }, + "node_modules/qrcodejs": { + "version": "0.0.0", + "resolved": "git+ssh://git@github.com/danielgjackson/qrcodejs.git#86770ec12f0f9abee8728fc9018ab7bd0949f4bc", + "license": "BSD-2-Clause" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/package.json b/package.json index 6a9104d..9d1e26a 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "floating-vue": "^2.0.0-beta.24", "interactjs": "^1.10.18", + "qrcodejs": "github:danielgjackson/qrcodejs", "vue": "^3.3.4", "vue-mention": "^2.0.0-alpha.3", "vue3-emoji-picker": "^1.1.7", diff --git a/src/App.vue b/src/App.vue index 1eb7451..e27475d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -19,6 +19,7 @@ import LocalStorage from './lib/LocalStorage'; import VideoFlag from './lib/VideoFlag'; import StatusMessage from './lib/StatusMessage'; import { SoundEffects, DefaultSounds } from './lib/sounds'; +import WatermarkImage from './lib/watermark'; // WebRTC configuration. const configuration = { @@ -181,6 +182,10 @@ export default { rememberExpresslyClosed: true, // remember cams we expressly closed autoMuteWebcams: false, // auto-mute other cameras' audio channels + // My watermark image for screen recording protection. + // Set after login in setWatermark. + watermark: null, + // Who all is watching me? map of users. watching: {}, @@ -1630,6 +1635,10 @@ export default { onLoggedIn() { // Called after the first 'me' is received from the chat server, e.g. once per login. + // Load our watermark image. + this.webcam.watermark = WatermarkImage(this.username); + this.ChatClient(`Watermark image created: `); + // Do we auto-broadcast our camera? if (this.webcam.autoshare) { this.startVideo({ force: true }); @@ -4709,20 +4718,36 @@ export default { - - diff --git a/src/components/VideoFeed.vue b/src/components/VideoFeed.vue index b6ac382..1f05ba0 100644 --- a/src/components/VideoFeed.vue +++ b/src/components/VideoFeed.vue @@ -11,6 +11,7 @@ export default { isSourceMuted: Boolean, // camera is muted on the broadcaster's end isWatchingMe: Boolean, // other video is watching us back isFrozen: Boolean, // video is detected as frozen + watermarkImage: Image, // watermark image to overlay (nullable) }, components: { Slider, @@ -102,7 +103,13 @@ export default { 'popped-out': poppedOut, 'popped-in': !poppedOut, }" @mouseover="mouseOver = true" @mouseleave="mouseOver = false"> - + + + +
+ + +
@@ -187,4 +194,29 @@ video { .seethru { opacity: 0.4; } + +/* Watermark image */ +.watermark { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 40%; + height: 40%; + opacity: 0.02; +} +.corner-watermark { + position: absolute; + right: 4px; + bottom: 4px; + width: 20%; + min-width: 32px; + min-height: 32px; + max-height: 20%; +} +.invert-color { + filter: invert(100%); +} diff --git a/src/lib/watermark.js b/src/lib/watermark.js new file mode 100644 index 0000000..9d92593 --- /dev/null +++ b/src/lib/watermark.js @@ -0,0 +1,27 @@ +import QrCode from 'qrcodejs'; + +// WatermarkImage outputs a QR code containing watermark data about the current user. +// +// To help detect when someone has screen recorded and shared it, and being able to know who/when/etc. +function WatermarkImage(username) { + let now = new Date(); + let dateString = [ + now.getFullYear(), + ('0' + (now.getMonth()+1)).slice(-2), + ('0' + (now.getDate())).slice(-2), + ].join('-'); + + let fields = [ + window.location.hostname, + username, + dateString, + ].join(' '); + + console.error("watermark message:", fields); + + const matrix = QrCode.generate(fields); + const uri = QrCode.render('svg-uri', matrix); + return uri; +} + +export default WatermarkImage;