Skip to content

Commit

Permalink
feat(isf-folder): adds media ISF folder to load ISF without a JavaScr…
Browse files Browse the repository at this point in the history
…ipt wrapper
  • Loading branch information
2xAA committed Apr 28, 2021
1 parent ca0a0c6 commit af50d2d
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/application/worker/store/modules/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const actions = {
...media.actions,

async addMedia({ commit }, { project, folder, item }) {
if (folder === "module") {
if (folder === "module" || folder === "isf") {
const stream = fs.createReadStream(
path.join(store.state.media.path, item.path)
);
Expand Down
2 changes: 2 additions & 0 deletions src/media-manager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import imageReadHandler from "./read-handlers/image";
import paletteReadHandler from "./read-handlers/palette";
import presetReadHandler from "./read-handlers/preset";
import moduleReadHandler from "./read-handlers/module";
import isfReadHandler from "./read-handlers/isf";

import presetSaveHandler from "./save-handlers/preset";

Expand Down Expand Up @@ -64,6 +65,7 @@ export default class MediaManager {
this.addReadHandler({ readHandler: paletteReadHandler });
this.addReadHandler({ readHandler: presetReadHandler });
this.addReadHandler({ readHandler: moduleReadHandler });
this.addReadHandler({ readHandler: isfReadHandler });

this.addSaveHandler({ saveHandler: presetSaveHandler });

Expand Down
9 changes: 9 additions & 0 deletions src/media-manager/media-manager-utils/stream-to-string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// https://stackoverflow.com/a/49428486
module.exports = function streamToString(stream) {
const chunks = [];
return new Promise((resolve, reject) => {
stream.on("data", chunk => chunks.push(Buffer.from(chunk)));
stream.on("error", err => reject(err));
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
});
};
167 changes: 167 additions & 0 deletions src/media-manager/read-handlers/isf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const fs = require("fs");
const path = require("path");
const util = require("util");
const webpack = require("webpack-2");

const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const mkdir = util.promisify(fs.mkdir);
const streamToString = require("../media-manager-utils/stream-to-string");

/**
* @typedef {OutputFileContext}
* @property {Stream|String} file Processed file or file path
* @property {String} [folder] Folder in which to save processed file
*/

/**
* @typedef {ReadHandler}
* @property {String} folder The folder name to watch
* @property {String} identifier An identifier for logging (usually a single emoji)
* @property {Array<String>} folderAccess A list of folder names for Stream writing
* @property {Array<String|Regex>} fileTypes File types to match this File Handler against
* @property {Function} init Called before File Hander is added, for setup
* @property {Function} process Function to process matched files
*/
export default {
folder: "isf",
identifier: "🌈",

// Requests stream writing to the video folder
folderAccess: ["isf/compiled"],

fileTypes: [
// @todo regex match
"fs"
],

ignored: [/isf[\\/]compiled/],

/**
* Takes in a readable stream, processes file accordingly and outputs file location plus stream
*
* @param {Stream} options.file Readable stream of file.
* @param {String} options.fileName The file's name.
* @param {String} options.fileType The file's extension type e.g. .jpeg.
* @param {String} options.filePath The path to the file.
* @param {Function} util.log Log something to the console.
*
* @return {Promise<true|OutputFileContext|Error>} A Promise resolving with `true` if the file
* needed no modification and can remain in the
* same folder.
* A Promise resolving a `OutputFileContext` if
* the file required processing.
* A Promise rejecting with `Error` if something
* went wrong.
*/
async process({ filePath, fileName, file }) {
return {
filePath: await compileModule({ filePath, fileName, file }),
folder: "isf/compiled"
};
}
};

function compileModule({ filePath, fileName, file }) {
return new Promise(async resolve => {
// Default vertex shader from ISF that is used when the user didn't specify anything
let vertexShader = "void main() {isf_vertShaderInit();}";
let fragmentShader;

// Load the vertex shader
try {
vertexShader = await readFile(filePath.replace(".fs", ".vs"), "utf8");
} catch (err) {
console.error(err);
}

// Convert the fragment shader stream into a string
try {
fragmentShader = await streamToString(file);
} catch (err) {
console.error(err);
}

// Create the module so that modV can understand it as this is the default format
const isfModule = {
meta: {
name: fileName.replace(/(\.\/|\.fs)/g, ""),
author: "",
version: "1.0.0",
type: "isf"
},
fragmentShader,
vertexShader
};

const tempFilePath = path.join(
path.dirname(filePath),
"temp",
path.basename(filePath)
);

const tempDirectoryPath = path.join(path.dirname(filePath), "temp");

try {
await mkdir(tempDirectoryPath);
} catch (err) {
if (err) {
// don't log errors if the folder already exists
if (err.code !== "EEXIST") {
console.error(err);
}
}
}

const compiledFilePath = path.join(
path.dirname(filePath),
"compiled",
path.basename(filePath)
);

const compiledDirectoryPath = path.join(path.dirname(filePath), "compiled");

try {
await mkdir(compiledDirectoryPath);
} catch (err) {
if (err) {
// don't log errors if the folder already exists
if (err.code !== "EEXIST") {
console.error(err);
}
}
}

const json = JSON.stringify(isfModule);

await writeFile(tempFilePath, `export default ${json}`);

const webpackConfig = {
entry: tempFilePath,
output: {
path: compiledDirectoryPath,
filename: path.basename(filePath),
libraryTarget: "var"
},
resolveLoader: {
modules: ["node_modules", __dirname + "/node_modules"]
}
};

webpack(webpackConfig, (err, stats) => {
if (err || stats.hasErrors()) {
const statsJson = stats.toJson("minimal");
const canada = statsJson.errors;
for (let i = 0, len = canada.length; i < len; i++) {
console.log(canada[i]);
}
console.error(err);
}

// 3. save compiled module to user media directory

// 4. update modv clients with new file contents (then eval in modv)
resolve(compiledFilePath);
});
});
}

0 comments on commit af50d2d

Please sign in to comment.