-
Notifications
You must be signed in to change notification settings - Fork 86
/
magickApi.ts
211 lines (185 loc) · 6.4 KB
/
magickApi.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import StackTrace from "stacktrace-js"
/**
* Base class for ImageMagick input and output files.
*/
export interface MagickFile {
name: string
}
/**
* Represents output files generated when an ImageMagick command executes.
*/
export interface MagickOutputFile extends MagickFile {
blob: Blob
buffer?: ArrayBuffer
}
/**
* Represents input files that need to be provided to {@link call} or [execute](https://github.com/KnicKnic/WASM-ImageMagick/tree/master/apidocs#execute).
*
* Can be builded using {@link buildInputFile}
*/
export interface MagickInputFile extends MagickFile {
content: Uint8Array
}
/**
* {@link call} shortcut that only returns the output files.
*/
export async function Call(inputFiles: MagickInputFile[], command: string[]): Promise<MagickOutputFile[]> {
const result = await call(inputFiles, command)
for(let outputFile of result.outputFiles)
{
outputFile.blob = new Blob([outputFile.buffer])
}
return result.outputFiles
}
/**
* The result of calling {@link call}. Also the base class of results of calling [execute](https://github.com/KnicKnic/WASM-ImageMagick/tree/master/apidocs#execute).
*/
export interface CallResult {
/**
* Output files generated by the command, if any
*/
outputFiles: MagickOutputFile[]
/**
* Output printed by the command to stdout. For example the command `identify rose:` will print useful information to stdout
*/
stdout: string[]
/**
* Output printed by the command to stderr. If `exitCode != 0` then this property could have some information about the error.
*/
stderr: string[]
/**
* Exit code of the command executed. If 0 the command executed successfully, otherwise an error occurred and `stderr` could have some information about what was wrong
*/
exitCode: number
}
/**
* Low level execution function. All the other functions like [execute](https://github.com/KnicKnic/WASM-ImageMagick/tree/master/apidocs#execute)
* ends up calling this one. It accept only one command and only in the form of array of strings.
*/
export function call(inputFiles: MagickInputFile[], command: string[]): Promise<CallResult> {
const request = {
files: inputFiles,
args: command,
requestNumber: magickWorkerPromisesKey,
// transferable: true,
}
// let transfer = [];
// for (let file of request.files) {
// if(file.content instanceof ArrayBuffer)
// {
// transfer.push(file.content)
// }
// else{
// transfer.push(file.content.buffer)
// }
// }
const promise = CreatePromiseEvent()
magickWorkerPromises[magickWorkerPromisesKey] = promise
magickWorker.postMessage(request)//,transfer)
magickWorkerPromisesKey++
return promise as Promise<CallResult>
}
export function CreatePromiseEvent() {
let resolver
let rejecter
const emptyPromise = new Promise((resolve, reject) => {
resolver = resolve
rejecter = reject
}) as Promise<{}> & { resolve?: any, reject?: any }
emptyPromise.resolve = resolver
emptyPromise.reject = rejecter
return emptyPromise
}
function ChangeUrl(url, fileName)
{
let splitUrl = url.split('/')
splitUrl[splitUrl.length -1] = fileName
return splitUrl.join('/')
}
function GetCurrentUrlDifferentFilename(currentUrl, fileName) {
return ChangeUrl(currentUrl, fileName);
}
let currentJavascriptURL = './magickApi.js';
// // instead of doing the sane code of being able to just use import.meta.url
// // (Edge doesn't work) (safari mobile, chrome, opera, firefox all do)
// //
// // I will use stacktrace-js library to get the current file name
// //
// try {
// // @ts-ignore
// let packageUrl = import.meta.url;
// currentJavascriptURL = packageUrl;
// } catch (error) {
// // eat
// }
function GenerateStackAndGetPathAtDepth(depth){
try {
let stacktrace$$1 = StackTrace.getSync();
let filePath = stacktrace$$1[depth].fileName;
// if the stack trace code doesn't return a path separator
if(filePath !== undefined && filePath.indexOf('/') === -1 && filePath.indexOf('\\') === -1){
return undefined
}
return filePath
} catch (error) {
return undefined
}
}
function GetCurrentFileURLHelper3() {
// 3rd call site didn't work, so I made this complicated maze of helpers..
// Pulling the filename from the 3rd call site of the stacktrace to get the full path
// to the module. The first index is inconsistent across browsers and does not return
// the full path in Safari and results in the worker failing to resolve.
// I am preferring to do depth 0 first, as that will ensure people that do minification still works
let filePath = GenerateStackAndGetPathAtDepth(0)
if(filePath === undefined){
filePath = GenerateStackAndGetPathAtDepth(2)
}
// if the stack trace code messes up
if(filePath === undefined){
filePath = './magickApi.js';
}
return filePath
}
function GetCurrentFileURLHelper2() {
return GetCurrentFileURLHelper3();
}
function GetCurrentFileURLHelper1() {
return GetCurrentFileURLHelper2();
}
function GetCurrentFileURL() {
return GetCurrentFileURLHelper1();
}
currentJavascriptURL = GetCurrentFileURL();
const magickWorkerUrl = GetCurrentUrlDifferentFilename(currentJavascriptURL, 'magick.js');
function GenerateMagickWorkerText(magickUrl){
// generates code for the following
// var magickJsCurrentPath = 'magickUrl';
// importScripts(magickJsCurrentPath);
return "var magickJsCurrentPath = '" + magickUrl +"';\n" +
'importScripts(magickJsCurrentPath);'
}
let magickWorker;
if(currentJavascriptURL.startsWith('http'))
{
// if worker is in a different domain fetch it, and run it
magickWorker = new Worker(window.URL.createObjectURL(new Blob([GenerateMagickWorkerText(magickWorkerUrl)])));
}
else{
magickWorker = new Worker(magickWorkerUrl);
}
const magickWorkerPromises = {}
let magickWorkerPromisesKey = 1
// handle responses as they stream in after being outputFiles by image magick
magickWorker.onmessage = e => {
const response = e.data
const promise = magickWorkerPromises[response.requestNumber]
delete magickWorkerPromises[response.requestNumber]
const result = {
outputFiles: response.outputFiles,
stdout: response.stdout,
stderr: response.stderr,
exitCode: response.exitCode || 0,
}
promise.resolve(result)
}