InternetOs / src /helpers /download.js
Hjm1a's picture
Upload 259 files
b72f5af verified
/**
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Launches a download process for an item, tracking its progress and handling success or error states.
* The function returns a promise that resolves with the downloaded item or rejects in case of an error.
* It uses XMLHttpRequest to manage the download and tracks progress both for the individual item and the entire batch it belongs to.
*
* @param {Object} options - Configuration options for the download process.
* @param {string} options.url - The URL from which the item will be downloaded.
* @param {string} options.operation_id - Unique identifier for the download operation, used for progress tracking.
* @param {string} options.item_upload_id - Identifier for the specific item being downloaded, used for individual progress tracking.
* @param {string} [options.name] - Optional name for the item being downloaded.
* @param {string} [options.dest_path] - Destination path for the downloaded item.
* @param {string} [options.shortcut_to] - Optional shortcut path for the item.
* @param {boolean} [options.dedupe_name=false] - Flag to enable or disable deduplication of item names.
* @param {boolean} [options.overwrite=false] - Flag to enable or disable overwriting of existing items.
* @param {function} [options.success] - Optional callback function that is executed on successful download.
* @param {function} [options.error] - Optional callback function that is executed in case of an error.
* @param {number} [options.return_timeout=500] - Optional timeout in milliseconds before resolving the download.
* @returns {Promise<Object>} A promise that resolves with the downloaded item or rejects with an error.
*/
const download = function(options){
return new Promise((resolve, reject) => {
// The item that is being downloaded and will be returned to the caller at the end of the process
let item;
// Intervals that check for progress and cancel every few milliseconds
let progress_check_interval, cancel_check_interval;
// Progress tracker for the entire batch to which this item belongs
let batch_download_progress = window.progress_tracker[options.operation_id];
// Tracker for this specific item's download progress
let item_download_progress = batch_download_progress[options.item_upload_id];
let xhr = new XMLHttpRequest();
xhr.open("post", (api_origin + '/download'), true);
xhr.setRequestHeader("Authorization", "Bearer " + auth_token);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.addEventListener('load', function(e){
// error
if(this.status !== 200){
if(options.error && typeof options.error === 'function')
options.error(JSON.parse(this.responseText))
return reject(JSON.parse(this.responseText))
}
// success
else{
item = JSON.parse(this.responseText);
}
});
// error
xhr.addEventListener('error', function(e){
if(options.error && typeof options.error === 'function')
options.error(e)
return reject(e)
})
xhr.send(JSON.stringify({
url: options.url,
operation_id: options.operation_id,
socket_id: window.socket ? window.socket.id : null,
item_upload_id: options.item_upload_id,
// original_client_socket_id: window.socket.id,
name: options.name,
path: options.dest_path,
shortcut_to: options.shortcut_to,
dedupe_name: options.dedupe_name ?? false,
overwrite: options.overwrite ?? false,
}));
//----------------------------------------------
// Regularly check if this operation has been cancelled by the user
//----------------------------------------------
cancel_check_interval = setInterval(() => {
if(operation_cancelled[options.operation_id]){
xhr.abort();
clearInterval(cancel_check_interval);
clearInterval(progress_check_interval);
}
}, 100);
//----------------------------------------------
// Regularly check the progress of the cloud-write operation
//----------------------------------------------
progress_check_interval = setInterval(function() {
// Individual item progress
let item_progress = 1;
if(item_download_progress.total)
item_progress = (item_download_progress.cloud_uploaded + item_download_progress.downloaded) / item_download_progress.total;
// Entire batch progress
let batch_progress = ((batch_download_progress[0].cloud_uploaded + batch_download_progress[0].downloaded)/batch_download_progress[0].total * 100).toFixed(0);
batch_progress = batch_progress > 100 ? 100 : batch_progress;
// Update the progress bar
$(`[data-download-operation-id="${options.operation_id}"]`).find('.download-progress-bar').css( 'width', batch_progress+'%');
// If download is finished resolve promise
if((item_progress >= 1 || item_progress === 0) && item){
// For a better UX, resolve 0.5 second after operation is finished.
setTimeout(function() {
clearInterval(progress_check_interval);
clearInterval(cancel_check_interval);
if(options.success && typeof options.success === 'function'){
options.success(item)
}
resolve(item);
}, options.return_timeout ?? 500);
// Stop and clear the the cloud progress check interval
clearInterval(progress_check_interval)
}
}, 200);
return xhr;
})
}
export default download;