Skip to content

Commit

Permalink
Refactor unzipFile method in class.core.php and `fetchAndUnzipMod…
Browse files Browse the repository at this point in the history
…ule` method in `class.action.quickPick.php` for improved logging and progress handling.

Progressbar now shows % extracted properly
  • Loading branch information
N6REJ committed Aug 27, 2024
1 parent c7e1d03 commit 3b52dcb
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 124 deletions.
106 changes: 52 additions & 54 deletions core/classes/actions/class.action.quickPick.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?php
/*
* Copyright (c) 2021-2024 Bearsampp
* License: GNU General Public License version 3 or later; see LICENSE.txt
* Author: Bear
* Website: https://bearsampp.com
* Github: https://github.com/Bearsampp
*
* * Copyright (c) 2021-2024 Bearsampp
* * License: GNU General Public License version 3 or later; see LICENSE.txt
* * Website: https://bearsampp.com
* * Github: https://github.com/Bearsampp
*
*/

/**
Expand Down Expand Up @@ -405,73 +406,70 @@ public function installModule(string $module, string $version): array
* @return array An array containing the status and message.
*/
public function fetchAndUnzipModule(string $moduleUrl, string $module): array
{
Util::logDebug( "$module is: " . $module );
{
Util::logDebug("$module is: " . $module);

global $bearsamppRoot, $bearsamppCore;
$tmpDir = $bearsamppRoot->getTmpPath();
Util::logDebug( 'Temporary Directory: ' . $tmpDir );
global $bearsamppRoot, $bearsamppCore;
$tmpDir = $bearsamppRoot->getTmpPath();
Util::logDebug('Temporary Directory: ' . $tmpDir);

$fileName = basename( $moduleUrl );
Util::logDebug( 'File Name: ' . $fileName );
$fileName = basename($moduleUrl);
Util::logDebug('File Name: ' . $fileName);

$tmpFilePath = $tmpDir . '/' . $fileName;
Util::logDebug( 'File Path: ' . $tmpFilePath );
$tmpFilePath = $tmpDir . '/' . $fileName;
Util::logDebug('File Path: ' . $tmpFilePath);

$moduleName = str_replace( 'module-', '', $module );
Util::logDebug( 'Module Name: ' . $moduleName );
$moduleName = str_replace('module-', '', $module);
Util::logDebug('Module Name: ' . $moduleName);

$moduleType = $this->modules[$module]['type'];
Util::logDebug( 'Module Type: ' . $moduleType );
$moduleType = $this->modules[$module]['type'];
Util::logDebug('Module Type: ' . $moduleType);

// Get module type
$destination = $this->getModuleDestinationPath( $moduleType, $moduleName );
Util::logDebug( 'Destination: ' . $destination );
// Get module type
$destination = $this->getModuleDestinationPath($moduleType, $moduleName);
Util::logDebug('Destination: ' . $destination);

// 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.
$result = $bearsamppCore->getFileFromUrl( $moduleUrl, $tmpFilePath, true );
// 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.
$result = $bearsamppCore->getFileFromUrl($moduleUrl, $tmpFilePath, true);

// Check if $result is false
if ( $result === false ) {
Util::logError( 'Failed to retrieve file from URL: ' . $moduleUrl );
// Check if $result is false
if ($result === false) {
Util::logError('Failed to retrieve file from URL: ' . $moduleUrl);
return ['error' => 'Failed to retrieve file from URL'];
}

return ['error' => 'Failed to retrieve file from URL'];
}
// Determine the file extension and call the appropriate unzipping function
$fileExtension = pathinfo($tmpFilePath, PATHINFO_EXTENSION);
Util::logDebug('File extension: ' . $fileExtension);

// Determine the file extension and call the appropriate unzipping function
$fileExtension = pathinfo( $tmpFilePath, PATHINFO_EXTENSION );
Util::logDebug( 'File extension: ' . $fileExtension );
if ($fileExtension === '7z' || $fileExtension === 'zip') {
// Send phase indicator for extraction
echo json_encode(['phase' => 'extracting']);
if (ob_get_length()) {
ob_flush();
}
flush();

if ( $fileExtension === '7z' || $fileExtension === 'zip' ) {
// Send phase indicator for extraction
echo json_encode( ['phase' => 'extracting'] );
if ( ob_get_length() ) {
$unzipResult = $bearsamppCore->unzipFile($tmpFilePath, $destination, function ($currentPercentage) {
echo json_encode(['progress' => "$currentPercentage%"]);
if (ob_get_length()) {
ob_flush();
}
flush();
});

$unzipResult = $bearsamppCore->unzipFile( $tmpFilePath, $destination, function ($currentFile, $totalFiles) {
echo json_encode( ['progress' => "$currentFile of $totalFiles"] );
if ( ob_get_length() ) {
ob_flush();
}
flush();
} );

if ( $unzipResult === false ) {
return ['error' => 'Failed to unzip file. File: ' . $tmpFilePath . ' could not be unzipped', 'Destination: ' . $destination];
}
}
else {
Util::logError( 'Unsupported file extension: ' . $fileExtension );

return ['error' => 'Unsupported file extension'];
if ($unzipResult === false) {
return ['error' => 'Failed to unzip file. File: ' . $tmpFilePath . ' could not be unzipped', 'Destination: ' . $destination];
}

return ['success' => 'Module installed successfully'];
} else {
Util::logError('Unsupported file extension: ' . $fileExtension);
return ['error' => 'Unsupported file extension'];
}

return ['success' => 'Module installed successfully'];
}

/**
* Get the destination path for a given module type and name.
*
Expand Down
145 changes: 76 additions & 69 deletions core/classes/class.core.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?php
/*
* Copyright (c) 2021-2024 Bearsampp
* License: GNU General Public License version 3 or later; see LICENSE.txt
* Author: Bear
* Website: https://bearsampp.com
* Github: https://github.com/Bearsampp
*
* * Copyright (c) 2021-2024 Bearsampp
* * License: GNU General Public License version 3 or later; see LICENSE.txt
* * Website: https://bearsampp.com
* * Github: https://github.com/Bearsampp
*
*/

/**
Expand Down Expand Up @@ -475,78 +476,84 @@ public function __toString()
* - 'success' => true and 'numFiles' => int on success.
* - 'error' => string and 'numFiles' => int on failure.
*/
public function unzipFile($filePath, $destination, $progressCallback = null)
{
global $bearsamppRoot;

$sevenZipPath = $this->getLibsPath() . '/7zip/7za.exe';

if ( !file_exists( $sevenZipPath ) ) {
Util::logError( '7za.exe not found at: ' . $sevenZipPath );

return false;
}

// Command to test the archive and get the number of files
$testCommand = escapeshellarg( $sevenZipPath ) . ' t ' . escapeshellarg( $filePath ) . ' -y -bsp1';
$testOutput = shell_exec( $testCommand );

// Extract the number of files from the test command output
preg_match( '/Files: (\d+)/', $testOutput, $matches );
$numFiles = isset( $matches[1] ) ? (int) $matches[1] : 0;
Util::logDebug( 'Number of files to be extracted: ' . $numFiles );

// Command to extract the archive
$command = escapeshellarg( $sevenZipPath ) . ' x ' . escapeshellarg( $filePath ) . ' -y -bb1 -o' . escapeshellarg( $destination );
Util::logTrace( 'Executing command: ' . $command );

$process = popen( $command, 'rb' );

if ( $process ) {
$filesExtracted = 0;
$buffer = '';

while ( !feof( $process ) ) {
$buffer .= fread( $process, 8192 ); // Read in smaller chunks of 4KB

while ( ($newlinePos = strpos( $buffer, "\n" )) !== false ) {
$line = substr( $buffer, 0, $newlinePos + 1 );
$buffer = substr( $buffer, $newlinePos + 1 );

if ( $progressCallback && preg_match( '/^- (.+)$/', $line, $matches ) ) {
$fileName = trim( $matches[1] );

// Check if the extracted item is a file and not a directory
if ( substr( $fileName, -1 ) !== '\\' ) {
$filesExtracted++;
if ( $filesExtracted <= $numFiles ) {
call_user_func( $progressCallback, $filesExtracted, $numFiles );
}
}
}
public function unzipFile($filePath, $destination, $progressCallback = null)
{
global $bearsamppRoot;

$sevenZipPath = $this->getLibsPath() . '/7zip/7za.exe';

if (!file_exists($sevenZipPath)) {
Util::logError('7za.exe not found at: ' . $sevenZipPath);
return false;
}

// Command to test the archive and get the number of files
$testCommand = escapeshellarg($sevenZipPath) . ' t ' . escapeshellarg($filePath) . ' -y -bsp1';
$testOutput = shell_exec($testCommand);

// Extract the number of files from the test command output
preg_match('/Files: (\d+)/', $testOutput, $matches);
$numFiles = isset($matches[1]) ? (int)$matches[1] : 0;
Util::logTrace('Number of files to be extracted: ' . $numFiles);

// Command to extract the archive
$command = escapeshellarg($sevenZipPath) . ' x ' . escapeshellarg($filePath) . ' -y -bsp1 -bb0 -o' . escapeshellarg($destination);
Util::logTrace('Executing command: ' . $command);

$process = popen($command, 'rb');

if ($process) {
$buffer = '';
while (!feof($process)) {
$buffer .= fread($process, 8192); // Read in chunks of 8KB
while (($pos = strpos($buffer, "\r")) !== false) {
$line = substr($buffer, 0, $pos);
$buffer = substr($buffer, $pos + 1);
$line = trim($line); // Remove any leading/trailing whitespace
Util::logTrace("Processing line: $line");

// Adjusted preg_match pattern to match the percentage preceded by a space or at the start of the line
if ($progressCallback && preg_match('/(?:^|\s)(\d+)%/', $line, $matches)) {
$currentPercentage = intval($matches[1]);
Util::logTrace("Extraction progress: $currentPercentage%");
call_user_func($progressCallback, $currentPercentage);
Util::logTrace("Progress callback called with percentage: $currentPercentage");
} else {
Util::logTrace("Line did not match pattern: $line");
}
}
}

$returnVar = pclose( $process );
Util::logDebug( 'Command return value: ' . $returnVar );

if ( $returnVar === 0 ) {
Util::logDebug( 'Successfully unzipped file to: ' . $destination );

return ['success' => true, 'numFiles' => $numFiles];
}
else {
Util::logError( 'Failed to unzip file. Command return value: ' . $returnVar );

return ['error' => 'Failed to unzip file', 'numFiles' => $numFiles];
// Process any remaining data in the buffer
if (!empty($buffer)) {
$line = trim($buffer);
Util::logTrace("Processing remaining line: $line");

if ($progressCallback && preg_match('/(?:^|\s)(\d+)%/', $line, $matches)) {
$currentPercentage = intval($matches[1]);
Util::logTrace("Extraction progress: $currentPercentage%");
call_user_func($progressCallback, $currentPercentage);
Util::logTrace("Progress callback called with percentage: $currentPercentage");
} else {
Util::logTrace("Remaining line did not match pattern: $line");
}
}
else {
Util::logError( 'Failed to open process for command: ' . $command );

return ['error' => 'Failed to open process', 'numFiles' => $numFiles];
$returnVar = pclose($process);
Util::logTrace('Command return value: ' . $returnVar);

if ($returnVar === 0) {
Util::logDebug('Successfully unzipped file to: ' . $destination);
return ['success' => true, 'numFiles' => $numFiles];
} else {
Util::logError('Failed to unzip file. Command return value: ' . $returnVar);
return ['error' => 'Failed to unzip file', 'numFiles' => $numFiles];
}
} else {
Util::logError('Failed to open process for command: ' . $command);
return ['error' => 'Failed to open process', 'numFiles' => $numFiles];
}
}

/**
* Fetches a file from a given URL and saves it to a specified file path.
Expand Down
2 changes: 1 addition & 1 deletion core/resources/homepage/js/quickpick.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ async function installModule(moduleName, version) {
if (isDownloading) {
progressbar.innerText = `${progressValue} KBytes Downloaded`;
} else {
progressbar.innerText = `${progressValue} Files Extracted`;
progressbar.innerText = `${progressValue} Extracted`;
}
} else if (data.success) {

Expand Down

0 comments on commit 3b52dcb

Please sign in to comment.