From 43207c940fca7a0313f90cdde0cef6e631c35d75 Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 16:29:17 +0200 Subject: [PATCH 01/14] Video Recorder --- features/features.json | 5 + features/video-recorder/data.json | 26 ++++++ features/video-recorder/popup.html | 53 +++++++++++ features/video-recorder/video-recorder.js | 109 ++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 features/video-recorder/data.json create mode 100644 features/video-recorder/popup.html create mode 100644 features/video-recorder/video-recorder.js diff --git a/features/features.json b/features/features.json index 9bc58c5c..6e545b93 100644 --- a/features/features.json +++ b/features/features.json @@ -1,4 +1,9 @@ [ + { + "version": 2, + "id": "video-recorder", + "versionAdded": "v4.0.0" + }, { "version": 2, "id": "studio-creation-date", diff --git a/features/video-recorder/data.json b/features/video-recorder/data.json new file mode 100644 index 00000000..0e4ac4fe --- /dev/null +++ b/features/video-recorder/data.json @@ -0,0 +1,26 @@ +{ + "title": "Video Recorder", + "description": "Record videos of Scratch projects.", + "credits": [ + { + "username": "stio_studio", + "url": "https://stio.studio/" + }, + { + "username": "blob2763", + "url": "https://blob2763.is-a.dev/" + } + ], + "type": ["Editor"], + "tags": ["New", "Featured"], + "dynamic": true, + "scripts": [ + { + "file": "video-recorder.js", + "runOn": "/projects/*" + } + ], + "resources": [ + { "name": "popup-html", "path": "/popup.html" } + ] +} diff --git a/features/video-recorder/popup.html b/features/video-recorder/popup.html new file mode 100644 index 00000000..e8cdd207 --- /dev/null +++ b/features/video-recorder/popup.html @@ -0,0 +1,53 @@ +
+ +
\ No newline at end of file diff --git a/features/video-recorder/video-recorder.js b/features/video-recorder/video-recorder.js new file mode 100644 index 00000000..3eeb96a5 --- /dev/null +++ b/features/video-recorder/video-recorder.js @@ -0,0 +1,109 @@ +export default async function ({ feature, console }) { + + const row = await new Promise(async (resolve, reject) => { + (async () => { + const rem = await ScratchTools.waitForElement(".preview .inner .flex-row.action-buttons") + resolve(rem); + })(); + (async () => { + const rem = await ScratchTools.waitForElement(".menu-bar_account-info-group_MeJZP") + resolve(rem); + })(); + }) + + let openPopup = document.createElement("button"); + openPopup.className = "button action-button ste-video-recorder-open"; + openPopup.textContent = "Record Video"; + row.insertAdjacentElement("afterbegin", openPopup); + + let popup = document.createElement("div"); + popup.insertAdjacentHTML("afterbegin", await (await fetch(feature.self.getResource("popup-html"))).text()) + popup = popup.querySelector("div.ReactModalPortal") + + let stopButton = popup.querySelector(".stopButton"); + let startButton = popup.querySelector(".startButton"); + let closeButton = popup.querySelector(".close-button_close-button_lOp2G"); + let downloadButton = popup.querySelector(".downloadButton"); + let lastDownloadFunction = ()=>{} + let mimeType = popup.querySelector("select"); + + + // console.log([stopButton, startButton]) + + openPopup.addEventListener('click', () => { + document.body.append(popup) + }) + + // console.log(closeButton) + closeButton.addEventListener('click', () => { + document.querySelector(".ReactModalPortal").remove() + }) + popup.querySelector(".ReactModal__Overlay").addEventListener('click', () => { + document.querySelector(".ReactModalPortal").remove() + }) + addEventListener("keydown", (e) => { + if (e.key === "Escape") { + document.querySelector(".ReactModalPortal").remove() + } + }) + + const canvas = feature.traps.vm.renderer.canvas; + const preview = popup.querySelector("video") + const projectTitle = document.querySelector("input.inplace-input") || document.querySelector("input.project-title-input_title-field_en5Gd") + // document.querySelector(".menu-bar_account-info-group_MeJZP").append(preview) + + let mediaRecorder; + let recordedChunks = []; + // console.log(startButton) + // Start recording + startButton.addEventListener('click', () => { + startButton.classList.add("STE-hide-button"); + stopButton.classList.remove("STE-hide-button"); + + // Capture the canvas element as a stream + const stream = canvas.captureStream(30); // 30 FPS + mediaRecorder = new MediaRecorder(stream); + + mediaRecorder.ondataavailable = function (event) { + if (event.data.size > 0) { + recordedChunks.push(event.data); + } + }; + + mediaRecorder.onstop = function () { + const blob = new Blob(recordedChunks, { + type: `video/${mimeType.value}` + }); + preview.src = URL.createObjectURL(blob); + preview.controls = true; + preview.download = `${projectTitle.value}.${mimeType.value}` + downloadButton.removeEventListener("click", lastDownloadFunction) + lastDownloadFunction = async () => { + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `${projectTitle.value}.${mimeType.value}` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + } + downloadButton.addEventListener("click", lastDownloadFunction) + recordedChunks = []; + }; + + mediaRecorder.start(); + startButton.disabled = true; + stopButton.disabled = false; + }); + + // Stop recording + stopButton.addEventListener('click', () => { + mediaRecorder.stop(); + startButton.disabled = false; + stopButton.disabled = true; + + stopButton.classList.add("STE-hide-button"); + startButton.classList.remove("STE-hide-button"); + }); +} From 785a87232fe545d2adb598dea5589fffcdbab282 Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 16:33:41 +0200 Subject: [PATCH 02/14] Bug fix --- features/video-recorder/popup.html | 2 +- features/video-recorder/video-recorder.js | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/features/video-recorder/popup.html b/features/video-recorder/popup.html index e8cdd207..59605748 100644 --- a/features/video-recorder/popup.html +++ b/features/video-recorder/popup.html @@ -1,4 +1,4 @@ -
+
- \ No newline at end of file + From 3925cc114c1a45f9118bc20a90cbb40b19652c42 Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 18:01:10 +0200 Subject: [PATCH 05/14] popup opener button not being shown --- features/video-recorder/video-recorder.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/features/video-recorder/video-recorder.js b/features/video-recorder/video-recorder.js index e9daa9cc..701eaba2 100644 --- a/features/video-recorder/video-recorder.js +++ b/features/video-recorder/video-recorder.js @@ -1,8 +1,11 @@ export default async function ({ feature, console }) { + let openPopup = document.createElement("button"); + openPopup.className = "button action-button ste-video-recorder-open"; + openPopup.textContent = "Record Video"; - const row = await new Promise(async (resolve, reject) => { + await new Promise(async (resolve, reject) => { (async () => { - const rem = await ScratchTools.waitForElement(".preview .inner .flex-row.action-buttons") + const rem = await ScratchTools.waitForElement(".preview .inner .flex-row.action-buttons") resolve(rem); })(); (async () => { @@ -10,12 +13,16 @@ export default async function ({ feature, console }) { resolve(rem); })(); }) - - let openPopup = document.createElement("button"); - openPopup.className = "button action-button ste-video-recorder-open"; - openPopup.textContent = "Record Video"; - row.insertAdjacentElement("afterbegin", openPopup); - + + ScratchTools.waitForElements(".preview .inner .flex-row.action-buttons", async function (row) { + if (row.querySelector(".ste-video-recorder-open")) return; + row.insertAdjacentElement("afterbegin", openPopup); + }) + ScratchTools.waitForElements(".menu-bar_account-info-group_MeJZP", async function (row) { + if (row.querySelector(".ste-video-recorder-open")) return; + row.insertAdjacentElement("afterbegin", openPopup); + }) + let popup = document.createElement("div"); popup.insertAdjacentHTML("afterbegin", await (await fetch(feature.self.getResource("popup-html"))).text()) popup = popup.querySelector("div.ReactModalPortal") From 09aa23d120b28cb6b9b24ae4bc6ab7fac2fb828a Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 18:10:14 +0200 Subject: [PATCH 06/14] Elements fix --- features/video-recorder/popup.html | 18 ++++++++----- features/video-recorder/video-recorder.js | 33 ++++++++++++----------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/features/video-recorder/popup.html b/features/video-recorder/popup.html index 83a404d2..f02484bb 100644 --- a/features/video-recorder/popup.html +++ b/features/video-recorder/popup.html @@ -13,19 +13,25 @@ @@ -43,11 +49,11 @@

Preview:
- + - + \ No newline at end of file diff --git a/features/video-recorder/video-recorder.js b/features/video-recorder/video-recorder.js index 701eaba2..ab51fe4e 100644 --- a/features/video-recorder/video-recorder.js +++ b/features/video-recorder/video-recorder.js @@ -1,11 +1,7 @@ export default async function ({ feature, console }) { - let openPopup = document.createElement("button"); - openPopup.className = "button action-button ste-video-recorder-open"; - openPopup.textContent = "Record Video"; - await new Promise(async (resolve, reject) => { (async () => { - const rem = await ScratchTools.waitForElement(".preview .inner .flex-row.action-buttons") + const rem = await ScratchTools.waitForElement(".preview .inner .flex-row.action-buttons") resolve(rem); })(); (async () => { @@ -13,16 +9,30 @@ export default async function ({ feature, console }) { resolve(rem); })(); }) + + let openPopup = document.createElement("button"); ScratchTools.waitForElements(".preview .inner .flex-row.action-buttons", async function (row) { if (row.querySelector(".ste-video-recorder-open")) return; + openPopup = document.createElement("button"); + openPopup.className = "button action-button ste-video-recorder-open"; + openPopup.textContent = "Record Video"; row.insertAdjacentElement("afterbegin", openPopup); + openPopup.addEventListener('click', () => { + document.body.append(popup) + }) }) ScratchTools.waitForElements(".menu-bar_account-info-group_MeJZP", async function (row) { if (row.querySelector(".ste-video-recorder-open")) return; + openPopup = document.createElement("div"); + openPopup.className = "menu-bar_menu-bar-item_oLDa- menu-bar_hoverable_c6WFB"; + openPopup.textContent = "Record Video"; row.insertAdjacentElement("afterbegin", openPopup); + openPopup.addEventListener('click', () => { + document.body.append(popup) + }) }) - + let popup = document.createElement("div"); popup.insertAdjacentHTML("afterbegin", await (await fetch(feature.self.getResource("popup-html"))).text()) popup = popup.querySelector("div.ReactModalPortal") @@ -31,17 +41,10 @@ export default async function ({ feature, console }) { let startButton = popup.querySelector(".startButton"); let closeButton = popup.querySelector(".close-button_close-button_lOp2G"); let downloadButton = popup.querySelector(".downloadButton"); - let lastDownloadFunction = ()=>{} + let lastDownloadFunction = () => { } let mimeType = popup.querySelector("select"); - // console.log([stopButton, startButton]) - - openPopup.addEventListener('click', () => { - document.body.append(popup) - }) - - // console.log(closeButton) closeButton.addEventListener('click', () => { document.querySelector(".STE-ReactModalPortal").remove() }) @@ -82,7 +85,7 @@ export default async function ({ feature, console }) { preview.controls = true; preview.download = `${projectTitle.value}.${mimeType.value}` downloadButton.removeEventListener("click", lastDownloadFunction) - lastDownloadFunction = async () => { + lastDownloadFunction = async () => { const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url From 91f81a0fc1adf8a4049654ab33dc412648220530 Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 18:20:10 +0200 Subject: [PATCH 07/14] Padding fix? --- features/video-recorder/video-recorder.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/video-recorder/video-recorder.js b/features/video-recorder/video-recorder.js index ab51fe4e..a991e00d 100644 --- a/features/video-recorder/video-recorder.js +++ b/features/video-recorder/video-recorder.js @@ -26,7 +26,9 @@ export default async function ({ feature, console }) { if (row.querySelector(".ste-video-recorder-open")) return; openPopup = document.createElement("div"); openPopup.className = "menu-bar_menu-bar-item_oLDa- menu-bar_hoverable_c6WFB"; - openPopup.textContent = "Record Video"; + let rem = document.createElement("div"); + rem.textContent = "Record Video"; + openPopup.append(rem); row.insertAdjacentElement("afterbegin", openPopup); openPopup.addEventListener('click', () => { document.body.append(popup) From 9f82912262dba8a261cf0eb15dfcc9396452c584 Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 18:37:54 +0200 Subject: [PATCH 08/14] scratchtoolsTag --- features/video-recorder/popup.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/video-recorder/popup.html b/features/video-recorder/popup.html index f02484bb..3681b7bc 100644 --- a/features/video-recorder/popup.html +++ b/features/video-recorder/popup.html @@ -41,7 +41,7 @@ name="Rename all "box size" variables to:" value="box size"> -->
- +

- - - -

- Preview:
- - -
- - +
+ + +

+ +

+ Preview:
+ + +
+ - \ No newline at end of file + + From 13fc06f6d5473f7fb0f100ba8eef961607765930 Mon Sep 17 00:00:00 2001 From: MaterArc <105017592+MaterArc@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:52:24 -0400 Subject: [PATCH 10/14] Add CSS file --- features/video-recorder/style.css | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 features/video-recorder/style.css diff --git a/features/video-recorder/style.css b/features/video-recorder/style.css new file mode 100644 index 00000000..7f62e73c --- /dev/null +++ b/features/video-recorder/style.css @@ -0,0 +1,21 @@ +.STE-ReactModalPortal .STE-recorded-video { + width: 100%; + height: 100%; + border: 10px solid #ccc; + border-radius: 10px; +} + +.STE-ReactModalPortal .STE-hide-button { + display: none; +} + +.STE-ReactModalPortal .STE-left-text { + text-align: left; +} + +.STE-ReactModalPortal .stopButton, +.STE-ReactModalPortal .startButton, +.STE-ReactModalPortal .downloadButton, +.STE-ReactModalPortal .video-format-select { + width: 100%; +} From ce95a08eb2d17d5904d2a84ba22ad0a3d24ca751 Mon Sep 17 00:00:00 2001 From: MaterArc <105017592+MaterArc@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:55:37 -0400 Subject: [PATCH 11/14] Update JSON file --- features/video-recorder/data.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/features/video-recorder/data.json b/features/video-recorder/data.json index 332b5422..97288bc0 100644 --- a/features/video-recorder/data.json +++ b/features/video-recorder/data.json @@ -20,7 +20,16 @@ "runOn": "/projects/*" } ], + "styles": [ + { + "file": "style.css", + "runOn": "/projects/*" + } + ], "resources": [ - { "name": "popup-html", "path": "/popup.html" } + { + "name": "popup-html", + "path": "/popup.html" + } ] } From 157f647adb11b1d16516a0f550aed2ec35510146 Mon Sep 17 00:00:00 2001 From: stio Date: Tue, 30 Jul 2024 20:49:14 +0200 Subject: [PATCH 12/14] Audio Update --- features/video-recorder/popup.html | 39 +++----- features/video-recorder/video-recorder.js | 107 +++++++++++++++++----- 2 files changed, 97 insertions(+), 49 deletions(-) diff --git a/features/video-recorder/popup.html b/features/video-recorder/popup.html index 746c1c6d..ef9c057c 100644 --- a/features/video-recorder/popup.html +++ b/features/video-recorder/popup.html @@ -1,33 +1,18 @@
-