class Extractor { constructor() { this._tasks = []; this._running = false; } /** * Add a task to Extractor. \n * One Extractor could has multiple tasks, which orgnized in a task chian. * If url arguments not given within later tasks, they will use previous task result as input (target url list). * @param {...any} args itemsSelector, fieldSelectors, and more args to specify target urls. */ task(...args) { this._tasks.push(new Task(...args)); return this; } /** * Clear tasks and task caches. */ clear() { this._tasks = []; return this; } /** * Start the task chain. */ async start() { return this._startTasks(0); } /** * restart from specified task, but don't restart the previous tasks. * @param {number} from where to restart the tasks, begins with 0 */ async restart(from = 0) { let id = this._checkTaskId(from, 0); if (!id) return; for (let i = id; i < this._tasks.length; i++) { this._tasks[i].clean(); } return this._startTasks(0); } async _startTasks(from) { if (this._running) { console.log('The Extractor is running. Please wait..'); return; } if (!this._tasks.length) { console.log('No task to run.'); return; } let tab; let task = this._tasks[0]; if (task.urls.length) { // task specifies target urls, create new tab with first url for it tab = await createTab(task.urls[0], false); } else { tab = await getActiveTab(true) || await getActiveTab(false); } this._running = true; return this._tasks.reduce((pms, task, i) => { return pms.then( () => { if (i < from) return; if (i > 0) { let prevTask = this._tasks[i - 1]; return task.execute(tab, new ExtractResult(prevTask.results)); } return task.execute(tab, undefined); }); }, Promise.resolve(undefined)).then( () => this.save() ).catch(err => { this._running = false; console.log(err) }); } /** * Save result of a task * @param {number} taskid which task id to save, begins with 0 */ save(taskid) { let id = this._checkTaskId(taskid, this._tasks.length - 1); if (!id) return; let result = new ExtractResult(this._tasks[id].results); if (!result.data.length) { console.log(`No result for task #${id}. Forget to call ".start()"?`); return; } let msg = ` Please confirm to download (${result.data.length - 1} items): ${result.toString(50) || "- Empty -"} `.trim(); if (confirm(msg)) { saveFile(result, "text/csv"); } } _checkTaskId(id, defaultId) { if (!this._tasks.length) { console.log("No task found."); return 0; } if (defaultId && id === undefined) id = defaultId; if (isNaN(id) || id < 0 || id >= this._tasks.length) { console.log(`Invalid task id. Rang(0-${this._tasks.length - 1})`); return 0; } return id } }