penguinmod-editor / src /components /loader /tw-progress-monitor.js
soiz1's picture
Update src/components/loader/tw-progress-monitor.js
6f54888 verified
raw
history blame
3.88 kB
const { exec } = require("child_process");
// CORS Anywhere サーバーを起動
const corsProxy = exec("npx cors-anywhere --port 8080", (error, stdout, stderr) => {
if (error) {
console.error(`CORS Proxy 起動エラー: ${error.message}`);
return;
}
if (stderr) {
console.error(`CORS Proxy 警告: ${stderr}`);
return;
}
console.log(`CORS Proxy 実行中: ${stdout}`);
});
// CORS Proxy のプロセスを監視
corsProxy.stdout.on("data", data => console.log(`CORS Proxy: ${data}`));
corsProxy.stderr.on("data", data => console.error(`CORS Proxy Error: ${data}`));
console.log("Node.js サーバーが起動しました!");
// グローバルな進行状況管理変数
let total = 0;
let complete = 0;
let state = 0;
let currentProgress = 0;
let progressHandler = (state, progress, complete, total) => {};
// 進行状況ハンドラを設定する関数
export const setProgressHandler = newHandler => {
progressHandler = newHandler;
progressHandler(state, currentProgress, complete, total);
};
// 進行状況更新をキューに入れる関数
const queueProgressHandlerUpdate = () => {
if (progressHandlerTimeout === null) {
progressHandlerTimeout = requestAnimationFrame(fireProgressHandler);
}
};
// 進行状況を更新する関数
const setProgress = progress => {
if (progress < 0) {
progress = 0;
}
if (progress > 1) {
progress = 1;
}
currentProgress = progress;
queueProgressHandlerUpdate();
};
// ステータスを設定する関数
const setState = newState => {
if (state === newState) {
return;
}
state = newState;
complete = 0;
total = 0;
setProgress(0);
};
// XMLHttpRequestで進行状況を監視してリソースを取得する関数
const fetchWithProgress = url => {
setState(1);
const proxyUrl = `http://127.0.0.1:8080/` + encodeURIComponent(url); // ローカルのCORSプロキシを使用
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = () => {
resolve(new Response(xhr.response, {
status: xhr.status,
statusText: xhr.statusText
}));
};
xhr.onloadend = () => setProgress(1);
xhr.onerror = () => reject(new Error('[tw-progress-monitor] xhr failed with status ' + xhr.status));
xhr.onprogress = e => {
if (e.lengthComputable) {
setProgress(e.loaded / e.total);
}
};
xhr.open('GET', proxyUrl);
xhr.send();
});
};
// fetchのオーバーライド
const originalFetch = window.fetch;
window.fetch = (url, opts) => {
const isGET = typeof opts === 'object' && opts && opts.method === 'GET';
const isProjectURL = typeof url === 'string' && /^https:\/\/projects\.scratch\.mit\.edu\/\d+$/.test(url);
if (isGET && isProjectURL) {
return fetchWithProgress(url);
}
return originalFetch(url, opts);
};
// Web Workerのメッセージ処理
const handleWorkerMessage = e => {
const data = e.data;
if (Array.isArray(data)) {
complete += data.length;
setProgress(complete / total);
}
};
if (window.Worker) {
let downloadWorker = null;
const originalPostMessage = window.Worker.prototype.postMessage;
window.Worker.prototype.postMessage = function (message) {
if (downloadWorker === null) {
if (message && message.url && message.id && message.options) {
downloadWorker = this;
downloadWorker.addEventListener('message', handleWorkerMessage);
}
}
if (downloadWorker === this) {
setState(2);
total++;
}
originalPostMessage.call(this, message);
};
}