diff --git a/lib/commands/serve/streempublic.js b/lib/commands/serve/streempublic.js index a13cb92..46fd9c5 100644 --- a/lib/commands/serve/streempublic.js +++ b/lib/commands/serve/streempublic.js @@ -14,51 +14,87 @@ const getFile = (url, dest) => new Promise((resolve, reject) => { maximumFractionDigits: 2 }); - var file = fs.createWriteStream(dest); - var request = http.get(url, function (response) { - const contentLength = response.headers['content-length']; - const totalBytes = contentLength ? parseInt(contentLength, 10) : NaN; - let downloadedBytes = 0; - - if (isNaN(totalBytes)) { - warn('Missing or invalid "Content-Length" header. Progress bar will be skipped.'); - } - - response.on('data', (chunk) => { - downloadedBytes += chunk.length; - - if (!isNaN(totalBytes)) { - if (downloadedBytes < totalBytes) { - const downloadedPercentage = downloadedBytes / totalBytes; - const formattedDownloadPercentage = percentFormatter.format(downloadedPercentage); - const barLength = Math.max(MIN_BAR_LENGTH, terminalWidth - BASE_DOWNLOAD_MESSAGE.length); - const filledLength = Math.round(barLength * downloadedPercentage); - const bar = '█'.repeat(filledLength) + '-'.repeat(barLength - filledLength); - - process.stdout.write(`\rDownloading MainFrame Xpublic Zip: ${formattedDownloadPercentage} [${bar}]`); - } else { - process.stdout.write('\n'); - } + const existingBytes = fs.existsSync(dest) ? fs.statSync(dest).size : 0; + + function startDownload(resumeFrom) { + const file = resumeFrom > 0 + ? fs.createWriteStream(dest, { flags: 'a' }) + : fs.createWriteStream(dest); + + const makeRequest = (callback) => resumeFrom > 0 + ? http.get(url, { headers: { 'Range': `bytes=${resumeFrom}-` } }, callback) + : http.get(url, callback); + + makeRequest(function (response) { + const isResuming = response.statusCode === 206; + + if (resumeFrom > 0 && response.statusCode === 200) { + warn('Server does not support resumable downloads. Starting fresh download.'); + file.destroy(); + fs.unlink(dest, (unlinkErr) => { + if (unlinkErr) { error(unlinkErr); } + startDownload(0); + }); + return; } - }); - response.pipe(file); - response.on('error', (err) => { - fs.unlink(dest, () => reject(err)); - }); + if (response.statusCode === 416) { + file.destroy(); + resolve(); + return; + } - file.on('error', (err) => { - fs.unlink(dest, () => reject(err)); - }); - file.on('finish', function () { - file.close(() => resolve()); + const contentLength = response.headers['content-length']; + const responseBytes = contentLength ? parseInt(contentLength, 10) : NaN; + const totalBytes = isResuming ? resumeFrom + responseBytes : responseBytes; + let downloadedBytes = isResuming ? resumeFrom : 0; + + if (isResuming) { + console.log(`Resuming MainFrame Xpublic Zip download from byte ${resumeFrom}...`); + } + + if (isNaN(totalBytes)) { + warn('Missing or invalid "Content-Length" header. Progress bar will be skipped.'); + } + + response.on('data', (chunk) => { + downloadedBytes += chunk.length; + + if (!isNaN(totalBytes)) { + if (downloadedBytes < totalBytes) { + const downloadedPercentage = downloadedBytes / totalBytes; + const formattedDownloadPercentage = percentFormatter.format(downloadedPercentage); + const barLength = Math.max(MIN_BAR_LENGTH, terminalWidth - BASE_DOWNLOAD_MESSAGE.length); + const filledLength = Math.round(barLength * downloadedPercentage); + const bar = '█'.repeat(filledLength) + '-'.repeat(barLength - filledLength); + + process.stdout.write(`\rDownloading MainFrame Xpublic Zip: ${formattedDownloadPercentage} [${bar}]`); + } else { + process.stdout.write('\n'); + } + } + }); + + response.pipe(file); + response.on('error', (err) => { + fs.unlink(dest, () => reject(err)); + }); + + file.on('error', (err) => { + fs.unlink(dest, () => reject(err)); + }); + file.on('finish', function () { + file.close(() => resolve()); + }); + }).on('error', function (err) { + error('Main Frame URL could not be found. Please make sure .development.env file has the correct MAINFRAME_URL settings.'); + error(err); + fs.unlink(dest, () => {}); + reject(err); }); - }).on('error', function (err) { - error('Main Frame URL could not be found. Please make sure .development.env file has the correct MAINFRAME_URL settings.'); - error(err); - fs.unlink(dest); - reject(err); - }); + } + + startDownload(existingBytes); }); exports.prapereDevPub = async function (api, pubzipurl) {