diff --git a/src/lib/libraries/extensions/SuperStorage/SuperStorage.svg b/src/lib/libraries/extensions/SuperStorage/SuperStorage.svg new file mode 100644 index 00000000000..aa4c46d0901 --- /dev/null +++ b/src/lib/libraries/extensions/SuperStorage/SuperStorage.svg @@ -0,0 +1 @@ +Made withhttps://penguinpaint.pages.dev \ No newline at end of file diff --git a/src/lib/libraries/extensions/cloudstorage/CloudStorage.svg b/src/lib/libraries/extensions/cloudstorage/CloudStorage.svg new file mode 100644 index 00000000000..615eb0fb0ab --- /dev/null +++ b/src/lib/libraries/extensions/cloudstorage/CloudStorage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/lib/libraries/extensions/index.jsx b/src/lib/libraries/extensions/index.jsx index d31b93f59ad..f93f23f47da 100644 --- a/src/lib/libraries/extensions/index.jsx +++ b/src/lib/libraries/extensions/index.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import VideoSharing from './VidShare/VideoSharing.svg'; +import SuperStorage from './SuperStorage/SuperStorage.svg'; import NoahgptThumb from './noahgpt/costume1.svg'; import typescriptIcon from './snail-ide/typescript.svg'; import twGalleryIcon from './snail-ide/turbowarpgallery.svg'; @@ -10,7 +11,7 @@ import pmGalleryIcon from './snail-ide/penguinmodgallery.svg'; import musicIconURL from './music/music.png'; import roku from './roku/roku.png'; import share from './share/share.svg'; -import cloudstorageIconURL from './cloudstorage/costume1.svg'; +import cloudstorageIconURL from './cloudstorage/CloudStorage.svg'; import pythonIcon from './python/py.svg'; import extCreateIcon from './ext-create/logo.svg'; import extCreateInset from './ext-create/inset.svg'; @@ -229,15 +230,6 @@ const menuItems = [ description: 'Do many things via the Scratch API; you can even fetch cloud data from projects!', featured: true }, - { - name: 'Screensharing', - extensionId: 'https://editor.snail-ide.com/screen-sharing.js', - iconURL: 'https://editor.snail-ide.com/Screensharing.png', // please forgive me the text is slightly offcenter - collaborator: 'pooiod7', - tags: ['penguinmod'], - description: 'Share your screen and get the current frame as a image.', - featured: true - }, { name: 'VideoSharing', extensionId: 'https://editor.snail-ide.com/VideoSharing.js', @@ -541,11 +533,20 @@ const menuItems = [ name: 'Cloud Storage', extensionId: 'https://editor.snail-ide.com/cloudstorage.js', collaborator: 'pooiod7', - iconURL: cloudstorageIconURL, // this needs to be redone soon + iconURL: cloudstorageIconURL, tags: ['penguinmod'], description: 'Store data in a database, similar to Storage and Better Storage, but powered by a Snap! extension.', featured: true }, + { + name: 'SuperStorage', + extensionId: 'https://editor.snail-ide.com/SuperStorage.js', + iconURL: SuperStorage, + tags: ['penguinmod'], + description: 'Store and retrieve data locally on device or remotely on a server.', + collaborator: 'pooiod7', + featured: true + }, { name: 'Text to Speech 2.0', extensionId: 'https://sharkpools-extensions.vercel.app/extension-code/Text-to-Speech.js', diff --git a/static/SuperStorage.js b/static/SuperStorage.js new file mode 100644 index 00000000000..0dbaf0a94ae --- /dev/null +++ b/static/SuperStorage.js @@ -0,0 +1,178 @@ + (function(Scratch) { + 'use strict'; + + if (!Scratch.extensions.unsandboxed) { + throw new Error('This extension must run unsandboxed'); + } + + class StorageV2 { + constructor() { + this.currentServer = "https://storage-ext.penguinmod.com/"; + this.useGlobal = true; + this.waitingForResponse = false; + this.serverFailedResponse = false; + this.serverError = ""; + } + + getInfo() { + return { + id: 'P7SuperStorage', + name: 'Super Storage', + color1: '#31b3d4', + color2: '#179fc2', + docsURI: 'https://pooiod7.neocities.org/markdown/#/projects/scratch/extensions/other/markdown/SuperStorage', + blocks: [ + { blockType: Scratch.BlockType.LABEL, text: "Local Storage" }, + { + opcode: 'getValue', + text: 'get local [KEY]', + disableMonitor: true, + blockType: Scratch.BlockType.REPORTER, + arguments: { + KEY: { type: Scratch.ArgumentType.STRING, defaultValue: "key" } + } + }, + { + opcode: 'setValue', + text: 'set local [KEY] to [VALUE]', + blockType: Scratch.BlockType.COMMAND, + arguments: { + KEY: { type: Scratch.ArgumentType.STRING, defaultValue: "key" }, + VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "value" } + } + }, + { + opcode: 'deleteValue', + text: 'delete local [KEY]', + blockType: Scratch.BlockType.COMMAND, + arguments: { KEY: { type: Scratch.ArgumentType.STRING, defaultValue: "key" } } + }, + { + opcode: 'getKeys', + text: 'get all local stored names', + disableMonitor: true, + blockType: Scratch.BlockType.REPORTER + }, + { + blockType: Scratch.BlockType.LABEL, + text: "Server Storage" + }, + { + opcode: 'waitingForConnection', + text: 'waiting for server to respond?', + disableMonitor: true, + blockType: Scratch.BlockType.BOOLEAN + }, + { + opcode: 'connectionFailed', + text: 'server failed to respond?', + disableMonitor: true, + blockType: Scratch.BlockType.BOOLEAN + }, + { + opcode: 'serverErrorOutput', + text: 'server error', + disableMonitor: false, + blockType: Scratch.BlockType.REPORTER + }, + "---", + { + opcode: 'getServerValue', + text: 'get server [KEY]', + disableMonitor: true, + blockType: Scratch.BlockType.REPORTER, + arguments: { KEY: { type: Scratch.ArgumentType.STRING, defaultValue: "key" } } + }, + { + opcode: 'setServerValue', + text: 'set server [KEY] to [VALUE]', + blockType: Scratch.BlockType.COMMAND, + arguments: { + KEY: { type: Scratch.ArgumentType.STRING, defaultValue: "key" }, + VALUE: { type: Scratch.ArgumentType.STRING, defaultValue: "value" } + } + }, + { + opcode: 'deleteServerValue', + text: 'delete server [KEY]', + blockType: Scratch.BlockType.COMMAND, + arguments: { KEY: { type: Scratch.ArgumentType.STRING, defaultValue: "key" } } + } + ] + }; + } + + getPrefix() { + return `P7_PROJECTSTORAGE_`; + } + + getAllKeys() { + return Object.keys(localStorage).filter(key => key.startsWith(this.getPrefix())).map(key => key.replace(this.getPrefix(), "")); + } + + runPenguinWebRequest(url, options, ifFailReturn) { + this.waitingForResponse = true; + this.serverFailedResponse = false; + this.serverError = ""; + + return fetch(url, options) + .then(response => response.ok ? response.text() : Promise.reject(response.text())) + .then(text => { + this.waitingForResponse = false; + return text; + }) + .catch(err => { + this.waitingForResponse = false; + this.serverFailedResponse = true; + this.serverError = err; + return ifFailReturn; + }); + } + + getKeys() { + return JSON.stringify(this.getAllKeys()); + } + + getValue(args) { + return localStorage.getItem(this.getPrefix() + args.KEY) || ""; + } + + setValue(args) { + localStorage.setItem(this.getPrefix() + args.KEY, args.VALUE); + } + + deleteValue(args) { + localStorage.removeItem(this.getPrefix() + args.KEY); + } + + waitingForConnection() { + return this.waitingForResponse; + } + + connectionFailed() { + return this.serverFailedResponse; + } + + serverErrorOutput() { + return this.serverError; + } + + getServerValue(args) { + return this.runPenguinWebRequest(`${this.currentServer}get?key=${args.KEY}`, null, ""); + } + + setServerValue(args) { + return this.runPenguinWebRequest(`${this.currentServer}set?key=${args.KEY}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ "value": args.VALUE }) + }); + } + + deleteServerValue(args) { + return this.runPenguinWebRequest(`${this.currentServer}delete?key=${args.KEY}`, { method: "DELETE" }); + } + } + + Scratch.extensions.register(new StorageV2()); +})(Scratch); diff --git a/static/VideoSharing.js b/static/VideoSharing.js index f959f95c954..2c4d6fe5e2f 100644 --- a/static/VideoSharing.js +++ b/static/VideoSharing.js @@ -1,5 +1,5 @@ -// Video sharing (v2.4.1) by pooiod7 -// The successor to ScreenSharing +// Video sharing (v2.4.2) by pooiod7 +// Was originally the "ScreenSharing" extension (function(Scratch) { 'use strict'; @@ -21,7 +21,7 @@ let haswarned; function shouldwarn(){ - return Scratch.vm.runtime.isPackaged; + return !Scratch.vm.runtime.isPackaged; } class VideoSharing { @@ -245,18 +245,12 @@ } startScreenSharing() { - if (this.isSharing()) { - this.stopSharing(); - } + if (this.isSharing()) this.stopSharing(); - if (!this.canScreen()) { - return; - } + if (!this.canScreen()) return; if (shouldwarn()) { - if (!this.warn("screen")) { - return; - } + if (!this.warn("screen")) return; } return new Promise((resolve) => { @@ -283,14 +277,10 @@ } startCameraSharing() { - if (this.isSharing()) { - this.stopSharing(); - } + if (this.isSharing()) this.stopSharing(); if (shouldwarn()) { - if (!this.warn("camera")) { - return; - } + if (!this.warn("camera")) return; } return new Promise((resolve, reject) => { @@ -326,14 +316,10 @@ } getHEX(args) { - if (!this.isSharing()) { - return; - } + if (!this.isSharing()) return; var rez = args.REZ; - if (rez > 1) { - rez = 1; - } + if (rez > 1) rez = 1; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const width = videoElement.videoWidth * rez; @@ -357,14 +343,10 @@ } getPNG(args) { - if (!this.isSharing()) { - return; - } + if (!this.isSharing()) return; var rez = args.REZ; - if (rez > 1) { - rez = 1; - } + if (rez > 1) rez = 1; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const width = videoElement.videoWidth * rez; @@ -380,14 +362,10 @@ } getJPEG(args) { - if (!this.isSharing()) { - return; - } + if (!this.isSharing()) return; let rez = args.REZ; - if (rez > 1) { - rez = 1; - } + if (rez > 1) rez = 1; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const width = videoElement.videoWidth * rez; @@ -404,14 +382,10 @@ } getWEBP(args) { - if (!this.isSharing()) { - return; - } + if (!this.isSharing()) return; let rez = args.REZ; - if (rez > 1) { - rez = 1; - } + if (rez > 1) rez = 1; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); diff --git a/static/cloudstorage.js b/static/cloudstorage.js index 89a15f13a81..eec5c845928 100644 --- a/static/cloudstorage.js +++ b/static/cloudstorage.js @@ -1,5 +1,4 @@ -// Simple file storage made with tools from Snap! -// Be careful, data can be replaced +// Simple file storage made with tools from Snap! (v1.2) by pooiod7 class ServerExtension { constructor(runtime) { @@ -29,6 +28,17 @@ class ServerExtension { }, }, }, + { + opcode: 'deleteFromServer', + blockType: Scratch.BlockType.COMMAND, + text: 'Delete [variableName]', + arguments: { + variableName: { + type: Scratch.ArgumentType.STRING, + defaultValue: 'default.txt', + }, + }, + }, { opcode: 'loadFromServer', blockType: Scratch.BlockType.REPORTER, @@ -36,7 +46,7 @@ class ServerExtension { arguments: { variableName: { type: Scratch.ArgumentType.STRING, - defaultValue: 'data.txt', + defaultValue: 'default.txt', }, }, }, @@ -64,6 +74,24 @@ class ServerExtension { return false; }); } + + deleteFromServer(args, util) { + const variableName = args.variableName; + const content = args.content; + + const url = + this.serverURL + + '?type=delete&filename=./textfiles/' + + encodeURIComponent(variableName); + + return fetch(url) + .then(response => response.text()) + .then(result => (result === 'ok')) + .catch(error => { + console.error('Failed to delete data:', error); + return false; + }); + } loadFromServer(args, util) { const variableName = args.variableName;