From 26b3fd910ca00a692e4df4e767fb9ceb6b3922ab Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Sun, 26 Jan 2025 20:52:55 +0100 Subject: [PATCH] More Schema options --- SCHEMA.md | 52 +++++++++++++++++++ app/src/processing/app/Schema.kt | 86 +++++++++++++++++++++++++++++--- 2 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 SCHEMA.md diff --git a/SCHEMA.md b/SCHEMA.md new file mode 100644 index 000000000..53d5ba8b6 --- /dev/null +++ b/SCHEMA.md @@ -0,0 +1,52 @@ +# Processing URI Schema Definition + +## Local File Schema +``` +pde:///path/to/sketch.pde +``` + +## Sketch Operations + +### Create New Sketch +``` +pde://sketch/new +``` + +### Load Base64 Encoded Sketch +``` +pde://sketch/base64/ +``` +Optional query parameters: +- `data`: Comma-separated URLs of data files to download +- `code`: Comma-separated URLs of code files to download +- `pde`: Comma-separated URLs of Processing sketch files to download +- `mode`: Processing mode identifier + +### Load Sketch from URL +``` +pde://sketch/url/domain.com/path/to/sketch.pde +``` +Supports the same query parameters as base64 endpoint. +Optional query parameters: + +- `data`: Comma-separated URLs of data files to download +- `code`: Comma-separated URLs of code files to download +- `pde`: Comma-separated URLs of Processing sketch files to download +- `mode`: Processing mode identifier + +Example with query parameters: +``` +pde://sketch/url/example.com/sketch.pde?data=image1.png,sound1.mp3&mode=java +``` + +## Preferences +``` +pde://preferences?key1=value1&key2=value2 +``` +Sets and saves multiple preferences in a single operation. + +## Security Considerations +- URL-based operations automatically prepend https:// if no scheme is provided +- All URLs and query parameters are decoded using UTF-8 +- File downloads occur asynchronously in a separate thread +- Base64 and remote sketches are saved to temporary folders \ No newline at end of file diff --git a/app/src/processing/app/Schema.kt b/app/src/processing/app/Schema.kt index 153eeec4b..148a8747c 100644 --- a/app/src/processing/app/Schema.kt +++ b/app/src/processing/app/Schema.kt @@ -2,7 +2,9 @@ package processing.app import processing.app.ui.Editor import java.io.File +import java.io.FileOutputStream import java.net.URI +import java.net.URL import java.net.URLDecoder import java.nio.charset.StandardCharsets @@ -18,7 +20,7 @@ class Schema { null -> handleLocalFile(uri.path) "sketch" -> handleSketch(uri) "preferences" -> handlePreferences(uri) - else -> handleRemoteFile(uri) + else -> null } } private fun handleLocalFile(input: String): Editor?{ @@ -27,10 +29,16 @@ class Schema { private fun handleSketch(uri: URI): Editor?{ val paths = uri.path.split("/") return when(paths.getOrNull(1)){ + "new" -> handleSketchNew(uri) "base64" -> handleSketchBase64(uri) + "url" -> handleSketchUrl(uri) else -> null } } + private fun handleSketchNew(uri: URI): Editor?{ + base?.handleNew() + return null + } private fun handleSketchBase64(uri: URI): Editor?{ val tempSketchFolder = SketchName.nextFolder(Base.untitledFolder); tempSketchFolder.mkdirs() @@ -38,9 +46,77 @@ class Schema { val sketchB64 = uri.path.replace("/base64/", "") val sketch = java.util.Base64.getDecoder().decode(sketchB64) tempSketchFile.writeBytes(sketch) - val editor = base?.handleOpenUntitled(tempSketchFile.absolutePath) - return editor + handleSketchOptions(uri, tempSketchFolder) + return base?.handleOpenUntitled(tempSketchFile.absolutePath) } + private fun handleSketchUrl(uri: URI): Editor?{ + val url = File(uri.path.replace("/url/", "")) + + val tempSketchFolder = File(Base.untitledFolder, url.nameWithoutExtension) + tempSketchFolder.mkdirs() + val tempSketchFile = File(tempSketchFolder, "${tempSketchFolder.name}.pde") + + + URL("https://$url").openStream().use { input -> + FileOutputStream(tempSketchFile).use { output -> + input.copyTo(output) + } + } + handleSketchOptions(uri, tempSketchFolder) + return base?.handleOpenUntitled(tempSketchFile.absolutePath) + } + private fun handleSketchOptions(uri: URI, sketchFolder: File){ + val options = uri.query?.split("&") + ?.map { it.split("=") } + ?.associate { + URLDecoder.decode(it[0], StandardCharsets.UTF_8) to + URLDecoder.decode(it[1], StandardCharsets.UTF_8) + } + ?: emptyMap() + + options["data"]?.let{ data -> + downloadFiles(uri, data, File(sketchFolder, "data")) + } + options["code"]?.let{ code -> + downloadFiles(uri, code, File(sketchFolder, "code")) + } + options["pde"]?.let{ pde -> + downloadFiles(uri, pde, sketchFolder) + } + options["mode"]?.let{ mode -> + val modeFile = File(sketchFolder, "sketch.properties") + modeFile.writeText("mode.id=$mode") + } + + } + private fun downloadFiles(uri: URI, urlList: String, sketchFolder: File){ + Thread{ + val base = uri.path.split("/").drop(2).dropLast(1).joinToString("/") + val files = urlList.split(",") + .map { + val fileUrl = URI.create(it) + if(fileUrl.host == null){ + return@map "https://$base/$it" + } + if(fileUrl.scheme == null){ + return@map "https://$it" + } + return@map it + } + sketchFolder.mkdirs() + for(file in files){ + val url = URL(file) + val name = url.path.split("/").last() + val dataFile = File(sketchFolder, name) + URL(file).openStream().use { input -> + FileOutputStream(dataFile).use { output -> + input.copyTo(output) + } + } + } + }.start() + } + private fun handlePreferences(uri: URI): Editor?{ val options = uri.query?.split("&") @@ -57,9 +133,5 @@ class Schema { return null } - - private fun handleRemoteFile(uri: URI): Editor?{ - return null - } } } \ No newline at end of file