From 31447706167e3650cf6f69b6fa0e2375b11736c5 Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Thu, 4 Sep 2025 13:49:37 +0200 Subject: [PATCH 1/7] Create cameratools.js --- extensions/libmaster169/cameratools.js | 131 +++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 extensions/libmaster169/cameratools.js diff --git a/extensions/libmaster169/cameratools.js b/extensions/libmaster169/cameratools.js new file mode 100644 index 0000000000..f4546f3822 --- /dev/null +++ b/extensions/libmaster169/cameratools.js @@ -0,0 +1,131 @@ +// Name: Camera Tools +// ID: cameraExtensionByLibmaster169 +// Description: Start and stop camera, read image from camera, and more +// By: libmaster169 +// License: MPL-2.0 +class CameraExtension { + constructor() { + this.video = document.createElement("video"); + this.canvas = document.createElement("canvas"); + this.context = this.canvas.getContext("2d"); + this.codeImage = ""; + this.stream = null; + this.mediaRecorder = null; + this.videoChunks = []; + this.filmDataURL = ""; + } + + getInfo() { + return { + id: 'cameraExtensionByLibmaster169', + name: 'Camera Tools', + blocks: [ + { opcode: 'startCamera', blockType: Scratch.BlockType.COMMAND, text: 'Uruchom kamerę' }, + { opcode: 'stopCamera', blockType: Scratch.BlockType.COMMAND, text: 'Wyłącz kamerę' }, + { opcode: 'captureFrame', blockType: Scratch.BlockType.COMMAND, text: 'Zapisz obraz z kamery' }, + { opcode: 'getBinaryImage', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz zakodowany obraz' }, + { opcode: 'getPixelBinary', blockType: Scratch.BlockType.REPORTER, text: 'Piksel [X], [Y] w formie kodu', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, + { opcode: 'joinFrames', blockType: Scratch.BlockType.COMMAND, text: 'Połącz obraz z [DATA]', arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "" } } }, + { opcode: 'setPixelTo', blockType: Scratch.BlockType.COMMAND, text: 'Ustaw piksel [X], [Y] na kod [VALUE]', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "900" } } }, + { opcode: 'getFrameDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz klatkę jako DATA:url' }, + { opcode: 'startFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Start nagrywania filmu' }, + { opcode: 'stopFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Zatrzymaj nagrywanie filmu' }, + { opcode: 'getFilmDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz film jako DATA:url' } + ] + }; + } + + startCamera() { + navigator.mediaDevices.getUserMedia({ video: true }).then(stream => { + this.stream = stream; + this.video.srcObject = stream; + this.video.play(); + }).catch(error => console.error("Błąd przy uruchamianiu kamery:", error)); + } + + stopCamera() { + if (this.stream) { + this.stream.getTracks().forEach(track => track.stop()); + this.video.srcObject = null; + this.stream = null; + } + } + + captureFrame() { + this.canvas.width = this.video.videoWidth; + this.canvas.height = this.video.videoHeight; + this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height); + + let imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); + let result = ""; + for (let i = 0; i < imageData.data.length; i += 4) { + result += this.rgbToCode(imageData.data[i], imageData.data[i + 1], imageData.data[i + 2]); + } + this.codeImage = result; + } + + getBinaryImage() { + return this.codeImage; + } + + getPixelBinary(args) { + let x = args.X, y = args.Y; + let pixelData = this.context.getImageData(x, y, 1, 1).data; + return this.rgbToCode(pixelData[0], pixelData[1], pixelData[2]); + } + + joinFrames(args) { + this.codeImage += args.DATA; + } + + setPixelTo(args) { + let x = args.X, y = args.Y, value = args.VALUE.toString(); + let color = this.codeToRGB(value); + let imageData = this.context.getImageData(x, y, 1, 1); + [imageData.data[0], imageData.data[1], imageData.data[2], imageData.data[3]] = color; + this.context.putImageData(imageData, x, y); + } + + rgbToCode(r, g, b) { + let R = Math.floor((r / 255) * 9); + let G = Math.floor((g / 255) * 9); + let B = Math.floor((b / 255) * 9); + return `${R}${G}${B}`; + } + + codeToRGB(code) { + let str = code.toString().padStart(3, '0'); + return [Math.round((parseInt(str[0]) / 9) * 255), Math.round((parseInt(str[1]) / 9) * 255), Math.round((parseInt(str[2]) / 9) * 255), 255]; + } + + getFrameDataURL() { + return this.canvas.toDataURL("image/png"); + } + + startFilmRecording() { + if (this.stream) { + this.videoChunks = []; + this.mediaRecorder = new MediaRecorder(this.stream, { mimeType: "video/webm" }); + this.mediaRecorder.ondataavailable = event => this.videoChunks.push(event.data); + this.mediaRecorder.start(); + } + } + + stopFilmRecording() { + if (this.mediaRecorder) { + this.mediaRecorder.onstop = () => { + const blob = new Blob(this.videoChunks, { type: "video/webm" }); + const reader = new FileReader(); + reader.onloadend = () => this.filmDataURL = reader.result; + reader.readAsDataURL(blob); + }; + this.mediaRecorder.stop(); + } + } + + getFilmDataURL() { + return this.filmDataURL; + } +} + +Scratch.extensions.register(new CameraExtension()); From a7e14cbd78774ac0e799a2aa102fbd80b4eeefd3 Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Thu, 4 Sep 2025 13:52:14 +0200 Subject: [PATCH 2/7] Update extensions.json --- extensions/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/extensions.json b/extensions/extensions.json index d8ed71e33d..e82458d2b5 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -76,6 +76,7 @@ "Longboost/color_channels", "CST1229/zip", "CST1229/images", + "libmaster169/cameratools", "TheShovel/LZ-String", "0832/rxFS2", "NexusKitten/sgrab", From 7a684ba517c20a1f7641a470acccf52cee0b723c Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Thu, 4 Sep 2025 14:15:57 +0200 Subject: [PATCH 3/7] Create audiotools.js --- extensions/libmaster169/audiotools.js | 98 +++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 extensions/libmaster169/audiotools.js diff --git a/extensions/libmaster169/audiotools.js b/extensions/libmaster169/audiotools.js new file mode 100644 index 0000000000..c1bc28318b --- /dev/null +++ b/extensions/libmaster169/audiotools.js @@ -0,0 +1,98 @@ +// Name: Audio Tools +// ID: audioExtension +// Description: Read raw audio data from the microphone +// By: libmaster169 +// License: MPL-2.0 + +class AudioExtension { + constructor() { + this.recorder = null; + this.audioData = []; + this.context = new (window.AudioContext || window.webkitAudioContext)(); + this.sampleRate = this.context.sampleRate; + } + + getInfo() { + return { + id: 'audioExtension', + name: 'Audio Tools', + blocks: [ + { opcode: 'startRecording', blockType: Scratch.BlockType.COMMAND, text: 'Rozpocznij nagrywanie' }, + { opcode: 'stopRecording', blockType: Scratch.BlockType.COMMAND, text: 'Zatrzymaj nagrywanie' }, + { opcode: 'getLen', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz długość nagrania w sekundach' }, + { opcode: 'getBinaryData', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz nagranie' }, + { opcode: 'getSampleBinary', blockType: Scratch.BlockType.REPORTER, text: 'Próbka [INDEX]', arguments: { INDEX: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, + { opcode: 'getSampleRate', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz częstotliwość próbkowania' }, + { opcode: 'playRecording', blockType: Scratch.BlockType.COMMAND, text: 'Odtwórz nagranie' }, + { opcode: 'joinRecording', blockType: Scratch.BlockType.COMMAND, text: 'Połącz nagranie z [DATA]', arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "0,0,0" } } }, + { opcode: 'setSampleTo', blockType: Scratch.BlockType.COMMAND, text: 'Ustaw próbkę [INDEX] na [VALUE]', arguments: { INDEX: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } } + ] + }; + } + + startRecording() { + navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { + this.recorder = new MediaRecorder(stream); + let dataChunks = []; + this.recorder.ondataavailable = event => dataChunks.push(event.data); + this.recorder.onstop = async () => { + let audioBlob = new Blob(dataChunks, { type: 'audio/wav' }); + let arrayBuffer = await audioBlob.arrayBuffer(); + let audioBuffer = await this.context.decodeAudioData(arrayBuffer); + this.audioData = audioBuffer.getChannelData(0); + this.sampleRate = audioBuffer.sampleRate; + }; + this.recorder.start(); + }); + } + + stopRecording() { + if (this.recorder) { + this.recorder.stop(); + } + } + + getBinaryData() { + return this.audioData.map(sample => (sample)).join(","); + } + + getSampleBinary(args) { + const index = args.INDEX; + if (this.audioData && this.audioData.length > index) { + return this.audioData[index]; + } + return 0; + } + + getSampleRate() { + return this.sampleRate; + } + + playRecording() { + if (!this.audioData.length) return; + + const buffer = this.context.createBuffer(1, this.audioData.length, this.sampleRate); + buffer.copyToChannel(new Float32Array(this.audioData), 0); + const source = this.context.createBufferSource(); + source.buffer = buffer; + source.connect(this.context.destination); + source.start(); + } + + joinRecording(args) { + const newData = args.DATA.split(",").map(Number); + this.audioData = [].concat(newData); + } + getLen() { + return this.audioData.map(sample => (sample)).join(",").length / this.sampleRate; + } + setSampleTo(args) { + const index = args.INDEX; + const value = args.VALUE; + if (this.audioData && this.audioData.length > index) { + this.audioData[index] = value; + } + } +} + +Scratch.extensions.register(new AudioExtension()); From 4d6e50487e65186356385d6e3b8d91535e083a4d Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Thu, 4 Sep 2025 14:18:15 +0200 Subject: [PATCH 4/7] Update extensions.json --- extensions/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/extensions.json b/extensions/extensions.json index e82458d2b5..85a9ef0d54 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -75,6 +75,7 @@ "TheShovel/CanvasEffects", "Longboost/color_channels", "CST1229/zip", + "libmaster169/audiotools", "CST1229/images", "libmaster169/cameratools", "TheShovel/LZ-String", From d5a8447010a0eee52b0a58413d9859672d50dfc4 Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Sun, 7 Sep 2025 12:03:30 +0200 Subject: [PATCH 5/7] Delete extensions/libmaster169/audiotools.js --- extensions/libmaster169/audiotools.js | 98 --------------------------- 1 file changed, 98 deletions(-) delete mode 100644 extensions/libmaster169/audiotools.js diff --git a/extensions/libmaster169/audiotools.js b/extensions/libmaster169/audiotools.js deleted file mode 100644 index c1bc28318b..0000000000 --- a/extensions/libmaster169/audiotools.js +++ /dev/null @@ -1,98 +0,0 @@ -// Name: Audio Tools -// ID: audioExtension -// Description: Read raw audio data from the microphone -// By: libmaster169 -// License: MPL-2.0 - -class AudioExtension { - constructor() { - this.recorder = null; - this.audioData = []; - this.context = new (window.AudioContext || window.webkitAudioContext)(); - this.sampleRate = this.context.sampleRate; - } - - getInfo() { - return { - id: 'audioExtension', - name: 'Audio Tools', - blocks: [ - { opcode: 'startRecording', blockType: Scratch.BlockType.COMMAND, text: 'Rozpocznij nagrywanie' }, - { opcode: 'stopRecording', blockType: Scratch.BlockType.COMMAND, text: 'Zatrzymaj nagrywanie' }, - { opcode: 'getLen', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz długość nagrania w sekundach' }, - { opcode: 'getBinaryData', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz nagranie' }, - { opcode: 'getSampleBinary', blockType: Scratch.BlockType.REPORTER, text: 'Próbka [INDEX]', arguments: { INDEX: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, - { opcode: 'getSampleRate', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz częstotliwość próbkowania' }, - { opcode: 'playRecording', blockType: Scratch.BlockType.COMMAND, text: 'Odtwórz nagranie' }, - { opcode: 'joinRecording', blockType: Scratch.BlockType.COMMAND, text: 'Połącz nagranie z [DATA]', arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "0,0,0" } } }, - { opcode: 'setSampleTo', blockType: Scratch.BlockType.COMMAND, text: 'Ustaw próbkę [INDEX] na [VALUE]', arguments: { INDEX: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } } - ] - }; - } - - startRecording() { - navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { - this.recorder = new MediaRecorder(stream); - let dataChunks = []; - this.recorder.ondataavailable = event => dataChunks.push(event.data); - this.recorder.onstop = async () => { - let audioBlob = new Blob(dataChunks, { type: 'audio/wav' }); - let arrayBuffer = await audioBlob.arrayBuffer(); - let audioBuffer = await this.context.decodeAudioData(arrayBuffer); - this.audioData = audioBuffer.getChannelData(0); - this.sampleRate = audioBuffer.sampleRate; - }; - this.recorder.start(); - }); - } - - stopRecording() { - if (this.recorder) { - this.recorder.stop(); - } - } - - getBinaryData() { - return this.audioData.map(sample => (sample)).join(","); - } - - getSampleBinary(args) { - const index = args.INDEX; - if (this.audioData && this.audioData.length > index) { - return this.audioData[index]; - } - return 0; - } - - getSampleRate() { - return this.sampleRate; - } - - playRecording() { - if (!this.audioData.length) return; - - const buffer = this.context.createBuffer(1, this.audioData.length, this.sampleRate); - buffer.copyToChannel(new Float32Array(this.audioData), 0); - const source = this.context.createBufferSource(); - source.buffer = buffer; - source.connect(this.context.destination); - source.start(); - } - - joinRecording(args) { - const newData = args.DATA.split(",").map(Number); - this.audioData = [].concat(newData); - } - getLen() { - return this.audioData.map(sample => (sample)).join(",").length / this.sampleRate; - } - setSampleTo(args) { - const index = args.INDEX; - const value = args.VALUE; - if (this.audioData && this.audioData.length > index) { - this.audioData[index] = value; - } - } -} - -Scratch.extensions.register(new AudioExtension()); From 1930456eee2de676ff728d0e649b77311144bed2 Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Sun, 7 Sep 2025 12:15:09 +0200 Subject: [PATCH 6/7] Update cameratools.js --- extensions/libmaster169/cameratools.js | 33 +++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/extensions/libmaster169/cameratools.js b/extensions/libmaster169/cameratools.js index f4546f3822..cc5692fe55 100644 --- a/extensions/libmaster169/cameratools.js +++ b/extensions/libmaster169/cameratools.js @@ -17,22 +17,23 @@ class CameraExtension { getInfo() { return { - id: 'cameraExtensionByLibmaster169', - name: 'Camera Tools', - blocks: [ - { opcode: 'startCamera', blockType: Scratch.BlockType.COMMAND, text: 'Uruchom kamerę' }, - { opcode: 'stopCamera', blockType: Scratch.BlockType.COMMAND, text: 'Wyłącz kamerę' }, - { opcode: 'captureFrame', blockType: Scratch.BlockType.COMMAND, text: 'Zapisz obraz z kamery' }, - { opcode: 'getBinaryImage', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz zakodowany obraz' }, - { opcode: 'getPixelBinary', blockType: Scratch.BlockType.REPORTER, text: 'Piksel [X], [Y] w formie kodu', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, - { opcode: 'joinFrames', blockType: Scratch.BlockType.COMMAND, text: 'Połącz obraz z [DATA]', arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "" } } }, - { opcode: 'setPixelTo', blockType: Scratch.BlockType.COMMAND, text: 'Ustaw piksel [X], [Y] na kod [VALUE]', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "900" } } }, - { opcode: 'getFrameDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz klatkę jako DATA:url' }, - { opcode: 'startFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Start nagrywania filmu' }, - { opcode: 'stopFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Zatrzymaj nagrywanie filmu' }, - { opcode: 'getFilmDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Pobierz film jako DATA:url' } - ] - }; + id: 'cameraExtensionByLibmaster169', + name: 'Camera Tools', + blocks: [ + { opcode: 'startCamera', blockType: Scratch.BlockType.COMMAND, text: 'Start camera' }, + { opcode: 'stopCamera', blockType: Scratch.BlockType.COMMAND, text: 'Stop camera' }, + { opcode: 'captureFrame', blockType: Scratch.BlockType.COMMAND, text: 'Capture image from camera' }, + { opcode: 'getBinaryImage', blockType: Scratch.BlockType.REPORTER, text: 'Get encoded image' }, + { opcode: 'getPixelBinary', blockType: Scratch.BlockType.REPORTER, text: 'Pixel [X], [Y] as code', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, + { opcode: 'joinFrames', blockType: Scratch.BlockType.COMMAND, text: 'Join image with [DATA]', arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "" } } }, + { opcode: 'setPixelTo', blockType: Scratch.BlockType.COMMAND, text: 'Set pixel [X], [Y] to code [VALUE]', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "900" } } }, + { opcode: 'getFrameDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Get frame as DATA:url' }, + { opcode: 'startFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Start film recording' }, + { opcode: 'stopFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Stop film recording' }, + { opcode: 'getFilmDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Get film as DATA:url' } + ] +}; + } startCamera() { From 9b243e02705a22371134f4b61f0533890ead18c7 Mon Sep 17 00:00:00 2001 From: libmaster169 Date: Sun, 7 Sep 2025 12:33:26 +0200 Subject: [PATCH 7/7] Update cameratools.js --- extensions/libmaster169/cameratools.js | 139 ++++++++++++++++++++++--- 1 file changed, 124 insertions(+), 15 deletions(-) diff --git a/extensions/libmaster169/cameratools.js b/extensions/libmaster169/cameratools.js index cc5692fe55..cf15c3717c 100644 --- a/extensions/libmaster169/cameratools.js +++ b/extensions/libmaster169/cameratools.js @@ -3,6 +3,115 @@ // Description: Start and stop camera, read image from camera, and more // By: libmaster169 // License: MPL-2.0 +/* generated l10n code */ +Scratch.translate.setup({ + pl: { + "_Start camera": "Uruchom kamerę", + "_Stop camera": "Wyłącz kamerę", + "_Capture image from camera": "Zapisz obraz z kamery", + "_Get encoded image": "Pobierz zakodowany obraz", + "_Pixel [X], [Y] as code": "Piksel [X], [Y] jako kod", + "_Join image with [DATA]": "Połącz obraz z [DATA]", + "_Set pixel [X], [Y] to code [VALUE]": "Ustaw piksel [X], [Y] na kod [VALUE]", + "_Get frame as DATA:url": "Pobierz klatkę jako DATA:url", + "_Start film recording": "Rozpocznij nagrywanie filmu", + "_Stop film recording": "Zatrzymaj nagrywanie filmu", + "_Get film as DATA:url": "Pobierz film jako DATA:url" + }, + en: { + "_Start camera": "Start camera", + "_Stop camera": "Stop camera", + "_Capture image from camera": "Capture image from camera", + "_Get encoded image": "Get encoded image", + "_Pixel [X], [Y] as code": "Pixel [X], [Y] as code", + "_Join image with [DATA]": "Join image with [DATA]", + "_Set pixel [X], [Y] to code [VALUE]": "Set pixel [X], [Y] to code [VALUE]", + "_Get frame as DATA:url": "Get frame as DATA:url", + "_Start film recording": "Start film recording", + "_Stop film recording": "Stop film recording", + "_Get film as DATA:url": "Get film as DATA:url" + }, + de: { + "_Start camera": "Kamera starten", + "_Stop camera": "Kamera stoppen", + "_Capture image from camera": "Bild von Kamera aufnehmen", + "_Get encoded image": "Kodiertes Bild abrufen", + "_Pixel [X], [Y] as code": "Pixel [X], [Y] als Code", + "_Join image with [DATA]": "Bild mit [DATA] verbinden", + "_Set pixel [X], [Y] to code [VALUE]": "Pixel [X], [Y] auf Code [VALUE] setzen", + "_Get frame as DATA:url": "Frame als DATA:url abrufen", + "_Start film recording": "Filmaufnahme starten", + "_Stop film recording": "Filmaufnahme stoppen", + "_Get film as DATA:url": "Film als DATA:url abrufen" + }, + fr: { + "_Start camera": "Démarrer la caméra", + "_Stop camera": "Arrêter la caméra", + "_Capture image from camera": "Capturer une image de la caméra", + "_Get encoded image": "Obtenir l'image encodée", + "_Pixel [X], [Y] as code": "Pixel [X], [Y] en code", + "_Join image with [DATA]": "Joindre l'image avec [DATA]", + "_Set pixel [X], [Y] to code [VALUE]": "Définir le pixel [X], [Y] sur le code [VALUE]", + "_Get frame as DATA:url": "Obtenir l'image comme DATA:url", + "_Start film recording": "Démarrer l'enregistrement vidéo", + "_Stop film recording": "Arrêter l'enregistrement vidéo", + "_Get film as DATA:url": "Obtenir le film comme DATA:url" + }, + es: { + "_Start camera": "Iniciar cámara", + "_Stop camera": "Detener cámara", + "_Capture image from camera": "Capturar imagen de la cámara", + "_Get encoded image": "Obtener imagen codificada", + "_Pixel [X], [Y] as code": "Píxel [X], [Y] como código", + "_Join image with [DATA]": "Unir imagen con [DATA]", + "_Set pixel [X], [Y] to code [VALUE]": "Establecer píxel [X], [Y] al código [VALUE]", + "_Get frame as DATA:url": "Obtener fotograma como DATA:url", + "_Start film recording": "Iniciar grabación de vídeo", + "_Stop film recording": "Detener grabación de vídeo", + "_Get film as DATA:url": "Obtener vídeo como DATA:url" + }, + ja: { + "_Start camera": "カメラを起動", + "_Stop camera": "カメラを停止", + "_Capture image from camera": "カメラから画像を取得", + "_Get encoded image": "エンコードされた画像を取得", + "_Pixel [X], [Y] as code": "ピクセル [X], [Y] をコードとして取得", + "_Join image with [DATA]": "[DATA] と画像を結合", + "_Set pixel [X], [Y] to code [VALUE]": "ピクセル [X], [Y] をコード [VALUE] に設定", + "_Get frame as DATA:url": "フレームを DATA:url として取得", + "_Start film recording": "録画を開始", + "_Stop film recording": "録画を停止", + "_Get film as DATA:url": "動画を DATA:url として取得" + }, + ru: { + "_Start camera": "Запустить камеру", + "_Stop camera": "Остановить камеру", + "_Capture image from camera": "Сделать снимок с камеры", + "_Get encoded image": "Получить закодированное изображение", + "_Pixel [X], [Y] as code": "Пиксель [X], [Y] как код", + "_Join image with [DATA]": "Объединить изображение с [DATA]", + "_Set pixel [X], [Y] to code [VALUE]": "Установить пиксель [X], [Y] на код [VALUE]", + "_Get frame as DATA:url": "Получить кадр как DATA:url", + "_Start film recording": "Начать запись видео", + "_Stop film recording": "Остановить запись видео", + "_Get film as DATA:url": "Получить видео как DATA:url" + }, + "zh-cn": { + "_Start camera": "启动摄像头", + "_Stop camera": "关闭摄像头", + "_Capture image from camera": "从摄像头捕获图像", + "_Get encoded image": "获取编码图像", + "_Pixel [X], [Y] as code": "像素 [X], [Y] 的编码", + "_Join image with [DATA]": "将图像与 [DATA] 合并", + "_Set pixel [X], [Y] to code [VALUE]": "将像素 [X], [Y] 设置为编码 [VALUE]", + "_Get frame as DATA:url": "获取帧为 DATA:url", + "_Start film recording": "开始录像", + "_Stop film recording": "停止录像", + "_Get film as DATA:url": "获取视频为 DATA:url" + } +}); +/* end generated l10n code */ + class CameraExtension { constructor() { this.video = document.createElement("video"); @@ -15,26 +124,26 @@ class CameraExtension { this.filmDataURL = ""; } - getInfo() { - return { +getInfo() { + return { id: 'cameraExtensionByLibmaster169', name: 'Camera Tools', blocks: [ - { opcode: 'startCamera', blockType: Scratch.BlockType.COMMAND, text: 'Start camera' }, - { opcode: 'stopCamera', blockType: Scratch.BlockType.COMMAND, text: 'Stop camera' }, - { opcode: 'captureFrame', blockType: Scratch.BlockType.COMMAND, text: 'Capture image from camera' }, - { opcode: 'getBinaryImage', blockType: Scratch.BlockType.REPORTER, text: 'Get encoded image' }, - { opcode: 'getPixelBinary', blockType: Scratch.BlockType.REPORTER, text: 'Pixel [X], [Y] as code', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, - { opcode: 'joinFrames', blockType: Scratch.BlockType.COMMAND, text: 'Join image with [DATA]', arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "" } } }, - { opcode: 'setPixelTo', blockType: Scratch.BlockType.COMMAND, text: 'Set pixel [X], [Y] to code [VALUE]', arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "900" } } }, - { opcode: 'getFrameDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Get frame as DATA:url' }, - { opcode: 'startFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Start film recording' }, - { opcode: 'stopFilmRecording', blockType: Scratch.BlockType.COMMAND, text: 'Stop film recording' }, - { opcode: 'getFilmDataURL', blockType: Scratch.BlockType.REPORTER, text: 'Get film as DATA:url' } + { opcode: 'startCamera', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Start camera") }, + { opcode: 'stopCamera', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Stop camera") }, + { opcode: 'captureFrame', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Capture image from camera") }, + { opcode: 'getBinaryImage', blockType: Scratch.BlockType.REPORTER, text: Scratch.translate("Get encoded image") }, + { opcode: 'getPixelBinary', blockType: Scratch.BlockType.REPORTER, text: Scratch.translate("Pixel [X], [Y] as code"), arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 } } }, + { opcode: 'joinFrames', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Join image with [DATA]"), arguments: { DATA: { type: Scratch.ArgumentType.STRING, defaultValue: "" } } }, + { opcode: 'setPixelTo', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Set pixel [X], [Y] to code [VALUE]"), arguments: { X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "900" } } }, + { opcode: 'getFrameDataURL', blockType: Scratch.BlockType.REPORTER, text: Scratch.translate("Get frame as DATA:url") }, + { opcode: 'startFilmRecording', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Start film recording") }, + { opcode: 'stopFilmRecording', blockType: Scratch.BlockType.COMMAND, text: Scratch.translate("Stop film recording") }, + { opcode: 'getFilmDataURL', blockType: Scratch.BlockType.REPORTER, text: Scratch.translate("Get film as DATA:url") } ] -}; + }; +} - } startCamera() { navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {