single-file-cli 2.0.68 → 2.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +1 -1
- package/deno.json +1 -1
- package/lib/browser.js +2 -2
- package/lib/cdp-client.js +22 -10
- package/lib/deno-polyfill.js +3 -1
- package/lib/single-file-script.js +1 -1
- package/lib/version.js +1 -1
- package/options.js +70 -41
- package/package.json +1 -1
- package/single-file-cli-api.js +17 -6
- package/single-file-launcher.js +56 -3
package/README.MD
CHANGED
|
@@ -65,7 +65,7 @@ Make sure Chrome or a Chromium-based browser is installed in the default folder.
|
|
|
65
65
|
|
|
66
66
|
- There are 3 ways to download the code of SingleFile, choose the one you prefer:
|
|
67
67
|
|
|
68
|
-
- Install with `npm` (`npm`
|
|
68
|
+
- Install with `npm` and run `single-file` via `npx` (`npm` and `npx` are installed with Node.js)
|
|
69
69
|
|
|
70
70
|
```sh
|
|
71
71
|
npm install "single-file-cli"
|
package/deno.json
CHANGED
package/lib/browser.js
CHANGED
|
@@ -92,7 +92,7 @@ async function launchBrowser(options = {}, indexPath = 0) {
|
|
|
92
92
|
async function getDebugPort(port = getRandomDebugPort(), usedPorts = []) {
|
|
93
93
|
try {
|
|
94
94
|
await fetch("http://localhost:" + port + "/json/version");
|
|
95
|
-
} catch
|
|
95
|
+
} catch {
|
|
96
96
|
return port;
|
|
97
97
|
}
|
|
98
98
|
if (usedPorts.length < DEBUG_PORT_RANGE) {
|
|
@@ -121,7 +121,7 @@ async function closeBrowser() {
|
|
|
121
121
|
try {
|
|
122
122
|
await remove(profilePath, { recursive: true });
|
|
123
123
|
profilePath = undefined;
|
|
124
|
-
} catch
|
|
124
|
+
} catch {
|
|
125
125
|
console.log("Warning: failed to remove profile directory: " + profilePath); // eslint-disable-line no-console
|
|
126
126
|
}
|
|
127
127
|
}
|
package/lib/cdp-client.js
CHANGED
|
@@ -159,7 +159,7 @@ async function getPageData(options) {
|
|
|
159
159
|
}
|
|
160
160
|
try {
|
|
161
161
|
await Fetch.continueRequest({ requestId });
|
|
162
|
-
} catch
|
|
162
|
+
} catch {
|
|
163
163
|
// ignored
|
|
164
164
|
}
|
|
165
165
|
});
|
|
@@ -177,6 +177,7 @@ async function getPageData(options) {
|
|
|
177
177
|
if (options.browserCookies && options.browserCookies.length) {
|
|
178
178
|
await Network.setCookies({ cookies: options.browserCookies });
|
|
179
179
|
}
|
|
180
|
+
await Browser.setDownloadBehavior({ behavior: "deny" });
|
|
180
181
|
await Page.addScriptToEvaluateOnNewDocument({
|
|
181
182
|
source: getHookScriptSource(),
|
|
182
183
|
runImmediately: true
|
|
@@ -200,14 +201,14 @@ async function getPageData(options) {
|
|
|
200
201
|
if (options.embedScreenshotOptions) {
|
|
201
202
|
try {
|
|
202
203
|
screenshotOptions = JSON.parse(options.embedScreenshotOptions);
|
|
203
|
-
} catch
|
|
204
|
+
} catch {
|
|
204
205
|
// ignored
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
screenshotOptions.format = "png";
|
|
208
209
|
const { data } = await Page.captureScreenshot(screenshotOptions);
|
|
209
210
|
await Runtime.evaluate({ expression: `globalThis.${SET_SCREENSHOT_FUNCTION_NAME}(${JSON.stringify(data)})`, contextId });
|
|
210
|
-
} catch
|
|
211
|
+
} catch {
|
|
211
212
|
await Runtime.evaluate({ expression: `globalThis.${SET_SCREENSHOT_FUNCTION_NAME}(${JSON.stringify("")})`, contextId });
|
|
212
213
|
}
|
|
213
214
|
}
|
|
@@ -221,14 +222,14 @@ async function getPageData(options) {
|
|
|
221
222
|
if (options.embedPdfOptions) {
|
|
222
223
|
try {
|
|
223
224
|
pdfOptions = JSON.parse(options.embedPdfOptions);
|
|
224
|
-
} catch
|
|
225
|
+
} catch {
|
|
225
226
|
// ignored
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
try {
|
|
229
230
|
const { data } = await Page.printToPDF(pdfOptions);
|
|
230
231
|
await Runtime.evaluate({ expression: `globalThis.${SET_PDF_FUNCTION_NAME}(${JSON.stringify(data)})`, contextId });
|
|
231
|
-
} catch
|
|
232
|
+
} catch {
|
|
232
233
|
await Runtime.evaluate({ expression: `globalThis.${SET_PDF_FUNCTION_NAME}(${JSON.stringify("")})`, contextId });
|
|
233
234
|
}
|
|
234
235
|
}
|
|
@@ -236,7 +237,7 @@ async function getPageData(options) {
|
|
|
236
237
|
}
|
|
237
238
|
const pageDataPromise = new Promise(resolve => {
|
|
238
239
|
let pageDataResponse = "";
|
|
239
|
-
Runtime.addEventListener("bindingCalled",
|
|
240
|
+
Runtime.addEventListener("bindingCalled", ({ params }) => {
|
|
240
241
|
if (params.name === SET_PAGE_DATA_FUNCTION_NAME) {
|
|
241
242
|
const { payload } = params;
|
|
242
243
|
if (payload.length) {
|
|
@@ -276,13 +277,19 @@ async function getPageData(options) {
|
|
|
276
277
|
const browserWaitUntil = NETWORK_STATES[(NETWORK_STATES.indexOf(options.browserWaitUntil) + 1)];
|
|
277
278
|
if (browserWaitUntil) {
|
|
278
279
|
options.browserWaitUntil = browserWaitUntil;
|
|
280
|
+
await closeTarget();
|
|
279
281
|
return await getPageData(options);
|
|
280
282
|
}
|
|
281
283
|
}
|
|
282
284
|
throw error;
|
|
283
285
|
} finally {
|
|
286
|
+
await closeTarget();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function closeTarget() {
|
|
284
290
|
if (targetInfo && !options.browserDebug) {
|
|
285
291
|
await CDP.closeTarget(targetInfo.id);
|
|
292
|
+
targetInfo = null;
|
|
286
293
|
}
|
|
287
294
|
}
|
|
288
295
|
}
|
|
@@ -374,6 +381,7 @@ async function getTopFrameContextId({ Page, Runtime }, options) {
|
|
|
374
381
|
}
|
|
375
382
|
|
|
376
383
|
async function waitForPageReady({ Page }, options) {
|
|
384
|
+
let resolveCallbackTimeout;
|
|
377
385
|
await Page.setLifecycleEventsEnabled({ enabled: true });
|
|
378
386
|
try {
|
|
379
387
|
await new Promise((resolve, reject) => {
|
|
@@ -384,9 +392,12 @@ async function getTopFrameContextId({ Page, Runtime }, options) {
|
|
|
384
392
|
|
|
385
393
|
function onLifecycleEvent({ params }) {
|
|
386
394
|
const { frameId, name } = params;
|
|
387
|
-
if (frameId === topFrameId && name === options.browserWaitUntil) {
|
|
388
|
-
|
|
389
|
-
|
|
395
|
+
if (frameId === topFrameId && name === options.browserWaitUntil || (resolveCallbackTimeout && NETWORK_STATES.indexOf(name) < NETWORK_STATES.indexOf(options.browserWaitUntil))) {
|
|
396
|
+
clearTimeout(resolveCallbackTimeout);
|
|
397
|
+
setTimeout(() => {
|
|
398
|
+
removeListeners();
|
|
399
|
+
resolve();
|
|
400
|
+
}, options.browserWaitUntilDelay);
|
|
390
401
|
}
|
|
391
402
|
}
|
|
392
403
|
|
|
@@ -394,6 +405,7 @@ async function getTopFrameContextId({ Page, Runtime }, options) {
|
|
|
394
405
|
const { frame } = params;
|
|
395
406
|
if (!frame.parentId) {
|
|
396
407
|
if (frame.unreachableUrl) {
|
|
408
|
+
clearTimeout(resolveCallbackTimeout);
|
|
397
409
|
removeListeners();
|
|
398
410
|
reject(new Error("Unreachable URL: " + frame.unreachableUrl));
|
|
399
411
|
} else {
|
|
@@ -433,7 +445,7 @@ async function getTopFrameContextId({ Page, Runtime }, options) {
|
|
|
433
445
|
contextId
|
|
434
446
|
});
|
|
435
447
|
return result.value === true;
|
|
436
|
-
} catch
|
|
448
|
+
} catch {
|
|
437
449
|
// ignored
|
|
438
450
|
}
|
|
439
451
|
return false;
|
package/lib/deno-polyfill.js
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
// deno-lint-ignore-file no-node-globals
|
|
25
|
+
|
|
24
26
|
/* global globalThis, URL, Deno, process */
|
|
25
27
|
|
|
26
28
|
import * as path from "path";
|
|
@@ -244,7 +246,7 @@ async function cwd() {
|
|
|
244
246
|
}
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
|
|
249
|
+
function dirname(filePath) {
|
|
248
250
|
return path.dirname(filePath);
|
|
249
251
|
}
|
|
250
252
|
|
|
@@ -41,7 +41,7 @@ function initSingleFile() {
|
|
|
41
41
|
xhrRequest.onreadystatechange = () => {
|
|
42
42
|
if (xhrRequest.readyState == XMLHttpRequest.DONE) {
|
|
43
43
|
resolve({
|
|
44
|
-
arrayBuffer:
|
|
44
|
+
arrayBuffer: () => xhrRequest.response || new ArrayBuffer(),
|
|
45
45
|
headers: { get: headerName => xhrRequest.getResponseHeader(headerName) },
|
|
46
46
|
status: xhrRequest.status
|
|
47
47
|
});
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.0.
|
|
1
|
+
export const version = "2.0.69";
|
package/options.js
CHANGED
|
@@ -50,6 +50,7 @@ const OPTIONS_INFO = {
|
|
|
50
50
|
"browser-load-max-time": { description: "Maximum delay of time to wait for page loading in ms", type: "number", defaultValue: 60000 },
|
|
51
51
|
"browser-wait-delay": { description: "Time to wait before capturing the page in ms", type: "number" },
|
|
52
52
|
"browser-wait-until": { description: "When to consider the page is loaded (InteractiveTime, networkIdle, networkAlmostIdle, load, domContentLoaded)", type: "string", defaultValue: "networkIdle" },
|
|
53
|
+
"browser-wait-until-delay": { description: "Delay of time in ms to wait before considering the page is loaded when the value of --browser-wait-until is reached", type: "number", defaultValue: 1000 },
|
|
53
54
|
"browser-wait-until-fallback": { description: "Retry with the next value of --browser-wait-until when a timeout error is thrown", type: "boolean", defaultValue: true },
|
|
54
55
|
"browser-debug": { description: "Enable debug mode", type: "boolean" },
|
|
55
56
|
"browser-script": { description: "Path of a script executed in the page (and all the frames) before it is loaded", type: "string[]" },
|
|
@@ -121,7 +122,7 @@ const OPTIONS_INFO = {
|
|
|
121
122
|
"remove-alternative-images": { description: "Remove images for alternative sizes of screen", type: "boolean", defaultValue: true },
|
|
122
123
|
"save-original-URLs": { description: "Save the original URLS in the embedded contents", type: "boolean" },
|
|
123
124
|
"save-raw-page": { description: "Save the original page without interpreting it into the browser", type: "boolean" },
|
|
124
|
-
"urls-file": { description: "Path to a text file containing a list of URLs (separated by a newline) to save", type: "string" },
|
|
125
|
+
"urls-file": { description: "Path to a text file containing a list of URLs (separated by a newline) to save. You can also pass the options after each URL (e.g. 'https://www.example.com --filename-template={page-title}.html')", type: "string" },
|
|
125
126
|
"user-agent": { description: "User-agent of the browser", type: "string" },
|
|
126
127
|
"accept-language": { description: "Accept language of the browser", type: "string" },
|
|
127
128
|
"platform": { description: "Platform of the browser (default value is \"Android\" when using --browser-mobile-emulation)", type: "string" },
|
|
@@ -149,10 +150,10 @@ const OPTIONS_INFO = {
|
|
|
149
150
|
};
|
|
150
151
|
|
|
151
152
|
const { args, exit } = Deno;
|
|
152
|
-
const options =
|
|
153
|
-
export
|
|
153
|
+
const options = getOptions();
|
|
154
|
+
export { options, parseArgs };
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
function getOptions() {
|
|
156
157
|
const { positionals, options } = parseArgs(Array.from(args));
|
|
157
158
|
const unknownOptions = [];
|
|
158
159
|
positionals.forEach(positional => {
|
|
@@ -185,43 +186,6 @@ async function getOptions() {
|
|
|
185
186
|
console.log(version); // eslint-disable-line no-console
|
|
186
187
|
exit(0);
|
|
187
188
|
}
|
|
188
|
-
Object.keys(OPTIONS_INFO).forEach(optionName => {
|
|
189
|
-
const optionInfo = getOptionInfo(optionName);
|
|
190
|
-
const optionKey = getOptionKey(optionName, optionInfo);
|
|
191
|
-
if (options[optionKey] === undefined && optionInfo.defaultValue !== undefined) {
|
|
192
|
-
options[optionKey] = OPTIONS_INFO[optionName].defaultValue;
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
options.acceptHeaders = {
|
|
196
|
-
font: options.acceptHeaderFont,
|
|
197
|
-
image: options.acceptHeaderImage,
|
|
198
|
-
stylesheet: options.acceptHeaderStylesheet,
|
|
199
|
-
script: options.acceptHeaderScript,
|
|
200
|
-
document: options.acceptHeaderDocument
|
|
201
|
-
};
|
|
202
|
-
if (options.browserArgs) {
|
|
203
|
-
const browserArguments = options.browserArguments || [];
|
|
204
|
-
browserArguments.push(...JSON.parse(options.browserArgs));
|
|
205
|
-
options.browserArgs = browserArguments;
|
|
206
|
-
delete options.browserArguments;
|
|
207
|
-
}
|
|
208
|
-
if (options.browserArguments) {
|
|
209
|
-
options.browserArgs = options.browserArguments;
|
|
210
|
-
delete options.browserArguments;
|
|
211
|
-
}
|
|
212
|
-
if (options.errorFile) {
|
|
213
|
-
options.errorsFile = options.errorFile;
|
|
214
|
-
delete options.errorFile;
|
|
215
|
-
}
|
|
216
|
-
if (options.errorTracesDisabled) {
|
|
217
|
-
options.errorsTracesDisabled = options.errorTracesDisabled;
|
|
218
|
-
delete options.errorTracesDisabled;
|
|
219
|
-
}
|
|
220
|
-
delete options.acceptHeaderFont;
|
|
221
|
-
delete options.acceptHeaderImage;
|
|
222
|
-
delete options.acceptHeaderStylesheet;
|
|
223
|
-
delete options.acceptHeaderScript;
|
|
224
|
-
delete options.acceptHeaderDocument;
|
|
225
189
|
return { ...options, url: positionals[0], output: positionals[1] };
|
|
226
190
|
}
|
|
227
191
|
|
|
@@ -280,6 +244,71 @@ function parseArgs(args) {
|
|
|
280
244
|
}
|
|
281
245
|
result.options[optionKey] = optionValue;
|
|
282
246
|
});
|
|
247
|
+
Object.keys(OPTIONS_INFO).forEach(optionName => {
|
|
248
|
+
const optionInfo = getOptionInfo(optionName);
|
|
249
|
+
const optionKey = getOptionKey(optionName, optionInfo);
|
|
250
|
+
if (result.options[optionKey] === undefined && optionInfo.defaultValue !== undefined) {
|
|
251
|
+
result.options[optionKey] = OPTIONS_INFO[optionName].defaultValue;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
result.options.acceptHeaders = {
|
|
255
|
+
font: result.options.acceptHeaderFont,
|
|
256
|
+
image: result.options.acceptHeaderImage,
|
|
257
|
+
stylesheet: result.options.acceptHeaderStylesheet,
|
|
258
|
+
script: result.options.acceptHeaderScript,
|
|
259
|
+
document: result.options.acceptHeaderDocument
|
|
260
|
+
};
|
|
261
|
+
if (result.options.browserArgs) {
|
|
262
|
+
const browserArguments = result.options.browserArguments || [];
|
|
263
|
+
browserArguments.push(...JSON.parse(result.options.browserArgs));
|
|
264
|
+
result.options.browserArgs = browserArguments;
|
|
265
|
+
delete result.options.browserArguments;
|
|
266
|
+
}
|
|
267
|
+
if (result.options.browserArguments) {
|
|
268
|
+
result.options.browserArgs = result.options.browserArguments;
|
|
269
|
+
delete result.options.browserArguments;
|
|
270
|
+
}
|
|
271
|
+
if (result.options.errorFile) {
|
|
272
|
+
result.options.errorsFile = result.options.errorFile;
|
|
273
|
+
delete result.options.errorFile;
|
|
274
|
+
}
|
|
275
|
+
if (result.options.errorTracesDisabled) {
|
|
276
|
+
result.options.errorsTracesDisabled = result.options.errorTracesDisabled;
|
|
277
|
+
delete result.options.errorTracesDisabled;
|
|
278
|
+
}
|
|
279
|
+
if (result.options.crawlReplaceUrls) {
|
|
280
|
+
result.options.crawlReplaceURLs = result.options.crawlReplaceUrls;
|
|
281
|
+
delete result.options.crawlReplaceUrls;
|
|
282
|
+
}
|
|
283
|
+
if (result.options.saveOriginalUrls) {
|
|
284
|
+
result.options.saveOriginalURLs = result.options.saveOriginalUrls;
|
|
285
|
+
delete result.options.saveOriginalUrls;
|
|
286
|
+
}
|
|
287
|
+
if (result.options.browserRemoteDebuggingUrl) {
|
|
288
|
+
result.options.browserRemoteDebuggingURL = result.options.browserRemoteDebuggingUrl;
|
|
289
|
+
delete result.options.browserRemoteDebuggingUrl;
|
|
290
|
+
}
|
|
291
|
+
if (result.options.crawlRemoveUrlFragment) {
|
|
292
|
+
result.options.crawlRemoveURLFragment = result.options.crawlRemoveUrlFragment;
|
|
293
|
+
delete result.options.crawlRemoveUrlFragment;
|
|
294
|
+
}
|
|
295
|
+
if (result.options.compressCss) {
|
|
296
|
+
result.options.compressCSS = result.options.compressCss;
|
|
297
|
+
delete result.options.compressCss;
|
|
298
|
+
}
|
|
299
|
+
if (result.options.compressHtml) {
|
|
300
|
+
result.options.compressHTML = result.options.compressHtml;
|
|
301
|
+
delete result.options.compressHtml;
|
|
302
|
+
}
|
|
303
|
+
if (result.options.insertMetaCsp) {
|
|
304
|
+
result.options.insertMetaCSP = result.options.insertMetaCsp;
|
|
305
|
+
delete result.options.insertMetaCsp;
|
|
306
|
+
}
|
|
307
|
+
delete result.options.acceptHeaderFont;
|
|
308
|
+
delete result.options.acceptHeaderImage;
|
|
309
|
+
delete result.options.acceptHeaderStylesheet;
|
|
310
|
+
delete result.options.acceptHeaderScript;
|
|
311
|
+
delete result.options.acceptHeaderDocument;
|
|
283
312
|
return result;
|
|
284
313
|
}
|
|
285
314
|
|
package/package.json
CHANGED
package/single-file-cli-api.js
CHANGED
|
@@ -101,7 +101,17 @@ async function initialize(options) {
|
|
|
101
101
|
async function capture(urls, options) {
|
|
102
102
|
let newTasks;
|
|
103
103
|
const taskUrls = tasks.map(task => task.url);
|
|
104
|
-
newTasks = await Promise.all(urls.map(
|
|
104
|
+
newTasks = await Promise.all(urls.map(value => {
|
|
105
|
+
let url, taskOptions;
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
url = value[0];
|
|
108
|
+
taskOptions = Object.assign({}, options, value[1]);
|
|
109
|
+
} else {
|
|
110
|
+
url = value;
|
|
111
|
+
taskOptions = options;
|
|
112
|
+
}
|
|
113
|
+
return createTask(url, taskOptions);
|
|
114
|
+
}));
|
|
105
115
|
newTasks = newTasks.filter(task => task && !taskUrls.includes(task.url));
|
|
106
116
|
if (newTasks.length) {
|
|
107
117
|
tasks = tasks.concat(newTasks);
|
|
@@ -127,7 +137,7 @@ async function finish(options) {
|
|
|
127
137
|
}
|
|
128
138
|
});
|
|
129
139
|
await writeTextFile(task.filename, pageContent);
|
|
130
|
-
} catch
|
|
140
|
+
} catch {
|
|
131
141
|
// ignored
|
|
132
142
|
}
|
|
133
143
|
}
|
|
@@ -137,7 +147,7 @@ async function finish(options) {
|
|
|
137
147
|
}
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
|
|
150
|
+
function runTasks() {
|
|
141
151
|
const availableTasks = tasks.filter(task => !task.status).length;
|
|
142
152
|
const processingTasks = tasks.filter(task => task.status == STATE_PROCESSING).length;
|
|
143
153
|
const promisesTasks = [];
|
|
@@ -151,7 +161,7 @@ async function runNextTask() {
|
|
|
151
161
|
const task = tasks.find(task => !task.status);
|
|
152
162
|
if (task) {
|
|
153
163
|
const options = task.options;
|
|
154
|
-
|
|
164
|
+
const taskOptions = JSON.parse(JSON.stringify(options));
|
|
155
165
|
taskOptions.url = task.url;
|
|
156
166
|
task.status = STATE_PROCESSING;
|
|
157
167
|
await saveTasks();
|
|
@@ -166,6 +176,7 @@ async function runNextTask() {
|
|
|
166
176
|
newTasks = newTasks.filter(task => task &&
|
|
167
177
|
testMaxDepth(task) &&
|
|
168
178
|
!tasks.find(otherTask => otherTask.url == task.url) &&
|
|
179
|
+
!newTasks.find(otherTask => otherTask != task && otherTask.url == task.url) &&
|
|
169
180
|
(!options.crawlInnerLinksOnly || task.isInnerLink) &&
|
|
170
181
|
(!options.crawlNoParent || (task.isChild || !task.isInnerLink)));
|
|
171
182
|
tasks.splice(tasks.length, 0, ...newTasks);
|
|
@@ -282,7 +293,7 @@ async function capturePage(options) {
|
|
|
282
293
|
if (options.outputJson) {
|
|
283
294
|
filename += filename.endsWith(".json") ? "" : ".json";
|
|
284
295
|
}
|
|
285
|
-
const directoryName =
|
|
296
|
+
const directoryName = path.dirname(filename);
|
|
286
297
|
if (directoryName !== ".") {
|
|
287
298
|
await mkdir(directoryName, { recursive: true });
|
|
288
299
|
}
|
|
@@ -338,7 +349,7 @@ async function getFilename(filename, options, index = 1) {
|
|
|
338
349
|
if (options.filenameConflictAction != "skip") {
|
|
339
350
|
return getFilename(filename, options, index + 1);
|
|
340
351
|
}
|
|
341
|
-
} catch
|
|
352
|
+
} catch {
|
|
342
353
|
return newFilename;
|
|
343
354
|
}
|
|
344
355
|
}
|
package/single-file-launcher.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import { initialize } from "./single-file-cli-api.js";
|
|
25
25
|
import { closeBrowser } from "./lib/browser.js";
|
|
26
26
|
import { Deno } from "./lib/deno-polyfill.js";
|
|
27
|
-
import options from "./options.js";
|
|
27
|
+
import { options, parseArgs } from "./options.js";
|
|
28
28
|
|
|
29
29
|
const { readTextFile, readFile, exit, addSignalListener } = Deno;
|
|
30
30
|
|
|
@@ -54,7 +54,7 @@ async function run() {
|
|
|
54
54
|
delete options.settingsFile;
|
|
55
55
|
}
|
|
56
56
|
if (options.urlsFile) {
|
|
57
|
-
urls =
|
|
57
|
+
urls = await getUrlsFile(options.urlsFile);
|
|
58
58
|
} else {
|
|
59
59
|
urls = [options.url];
|
|
60
60
|
}
|
|
@@ -80,7 +80,7 @@ async function run() {
|
|
|
80
80
|
const cookiesContent = await readTextFile(options.browserCookiesFile);
|
|
81
81
|
try {
|
|
82
82
|
options.browserCookies = JSON.parse(cookiesContent);
|
|
83
|
-
} catch
|
|
83
|
+
} catch {
|
|
84
84
|
options.browserCookies = parseCookies(cookiesContent);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -137,3 +137,56 @@ async function closeBrowserAndExit(code) {
|
|
|
137
137
|
await closeBrowser();
|
|
138
138
|
exit(code);
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
async function getUrlsFile(urlsFile) {
|
|
142
|
+
let urls = (await readTextFile(urlsFile)).split("\n");
|
|
143
|
+
urls = urls.map(value => {
|
|
144
|
+
value = value.trim();
|
|
145
|
+
let optionPosition = value.indexOf(" --");
|
|
146
|
+
if (optionPosition < 0) {
|
|
147
|
+
optionPosition = value.indexOf("\t--");
|
|
148
|
+
}
|
|
149
|
+
if (optionPosition > 0) {
|
|
150
|
+
const url = value.substring(0, optionPosition).trim();
|
|
151
|
+
const argsString = value.substring(optionPosition + 1).trim();
|
|
152
|
+
const args = [];
|
|
153
|
+
let previousCharacter, previousPreviousCharacter, lastQuoteCharacter;
|
|
154
|
+
let lastCharIndex = 0;
|
|
155
|
+
for (let currentCharIndex = 0; currentCharIndex < argsString.length; currentCharIndex++) {
|
|
156
|
+
const character = argsString[currentCharIndex];
|
|
157
|
+
if (character == lastQuoteCharacter && (previousCharacter != "\\" || previousPreviousCharacter == "\\")) {
|
|
158
|
+
args.push(argsString.substring(lastCharIndex, currentCharIndex));
|
|
159
|
+
lastQuoteCharacter = null;
|
|
160
|
+
lastCharIndex = currentCharIndex + 1;
|
|
161
|
+
} else if (!lastQuoteCharacter) {
|
|
162
|
+
if (character == "'" || character == "\"") {
|
|
163
|
+
lastQuoteCharacter = argsString[currentCharIndex];
|
|
164
|
+
lastCharIndex = currentCharIndex + 1;
|
|
165
|
+
} else {
|
|
166
|
+
const isSpaceCharacter = character == " " || character == "\t";
|
|
167
|
+
if (isSpaceCharacter || character == "=") {
|
|
168
|
+
if (isSpaceCharacter && (currentCharIndex == lastCharIndex + 1)) {
|
|
169
|
+
lastCharIndex++;
|
|
170
|
+
} else if (lastCharIndex < currentCharIndex) {
|
|
171
|
+
args.push(argsString.substring(lastCharIndex, currentCharIndex));
|
|
172
|
+
lastCharIndex = currentCharIndex + 1;
|
|
173
|
+
} else {
|
|
174
|
+
lastCharIndex = currentCharIndex + 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
previousPreviousCharacter = previousCharacter;
|
|
180
|
+
previousCharacter = character;
|
|
181
|
+
}
|
|
182
|
+
if (lastCharIndex < argsString.length) {
|
|
183
|
+
args.push(argsString.substring(lastCharIndex).trim());
|
|
184
|
+
}
|
|
185
|
+
const { options } = parseArgs(args);
|
|
186
|
+
return [url, options];
|
|
187
|
+
} else {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return urls;
|
|
192
|
+
}
|