diff --git a/core/classes/actions/class.action.quickPick.php b/core/classes/actions/class.action.quickPick.php index 4532ba1e2..510c5cf4b 100644 --- a/core/classes/actions/class.action.quickPick.php +++ b/core/classes/actions/class.action.quickPick.php @@ -432,19 +432,19 @@ public function fetchAndUnzipModule(string $moduleUrl, string $module): array // Retrieve the file path from the URL using the bearsamppCore module, // passing the module URL and temporary file path, with the use Progress Bar parameter set to true. - $filePath = $bearsamppCore->getFileFromUrl($moduleUrl, $tmpFilePath, true); + $result = $bearsamppCore->getFileFromUrl($moduleUrl, $tmpFilePath, true); // Determine the file extension and call the appropriate unzipping function - $fileExtension = pathinfo( $filePath, PATHINFO_EXTENSION ); + $fileExtension = pathinfo( $tmpFilePath, PATHINFO_EXTENSION ); Util::logDebug( 'File extension: ' . $fileExtension ); if ( $fileExtension === '7z' ) { - if ( !$bearsamppCore->unzip7zFile( $filePath, $destination ) ) { - return ['error' => 'Failed to unzip .7z file. File: ' . $filePath . ' could not be unzipped', 'Destination: ' . $destination]; + if ( !$bearsamppCore->unzip7zFile( $tmpFilePath, $destination ) ) { + return ['error' => 'Failed to unzip .7z file. File: ' . $tmpFilePath . ' could not be unzipped', 'Destination: ' . $destination]; } } elseif ( $fileExtension === 'zip' ) { - if ( !$bearsamppCore->unzipFile( $filePath, $destination ) ) { - return ['error' => 'Failed to unzip .zip file' . $filePath . ' could not be unzipped', 'Destination: ' . $destination]; + if ( !$bearsamppCore->unzipFile( $tmpFilePath, $destination ) ) { + return ['error' => 'Failed to unzip .zip file' . $tmpFilePath . ' could not be unzipped', 'Destination: ' . $destination]; } } else { diff --git a/core/classes/class.core.php b/core/classes/class.core.php index db2b1a0cd..c25d81cef 100644 --- a/core/classes/class.core.php +++ b/core/classes/class.core.php @@ -564,44 +564,37 @@ public function getFileFromUrl(string $moduleUrl, string $filePath, $progressBar } // Ensure $inputStream is a valid file resource - if (is_resource($inputStream)) { + if ( is_resource( $inputStream ) ) { // Get file statistics - $fileStats = fstat($inputStream); - + $fileStats = fstat( $inputStream ); // Extract the file size $fileSize = $fileStats['size']; - // Output the file size Util::logError( 'File size: ' . $fileSize . ' bytes' ); } // Read and write in chunks to avoid memory overload $bufferSize = 32768; // 32KB + $steps = $fileSize / $bufferSize; + $bytesRead = 0; + while ( !feof( $inputStream ) ) { $buffer = fread( $inputStream, $bufferSize ); - if ( $buffer === false ) { - Util::logError( 'Error reading from URL: ' . $moduleUrl ); - fclose( $inputStream ); - fclose( $outputStream ); - - return ['error' => 'Error reading module']; - } - - if ( fwrite( $outputStream, $buffer ) === false ) { - Util::logError( 'Error writing to file: ' . $filePath ); - fclose( $inputStream ); - fclose( $outputStream ); - - return ['error' => 'Error saving module']; + fwrite( $outputStream, $buffer ); + $bytesRead += strlen( $buffer ); + + // Send progress update + if ( $progressBar ) { + $progress = min( 100, ($bytesRead / $fileSize) * 100 ); + echo json_encode( ['progress' => $progress] ); + ob_flush(); + flush(); } } - // Close the streams fclose( $inputStream ); fclose( $outputStream ); - Util::logDebug( 'File saved to: ' . $filePath ); - - return $filePath; + return ['success' => true]; } } diff --git a/core/resources/homepage/ajax/ajax.quickpick.php b/core/resources/homepage/ajax/ajax.quickpick.php index 23f82f184..89df5f80b 100644 --- a/core/resources/homepage/ajax/ajax.quickpick.php +++ b/core/resources/homepage/ajax/ajax.quickpick.php @@ -39,29 +39,25 @@ $response = array(); -if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) { - $module = isset( $_POST['module'] ) ? $_POST['module'] : null; - $version = isset( $_POST['version'] ) ? $_POST['version'] : null; +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $module = isset($_POST['module']) ? $_POST['module'] : null; + $version = isset($_POST['version']) ? $_POST['version'] : null; - if ( $module && $version ) { - $response = $QuickPick->installModule( $module, $version ); - if ( !isset( $response['error'] ) ) { - $response['message'] = "Module $module version $version installed successfully."; - if ( isset( $QuickPick->modules[$module] ) && $QuickPick->modules[$module]['type'] === "binary" ) { - $response['message'] .= "\nReload needed... Right click on menu and choose reload."; - } - else { - $response['message'] .= "\nEdit Bearsampp.conf to use new version then"; - $response['message'] .= "\nRight click on menu and choose reload."; - } - } - } - else { - $response['error'] = "Invalid module or version."; - } + if ($module && $version) { + $response = $QuickPick->installModule($module, $version); + if (!isset($response['error'])) { + $response['message'] = "Module $module version $version installed successfully."; + if (isset($QuickPick->modules[$module]) && $QuickPick->modules[$module]['type'] === "binary") { + $response['message'] .= "\nReload needed... Right click on menu and choose reload."; + } else { + $response['message'] .= "\nEdit Bearsampp.conf to use new version then"; + $response['message'] .= "\nRight click on menu and choose reload."; + } + } + echo json_encode($response); + } else { + echo json_encode(['error' => 'Invalid module or version.']); + } +} else { + echo json_encode(['error' => 'Invalid request method.']); } -else { - $response['error'] = "Invalid request method."; -} - -echo json_encode( $response ); diff --git a/core/resources/homepage/js/quickpick.js b/core/resources/homepage/js/quickpick.js index 09a1a348a..665fd4a35 100644 --- a/core/resources/homepage/js/quickpick.js +++ b/core/resources/homepage/js/quickpick.js @@ -134,10 +134,10 @@ async function installModule(moduleName, version) { const downloadmodule = document.getElementById('download-module'); const downloadversion = document.getElementById('download-version'); - progressbar.innerText="Downloading ".concat(moduleName).concat(' ').concat(version) - progress.style.display="block"; - downloadmodule.innerText=moduleName; - downloadversion.innerText=version; + progressbar.innerText = "Downloading ".concat(moduleName).concat(' ').concat(version); + progress.style.display = "block"; + downloadmodule.innerText = moduleName; + downloadversion.innerText = version; senddata.append('module', moduleName); senddata.append('version', version); senddata.append('proc', 'quickpick'); // Setting 'proc' to 'quickpick' @@ -151,14 +151,34 @@ async function installModule(moduleName, version) { }; try { - let response = await fetch(url, options); + const response = await fetch(url, options); if (!response.ok) { throw new Error('Network response was not ok'); } - let responseText = await response.text(); + + const reader = response.body.getReader(); + const contentLength = +response.headers.get('Content-Length'); + let receivedLength = 0; + let chunks = []; + + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + chunks.push(value); + receivedLength += value.length; + + const progress = (receivedLength / contentLength) * 100; + progressbar.style.width = progress + '%'; + progressbar.innerText = `Downloading ${moduleName} ${version} (${Math.round(progress)}%)`; + } + + const responseText = new TextDecoder("utf-8").decode(new Uint8Array(chunks.flat())); console.log('Response Text:', responseText); // Log the response text + try { - let data = JSON.parse(responseText); + const data = JSON.parse(responseText); if (data.error) { console.error('Error:', data.error); window.alert(`Error: ${data.error}`); @@ -178,7 +198,6 @@ async function installModule(moduleName, version) { } } - function setProgress(progpercent) { const progress = document.getElementById('progress'); const progressbar = document.getElementById('progress-bar');