Worker + NodeJS support for puter.ai.txt2img and puter.ai.txt2vid (#2051)

* Attempt to get workers working with AI media generation

* add FileReader polyfill

* fix import path
This commit is contained in:
Neal Shah
2025-11-26 18:10:11 -05:00
committed by GitHub
parent 93eddd2238
commit 6dffa3d6f2
3 changed files with 52 additions and 13 deletions
@@ -0,0 +1,40 @@
function toBase64FromBuffer(buffer) {
const bytes = new Uint8Array(buffer);
// use the requested reduce logic
const binary = bytes.reduce((data, byte) => data + String.fromCharCode(byte), '');
return typeof btoa === 'function' ? btoa(binary) : Buffer.from(binary, 'binary').toString('base64');
}
export class FileReaderPoly {
constructor() {
this.result = null;
this.error = null;
this.onloadend = null;
}
readAsDataURL(blob) {
const self = this;
(async function () {
try {
let buffer;
if (blob && typeof blob.arrayBuffer === 'function') {
buffer = await blob.arrayBuffer();
} else if (blob instanceof ArrayBuffer) {
buffer = blob;
} else if (ArrayBuffer.isView(blob)) {
buffer = blob.buffer;
} else {
buffer = new Uint8Array(0).buffer;
}
const base64 = toBase64FromBuffer(buffer);
const mime = (blob && blob.type) || 'application/octet-stream';
self.result = 'data:' + mime + ';base64,' + base64;
if (typeof self.onloadend === 'function') self.onloadend();
} catch (err) {
self.error = err;
if (typeof self.onloadend === 'function') self.onloadend();
}
})();
}
}
+8 -10
View File
@@ -1,3 +1,5 @@
import { FileReaderPoly } from "./polyfills/fileReaderPoly.js";
/**
* Parses a given response text into a JSON object. If the parsing fails due to invalid JSON format,
* the original response text is returned.
@@ -589,7 +591,7 @@ class TeePromise {
async function blob_to_url (blob) {
const tp = new TeePromise();
const reader = new FileReader();
const reader = new (globalThis.FileReader || FileReaderPoly)();
reader.onloadend = () => tp.resolve(reader.result);
reader.readAsDataURL(blob);
return await tp;
@@ -597,7 +599,7 @@ async function blob_to_url (blob) {
function blobToDataUri (blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const reader = new (globalThis.FileReader || FileReaderPoly)();
reader.onload = function (event) {
resolve(event.target.result);
};
@@ -611,7 +613,7 @@ function blobToDataUri (blob) {
function arrayBufferToDataUri (arrayBuffer) {
return new Promise((resolve, reject) => {
const blob = new Blob([arrayBuffer]);
const reader = new FileReader();
const reader = new (globalThis.FileReader || FileReaderPoly)();
reader.onload = function (event) {
resolve(event.target.result);
};
@@ -622,10 +624,6 @@ function arrayBufferToDataUri (arrayBuffer) {
});
}
export { parseResponse, uuidv4, handle_resp, handle_error, initXhr, setupXhrEventHandlers, driverCall,
TeePromise,
make_driver_method,
blob_to_url,
arrayBufferToDataUri,
blobToDataUri,
};
export {
arrayBufferToDataUri, blob_to_url, blobToDataUri, driverCall, handle_error, handle_resp, initXhr, make_driver_method, parseResponse, setupXhrEventHandlers, TeePromise, uuidv4
};
+4 -3
View File
@@ -391,7 +391,8 @@ class AI {
} else {
throw { message: 'Unexpected audio response format', code: 'invalid_audio_response' };
}
const audio = new Audio(url);
const audio = new (globalThis.Audio || Object)();
audio.src = url;
audio.toString = () => url;
audio.valueOf = () => url;
return audio;
@@ -1061,7 +1062,7 @@ class AI {
} else {
throw { message: 'Unexpected image response format', code: 'invalid_image_response' };
}
let img = new Image();
let img = new (globalThis.Image || Object)();
img.src = url;
img.toString = () => img.src;
img.valueOf = () => img.src;
@@ -1153,7 +1154,7 @@ class AI {
return result;
}
const video = document.createElement('video');
const video = (globalThis.document?.createElement('video') || {setAttribute: ()=>{}});
video.src = sourceUrl;
video.controls = true;
video.preload = 'metadata';