File "wp-short-pixel.php"

Full path: /home/kosmetik/public_html/wp-content/plugins/shortpixel-image-optimiser/class/wp-short-pixel.php
File size: 141.64 B
MIME-type: text/x-php
Charset: utf-8

Download   Open   Edit   Advanced Editor   Back

<?php

use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Notices\NoticeController as Notices;
use ShortPixel\Model\FileModel as FileModel;
use ShortPixel\Model\Directorymodel as DirectoryModel;
use ShortPixel\Model\ImageModel as ImageModel;
use ShortPixel\Controller\AdminNoticesController as AdminNoticesController;
class WPShortPixel
{
    const BULK_EMPTY_QUEUE = 0;
    private $_apiInterface = null;
    private $_settings = null;
    private $prioQ = null;
    private $view = null;
    private $thumbnailsRegenerating = array();
    private $spMetaDao = null;
    private $jsSuffix = '.min.js';
    private $timer;
    public static $PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'gif', 'png', 'pdf');
    private static $first_run = false;
    public function __construct()
    {
        $this->timer = time();
        if (Log::debugIsActive()) {
            $this->jsSuffix = '.js';
        }
        load_plugin_textdomain('shortpixel-image-optimiser', false, plugin_basename(dirname(SHORTPIXEL_PLUGIN_FILE)) . '/lang');
        $isAdminUser = current_user_can('manage_options');
        $this->_settings = new WPShortPixelSettings();
        $this->_apiInterface = new ShortPixelAPI($this->_settings);
        $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $this->_settings->excludePatterns);
        $this->prioQ = !defined('SHORTPIXEL_NOFLOCK') ? new ShortPixelQueue($this, $this->_settings) : new ShortPixelQueueDB($this, $this->_settings);
        $this->view = new ShortPixelView($this);
    }
    public function loadHooks()
    {
        self::$first_run = true;
        load_plugin_textdomain('shortpixel-image-optimiser', false, plugin_basename(dirname(SHORTPIXEL_PLUGIN_FILE)) . '/lang');
        $isAdminUser = current_user_can('manage_options');
        define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
        if (!defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES')) {
            if (is_plugin_active('envira-gallery/envira-gallery.php') || is_plugin_active('soliloquy-lite/soliloquy-lite.php') || is_plugin_active('soliloquy/soliloquy.php')) {
                define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', '_c,_tl,_tr,_br,_bl');
            } elseif (defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIX')) {
                define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', SHORTPIXEL_CUSTOM_THUMB_SUFFIX);
            }
        }
        $this->setDefaultViewModeList();
        add_filter('plugin_action_links_' . plugin_basename(SHORTPIXEL_PLUGIN_FILE), array(&$this, 'generatePluginLinks'));
        add_filter('manage_media_columns', array(&$this, 'columns'));
        add_action('manage_media_custom_column', array(&$this, 'generateCustomColumn'), 10, 2);
        add_filter('manage_upload_sortable_columns', array(&$this, 'columnRegisterSortable'));
        add_filter('request', array(&$this, 'columnOrderFilterBy'));
        add_action('restrict_manage_posts', array(&$this, 'mediaAddFilterDropdown'));
        add_action('add_meta_boxes', array(&$this, 'shortpixelInfoBox'));
        add_action('delete_attachment', array(&$this, 'onDeleteImage'));
        add_action('mime_types', array($this, 'addWebpMime'));
        add_action('mime_types', array($this, 'addAvifMime'));
        add_action('wplr_update_media', array(&$this, 'onWpLrUpdateMedia'), 10, 2);
        add_action('shortpixel-optimize-now', array(&$this, 'optimizeNowHook'), 10, 1);
        add_filter('shortpixel_get_backup', array(&$this, 'shortpixelGetBackupFilter'), 10, 1);
        if ($isAdminUser) {
            add_action('wp_ajax_shortpixel_browse_content', array(&$this, 'browseContent'));
            add_action('wp_ajax_shortpixel_get_backup_size', array(&$this, 'getBackupSize'));
            add_action('wp_ajax_shortpixel_get_comparer_data', array(&$this, 'getComparerData'));
            add_action('wp_ajax_shortpixel_new_api_key', array(&$this, 'newApiKey'));
            add_action('wp_ajax_shortpixel_propose_upgrade', array(&$this, 'proposeUpgrade'));
            add_action('delete_attachment', array(&$this, 'handleDeleteAttachmentInBackup'));
            add_action('load-upload.php', array(&$this, 'handleCustomBulk'));
            add_action('admin_action_shortpixel_restore_backup', array(&$this, 'handleRestoreBackup'));
            add_action('wp_ajax_shortpixel_redo', array(&$this, 'handleRedo'));
            add_action('wp_ajax_shortpixel_optimize_thumbs', array(&$this, 'handleOptimizeThumbs'));
            add_action('admin_bar_menu', array(&$this, 'toolbar_shortpixel_processing'), 999);
            add_action('admin_post_shortpixel_deactivate_plugin', array(&$this, 'deactivatePlugin'));
            $stats = $this->_settings->currentStats;
            $totalCredits = isset($stats["APICallsQuotaNumeric"]) ? $stats['APICallsQuotaNumeric'] + $stats['APICallsQuotaOneTimeNumeric'] : 0;
            if (true || !$this->_settings->verifiedKey || $totalCredits < 4000) {
                require_once 'view/shortpixel-feedback.php';
                new ShortPixelFeedback(SHORTPIXEL_PLUGIN_FILE, 'shortpixel-image-optimiser', $this->_settings->apiKey, $this);
            }
        }
        add_action('wp_ajax_shortpixel_image_processing', array(&$this, 'handleImageProcessing'));
        add_action('wp_ajax_shortpixel_manual_optimization', array(&$this, 'handleManualOptimization'));
        add_action('wp_ajax_shortpixel_check_status', array(&$this, 'checkStatus'));
        add_action('wp_ajax_shortpixel_dismiss_notice', array(&$this, 'dismissAdminNotice'));
        add_action('wp_ajax_shortpixel_dismiss_media_alert', array($this, 'dismissMediaAlert'));
        add_action('wp_ajax_shortpixel_dismissFileError', array($this, 'dismissFileError'));
        add_action('wp_ajax_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
        add_action('admin_action_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
        add_action('admin_enqueue_scripts', array($this, 'shortPixelJS'));
        add_action('admin_footer', array($this, 'admin_footer_js'));
        add_action('admin_notices', array(&$this, 'displayAdminNotices'));
        $this->migrateBackupFolder();
    }
    public function displayAdminNotices()
    {
    }
    public function dismissAdminNotice()
    {
        $noticeId = preg_replace('|[^a-z0-9]|i', '', $_GET['notice_id']);
        $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
        $dismissed[$noticeId] = true;
        $this->_settings->dismissedNotices = $dismissed;
        if ($_GET['notice_id'] == 'unlisted' && isset($_GET['notice_data']) && $_GET['notice_data'] == 'true') {
            $this->_settings->optimizeUnlisted = 1;
        }
        die(json_encode(array("Status" => 'success', "Message" => 'Notice ID: ' . $noticeId . ' dismissed')));
    }
    public function dismissMediaAlert()
    {
        $this->_settings->mediaAlert = 1;
        die(json_encode(array("Status" => 'success', "Message" => __('Media alert dismissed', 'shortpixel-image-optimiser'))));
    }
    public function dismissFileError()
    {
        $this->_settings->bulkLastStatus = null;
        die(json_encode(array("Status" => 'success', "Message" => __('Error dismissed', 'shortpixel-image-optimiser'))));
    }
    public function setDefaultViewModeList()
    {
        if ($this->_settings->mediaLibraryViewMode === false) {
            $this->_settings->mediaLibraryViewMode = 1;
            $currentUserID = false;
            if (function_exists('wp_get_current_user')) {
                $current_user = wp_get_current_user();
                $currentUserID = $current_user->ID;
                update_user_meta($currentUserID, "wp_media_library_mode", "list");
            }
        }
    }
    static function log($message, $force = false)
    {
        Log::addInfo($message);
    }
    protected static function doLog($message, $force = false)
    {
        Log::addInfo($message);
    }
    function shortPixelJS()
    {
        $is_front = wpSPIO()->env()->is_front ? true : false;
        wp_enqueue_style('short-pixel-bar.min.css', plugins_url('/res/css/short-pixel-bar.min.css', SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
        if (\wpSPIO()->env()->is_our_screen) {
            wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css', SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
            wp_enqueue_style('short-pixel-modal.min.css', plugins_url('/res/css/short-pixel-modal.min.css', SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
            wp_register_style('shortpixel-admin', plugins_url('/res/css/shortpixel-admin.css', SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
            wp_enqueue_style('shortpixel-admin');
        }
        wp_register_script('shortpixel', plugins_url('/res/js/shortpixel' . $this->jsSuffix, SHORTPIXEL_PLUGIN_FILE), array('jquery', 'jquery.knob.min.js'), SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
        $cacheControl = new \ShortPixel\Controller\CacheController();
        $bulkSecret = $cacheControl->getItem('bulk-secret');
        $secretKey = !is_null($bulkSecret->getValue()) ? $bulkSecret->getValue() : false;
        $keyControl = \ShortPixel\Controller\ApiKeyController::getInstance();
        $apikey = $keyControl->getKeyForDisplay();
        $ShortPixelConstants = array(array('STATUS_SUCCESS' => ShortPixelAPI::STATUS_SUCCESS, 'STATUS_EMPTY_QUEUE' => self::BULK_EMPTY_QUEUE, 'STATUS_ERROR' => ShortPixelAPI::STATUS_ERROR, 'STATUS_FAIL' => ShortPixelAPI::STATUS_FAIL, 'STATUS_QUOTA_EXCEEDED' => ShortPixelAPI::STATUS_QUOTA_EXCEEDED, 'STATUS_SKIP' => ShortPixelAPI::STATUS_SKIP, 'STATUS_NO_KEY' => ShortPixelAPI::STATUS_NO_KEY, 'STATUS_RETRY' => ShortPixelAPI::STATUS_RETRY, 'STATUS_QUEUE_FULL' => ShortPixelAPI::STATUS_QUEUE_FULL, 'STATUS_MAINTENANCE' => ShortPixelAPI::STATUS_MAINTENANCE, 'STATUS_SEARCHING' => ShortPixelAPI::STATUS_SEARCHING, 'WP_PLUGIN_URL' => plugins_url('', SHORTPIXEL_PLUGIN_FILE), 'WP_ADMIN_URL' => admin_url(), 'API_IS_ACTIVE' => $keyControl->keyIsVerified(), 'DEFAULT_COMPRESSION' => 0 + intval($this->_settings->compressionType), 'MEDIA_ALERT' => $this->_settings->mediaAlert ? "done" : "todo", 'FRONT_BOOTSTRAP' => $this->_settings->frontBootstrap && (!isset($this->_settings->lastBackAction) || time() - $this->_settings->lastBackAction > 600) ? 1 : 0, 'AJAX_URL' => admin_url('admin-ajax.php'), 'BULK_SECRET' => $secretKey));
        if (Log::isManualDebug()) {
            Log::addInfo('Ajax Manual Debug Mode');
            $logLevel = Log::getLogLevel();
            $ShortPixelConstants[0]['AJAX_URL'] = admin_url('admin-ajax.php?SHORTPIXEL_DEBUG=' . $logLevel);
        }
        $jsTranslation = array('optimizeWithSP' => __('Optimize with ShortPixel', 'shortpixel-image-optimiser'), 'redoLossy' => __('Re-optimize Lossy', 'shortpixel-image-optimiser'), 'redoGlossy' => __('Re-optimize Glossy', 'shortpixel-image-optimiser'), 'redoLossless' => __('Re-optimize Lossless', 'shortpixel-image-optimiser'), 'restoreOriginal' => __('Restore Originals', 'shortpixel-image-optimiser'), 'changeMLToListMode' => __('In order to access the ShortPixel Optimization actions and info, please change to {0}List View{1}List View{2}Dismiss{3}', 'shortpixel-image-optimiser'), 'alertOnlyAppliesToNewImages' => __('This type of optimization will apply to new uploaded images. Images that were already processed will not be re-optimized unless you restart the bulk process.', 'shortpixel-image-optimiser'), 'areYouSureStopOptimizing' => __('Are you sure you want to stop optimizing the folder {0}?', 'shortpixel-image-optimiser'), 'reducedBy' => __('Reduced by', 'shortpixel-image-optimiser'), 'bonusProcessing' => __('Bonus processing', 'shortpixel-image-optimiser'), 'plusXthumbsOpt' => __('+{0} thumbnails optimized', 'shortpixel-image-optimiser'), 'plusXretinasOpt' => __('+{0} Retina images optimized', 'shortpixel-image-optimiser'), 'optXThumbs' => __('Optimize {0} thumbnails', 'shortpixel-image-optimiser'), 'reOptimizeAs' => __('Reoptimize {0}', 'shortpixel-image-optimiser'), 'restoreBackup' => __('Restore backup', 'shortpixel-image-optimiser'), 'getApiKey' => __('Get API Key', 'shortpixel-image-optimiser'), 'extendQuota' => __('Extend Quota', 'shortpixel-image-optimiser'), 'check__Quota' => __('Check&nbsp;&nbsp;Quota', 'shortpixel-image-optimiser'), 'retry' => __('Retry', 'shortpixel-image-optimiser'), 'thisContentNotProcessable' => __('This content is not processable.', 'shortpixel-image-optimiser'), 'imageWaitOptThumbs' => __('Image waiting to optimize thumbnails', 'shortpixel-image-optimiser'), 'pleaseDoNotSetLesserSize' => __("Please do not set a {0} less than the {1} of the largest thumbnail which is {2}, to be able to still regenerate all your thumbnails in case you'll ever need this.", 'shortpixel-image-optimiser'), 'pleaseDoNotSetLesser1024' => __("Please do not set a {0} less than 1024, to be able to still regenerate all your thumbnails in case you'll ever need this.", 'shortpixel-image-optimiser'), 'confirmBulkRestore' => __("Are you sure you want to restore from backup all the images in your Media Library optimized with ShortPixel?", 'shortpixel-image-optimiser'), 'confirmBulkCleanup' => __("Are you sure you want to cleanup the ShortPixel metadata info for the images in your Media Library optimized with ShortPixel? This will make ShortPixel 'forget' that it optimized them and will optimize them again if you re-run the Bulk Optimization process.", 'shortpixel-image-optimiser'), 'confirmBulkCleanupPending' => __("Are you sure you want to cleanup the pending metadata?", 'shortpixel-image-optimiser'), 'alertDeliverWebPAltered' => __("Warning: Using this method alters the structure of the rendered HTML code (IMG tags get included in PICTURE tags),\nwhich in some rare cases can lead to CSS/JS inconsistencies.\n\nPlease test this functionality thoroughly after activating!\n\nIf you notice any issue, just deactivate it and the HTML will will revert to the previous state.", 'shortpixel-image-optimiser'), 'alertDeliverWebPUnaltered' => __('This option will serve both WebP and the original image using the same URL, based on the web browser capabilities, please make sure you\'re serving the images from your server and not using a CDN which caches the images.', 'shortpixel-image-optimiser'), 'originalImage' => __('Original image', 'shortpixel-image-optimiser'), 'optimizedImage' => __('Optimized image', 'shortpixel-image-optimiser'), 'loading' => __('Loading...', 'shortpixel-image-optimiser'));
        $actions = array('nonce_check_quota' => wp_create_nonce('check_quota'));
        wp_localize_script('shortpixel', '_spTr', $jsTranslation);
        wp_localize_script('shortpixel', 'ShortPixelConstants', $ShortPixelConstants);
        wp_localize_script('shortpixel', 'ShortPixelActions', $actions);
        wp_register_script('jquery.knob.min.js', plugins_url('/res/js/jquery.knob.min.js', SHORTPIXEL_PLUGIN_FILE));
        wp_register_script('jquery.tooltip.min.js', plugins_url('/res/js/jquery.tooltip.min.js', SHORTPIXEL_PLUGIN_FILE));
        if (!\wpSPIO()->env()->is_screen_to_use) {
            if (!wpSPIO()->env()->is_front) {
                return;
            }
        }
        wp_enqueue_script('shortpixel');
        wp_enqueue_script('jquery.knob.min.js');
        wp_enqueue_script('jquery.tooltip.min.js');
        wp_enqueue_script('punycode.min.js', plugins_url('/res/js/punycode.min.js', SHORTPIXEL_PLUGIN_FILE));
    }
    public function admin_footer_js()
    {
        if (!\wpSPIO()->env()->is_screen_to_use) {
            return;
        }
        if (function_exists('get_current_screen')) {
            $screen = get_current_screen();
            if (is_object($screen)) {
                if (in_array($screen->id, array('attachment', 'upload', 'media_page_wp-short-pixel-custom'))) {
                    $this->view->outputComparerHTML();
                    $this->view->renderListCell("__SP_ID__", 'imgOptimized', true, "__SP_THUMBS_TOTAL__", true, true, array("__SP_FIRST_TYPE__", "__SP_SECOND_TYPE__"), "__SP_CELL_MESSAGE__", 'sp-column-actions-template');
                }
            }
        }
        ?>
      <script type="text/javascript" >
          //check after 10 seconds if ShortPixel initialized OK, if not, force the init (could happen if a JS error somewhere else stopped the JS execution).
          function delayedInit() {
              if(typeof ShortPixel !== "undefined") {
                  ShortPixel.init();
              } else {
                  setTimeout(delayedInit, 10000);
              }
          }
          setTimeout(delayedInit, 10000);
      </script>
      <?php 
    }
    function toolbar_shortpixel_processing($wp_admin_bar)
    {
        if (!\wpSPIO()->env()->is_screen_to_use) {
            return;
        }
        $extraClasses = " shortpixel-hide";
        $id = 'short-pixel-notice-toolbar';
        $tooltip = __('ShortPixel optimizing...', 'shortpixel-image-optimiser') . " " . __('Please do not close this admin page.', 'shortpixel-image-optimiser');
        $icon = "shortpixel.png";
        $successLink = $link = admin_url(current_user_can('edit_others_posts') ? 'upload.php?page=wp-short-pixel-bulk' : 'upload.php');
        $blank = "";
        if ($this->prioQ->processing()) {
            $extraClasses = " shortpixel-processing";
        }
        if ($this->_settings->quotaExceeded && !isset($this->_settings->dismissedNotices['exceed'])) {
            $extraClasses = " shortpixel-alert shortpixel-quota-exceeded";
            $id = 'short-pixel-notice-exceed';
            $tooltip = '';
            $exceedTooltip = __('ShortPixel quota exceeded. Click for details.', 'shortpixel-image-optimiser');
            $link = "options-general.php?page=wp-shortpixel-settings";
        }
        $lastStatus = $this->_settings->bulkLastStatus;
        if ($lastStatus && $lastStatus['Status'] !== ShortPixelAPI::STATUS_SUCCESS) {
            $extraClasses = " shortpixel-alert shortpixel-processing";
            $tooltip = '';
            $link = '';
            if (admin_url(current_user_can('edit_others_posts'))) {
                $link = 'post.php?post=' . $lastStatus['ImageID'] . '&action=edit';
            } else {
                $link = 'upload.php';
            }
            $successLink = $link;
            $wp_admin_bar->add_node(array('id' => 'shortpixel_processing-title', 'parent' => 'shortpixel_processing', 'title' => $lastStatus['Message'], 'href' => $successLink));
            $wp_admin_bar->add_node(array('id' => 'shortpixel_processing-dismiss', 'parent' => 'shortpixel_processing', 'title' => '<div style="text-align: right;">Dismiss</div>', 'href' => "#", 'meta' => array('onclick' => 'dismissFileError(event)')));
        }
        $args = array('id' => 'shortpixel_processing', 'title' => '<div id="' . $id . '" title="' . $tooltip . '" ><img alt="' . __('ShortPixel icon', 'shortpixel-image-optimiser') . '" src="' . plugins_url('res/img/' . $icon, SHORTPIXEL_PLUGIN_FILE) . '" success-url="' . $successLink . '"><span class="shp-alert">!</span>' . '<div class="cssload-container"><div class="cssload-speeding-wheel"></div></div></div>', 'href' => $link, 'meta' => array('target' => $blank, 'class' => 'shortpixel-toolbar-processing' . $extraClasses));
        $wp_admin_bar->add_node($args);
        if ($this->_settings->quotaExceeded && !isset($this->_settings->dismissedNotices['exceed'])) {
            $wp_admin_bar->add_node(array('id' => 'shortpixel_processing-title', 'parent' => 'shortpixel_processing', 'title' => $exceedTooltip, 'href' => $link));
        }
    }
    public function handleCustomBulk()
    {
        $wp_list_table = _get_list_table('WP_Media_List_Table');
        $action = $wp_list_table->current_action();
        if (strpos($action, 'short-pixel-bulk') === 0) {
            check_admin_referer('bulk-media');
            if (!is_array($_GET['media'])) {
                return;
            }
            $mediaIds = array_reverse($_GET['media']);
        }
        switch ($action) {
            case 'short-pixel-bulk':
                foreach ($mediaIds as $ID) {
                    $meta = wp_get_attachment_metadata($ID);
                    if (!is_array($meta)) {
                        self::log('CUSTOM BULK META NOT AN ARRAY: ' . json_encode($meta));
                        $meta = ShortPixelMetaFacade::sanitizeMeta($meta, false);
                        if (isset($meta['previous_meta'])) {
                            self::log('COULDN\'T SANITIZE PROPERLY.');
                            continue;
                        } else {
                            self::log('SANITIZED.');
                        }
                    }
                    if (!is_array($meta)) {
                        continue;
                    }
                    if ((!isset($meta['ShortPixel']) || isset($meta['ShortPixel']['WaitingProcessing']) && $meta['ShortPixel']['WaitingProcessing'] == true) && (!isset($meta['ShortPixelImprovement']) || $meta['ShortPixelImprovement'] == __('Optimization N/A', 'shortpixel-image-optimiser'))) {
                        $this->prioQ->push($ID);
                        if (!isset($meta['ShortPixel'])) {
                            $meta['ShortPixel'] = array();
                        }
                        $meta['ShortPixel']['WaitingProcessing'] = true;
                        update_post_meta($ID, '_wp_attachment_metadata', $meta);
                        ShortPixelMetaFacade::optimizationStarted($ID);
                    }
                }
                break;
            case 'short-pixel-bulk-lossy':
                $this->restoreAndQueueList($mediaIds, 'lossy');
                break;
            case 'short-pixel-bulk-glossy':
                $this->restoreAndQueueList($mediaIds, 'glossy');
                break;
            case 'short-pixel-bulk-lossless':
                $this->restoreAndQueueList($mediaIds, 'lossless');
                break;
            case 'short-pixel-bulk-restore':
                foreach ($mediaIds as $ID) {
                    $this->doRestore($ID);
                }
                break;
        }
    }
    protected function restoreAndQueueList($mediaIds, $type)
    {
        foreach ($mediaIds as $ID) {
            $meta = wp_get_attachment_metadata($ID);
            if (isset($meta['ShortPixel']['type']) && $meta['ShortPixel']['type'] !== $type) {
                $meta = $this->doRestore($ID);
                if ($meta) {
                    $meta['ShortPixel'] = array("type" => $type, 'WaitingProcessing' => true);
                    update_post_meta($ID, '_wp_attachment_metadata', $meta);
                    $this->prioQ->push($ID);
                }
            }
        }
    }
    public function handleMediaLibraryImageUpload($meta, $ID = null)
    {
        if (!$this->_settings->verifiedKey) {
            return $meta;
        }
        $fs = \wpSPIO()->filesystem();
        $dbMeta = wp_get_attachment_metadata($ID);
        $currentFile = $fs->getAttachedFile($ID);
        $refresh = false;
        if (isset($dbMeta['ShortPixelImprovement'])) {
            return $meta;
        }
        if (isset($this->thumbnailsRegenerating[$ID])) {
            return $meta;
        }
        Log::addDebug("Handle Media Library Image Upload #{$ID}", $currentFile->exists());
        if (!$this->_settings->optimizePdfs && 'pdf' === $currentFile->getExtension()) {
            return $meta;
        } elseif (!$currentFile->exists() && isset($meta['file']) && in_array(strtolower(pathinfo($meta['file'], PATHINFO_EXTENSION)), self::$PROCESSABLE_EXTENSIONS)) {
            $this->prioQ->push($ID);
            $meta['ShortPixel'] = array('WaitingProcessing' => true);
        } elseif (self::_isProcessable($ID, array(), $this->_settings->excludePatterns, $meta) == false) {
            $meta['ShortPixelImprovement'] = __('Optimization N/A', 'shortpixel-image-optimiser');
            return $meta;
        } else {
            $this->prioQ->push($ID);
            $itemHandler = new ShortPixelMetaFacade($ID);
            $itemHandler->setRawMeta($meta);
            include_once ABSPATH . 'wp-admin/includes/plugin.php';
            if (!is_plugin_active('image-watermark/image-watermark.php') && !is_plugin_active('amazon-s3-and-cloudfront/wordpress-s3.php') && !is_plugin_active('amazon-s3-and-cloudfront-pro/amazon-s3-and-cloudfront-pro.php') && !is_plugin_active('easy-watermark/easy-watermark.php')) {
                try {
                    $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler);
                    $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $itemHandler, false, $refresh);
                } catch (Exception $e) {
                    Log::addWarn('Handle Media Library Image Exceptions', $e);
                    $meta['ShortPixelImprovement'] = $e->getMessage();
                    return $meta;
                }
            }
            $meta['ShortPixel']['WaitingProcessing'] = true;
            if ($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
                $imagePath = $itemHandler->getMeta()->getPath();
                if (isset($this->_settings->convertedPng2Jpg[$imagePath])) {
                    $conv = $this->_settings->convertedPng2Jpg;
                    $params = $conv[$imagePath];
                    unset($conv[$imagePath]);
                    $this->_settings->convertedPng2Jpg == $conv;
                    $meta['ShortPixelPng2Jpg'] = array('originalFile' => $params['pngFile'], 'originalSizes' => array(), 'backup' => $params['backup'], 'optimizationPercent' => $params['optimizationPercent']);
                }
            }
            return $meta;
        }
    }
    public function maybeDumpFromProcessedOnServer($itemHandler, $urlsAndPaths)
    {
        $meta = $itemHandler->getMeta();
        $doDump = false;
        if ($meta->getStatus() <= 0) {
            $doDump = true;
        } else {
            if (time() - strtotime($meta->getTsOptimized()) < 3600) {
                $doDump = true;
            }
        }
        if ($doDump) {
            $this->_apiInterface->doDumpRequests($urlsAndPaths["URLs"]);
        }
    }
    public function convertPng2Jpg($params)
    {
        $converter = new ShortPixelPng2Jpg($this->_settings);
        return $converter->convertPng2Jpg($params);
    }
    public function checkConvertMediaPng2Jpg($itemHandler)
    {
        $converter = new ShortPixelPng2Jpg($this->_settings);
        return $converter->checkConvertMediaPng2Jpg($itemHandler);
    }
    public function addPathToCustomFolder($imageFsPath, $folderId, $pid)
    {
        $existing = $this->spMetaDao->getMetaForPath($imageFsPath);
        if ($existing) {
            return $existing;
        }
        $pathParts = explode('/', trim($imageFsPath));
        $meta = new ShortPixelMeta();
        $meta->setPath($imageFsPath);
        $meta->setName($pathParts[count($pathParts) - 1]);
        $meta->setFolderId($folderId);
        $meta->setExtMetaId($pid);
        $meta->setCompressionType($this->_settings->compressionType);
        $meta->setKeepExif($this->_settings->keepExif);
        $meta->setCmyk2rgb($this->_settings->CMYKtoRGBconversion);
        $meta->setResize($this->_settings->resizeImages);
        $meta->setResizeWidth($this->_settings->resizeWidth);
        $meta->setResizeHeight($this->_settings->resizeHeight);
        $meta->setTsAdded(date("Y-m-d H:i:s"));
        $ID = $this->spMetaDao->addImage($meta);
        $meta->setId($ID);
        if ($this->_settings->autoMediaLibrary) {
            $this->prioQ->push('C-' . $ID);
        }
        $pathParts[] = "thumbs_" . $pathParts[count($pathParts) - 1];
        $pathParts[count($pathParts) - 2] = "thumbs";
        $thumbPath = implode('/', $pathParts);
        if (file_exists($thumbPath)) {
            $metaThumb = new ShortPixelMeta();
            $metaThumb->setPath($thumbPath);
            $metaThumb->setName($pathParts[count($pathParts) - 1]);
            $metaThumb->setFolderId($folderId);
            $metaThumb->setCompressionType($this->_settings->compressionType);
            $metaThumb->setKeepExif($this->_settings->keepExif);
            $metaThumb->setCmyk2rgb($this->_settings->CMYKtoRGBconversion);
            $metaThumb->setResize($this->_settings->resizeImages);
            $metaThumb->setResizeWidth($this->_settings->resizeWidth);
            $metaThumb->setResizeHeight($this->_settings->resizeHeight);
            $metaThumb->setTsAdded(date("Y-m-d H:i:s"));
            $ID = $this->spMetaDao->addImage($metaThumb);
            $metaThumb->setId($ID);
            if ($this->_settings->autoMediaLibrary) {
                $this->prioQ->push('C-' . $ID);
            }
        }
        return $meta;
    }
    public function optimizeCustomImage($id)
    {
        $itemHandler = new ShortPixelMetaFacade('C-' . $id);
        $meta = $itemHandler->getMeta();
        if ($meta->getStatus() <= 0) {
            $URLsAndPATHs = $itemHandler->getURLsAndPATHs(false);
            $this->maybeDumpFromProcessedOnServer($itemHandler, $URLsAndPATHs);
        }
        if ($meta->getStatus() != ShortPixelMeta::FILE_STATUS_SUCCESS) {
            $meta->setStatus(ShortPixelMeta::FILE_STATUS_PENDING);
            $meta->setRetries(0);
            $meta->setCompressionType($this->_settings->compressionType);
            $meta->setKeepExif($this->_settings->keepExif);
            $meta->setCmyk2rgb($this->_settings->CMYKtoRGBconversion);
            $meta->setResize($this->_settings->resizeImages);
            $meta->setResizeWidth($this->_settings->resizeWidth);
            $meta->setResizeHeight($this->_settings->resizeHeight);
            $this->spMetaDao->update($meta);
            $this->prioQ->push('C-' . $id);
        }
    }
    public function bulkRestore()
    {
        global $wpdb;
        $startQueryID = $crtStartQueryID = $this->prioQ->getStartBulkId();
        $endQueryID = $this->prioQ->getStopBulkId();
        Log::addDebug('Bulk Restore' . $startQueryID . ' ' . $endQueryID);
        if ($startQueryID <= $endQueryID) {
            return false;
        }
        $this->prioQ->resetPrio();
        $startTime = time();
        $maxTime = min(30, is_numeric(SHORTPIXEL_MAX_EXECUTION_TIME) && SHORTPIXEL_MAX_EXECUTION_TIME > 10 ? SHORTPIXEL_MAX_EXECUTION_TIME - 5 : 25);
        $maxResults = SHORTPIXEL_MAX_RESULTS_QUERY * 2;
        if (in_array($this->prioQ->getBulkType(), array(ShortPixelQueue::BULK_TYPE_CLEANUP, ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING))) {
            $maxResults *= 20;
        }
        $restored = array();
        while ($crtStartQueryID >= $endQueryID && time() - $startTime < $maxTime) {
            $crtStartQueryID = $this->prioQ->getStartBulkId();
            $resultsPostMeta = WpShortPixelMediaLbraryAdapter::getPostMetaSlice($crtStartQueryID, $endQueryID, $maxResults);
            if (empty($resultsPostMeta)) {
                $pendingCustomMeta = $this->spMetaDao->getPendingBulkRestore(SHORTPIXEL_MAX_RESULTS_QUERY * 2);
                if (count($pendingCustomMeta) > 0) {
                    foreach ($pendingCustomMeta as $cObj) {
                        $this->doCustomRestore($cObj->id);
                    }
                } else {
                    $crtStartQueryID -= $maxResults;
                    $startQueryID = $crtStartQueryID;
                    $this->prioQ->setStartBulkId($startQueryID);
                    continue;
                }
            }
            foreach ($resultsPostMeta as $itemMetaData) {
                $crtStartQueryID = $itemMetaData->post_id;
                $item = new ShortPixelMetaFacade($crtStartQueryID);
                $meta = $item->getMeta();
                if ($meta->getStatus() == ShortPixelMeta::FILE_STATUS_SUCCESS || $meta->getStatus() == ShortPixelMeta::FILE_STATUS_PENDING) {
                    if ($meta->getStatus() == ShortPixelMeta::FILE_STATUS_SUCCESS && $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE) {
                        $res = $this->doRestore($crtStartQueryID);
                        $item->cleanupMeta($this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING);
                    } else {
                        $item->cleanupMeta($this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING);
                        $res = true;
                    }
                    $restored[] = array('id' => $crtStartQueryID, 'status' => $res ? 'success' : 'fail');
                }
                if ($meta->getStatus() < 0) {
                    $item->cleanupMeta();
                }
            }
            $this->advanceBulk($crtStartQueryID);
        }
        return $restored;
    }
    public function getBulkItemsFromDb()
    {
        global $wpdb;
        $startQueryID = $this->prioQ->getStartBulkId();
        $endQueryID = $this->prioQ->getStopBulkId();
        $skippedAlreadyProcessed = 0;
        if ($startQueryID <= $endQueryID) {
            return false;
        }
        $idList = array();
        $itemList = array();
        $timeoutMinThreshold = SHORTPIXEL_MAX_EXECUTION_TIME < 10 ? 2 : (SHORTPIXEL_MAX_EXECUTION_TIME < 30 ? 3 : 5);
        $maxTime = min(SHORTPIXEL_MAX_EXECUTION_TIME, 90);
        $timeoutThreshold = 5;
        $passTime = time();
        for ($sanityCheck = 0, $crtStartQueryID = $startQueryID; $crtStartQueryID >= $endQueryID && count($itemList) < SHORTPIXEL_PRESEND_ITEMS && $sanityCheck < 150 && time() - $this->timer < $maxTime - $timeoutThreshold; $sanityCheck++) {
            $timeoutThreshold = max($timeoutThreshold, $timeoutMinThreshold + time() - $passTime);
            $passTime = time();
            $maxResults = $timeoutThreshold > 15 ? SHORTPIXEL_MAX_RESULTS_QUERY / 3 : ($timeoutThreshold > 10 ? SHORTPIXEL_MAX_RESULTS_QUERY / 2 : SHORTPIXEL_MAX_RESULTS_QUERY);
            Log::addInfo("GETDB: pass {$sanityCheck} current StartID: {$crtStartQueryID} Threshold: {$timeoutThreshold}, MaxResults: {$maxResults}");
            $resultsPosts = WpShortPixelMediaLbraryAdapter::getPostsJoinLessReverse($crtStartQueryID, $endQueryID, $maxResults);
            if (time() - $this->timer >= 60) {
                Log::addWarn("GETDB is SLOW. Got meta slice.");
            }
            if (empty($resultsPosts)) {
                $crtStartQueryID -= SHORTPIXEL_MAX_RESULTS_QUERY;
                $startQueryID = $crtStartQueryID;
                if (!count($idList)) {
                    Log::addInfo("GETDB: empty slice. setStartBulkID to {$startQueryID}");
                    $this->prioQ->setStartBulkId($startQueryID);
                }
                continue;
            }
            if ($timeoutThreshold > 10) {
                Log::addInfo("GETDB is SLOW. Meta slice has " . count($resultsPosts) . ' items.');
            }
            $counter = 0;
            foreach ($resultsPosts as $index => $post_id) {
                $crtStartQueryID = $post_id;
                if (time() - $this->timer >= 60) {
                    Log::addInfo("GETDB is SO SLOW. Check processable for {$crtStartQueryID}.");
                }
                if (time() - $this->timer >= $maxTime - $timeoutThreshold) {
                    if ($counter == 0 && \wpSPIO()->env()->is_function_usable('set_time_limit') && set_time_limit(30)) {
                        self::log("GETDB is SO SLOW. Increasing time limit by 30 sec succeeded.");
                        $maxTime += 30 - $timeoutThreshold;
                    } else {
                        self::log("GETDB is SO SLOW. Breaking after processing {$counter} items. Time limit is over: " . ($maxTime - $timeoutThreshold));
                        break;
                    }
                }
                $counter++;
                if (!in_array($crtStartQueryID, $idList) && $this->isProcessable($crtStartQueryID, $this->_settings->optimizePdfs ? array() : array('pdf'))) {
                    $item = new ShortPixelMetaFacade($crtStartQueryID);
                    if ($timeoutThreshold > 15) {
                        Log::addInfo("GETDB is SO SLOW. Get meta for {$crtStartQueryID}.");
                    }
                    $meta = $item->getMeta();
                    if ($timeoutThreshold > 15) {
                        Log::addInfo("GETDB is SO SLOW. Got meta.");
                    }
                    if ($meta->getStatus() != ShortPixelMeta::FILE_STATUS_SUCCESS) {
                        $addIt = strpos($meta->getMessage(), __('Image files are missing.', 'shortpixel-image-optimiser')) === false;
                        if (!$addIt) {
                            if (!$this->doRestore($crtStartQueryID)) {
                                delete_transient("shortpixel_thrown_notice");
                            }
                            $addIt = true;
                            $item = new ShortPixelMetaFacade($crtStartQueryID);
                        }
                        if ($addIt) {
                            $itemList[] = $item;
                            $idList[] = $crtStartQueryID;
                            if (count($itemList) > SHORTPIXEL_PRESEND_ITEMS) {
                                break;
                            }
                        }
                    } elseif ($meta->getCompressionType() !== null && $meta->getCompressionType() != $this->_settings->compressionType) {
                        if ($this->doRestore($crtStartQueryID)) {
                            $itemList[] = $item = new ShortPixelMetaFacade($crtStartQueryID);
                            $idList[] = $crtStartQueryID;
                            if (count($itemList) > SHORTPIXEL_PRESEND_ITEMS) {
                                break;
                            }
                        } else {
                            $skippedAlreadyProcessed++;
                        }
                    } elseif ($this->_settings->processThumbnails && $meta->getThumbsOpt() !== null && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0 || is_array($meta->getThumbsOptList()) && count(array_diff(array_keys(WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes)), $meta->getThumbsOptList())) || $this->_settings->optimizeUnlisted && count(array_diff(WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath()), $meta->getThumbsOptList())))) {
                        $item->searchUnlistedFiles();
                        $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
                        Log::addDebug('Gathering URLS AND PATHS', array($URLsAndPATHs));
                        if (count($URLsAndPATHs["URLs"])) {
                            $meta->setThumbsTodo(true);
                            $item->updateMeta($meta);
                            $itemList[] = $item;
                            $idList[] = $crtStartQueryID;
                            if (count($itemList) > SHORTPIXEL_PRESEND_ITEMS) {
                                break;
                            }
                        }
                    }
                }
            }
            if (!count($idList) && $crtStartQueryID <= $startQueryID) {
                $leapStart = $this->prioQ->getStartBulkId();
                $crtStartQueryID = $startQueryID = $post_id - 1;
                $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->_settings, $leapStart, $crtStartQueryID);
                $skippedAlreadyProcessed += $res["mainProcessedFiles"] - $res["mainProc" . ($this->getCompressionType() == 1 ? "Lossy" : "Lossless") . "Files"];
                Log::addInfo("GETDB: empty list. setStartBulkID to {$startQueryID}");
                $this->prioQ->setStartBulkId($startQueryID);
            } else {
                $crtStartQueryID--;
                Log::addInfo("GETDB just decrementing. Crt: {$crtStartQueryID} Start: {$startQueryID}, list: " . json_encode($idList));
            }
        }
        $ret = array("items" => $itemList, "skipped" => $skippedAlreadyProcessed, "searching" => $sanityCheck >= 150 || time() - $this->timer >= $maxTime - $timeoutThreshold);
        self::log('GETDB returns ' . json_encode($ret));
        return $ret;
    }
    public function getFromPrioAndCheck($limit = PHP_INT_MAX)
    {
        $items = array();
        $i = 1;
        foreach ($this->prioQ->getFromPrioAndCheck() as $id) {
            $items[] = new ShortPixelMetaFacade($id);
            if ($i++ > $limit) {
                break;
            }
        }
        return $items;
    }
    private function checkKey($ID)
    {
        if ($this->_settings->verifiedKey == false) {
            if ($ID == null) {
                $ids = $this->getFromPrioAndCheck(1);
                $itemHandler = count($ids) > 0 ? $ids[0] : null;
            }
            $response = array("Status" => ShortPixelAPI::STATUS_NO_KEY, "ImageID" => $itemHandler ? $itemHandler->getId() : "-1", "Message" => __('Missing API Key', 'shortpixel-image-optimiser'));
            $this->_settings->bulkLastStatus = $response;
            die(json_encode($response));
        }
    }
    private function sendEmptyQueue()
    {
        $avg = $this->getAverageCompression();
        $fileCount = $this->_settings->fileCount;
        if ($this->prioQ->bulkRunning()) {
            $bulkstatus = '1';
        } elseif ($this->prioQ->bulkPaused()) {
            $bulkstatus = '2';
        } else {
            $bulkstatus = '0';
        }
        $response = array("Status" => self::BULK_EMPTY_QUEUE, "Message" => __('Empty queue ', 'shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(), "BulkStatus" => $bulkstatus, "AverageCompression" => $avg, "FileCount" => $fileCount, "BulkPercent" => $this->prioQ->getBulkPercent());
        die(json_encode($response));
    }
    public function handleImageProcessing($ID = null)
    {
        $this->checkKey($ID);
        if ($this->_settings->frontBootstrap && is_admin() && !ShortPixelTools::requestIsFrontendAjax()) {
            $this->_settings->lastBackAction = time();
        }
        if (isset($_POST['bulk-secret'])) {
            $secret = sanitize_text_field($_POST['bulk-secret']);
            $cacheControl = new \ShortPixel\Controller\CacheController();
            $cachedObj = $cacheControl->getItem('bulk-secret');
            if (!$cachedObj->exists()) {
                $cachedObj->setValue($secret);
                $cachedObj->setExpires(3 * MINUTE_IN_SECONDS);
                $cacheControl->storeItemObject($cachedObj);
            }
        }
        $rawPrioQ = $this->prioQ->get();
        if (count($rawPrioQ)) {
            Log::addInfo("HIP: 0 Priority Queue: " . json_encode($rawPrioQ));
        }
        Log::addInfo("HIP: 0 Bulk running? " . $this->prioQ->bulkRunning() . " START " . $this->_settings->startBulkId . " STOP " . $this->_settings->stopBulkId . " MaxTime: " . SHORTPIXEL_MAX_EXECUTION_TIME);
        Log::addDebug('Bulk Running', array($this->prioQ->bulkRunning()));
        if ($this->prioQ->bulkRunning() && ($this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING)) {
            $res = $this->bulkRestore();
            if ($res === false) {
                $this->sendEmptyQueue();
            } else {
                die(json_encode(array("Status" => ShortPixelAPI::STATUS_RETRY, "Message" => __('Restoring images...  ', 'shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(), "BulkPercent" => $this->prioQ->getBulkPercent(), "Restored" => $res)));
            }
        }
        $ids = $this->getFromPrioAndCheck(SHORTPIXEL_PRESEND_ITEMS);
        if (count($ids) < SHORTPIXEL_PRESEND_ITEMS) {
            if ($this->prioQ->bulkRunning()) {
                $res = $this->getBulkItemsFromDb();
                $bulkItems = $res['items'];
                if ($bulkItems) {
                    foreach ($bulkItems as $bi) {
                        $add = true;
                        foreach ($ids as $pi) {
                            if ($pi->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE && $bi->getId() == $pi->getId()) {
                                $add = false;
                            }
                        }
                        $ids[] = $bi;
                    }
                }
            }
        }
        $customIds = false;
        if (count($ids) < SHORTPIXEL_PRESEND_ITEMS && $this->prioQ->bulkRan() && $this->_settings->hasCustomFolders && (!$this->_settings->cancelPointer || $this->_settings->skipToCustom) && !$this->_settings->customBulkPaused) {
            $otherMedia = new \ShortPixel\Controller\OtherMediaController();
            $otherMedia->refreshFolders();
            $customIds = $this->spMetaDao->getPendingMetas(SHORTPIXEL_PRESEND_ITEMS - count($ids));
            if (is_array($customIds)) {
                $ids = array_merge($ids, array_map(array('ShortPixelMetaFacade', 'getNewFromRow'), $customIds));
            }
        }
        if (count($ids)) {
            $idl = '';
            foreach ($ids as $i) {
                $idl .= $i->getId() . ' ';
            }
            Log::addInfo("HIP: 1 Selected IDs: {$idl}");
        }
        for ($i = 0, $itemHandler = false; $ids !== false && $i < min(SHORTPIXEL_PRESEND_ITEMS, count($ids)); $i++) {
            $crtItemHandler = $ids[$i];
            $tmpMeta = $crtItemHandler->getMeta();
            $compType = $tmpMeta->getCompressionType() !== null ? $tmpMeta->getCompressionType() : $this->_settings->compressionType;
            try {
                self::log("HIP: 1 sendToProcessing: " . $crtItemHandler->getId());
                $URLsAndPATHs = $this->sendToProcessing($crtItemHandler, $compType, $tmpMeta->getThumbsTodo());
                if (!$itemHandler) {
                    $itemHandler = $ids[$i];
                    $firstUrlAndPaths = $URLsAndPATHs;
                }
            } catch (Exception $e) {
                if ($tmpMeta->getStatus() != 2) {
                    $crtItemHandler->incrementRetries(1, $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::ERR_FILE_NOT_FOUND, $e->getMessage());
                }
                if (!$this->prioQ->remove($crtItemHandler->getQueuedId())) {
                    $this->advanceBulk($crtItemHandler->getId());
                    $res['searching'] = true;
                }
            }
        }
        if (!$itemHandler) {
            if (isset($res['searching']) && $res['searching']) {
                die(json_encode(array("Status" => ShortPixelAPI::STATUS_SEARCHING, "Message" => __('Searching images to optimize...  ', 'shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId())));
            }
            Log::addDebug("HIP: 1 STOP BULK");
            $bulkEverRan = $this->prioQ->stopBulk();
            $this->sendEmptyQueue();
        }
        self::log("HIP: 2 Prio Queue: " . json_encode($this->prioQ->get()));
        $itemId = $itemHandler->getQueuedId();
        self::log("HIP: 2 Process Image: " . json_encode($itemHandler->getId()));
        $result = $this->_apiInterface->processImage($firstUrlAndPaths['URLs'], $firstUrlAndPaths['PATHs'], $itemHandler);
        $result["ImageID"] = $itemId;
        $meta = $itemHandler->getMeta();
        $result["Filename"] = ShortPixelAPI::MB_basename($meta->getPath());
        self::log("HIP: 3 Prio Queue: " . json_encode($this->prioQ->get()));
        if ($result["Status"] == ShortPixelAPI::STATUS_SUCCESS) {
            self::log("HIP: Image ID " . $itemId . " optimized successfully: " . json_encode($result));
            $prio = $this->prioQ->remove($itemId);
            $prio = $this->prioQ->removeFromFailed($itemId);
            $result["Type"] = $meta->getCompressionType() !== null ? ShortPixelAPI::getCompressionTypeName($meta->getCompressionType()) : '';
            $result["ThumbsTotal"] = $meta->getThumbs() && is_array($meta->getThumbs()) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta->getThumbs()) : 0;
            $miss = $meta->getThumbsMissing();
            $result["ThumbsTotal"] -= is_array($miss) ? count($miss) : 0;
            $result["ThumbsCount"] = $meta->getThumbsOpt() ? $meta->getThumbsOpt() : ($this->_settings->processThumbnails ? $result["ThumbsTotal"] : 0);
            $result["RetinasCount"] = $meta->getRetinasOpt();
            $result["BackupEnabled"] = $this->getBackupFolderAny($meta->getPath(), $meta->getThumbs()) ? true : false;
            $tsOptimized = $meta->getTsOptimized();
            if (!is_null($tsOptimized)) {
                $tsOptObj = new DateTime($tsOptimized);
                if ($tsOptObj) {
                    $result['TsOptimized'] = ShortPixelTools::format_nice_date($tsOptObj);
                }
            }
            if (!$prio && $itemId <= $this->prioQ->getStartBulkId()) {
                $this->advanceBulk($itemId);
                $this->setBulkInfo($itemId, $result);
            }
            $result["AverageCompression"] = $this->getAverageCompression();
            if ($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
                $thumb = $bkThumb = "";
                $percent = $meta->getImprovementPercent();
                if ($percent) {
                    $filePath = explode("/", $meta->getPath());
                    $sizes = $meta->getThumbs();
                    if ('pdf' == strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))) {
                        $thumb = wpSPIO()->plugin_url('res/img/logo-pdf.png');
                        $bkThumb = '';
                    } else {
                        if (count($sizes)) {
                            $exclude = $this->_settings->excludeSizes;
                            $exclude = is_array($exclude) ? $exclude : array();
                            $thumb = isset($sizes["medium"]) && !in_array("medium", $exclude) ? $sizes["medium"]["file"] : (isset($sizes["thumbnail"]) && !in_array("thumbnail", $exclude) ? $sizes["thumbnail"]["file"] : "");
                            if (!strlen($thumb)) {
                                foreach ($sizes as $sizeName => $sizeVal) {
                                    $exclude = $this->_settings->excludeSizes;
                                    if (!in_array($sizeName, $exclude)) {
                                        $thumb = isset($sizeVal['file']) ? $sizeVal['file'] : '';
                                        break;
                                    }
                                }
                            }
                        }
                        if (!strlen($thumb)) {
                            $thumb = is_array($filePath) ? $filePath[count($filePath) - 1] : $filePath;
                        }
                        if (strlen($thumb) && $this->_settings->backupImages && $this->_settings->processThumbnails) {
                            $backupUrl = SHORTPIXEL_BACKUP_URL . "/";
                            $urlBkPath = ShortPixelMetaFacade::returnSubDir($meta->getPath(), ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE);
                            $bkThumb = $backupUrl . $urlBkPath . $thumb;
                        }
                        if (strlen($thumb)) {
                            if ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
                                $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
                                $urlPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
                                $thumb = $uploadsUrl . $urlPath . $thumb;
                            } else {
                                try {
                                    $mainUrl = ShortPixelMetaFacade::safeGetAttachmentUrl($itemHandler->getId());
                                } catch (Exception $e) {
                                    Log::addError('Attachment seems corrupted!', array($e->getMessage()));
                                    $mainUrl = null;
                                }
                                $thumb = dirname($mainUrl) . '/' . $thumb;
                            }
                        }
                    }
                    $result["Thumb"] = $thumb;
                    $result["BkThumb"] = $bkThumb;
                }
            } elseif (is_array($customIds)) {
                foreach ($customIds as $customId) {
                    $rootUrl = ShortPixelMetaFacade::getHomeUrl();
                    if ($customId->id == $itemHandler->getId()) {
                        if ('pdf' == strtolower(pathinfo($meta->getName(), PATHINFO_EXTENSION))) {
                            $result["Thumb"] = plugins_url('shortpixel-image-optimiser/res/img/logo-pdf.png');
                            $result["BkThumb"] = "";
                        } else {
                            $result["Thumb"] = $thumb = $rootUrl . $meta->getWebPath();
                            if ($this->_settings->backupImages) {
                                $result["BkThumb"] = str_replace($rootUrl, $rootUrl . "/" . basename(dirname(dirname(SHORTPIXEL_BACKUP_FOLDER))) . "/" . SHORTPIXEL_UPLOADS_NAME . "/" . SHORTPIXEL_BACKUP . "/", $thumb);
                            }
                        }
                        $this->setBulkInfo($itemId, $result);
                        break;
                    }
                }
            }
        } elseif ($result["Status"] == ShortPixelAPI::STATUS_ERROR) {
            if ($meta->getRetries() > SHORTPIXEL_MAX_ERR_RETRIES) {
                if (!$this->prioQ->remove($itemId)) {
                    self::log("HIP RES: advanceBulk - too many retries for {$itemId} - skipping.");
                    $this->advanceBulk($meta->getId());
                }
                if ($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
                    $itemHandler->deleteMeta();
                }
                $result["Status"] = ShortPixelAPI::STATUS_SKIP;
                $result["Message"] .= __(' Retry limit reached. Skipping file ID ', 'shortpixel-image-optimiser') . $itemId . ".";
                $itemHandler->setError(isset($result['Code']) ? $result['Code'] : ShortPixelAPI::ERR_INCORRECT_FILE_SIZE, $result["Message"]);
            } else {
                if (isset($result['Code'])) {
                    $itemHandler->incrementRetries(1, $result['Code'], $result["Message"]);
                } else {
                    $itemHandler->incrementRetries(1, ShortPixelAPI::ERR_UNKNOWN, "Connection error (" . $result["Message"] . ")");
                }
            }
        } elseif ($result["Status"] == ShortPixelAPI::STATUS_SKIP || $result["Status"] == ShortPixelAPI::STATUS_FAIL) {
            $meta = $itemHandler->getMeta();
            $prio = $this->prioQ->remove($itemId);
            if (isset($result["Code"]) && (in_array($result["Code"], array("write-fail", "backup-fail")) || in_array(0 + $result["Code"], array(-201)) && $meta->getRetries() >= 3)) {
                $prio = $this->prioQ->addToFailed($itemHandler->getQueuedId());
            }
            if (isset($result['Code'])) {
                $itemHandler->incrementRetries(1, $result['Code'], $result["Message"]);
            } else {
                $itemHandler->incrementRetries(1, ShortPixelAPI::ERR_UNKNOWN, "Connection error (" . $result["Message"] . ")");
            }
            self::log("HIP RES: skipping {$itemId}");
            $this->advanceBulk($meta->getId());
            if ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
                $result["CustomImageLink"] = ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath();
            }
        } elseif ($result["Status"] == ShortPixelAPI::STATUS_QUEUE_FULL) {
        } elseif ($result["Status"] == ShortPixelAPI::STATUS_MAINTENANCE) {
        } elseif ($this->prioQ->isPrio($itemId) && $result["Status"] == ShortPixelAPI::STATUS_QUOTA_EXCEEDED) {
            if (!$this->prioQ->skippedCount()) {
                $this->prioQ->reverse();
            }
            if ($this->prioQ->allSkipped()) {
                $result["Stop"] = true;
            } else {
                $result["Stop"] = false;
                $this->prioQ->skip($itemId);
            }
            self::log("HIP: 5 Prio Skipped: " . json_encode($this->prioQ->getSkipped()));
        } elseif ($result["Status"] == ShortPixelAPI::STATUS_RETRY && is_array($customIds)) {
            $result["CustomImageLink"] = $thumb = ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath();
        }
        if ($result["Status"] !== ShortPixelAPI::STATUS_RETRY) {
            $this->_settings->bulkLastStatus = null;
            $this->_settings->bulkLastStatus = $result;
        }
        if ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
            $othermediaView = new \ShortPixel\Controller\View\OtherMediaViewController();
            $othermediaView->setShortPixel($this);
            $result['actions'] = $othermediaView->renderNewActions(substr($itemId, 2));
        }
        $ret = json_encode($result);
        self::log("HIP RET " . $ret);
        die($ret);
    }
    private function advanceBulk($processedID)
    {
        if ($processedID <= $this->prioQ->getStartBulkId()) {
            $this->prioQ->setStartBulkId($processedID - 1);
            $this->prioQ->logBulkProgress();
        }
    }
    private function setBulkInfo($processedID, &$result)
    {
        $deltaBulkPercent = $this->prioQ->getDeltaBulkPercent();
        $minutesRemaining = $this->prioQ->getTimeRemaining();
        $pendingMeta = $this->_settings->hasCustomFolders ? $this->spMetaDao->getPendingMetaCount() : 0;
        $percent = $this->prioQ->getBulkPercent();
        if (0 + $pendingMeta > 0) {
            $customMeta = $this->spMetaDao->getCustomMetaCount();
            $crtTotalFiles = is_array($this->_settings->currentStats) ? $this->_settings->currentStats['totalFiles'] : $this->_settings->currentStats;
            $totalPercent = round(($percent * $crtTotalFiles + ($customMeta - $pendingMeta) * 100) / ($crtTotalFiles + $customMeta));
            $minutesRemaining = round($minutesRemaining * (100 - $totalPercent) / max(1, 100 - $percent));
            $percent = $totalPercent;
        }
        $result["BulkPercent"] = $percent;
        $result["BulkMsg"] = $this->bulkProgressMessage($deltaBulkPercent, $minutesRemaining);
    }
    private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false)
    {
        if ($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
            $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
            if (isset($rawMeta['type']) && $rawMeta['type'] == 'image/jpeg') {
                $itemHandler->getMeta(true);
            }
        }
        $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
        Log::addDebug('Send to PRocessing - URLS -', array($URLsAndPATHs));
        $result = WpShortPixelMediaLbraryAdapter::checkRequestLimiter($URLsAndPATHs['URLs']);
        if (!$result) {
            Log::addDebug('Preventing sentToProcessing. Reported as already sent');
            return $URLsAndPATHs;
        }
        $meta = $itemHandler->getMeta();
        $itemHandler->searchUnlistedFiles();
        $miss = $meta->getThumbsMissing();
        if (is_numeric($miss)) {
            $miss = array();
        }
        if (isset($URLsAndPATHs['sizesMissing']) && count($URLsAndPATHs['sizesMissing']) && (null === $miss || count(array_diff_key($miss, array_merge($URLsAndPATHs['sizesMissing'], $miss))))) {
            $meta->setThumbsMissing($URLsAndPATHs['sizesMissing']);
            $itemHandler->updateMeta();
        }
        $original_status = $meta->getStatus();
        $refresh = $meta->getStatus() === ShortPixelAPI::ERR_INCORRECT_FILE_SIZE;
        $itemHandler->setWaitingProcessing();
        if ($original_status < 0 && count($URLsAndPATHs['URLs']) == 0) {
            if (!is_array($meta->getThumbsMissing()) || count($meta->getThumbsMissing()) == 0) {
                $meta->setStatus(ShortPixelAPI::STATUS_SUCCESS);
                $meta->setMessage(0);
                $meta->setThumbsTodo(0);
                $itemHandler->updateMeta($meta);
                Log::addWarn('Processing override, no URLS, no jobs, something was incorrect - ');
                return $URLsAndPATHs;
            }
        }
        $thumbObtList = $meta->getThumbsOptList();
        $missing = $meta->getThumbsMissing();
        try {
            $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $itemHandler, $compressionType === false ? $this->_settings->compressionType : $compressionType, $refresh);
        } catch (Exception $e) {
            Log::addError('Api DoRequest Thrown ' . $e->getMessage());
        }
        return $URLsAndPATHs;
    }
    public function handleManualOptimization()
    {
        $imageId = intval($_GET['image_id']);
        Log::addInfo("Handle Manual Optimization #{$imageId}");
        switch (substr($imageId, 0, 2)) {
            case "N-":
                return "Add the gallery to the custom folders list in ShortPixel settings.";
                if (class_exists("C_Image_Mapper")) {
                    $image_mapper = C_Image_Mapper::get_instance();
                    $image = $image_mapper->find(intval(substr($imageId, 2)));
                    if ($image) {
                        $this->handleNextGenImageUpload($image, true);
                        return array("Status" => ShortPixelAPI::STATUS_SUCCESS, "message" => "");
                    }
                }
                return array("Status" => ShortPixelAPI::STATUS_FAIL, "message" => __('NextGen image not found', 'shortpixel-image-optimiser'));
                break;
            case "C-":
                Log::addError("Throw: HandleManualOptimization for custom images not implemented");
                throw new Exception("HandleManualOptimization for custom images not implemented");
            default:
                $this->optimizeNowHook(intval($imageId), true);
                break;
        }
    }
    public function checkStatus()
    {
        $itemHandler = new ShortPixelMetaFacade(intval($_GET['image_id']));
        $meta = $itemHandler->getMeta();
        die(json_encode(array("Status" => $meta->getStatus(), "Message" => $meta->getMessage())));
    }
    public function optimizeNowHook($imageId, $manual = false)
    {
        if ($this->isProcessable($imageId)) {
            $this->prioQ->push($imageId);
            $itemHandler = new ShortPixelMetaFacade($imageId);
            $itemFile = \wpSPIO()->filesystem()->getAttachedFile($imageId);
            if ($manual) {
                $meta = $itemHandler->getMeta();
                $meta->setRetries(0);
                $meta->setStatus(\ShortPixelMeta::FILE_STATUS_PENDING);
                $itemHandler->updateMeta($meta);
            }
            if (!$manual && 'pdf' === $itemFile->getExtension() && !$this->_settings->optimizePdfs) {
                $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => $imageId);
            } else {
                try {
                    $this->sendToProcessing($itemHandler, false, $itemHandler->getMeta()->getThumbsTodo());
                    $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
                } catch (Exception $e) {
                    $itemHandler->getMeta();
                    $errCode = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::ERR_FILE_NOT_FOUND;
                    $itemHandler->setError($errCode, $e->getMessage());
                    $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
                }
            }
        } else {
            $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => $imageId);
        }
        die(json_encode($ret));
    }
    public function thumbnailsBeforeRegenerateHook($postId)
    {
        $this->thumbnailsRegenerating[$postId] = true;
    }
    public function thumbnailsRegeneratedHook($postId, $originalMeta, $regeneratedSizes = array(), $bulk = false)
    {
        $fs = \wpSPIO()->filesystem();
        $settings = \wpSPIO()->settings();
        if (isset($originalMeta["ShortPixelImprovement"]) && is_numeric($originalMeta["ShortPixelImprovement"])) {
            $shortPixelMeta = $originalMeta["ShortPixel"];
            unset($shortPixelMeta['thumbsMissing']);
            if (count($regeneratedSizes) == 0 || !isset($shortPixelMeta["thumbsOptList"])) {
                $shortPixelMeta["thumbsOpt"] = 0;
                $shortPixelMeta["thumbsOptList"] = array();
                $shortPixelMeta["retinasOpt"] = 0;
            } else {
                $regeneratedThumbs = array();
                $mainFile = $fs->getAttachedFile($postId);
                foreach ($regeneratedSizes as $size) {
                    if (isset($size['file']) && in_array($size['file'], $shortPixelMeta["thumbsOptList"])) {
                        $regeneratedThumbs[] = $size['file'];
                        $fileObj = $fs->getFile((string) $mainFile->getFileDir() . $size['file']);
                        if ($settings->createWebp) {
                            if (SHORTPIXEL_USE_DOUBLE_WEBP_EXTENSION) {
                                $webpObj = $fs->getFile((string) $fileObj->getFileDir() . $fileObj->getFileName() . '.webp');
                            } else {
                                $webpObj = $fs->getFile((string) $fileObj->getFileDir() . $fileObj->getFileBase() . '.webp');
                            }
                            if ($webpObj->exists()) {
                                $webpObj->delete();
                            }
                        }
                        if ($settings->createAvif) {
                            $avifObj = $fs->getFile((string) $fileObj->getFileDir() . $fileObj->getFileBase() . '.avif');
                            if ($avifObj->exists()) {
                                $avifObj->delete();
                            }
                        }
                        $shortPixelMeta["thumbsOpt"] = max(0, $shortPixelMeta["thumbsOpt"] - 1);
                        $shortPixelMeta["retinasOpt"] = max(0, $shortPixelMeta["retinasOpt"] - 1);
                    }
                }
                $shortPixelMeta["thumbsOptList"] = array_diff($shortPixelMeta["thumbsOptList"], $regeneratedThumbs);
            }
            $meta = wp_get_attachment_metadata($postId);
            $meta["ShortPixel"] = $shortPixelMeta;
            $meta["ShortPixelImprovement"] = $originalMeta["ShortPixelImprovement"];
            if (isset($originalMeta["ShortPixelPng2Jpg"])) {
                $meta["ShortPixelPng2Jpg"] = $originalMeta["ShortPixelPng2Jpg"];
            }
            update_post_meta($postId, '_wp_attachment_metadata', $meta);
            if (!$bulk) {
                $this->prioQ->push($postId);
            }
        }
        unset($this->thumbnailsRegenerating[$postId]);
    }
    public function shortpixelGetBackupFilter($imagePath)
    {
        $backup = str_replace(dirname(dirname(dirname(SHORTPIXEL_BACKUP_FOLDER))), SHORTPIXEL_BACKUP_FOLDER, $imagePath);
        return file_exists($backup) ? $backup : false;
    }
    public function onWpLrUpdateMedia($imageId, $galleryIdsUnused)
    {
        $meta = wp_get_attachment_metadata($imageId);
        if (is_array($meta)) {
            unset($meta['ShortPixel']);
            $meta['ShortPixel'] = array();
            $meta['ShortPixel']['WaitingProcessing'] = true;
            $this->prioQ->push($imageId);
            update_post_meta($imageId, '_wp_attachment_metadata', $meta);
            ShortPixelMetaFacade::optimizationStarted($imageId);
        }
    }
    public function getBackupFolder($file)
    {
        $fs = \wpSPIO()->filesystem();
        $fsFile = $fs->getFile($file);
        $directory = $this->getBackupFolderInternal($fsFile);
        if ($directory !== false) {
            return $directory->getPath();
        } else {
            return false;
        }
    }
    private function getBackupFolderInternal(FileModel $file)
    {
        $fs = \wpSPIO()->filesystem();
        $settings = \wpSPIO()->settings();
        $SubDir = ShortPixelMetaFacade::returnSubDir($file);
        $SubDirOld = ShortPixelMetaFacade::returnSubDirOld($file);
        $basename = $file->getFileName();
        $backupFile = $file->getBackupFile();
        if ($backupFile) {
            $backupFolder = $backupFile->getFileDir();
            return $backupFolder;
        }
        $backupFile = $fs->getFile(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . '/' . $basename);
        if ($backupFile->exists()) {
            return $backupFile->getFileDir();
        }
        $backupFile = $fs->getFile(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDirOld . '/' . $basename);
        if ($backupFile->exists()) {
            return $backupFile->getFileDir();
        }
        $backupFile = $fs->getFile(SHORTPIXEL_BACKUP_FOLDER . '/' . date("Y") . "/" . date("m") . '/' . $basename);
        if ($backupFile->exists()) {
            return $backupFile->getFileDir();
        }
        Log::addError('Backup Directory could not be established! ', array($file->getFullPath(), $SubDir, $SubDirOld, $basename));
        return false;
    }
    public function getBackupFolderAny($file, $thumbs)
    {
        $ret = $this->getBackupFolder($file);
        if (!$ret && isset($thumbs)) {
            foreach ($thumbs as $size) {
                $backup = $this->getBackupFolder(trailingslashit(dirname($file)) . $size['file']);
                if ($backup) {
                    $ret = $backup;
                    break;
                }
            }
        }
        return apply_filters("shortpixel_backup_folder", $ret, $file, $thumbs);
    }
    protected function setFilePerms($file)
    {
        $perms = @fileperms($file);
        if ($perms & 0x100 && $perms & 0x80) {
            return true;
        }
        if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
            $owner = fileowner($file);
            if ($owner !== false && function_exists('posix_getuid') && $owner != posix_getuid()) {
                return false;
            }
        }
        if (!@chmod($file, $perms | 0x100 | 0x80)) {
            return false;
        }
        return true;
    }
    protected function doRestore($attachmentID, $rawMeta = null)
    {
        do_action("shortpixel_before_restore_image", $attachmentID);
        $fs = \wpSPIO()->filesystem();
        $imageObj = new ImageModel();
        $imageObj->setbyPostID($attachmentID);
        $fsFile = $imageObj->getFile();
        $filePath = (string) $fsFile->getFileDir();
        $itemHandler = $imageObj->getFacade();
        if ($rawMeta) {
            $itemHandler->setRawMeta($rawMeta);
        } else {
            $itemHandler->getMeta();
            $rawMeta = $itemHandler->getRawMeta();
        }
        try {
            $toUnlink = $itemHandler->getURLsAndPATHs(true, false, true, array(), true);
        } catch (Exception $e) {
            return false;
        }
        do_action('shortpixel_restore_after_pathget', $attachmentID);
        $sizes = isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array();
        $oldBackupFolder = $this->getBackupFolderAny($fsFile->getFullPath(), $sizes);
        if (!$oldBackupFolder) {
            $notice = Notices::addWarning(__("Not all backup files found. Restore not performed on these files ", 'shortpixel-image-optimiser'), true);
            Notices::addDetail($notice, (string) $bkFile);
            Log::addError('No Backup Files Found: ' . $bkFile);
            return false;
        } else {
            $bkFolder = $fs->getDirectory($oldBackupFolder);
            $bkFile = $fs->getFile($bkFolder->getPath() . $fsFile->getFileName());
        }
        Log::addDebug('Restore, Backup File -- ', array($bkFile->getFullPath(), $fsFile->getFullPath()));
        $png2jpgMain = isset($rawMeta['ShortPixelPng2Jpg']['originalFile']) ? $rawMeta['ShortPixelPng2Jpg']['originalFile'] : false;
        $toReplace = array();
        if ($png2jpgMain) {
            $png2jpgSizes = $png2jpgMain ? $rawMeta['ShortPixelPng2Jpg']['originalSizes'] : array();
            $image = $rawMeta['file'];
            $imageUrl = wp_get_attachment_url($attachmentID);
            Log::addDebug('PHP2JPG - OriginFile -- ' . $fsFile->getFullPath());
            $imageName = $fsFile->getFileName();
            $baseUrl = str_replace($fsFile->getFileName(), '', $imageUrl);
            $baseUrl = ShortPixelPng2Jpg::removeUrlProtocol($baseUrl);
            $backupFileDir = $bkFile->getFileDir();
            if ($bkFile->exists()) {
                $toUnlink['PATHs'][] = $bkFile->getFullPath();
            }
            $baseRelPath = '';
            $toReplace[ShortPixelPng2Jpg::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($png2jpgMain);
            foreach ($sizes as $key => $size) {
                if (isset($png2jpgSizes[$key])) {
                    $toReplace[$baseUrl . $baseRelPath . $size['file']] = $baseUrl . $baseRelPath . wp_basename($png2jpgSizes[$key]['file']);
                }
                $backuppedSize = $fs->getFile($backupFileDir . $size['file']);
                Log::addDebug('Checking for PNG Backup at - ', $backuppedSize->getFullPath());
                if ($backuppedSize->exists()) {
                    $toUnlink['PATHs'][] = $backuppedSize->getFullPath();
                }
            }
            $sizes = $png2jpgSizes;
            $fsFile = $fs->getFile($png2jpgMain);
            $bkFile = $fs->getFile($bkFolder->getPath() . $fsFile->getFileName());
            wp_update_post(array('ID' => $attachmentID, 'post_mime_type' => 'image/png'));
        }
        $bkCount = 0;
        if (isset($rawMeta["ShortPixel"]['ErrCode'])) {
            $lastStatus = $this->_settings->bulkLastStatus;
            if (isset($lastStatus['ImageID']) && $lastStatus['ImageID'] == $attachmentID) {
                $this->_settings->bulkLastStatus = null;
            }
        }
        if ($bkFile->exists()) {
            if (!$bkFile->is_readable() || $fsFile->exists() && !$fsFile->is_writable()) {
                $this->throwNotice('generic-err', sprintf(__("File %s cannot be restored due to lack of permissions, please contact your hosting provider to assist you in fixing this.", 'shortpixel-image-optimiser'), $fsFile->getFullPath()));
                Log::addError('DoRestore could not restore file', array($bkFile->getFullPath(), $fsFile->getFullPath(), $fsFile->exists(), $bkFile->is_readable(), $fsFile->is_writable()));
                return false;
            }
            $bkCount++;
            $main = true;
        }
        $thumbsPaths = array();
        if ($bkFolder->exists() && !empty($rawMeta['file']) && count($sizes)) {
            foreach ($sizes as $size => $imageData) {
                $destination = $fs->getFile($filePath . $imageData['file']);
                $source = $fs->getFile($bkFolder->getPath() . $imageData['file']);
                if (!$source->exists()) {
                    continue;
                }
                if (!$source->is_readable() || $destination->exists() && !$destination->is_writable()) {
                    $failedFile = $destination->is_writable() ? $source->getFullPath() : $destination->getFullPath();
                    $this->throwNotice('generic-err', sprintf(__("The file %s cannot be restored due to lack of permissions, please contact your hosting provider to assist you in fixing this.", 'shortpixel-image-optimiser'), "{$failedFile} (current permissions: " . sprintf("%o", fileperms($failedFile)) . ")"));
                    return false;
                }
                $bkCount++;
                $thumbsPaths[$destination->getFileName()] = array('source' => $source, 'destination' => $destination);
            }
        }
        if (!$bkCount) {
            $notice = Notices::addWarning(__("Not all backup files found. Restore not performed on these files ", 'shortpixel-image-optimiser'), true);
            Notices::addDetail($notice, (string) $bkFile);
            Log::addError('No Backup Files Found: ' . $bkFile);
            return false;
        }
        try {
            $width = false;
            if ($bkCount) {
                if ($main) {
                    $origFile = $imageObj->has_original();
                    if (is_object($origFile)) {
                        $bkOrigFile = $origFile->getBackUpFile();
                        if ($bkOrigFile && $bkOrigFile->exists()) {
                            $bkOrigFile->move($origFile);
                            Log::addDebug('Restore result - Backup original file', array($bkOrigFile->getFullPath(), $origFile->getFullPath()));
                        }
                    }
                    if (!$bkFile->move($fsFile)) {
                        Log::addError('DoRestore failed restoring backup', array($bkFile->getFullPath(), $fsFile->getFullPath()));
                    }
                    $retinaBK = $fs->getFile($bkFile->getFileDir()->getPath() . $bkFile->getFileBase() . '@2x' . $bkFile->getExtension());
                    if ($retinaBK->exists()) {
                        $retinaDest = $fs->getFile($fsFile->getFileDir()->getPath() . $fsFile->getFileBase() . '@2x' . $fsFile->getExtension());
                        if (!$retinaBK->move($retinaDest)) {
                            Log::addError('DoRestore failed restoring retina backup', array($retinaBK->getFullPath(), $retinaDest->getFullPath()));
                        }
                    }
                }
                if ($fsFile->exists()) {
                    $size = getimagesize($fsFile->getFullPath());
                    $width = $size[0];
                    $height = $size[1];
                }
                foreach ($thumbsPaths as $index => $data) {
                    $source = $data['source'];
                    $destination = $data['destination'];
                    if (!$source->move($destination)) {
                        Log::addError('DoRestore failed restoring backup', array($source->getFullPath(), $destination->getFullPath()));
                    }
                    $retinaBK = $fs->getFile($source->getFileDir()->getPath() . $source->getFileBase() . '@2x' . $source->getExtension());
                    if ($retinaBK->exists()) {
                        $retinaDest = $fs->getFile($destination->getFileDir()->getPath() . $destination->getFileBase() . '@2x' . $destination->getExtension());
                        if (!$retinaBK->move($retinaDest)) {
                            Log::addError('DoRestore failed restoring retina backup', array($retinaBK->getFullPath(), $retinaDest->getFullPath()));
                        }
                    }
                }
            }
            $duplicates = ShortPixelMetaFacade::getWPMLDuplicates($attachmentID);
            foreach ($duplicates as $ID) {
                $facade = new ShortPixelMetaFacade($ID);
                if ($attachmentID == $ID) {
                    $crtMeta = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($ID));
                } else {
                    $crtMeta = $rawMeta;
                }
                if (isset($crtMeta['previous_meta'])) {
                    continue;
                }
                if (isset($crtMeta["ShortPixelImprovement"]) && is_numeric($crtMeta["ShortPixelImprovement"]) && 0 + $crtMeta["ShortPixelImprovement"] < 5 && $this->_settings->under5Percent > 0) {
                    $this->_settings->under5Percent = $this->_settings->under5Percent - 1;
                }
                unset($crtMeta["ShortPixelImprovement"]);
                unset($crtMeta['ShortPixel']);
                unset($crtMeta['ShortPixelPng2Jpg']);
                delete_post_meta($ID, '_shortpixel_status');
                if ($width && $height) {
                    $crtMeta['width'] = $width;
                    $crtMeta['height'] = $height;
                }
                if ($png2jpgMain) {
                    $dirname = dirname($crtMeta['file']);
                    if ($dirname == '.') {
                        $dirname = '';
                    } else {
                        $dirname = trailingslashit($dirname);
                    }
                    $crtMeta['file'] = $dirname . $fsFile->getFileName();
                    update_attached_file($ID, $crtMeta['file']);
                    if ($png2jpgSizes && count($png2jpgSizes)) {
                        $crtMeta['sizes'] = $png2jpgSizes;
                    } else {
                        $admin = \ShortPixel\Controller\AdminController::getInstance();
                        $test = remove_filter('wp_generate_attachment_metadata', array($admin, 'handleImageUploadHook'), 10);
                        if (!$test) {
                            Log::addWarn('Wp generate Attachment metadta filter not removed');
                        }
                        $crtMeta = wp_generate_attachment_metadata($ID, $png2jpgMain);
                        add_filter('wp_generate_attachment_metadata', array($admin, 'handleImageUploadHook'), 10, 2);
                    }
                }
                update_post_meta($ID, '_wp_attachment_metadata', $crtMeta);
                if ($attachmentID == $ID) {
                    $rawMeta = $crtMeta;
                }
            }
            if ($png2jpgMain) {
                $spPng2Jpg = new ShortPixelPng2Jpg($this->_settings);
                $spPng2Jpg->png2JpgUpdateUrls(array(), $toReplace);
            }
            if (isset($toUnlink['PATHs'])) {
                foreach ($toUnlink['PATHs'] as $unlink) {
                    if ($png2jpgMain) {
                        Log::addDebug("PNG2JPG unlink {$unlink}");
                        $unlinkFile = $fs->getFile($unlink);
                        $unlinkFile->delete();
                    }
                    $unlinkWebpSymlink = trailingslashit(dirname($unlink)) . wp_basename($unlink, '.' . pathinfo($unlink, PATHINFO_EXTENSION)) . '.webp';
                    $unlinkWebp = $unlink . '.webp';
                    $unlinkFile = $fs->getFile($unlinkWebpSymlink);
                    if ($unlinkFile->exists()) {
                        Log::addDebug('DoRestore, Deleting Webp - ', $unlinkWebpSymlink);
                        $unlinkFile->delete();
                    }
                    $unlinkFileDoubleExt = $fs->getFile($unlinkWebp);
                    if ($unlinkFileDoubleExt->exists()) {
                        Log::addDebug('DoRestore, Deleting DoubleWebp - ', $unlinkWebp);
                        $unlinkFileDoubleExt->delete();
                    }
                    $unlinkAvif = $fs->getFile($unlinkFile->getFileDir() . $unlinkFile->getFileBase() . '.avif');
                    if ($unlinkAvif->exists()) {
                        $unlinkAvif->delete();
                        Log::addDebug('DoRestore, Deleting Avif :' . $unlinkAvif->getFullPath());
                    }
                }
            }
        } catch (Exception $e) {
            $this->throwNotice('generic-err', $e->getMessage());
            return false;
        }
        $this->maybeDumpFromProcessedOnServer($itemHandler, $toUnlink);
        $itemHandler->deleteItemCache();
        $rawMeta = $itemHandler->getRawMeta();
        do_action("shortpixel_after_restore_image", $attachmentID);
        return $rawMeta;
    }
    public function throwNotice($when = 'activate', $extra = '')
    {
        Notices::addError($extra);
    }
    public function doCustomRestore($ID)
    {
        $itemHandler = new ShortPixelMetaFacade('C-' . $ID);
        $meta = $itemHandler->getMeta();
        $URLsAndPATHs = $itemHandler->getURLsAndPATHs(false);
        $this->maybeDumpFromProcessedOnServer($itemHandler, $URLsAndPATHs);
        if (!$meta || $meta->getStatus() != shortPixelMeta::FILE_STATUS_SUCCESS && $meta->getStatus() != shortpixelMeta::FILE_STATUS_TORESTORE) {
            return false;
        }
        $file = $meta->getPath();
        $fullSubDir = str_replace(get_home_path(), "", dirname($file)) . '/';
        $bkFile = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . ShortPixelAPI::MB_basename($file);
        $fs = \wpSPIO()->fileSystem();
        $fileObj = $fs->getFile($file);
        $backupFile = $fileObj->getBackupFile();
        if ($backupFile === false) {
            Log::addWarn("Custom File {$ID} - {$file} does not have a backup");
            $notice = Notices::addWarning(__('Not able to restore file(s). Could not find backup', 'shortpixel-image-optimiser'), true);
            Notices::addDetail($notice, (string) $file);
            return false;
        } elseif ($backupFile->copy($fileObj)) {
            $backupFile->delete();
        } else {
            Log::addError('Could not restore back to source' . $backupFile->getFullPath());
            $notice = Notices::addError('These file(s) could not be restored from backup. Plugin could not copy backup back to original location. Check file permissions. ', 'shortpixel-image-optimiser');
            Notices::addDetail($notice, (string) $backupFile);
            return false;
        }
        $meta->setTsOptimized(0);
        $meta->setCompressedSize(0);
        $meta->setCompressionType(0);
        $meta->setKeepExif(0);
        $meta->setCmyk2rgb(0);
        $meta->setMessage('');
        $meta->setRetries(0);
        $meta->setBackup(0);
        $meta->setResizeWidth(0);
        $meta->setResizeHeight(0);
        $meta->setResize(0);
        $meta->setStatus(3);
        $this->spMetaDao->update($meta);
        $itemHandler->deleteItemCache();
        return $meta;
    }
    public function handleRestoreBackup()
    {
        $attachmentID = intval($_GET['attachment_ID']);
        self::log("Handle Restore Backup #{$attachmentID}");
        $this->doRestore($attachmentID);
        $sendback = wp_get_referer();
        $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
        wp_redirect($sendback);
    }
    public function handleRedo()
    {
        Log::addDebug("Handle Redo #{$_GET['attachment_ID']} type {$_GET['type']}");
        $attach_id = intval($_GET['attachment_ID']);
        $type = sanitize_text_field($_GET['type']);
        die(json_encode($this->redo($attach_id, $type)));
    }
    public function redo($qID, $type = false)
    {
        $compressionType = $type == 'lossless' ? 'lossless' : ($type == 'glossy' ? 'glossy' : 'lossy');
        if (ShortPixelMetaFacade::isCustomQueuedId($qID)) {
            $ID = ShortPixelMetaFacade::stripQueuedIdType($qID);
            $meta = $this->spMetaDao->getMeta($ID);
            $path = $meta->getPath();
            $folder_id = $meta->getFolderId();
            $this->doCustomRestore($ID);
            if ($meta) {
                $meta->setCompressionType(ShortPixelAPI::getCompressionTypeCode($compressionType));
                $meta->setStatus(1);
                $this->spMetaDao->update($meta);
                $this->prioQ->push($qID);
                $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
            } else {
                $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => __('Could not restore from backup: ', 'shortpixel-image-optimiser') . $qID);
            }
        } else {
            $ID = intval($qID);
            $meta = $this->doRestore($ID);
            if ($meta) {
                $meta['ShortPixel'] = array("type" => $compressionType);
                update_post_meta($ID, '_wp_attachment_metadata', $meta);
                try {
                    $this->sendToProcessing(new ShortPixelMetaFacade($ID), ShortPixelAPI::getCompressionTypeCode($compressionType));
                    $this->prioQ->push($ID);
                    $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
                } catch (Exception $e) {
                    $meta['ShortPixelImprovement'] = $e->getMessage();
                    $meta['ShortPixel']['ErrCode'] = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::STATUS_FAIL;
                    unset($meta['ShortPixel']['WaitingProcessing']);
                    update_post_meta($ID, '_wp_attachment_metadata', $meta);
                    $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
                }
            } else {
                $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => __('Could not restore from backup: ', 'shortpixel-image-optimiser') . $ID);
            }
        }
        return $ret;
    }
    public function handleOptimizeThumbs()
    {
        $ID = intval($_GET['attachment_ID']);
        $meta = wp_get_attachment_metadata($ID);
        $fs = \wpSPIO()->filesystem();
        $error = array('Status' => ShortPixelAPI::STATUS_SKIP, 'message' => __('Unspecified Error on Thumbnails for: ') . $ID);
        $optFile = $fs->getAttachedFile($ID);
        list($includedSizes, $thumbsCount) = $this->getThumbsToOptimize($meta, $optFile->getFullPath());
        $thumbsCount = count($includedSizes);
        if (!isset($meta['ShortPixelImprovement'])) {
            $error['message'] = __('Please optimize image for ID: ', 'shortpixel-image-optimiser') . $ID;
            die(json_encode($error));
        }
        if (!isset($meta['sizes']) || count($meta['sizes']) == 0) {
            $error['message'] = __('No thumbnails to optimize for ID: ', 'shortpixel-image-optimiser') . $ID;
            die(json_encode($error));
        }
        if (isset($meta['ShortPixel']['thumbsOptList']) && is_array($meta['ShortPixel']['thumbsOptList'])) {
            $thumbList = array();
            foreach ($meta['ShortPixel']['thumbsOptList'] as $fileName) {
                if (isset($includedSizes[$fileName])) {
                    $thumbList[] = $fileName;
                }
            }
            $meta['ShortPixel']['thumbsOptList'] = $thumbList;
        }
        if ($thumbsCount && (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && count($meta['ShortPixel']['thumbsOptList']) < $thumbsCount)) {
            $meta['ShortPixel']['thumbsTodo'] = true;
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
            $this->prioQ->push($ID);
            try {
                $this->sendToProcessing(new ShortPixelMetaFacade($ID), false, true);
                $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "message" => "");
            } catch (Exception $e) {
                if (!isset($meta['ShortPixelImprovement']) || !is_numeric($meta['ShortPixelImprovement'])) {
                    $meta['ShortPixelImprovement'] = $e->getMessage();
                    $meta['ShortPixel']['ErrCode'] = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::STATUS_FAIL;
                    unset($meta['ShortPixel']['WaitingProcessing']);
                    update_post_meta($ID, '_wp_attachment_metadata', $meta);
                }
                $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
            }
        } else {
            $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "message" => (isset($meta['ShortPixelImprovement']) ? __('No thumbnails to optimize for ID: ', 'shortpixel-image-optimiser') : __('Please optimize image for ID: ', 'shortpixel-image-optimiser')) . $ID);
        }
        die(json_encode($ret));
    }
    public function handleCheckQuota()
    {
        $return_json = isset($_POST['return_json']) ? true : false;
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'check_quota')) {
            Log::addError('Handle Check Quota, No nonce');
            exit('no nonce');
        }
        $result = $this->getQuotaInformation();
        AdminNoticesController::resetQuotaNotices();
        $sendback = wp_get_referer();
        $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
        if ($return_json) {
            $result = array('status' => 'no-quota', 'redirect' => $sendback);
            if (!$this->_settings->quotaExceeded) {
                $result['status'] = 'has-quota';
            } else {
                Notices::addWarning(__('You have no available image credits. If you just bought a package, please note that sometimes it takes a few minutes for the payment confirmation to be sent to us by the payment processor.', 'shortpixel-image-optimiser'));
            }
            wp_send_json($result);
        } else {
            wp_redirect($sendback);
        }
    }
    public function handleDeleteAttachmentInBackup($ID)
    {
        $fileObj = \wpSPIO()->filesystem()->getAttachedFile($ID);
        $file = $fileObj->getFullPath();
        $meta = wp_get_attachment_metadata($ID);
        if (self::_isProcessable($ID) != false) {
            try {
                $SubDir = ShortPixelMetaFacade::returnSubDir($file);
                if (file_exists(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file))) {
                    @unlink(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file));
                }
                if (!empty($meta['file'])) {
                    $filesPath = SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir;
                    if (isset($meta["sizes"])) {
                        foreach ($meta["sizes"] as $size => $imageData) {
                            if (file_exists($filesPath . ShortPixelAPI::MB_basename($imageData['file']))) {
                                @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));
                            }
                        }
                    }
                }
            } catch (Exception $e) {
            }
        }
    }
    public function deactivatePlugin()
    {
        if (!wp_verify_nonce($_GET['_wpnonce'], 'sp_deactivate_plugin_nonce')) {
            wp_nonce_ays('');
        }
        $referrer_url = wp_get_referer();
        $conflict = \ShortPixelTools::getConflictingPlugins();
        foreach ($conflict as $c => $value) {
            $conflictingString = $value['page'];
            if ($conflictingString != null && strpos($referrer_url, $conflictingString) !== false) {
                $this->deactivateAndRedirect(get_dashboard_url());
                break;
            }
        }
        $this->deactivateAndRedirect(wp_get_referer());
    }
    protected function deactivateAndRedirect($url)
    {
        deactivate_plugins(sanitize_text_field($_GET['plugin']));
        wp_safe_redirect($url);
        die;
    }
    public function countAllIfNeeded($quotaData, $time)
    {
        if (!(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) && is_array($this->_settings->currentStats) && $this->_settings->currentStats['optimizePdfs'] == $this->_settings->optimizePdfs && isset($this->_settings->currentStats['time']) && time() - $this->_settings->currentStats['time'] < $time) {
            Log::addDebug("CURRENT STATS FROM CACHE (not older than {$time} sec., currently " . (time() - $this->_settings->currentStats['time']) . ' sec. old)');
            return $this->_settings->currentStats;
        } else {
            Log::addDebug("CURRENT STATS (not older than {$time}) ARE BEING CALCULATED...");
            if (!is_array($quotaData)) {
                $quotaData = array();
            }
            $imageCount = WpShortPixelMediaLbraryAdapter::countAllProcessable($this->_settings);
            $quotaData['time'] = time();
            $quotaData['optimizePdfs'] = $this->_settings->optimizePdfs;
            foreach ($imageCount as $key => $val) {
                $quotaData[$key] = $val;
            }
            if ($this->_settings->hasCustomFolders) {
                $customImageCount = $this->spMetaDao->countAllProcessableFiles();
                foreach ($customImageCount as $key => $val) {
                    $quotaData[$key] = isset($quotaData[$key]) ? is_array($quotaData[$key]) ? array_merge($quotaData[$key], $val) : (is_numeric($quotaData[$key]) ? $quotaData[$key] + $val : $quotaData[$key] . ", " . $val) : $val;
                }
            }
            $this->_settings->currentStats = $quotaData;
            return $quotaData;
        }
    }
    public function checkQuotaAndAlert($quotaData = null, $recheck = false, $refreshFiles = 300)
    {
        if (!$quotaData) {
            $quotaData = $this->getQuotaInformation();
        }
        if (!$quotaData['APIKeyValid']) {
            if (strlen($this->_settings->apiKey)) {
                Notices::addError(sprintf(__('Shortpixel Remote API Error: %s', 'shortpixel-image-optimiser'), $quotaData['Message']));
            }
            return $quotaData;
        }
        $quotaData = $this->countAllIfNeeded($quotaData, $refreshFiles);
        if ($quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] > $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']) {
            $this->_settings->quotaExceeded = '0';
            $this->_settings->prioritySkip = NULL;
            Log::addInfo("CHECK QUOTA: Skipped: " . json_encode($this->prioQ->getSkipped()));
            ?><script>var shortPixelQuotaExceeded = 0;</script><?php 
        } else {
            ?><script>var shortPixelQuotaExceeded = 1;</script><?php 
        }
        return $quotaData;
    }
    public function isValidMetaId($id)
    {
        return substr($id, 0, 2) == "C-" ? $this->spMetaDao->getMeta(substr($id, 2)) : wp_get_attachment_url($id);
    }
    public function getPercent($quotaData)
    {
        if ($this->_settings->processThumbnails) {
            return $quotaData["totalFiles"] ? min(99, round($quotaData["totalProcessedFiles"] * 100.0 / $quotaData["totalFiles"])) : 0;
        } else {
            return $quotaData["mainFiles"] ? min(99, round($quotaData["mainProcessedFiles"] * 100.0 / $quotaData["mainFiles"])) : 0;
        }
    }
    public function bulkProgressMessage($percent, $minutes)
    {
        $timeEst = "";
        self::log("bulkProgressMessage(): percent: " . $percent);
        if ($percent < 1 || $minutes == 0) {
            $timeEst = "";
        } elseif ($minutes > 2880) {
            $timeEst = "~ " . round($minutes / 1440) . " days left";
        } elseif ($minutes > 240) {
            $timeEst = "~ " . round($minutes / 60) . " hours left";
        } elseif ($minutes > 60) {
            $hours = round($minutes / 60);
            $minutes = round(max(0, $minutes - $hours * 60) / 10) * 10;
            $timeEst = "~ " . $hours . " hours " . ($minutes > 0 ? $minutes . " min." : "") . " left";
        } elseif ($minutes > 20) {
            $timeEst = "~ " . round($minutes / 10) * 10 . " minutes left";
        } else {
            $timeEst = "~ " . $minutes . " minutes left";
        }
        return $timeEst;
    }
    public function emptyBackup()
    {
        if (file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
            $this->deleteDir(SHORTPIXEL_BACKUP_FOLDER);
        }
    }
    public function backupFolderIsEmpty()
    {
        if (file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
            return count(scandir(SHORTPIXEL_BACKUP_FOLDER)) > 2 ? false : true;
        }
    }
    public function getBackupSize()
    {
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.', 'shortpixel-image-optimiser'));
        }
        die(self::formatBytes(self::folderSize(SHORTPIXEL_BACKUP_FOLDER)));
    }
    public function browseContent()
    {
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.', 'shortpixel-image-optimiser'));
        }
        $root = self::getCustomFolderBase();
        $fs = \wpSPIO()->filesystem();
        $postDir = rawurldecode($root . (isset($_POST['dir']) ? trim($_POST['dir']) : null));
        $checkbox = isset($_POST['multiSelect']) && $_POST['multiSelect'] == 'true' ? "<input type='checkbox' />" : null;
        $onlyFolders = $_POST['dir'] == '/' || isset($_POST['onlyFolders']) && $_POST['onlyFolders'] == 'true' ? true : false;
        $onlyFiles = isset($_POST['onlyFiles']) && $_POST['onlyFiles'] == 'true' ? true : false;
        if (file_exists($postDir)) {
            $dir = $fs->getDirectory($postDir);
            $files = $dir->getFiles();
            $subdirs = $fs->sortFiles($dir->getSubDirectories());
            $returnDir = substr($postDir, strlen($root));
            foreach ($subdirs as $index => $dir) {
                $dirname = $dir->getName();
                if ($dirname == 'ShortpixelBackups' || ShortPixelMetaFacade::isMediaSubfolder($dirname, false)) {
                    unset($subdirs[$index]);
                }
            }
            if (count($subdirs) > 0) {
                echo "<ul class='jqueryFileTree'>";
                foreach ($subdirs as $dir) {
                    $dirpath = $dir->getPath();
                    $dirname = $dir->getName();
                    $htmlRel = str_replace("'", "&apos;", $returnDir . $dirname);
                    $htmlName = htmlentities($dirname);
                    if ($dir->exists()) {
                        echo "<li class='directory collapsed'>{$checkbox}<a rel=' " . $htmlRel . "/'>" . $htmlName . "</a></li>";
                    }
                }
                echo "</ul>";
            } elseif ($_POST['dir'] == '/') {
                echo "<ul class='jqueryFileTree'>";
                _e('No Directories found that can be added to Custom Folders', 'shortpixel-image-optimiser');
                echo "</ul>";
            }
        }
        die;
    }
    public function getComparerData()
    {
        if (!isset($_POST['id']) || !current_user_can('upload_files') && !current_user_can('edit_posts')) {
            wp_die(json_encode((object) array('origUrl' => false, 'optUrl' => false, 'width' => 0, 'height' => 0)));
        }
        $ret = array();
        $fs = \wpSPIO()->filesystem();
        $imageObj = new ImageModel();
        $imageObj->setByPostID(sanitize_text_field($_POST['id']));
        $file = $imageObj->getFile();
        $backupFile = $file->getBackupFile();
        if (!$backupFile && $imageObj->has_original()) {
            $file = $imageObj->has_original();
            $backupFile = $file->getBackupFile();
        }
        $backup_url = $fs->pathToUrl($backupFile);
        $meta = $imageObj->getMeta();
        $ret['origUrl'] = $backup_url;
        $ret['optUrl'] = $fs->pathToUrl($file);
        $ret['width'] = $meta->getActualWidth();
        $ret['height'] = $meta->getActualHeight();
        if (is_null($ret['width'])) {
            $imageSizes = getimagesize($meta->getPath());
            if ($imageSizes) {
                $ret['width'] = $imageSizes[0];
                $ret['height'] = $imageSizes[1];
            }
        }
        die(json_encode((object) $ret));
    }
    public function newApiKey()
    {
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.', 'shortpixel-image-optimiser'));
        }
        if ($this->_settings->verifiedKey) {
            die(json_encode((object) array('Status' => 'success', 'Details' => $this->_settings->apiKey)));
        }
        $params = array('method' => 'POST', 'timeout' => 10, 'redirection' => 5, 'httpversion' => '1.0', 'blocking' => true, 'sslverify' => false, 'headers' => array(), 'body' => array('plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION, 'email' => isset($_POST['email']) ? trim($_POST['email']) : null, 'ip' => isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ? $_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER['REMOTE_ADDR']));
        $newKeyResponse = wp_remote_post("https://shortpixel.com/free-sign-up-plugin", $params);
        if (is_object($newKeyResponse) && get_class($newKeyResponse) == 'WP_Error') {
            die(json_encode((object) array('Status' => 'fail', 'Details' => '503')));
        } elseif (isset($newKeyResponse['response']['code']) && $newKeyResponse['response']['code'] != 200) {
            die(json_encode((object) array('Status' => 'fail', 'Details' => $newKeyResponse['response']['code'])));
        }
        $body = (object) $this->_apiInterface->parseResponse($newKeyResponse);
        if ($body->Status == 'success') {
            $key = trim($body->Details);
            $validityData = $this->getQuotaInformation($key, true, true);
            if ($validityData['APIKeyValid']) {
                $this->_settings->apiKey = $key;
                $this->_settings->verifiedKey = true;
                \ShortPixel\Controller\AdminNoticesController::resetAPINotices();
                Notices::addSuccess(__('Great, you successfully claimed your API Key! Please take a few moments to review the plugin settings below before starting to optimize your images.', 'shortpixel-image-optimiser'));
            }
        }
        die(json_encode($body));
    }
    public function proposeUpgrade()
    {
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.', 'shortpixel-image-optimiser'));
        }
        $stats = $this->countAllIfNeeded($this->_settings->currentStats, 300);
        $proposal = wp_remote_post("https://shortpixel.com/propose-upgrade-frag", array('method' => 'POST', 'timeout' => 10, 'redirection' => 5, 'httpversion' => '1.0', 'blocking' => true, 'headers' => array(), 'body' => array("params" => json_encode(array('plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION, 'key' => $this->_settings->apiKey, 'm1' => $stats['totalM1'], 'm2' => $stats['totalM2'], 'm3' => $stats['totalM3'], 'm4' => $stats['totalM4'], 'filesTodo' => $stats['totalFiles'] - $stats['totalProcessedFiles'], 'estimated' => $this->_settings->optimizeUnlisted || $this->_settings->optimizeRetina ? 'true' : 'false', 'iconsUrl' => base64_encode(wpSPIO()->plugin_url('res/img'))))), 'cookies' => array()));
        if (is_wp_error($proposal)) {
            $proposal = array('body' => '');
        }
        die($proposal['body']);
    }
    public static function getCustomFolderBase()
    {
        Log::addDebug('Call to legacy function getCustomFolderBase');
        $fs = \wpSPIO()->filesystem();
        $dir = $fs->getWPFileBase();
        return $dir->getPath();
    }
    public static function alterHtaccess($webp = false, $avif = false)
    {
        if (!function_exists('insert_with_markers')) {
            Log::addWarn('AlterHtaccess Called before WP init');
            return;
        }
        $upload_dir = wp_upload_dir();
        $upload_base = trailingslashit($upload_dir['basedir']);
        if (!$webp && !$avif) {
            insert_with_markers(get_home_path() . '.htaccess', 'ShortPixelWebp', '');
            insert_with_markers($upload_base . '.htaccess', 'ShortPixelWebp', '');
            insert_with_markers(trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', '');
        } else {
            $avif_rules = '
        <IfModule mod_rewrite.c>
        RewriteEngine On

        ##### IF try the file with replaced extension (test.avif) #####
        RewriteCond %{HTTP_ACCEPT} image/avif
        # AND is the request a jpg or png? (also grab the basepath %1 to match in the next rule)
        RewriteCond %{REQUEST_URI} ^(.+)\\.(?:jpe?g|png)$
        # AND does a .avif image exist?
        RewriteCond %{DOCUMENT_ROOT}/%1.avif -f
        # THEN send the webp image and set the env var avif
        RewriteRule (.+)\\.(?:jpe?g|png)$ $1.avif [NC,T=image/avif,E=avif,L]

        </IfModule>
        <IfModule mod_headers.c>
        # If REDIRECT_webp env var exists, append Accept to the Vary header
        Header append Vary Accept env=REDIRECT_avif
        </IfModule>

        <IfModule mod_mime.c>
        AddType image/avif .avif
        </IfModule>
              ';
            $webp_rules = '
        <IfModule mod_rewrite.c>
          RewriteEngine On

          ##### TRY FIRST the file appended with .webp (ex. test.jpg.webp) #####
          # Does browser explicitly support webp?
          RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
          # OR Is request from Page Speed
          RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
          # OR does this browser explicitly support webp
          RewriteCond %{HTTP_ACCEPT} image/webp
          # AND NOT MS EDGE 42/17 - doesnt work.
          RewriteCond %{HTTP_USER_AGENT} !Edge/17
          # AND is the request a jpg or png?
          RewriteCond %{REQUEST_URI} ^(.+)\\.(?:jpe?g|png)$
          # AND does a .ext.webp image exist?
          RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.webp -f
          # THEN send the webp image and set the env var webp
          RewriteRule ^(.+)$ $1.webp [NC,T=image/webp,E=webp,L]

          ##### IF NOT, try the file with replaced extension (test.webp) #####
          RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
          RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
          RewriteCond %{HTTP_ACCEPT} image/webp
          RewriteCond %{HTTP_USER_AGENT} !Edge/17
          # AND is the request a jpg or png? (also grab the basepath %1 to match in the next rule)
          RewriteCond %{REQUEST_URI} ^(.+)\\.(?:jpe?g|png)$
          # AND does a .ext.webp image exist?
          RewriteCond %{DOCUMENT_ROOT}/%1.webp -f
          # THEN send the webp image and set the env var webp
          RewriteRule (.+)\\.(?:jpe?g|png)$ $1.webp [NC,T=image/webp,E=webp,L]

        </IfModule>
        <IfModule mod_headers.c>
          # If REDIRECT_webp env var exists, append Accept to the Vary header
          Header append Vary Accept env=REDIRECT_webp
        </IfModule>

        <IfModule mod_mime.c>
          AddType image/webp .webp
        </IfModule>
        ';
            $rules = '';
            $rules .= $avif_rules;
            $rules .= $webp_rules;
            insert_with_markers(get_home_path() . '.htaccess', 'ShortPixelWebp', $rules);
            $rules = str_replace('RewriteEngine On', 'RewriteEngine On' . PHP_EOL . 'RewriteOptions Inherit', $rules);
            insert_with_markers($upload_base . '.htaccess', 'ShortPixelWebp', $rules);
            insert_with_markers(trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', $rules);
        }
    }
    public function getAverageCompression()
    {
        return $this->_settings->totalOptimized > 0 ? round((1 - $this->_settings->totalOptimized / $this->_settings->totalOriginal) * 100, 2) : 0;
    }
    public function addWebpMime($mimes)
    {
        if ($this->_settings->createWebp) {
            if (!isset($mimes['webp'])) {
                $mimes['webp'] = 'image/webp';
            }
        }
        return $mimes;
    }
    public function addAvifMime($mimes)
    {
        if ($this->_settings->createAvif) {
            if (!isset($mimes['avif'])) {
                $mimes['webp'] = 'image/avif';
            }
        }
        return $mimes;
    }
    public function getQuotaInformation($apiKey = null, $appendUserAgent = false, $validate = false, $settings = false)
    {
        if (is_null($apiKey)) {
            $apiKey = $this->_settings->apiKey;
        }
        if ($this->_settings->httpProto != 'https' && $this->_settings->httpProto != 'http') {
            $this->_settings->httpProto = 'https';
        }
        $requestURL = $this->_settings->httpProto . '://' . SHORTPIXEL_API . '/v2/api-status.php';
        $args = array('timeout' => SHORTPIXEL_VALIDATE_MAX_TIMEOUT, 'body' => array('key' => $apiKey));
        $argsStr = "?key=" . $apiKey;
        if ($appendUserAgent) {
            $args['body']['useragent'] = "Agent" . urlencode($_SERVER['HTTP_USER_AGENT']);
            $argsStr .= "&useragent=Agent" . $args['body']['useragent'];
        }
        if ($validate) {
            $args['body']['DomainCheck'] = get_site_url();
            $args['body']['Info'] = get_bloginfo('version') . '|' . phpversion();
            $imageCount = WpShortPixelMediaLbraryAdapter::countAllProcessable($this->_settings);
            $args['body']['ImagesCount'] = $imageCount['mainFiles'];
            $args['body']['ThumbsCount'] = $imageCount['totalFiles'] - $imageCount['mainFiles'];
            $argsStr .= "&DomainCheck={$args['body']['DomainCheck']}&Info={$args['body']['Info']}&ImagesCount={$imageCount['mainFiles']}&ThumbsCount={$args['body']['ThumbsCount']}";
        }
        $args['body']['host'] = parse_url(get_site_url(), PHP_URL_HOST);
        $argsStr .= "&host={$args['body']['host']}";
        if (strlen($this->_settings->siteAuthUser)) {
            $args['body']['user'] = stripslashes($this->_settings->siteAuthUser);
            $args['body']['pass'] = stripslashes($this->_settings->siteAuthPass);
            $argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
        }
        if ($settings !== false) {
            $args['body']['Settings'] = $settings;
        }
        $time = microtime(true);
        $comm = array();
        if ($this->_settings->httpProto === 'https') {
            $args['sslverify'] = false;
        }
        $response = wp_remote_post($requestURL, $args);
        $comm['A: ' . number_format(microtime(true) - $time, 2)] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
        if (is_wp_error($response)) {
            $requestURL = $this->_settings->httpProto == 'https' ? str_replace('https://', 'http://', $requestURL) : str_replace('http://', 'https://', $requestURL);
            if ($this->_settings->httpProto === 'http') {
                $args['sslverify'] = false;
            } else {
                unset($args['sslverify']);
            }
            $response = wp_remote_post($requestURL, $args);
            $comm['B: ' . number_format(microtime(true) - $time, 2)] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
            if (!is_wp_error($response)) {
                $this->_settings->httpProto = $this->_settings->httpProto == 'https' ? 'http' : 'https';
            } else {
            }
        }
        if (is_wp_error($response)) {
            $args['body'] = null;
            $requestURL .= $argsStr;
            $response = wp_remote_get($requestURL, $args);
            $comm['C: ' . number_format(microtime(true) - $time, 2)] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
        }
        Log::addInfo("API STATUS COMM: " . json_encode($comm));
        $defaultData = array("APIKeyValid" => false, "Message" => __('API Key could not be validated due to a connectivity error.<BR>Your firewall may be blocking us. Please contact your hosting provider and ask them to allow connections from your site to api.shortpixel.com (IP 176.9.21.94).<BR> If you still cannot validate your API Key after this, please <a href="https://shortpixel.com/contact" target="_blank">contact us</a> and we will try to help. ', 'shortpixel-image-optimiser'), "APICallsMade" => __('Information unavailable. Please check your API key.', 'shortpixel-image-optimiser'), "APICallsQuota" => __('Information unavailable. Please check your API key.', 'shortpixel-image-optimiser'), "APICallsMadeOneTime" => 0, "APICallsQuotaOneTime" => 0, "APICallsMadeNumeric" => 0, "APICallsQuotaNumeric" => 0, "APICallsMadeOneTimeNumeric" => 0, "APICallsQuotaOneTimeNumeric" => 0, "APICallsRemaining" => 0, "APILastRenewalDate" => 0, "DomainCheck" => 'NOT Accessible');
        $defaultData = is_array($this->_settings->currentStats) ? array_merge($this->_settings->currentStats, $defaultData) : $defaultData;
        if (is_object($response) && get_class($response) == 'WP_Error') {
            $urlElements = parse_url($requestURL);
            $portConnect = @fsockopen($urlElements['host'], 8, $errno, $errstr, 15);
            if (!$portConnect) {
                $defaultData['Message'] .= "<BR>Debug info: <i>{$errstr}</i>";
            }
            return $defaultData;
        }
        if ($response['response']['code'] != 200) {
            return $defaultData;
        }
        $data = $response['body'];
        $data = json_decode($data);
        if (empty($data)) {
            return $defaultData;
        }
        if ($data->Status->Code != 2) {
            $defaultData['Message'] = $data->Status->Message;
            return $defaultData;
        }
        if ($data->APICallsMade + $data->APICallsMadeOneTime < $data->APICallsQuota + $data->APICallsQuotaOneTime) {
            $this->resetQuotaExceeded();
        } else {
            $this->_settings->quotaExceeded = 1;
        }
        $lastStatus = $this->_settings->bulkLastStatus;
        if ($lastStatus && $lastStatus['Status'] == ShortPixelAPI::STATUS_NO_KEY) {
            $this->_settings->bulkLastStatus = null;
        }
        $dataArray = array("APIKeyValid" => true, "APICallsMade" => number_format($data->APICallsMade) . __(' images', 'shortpixel-image-optimiser'), "APICallsQuota" => number_format($data->APICallsQuota) . __(' images', 'shortpixel-image-optimiser'), "APICallsMadeOneTime" => number_format($data->APICallsMadeOneTime) . __(' images', 'shortpixel-image-optimiser'), "APICallsQuotaOneTime" => number_format($data->APICallsQuotaOneTime) . __(' images', 'shortpixel-image-optimiser'), "APICallsMadeNumeric" => $data->APICallsMade, "APICallsQuotaNumeric" => $data->APICallsQuota, "APICallsMadeOneTimeNumeric" => $data->APICallsMadeOneTime, "APICallsQuotaOneTimeNumeric" => $data->APICallsQuotaOneTime, "APICallsRemaining" => $data->APICallsQuota + $data->APICallsQuotaOneTime - $data->APICallsMade - $data->APICallsMadeOneTime, "APILastRenewalDate" => $data->DateSubscription, "DomainCheck" => isset($data->DomainCheck) ? $data->DomainCheck : null);
        $crtStats = is_array($this->_settings->currentStats) ? array_merge($this->_settings->currentStats, $dataArray) : $dataArray;
        $crtStats['optimizePdfs'] = $this->_settings->optimizePdfs;
        $this->_settings->currentStats = $crtStats;
        Log::addDebug('GetQuotaInformation Result ', $dataArray);
        return $dataArray;
    }
    public function resetQuotaExceeded()
    {
        if ($this->_settings->quotaExceeded == 1) {
            $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
            $this->_settings->prioritySkip = array();
            $this->_settings->dismissedNotices = $dismissed;
            \ShortPixel\Controller\adminNoticesController::resetAPINotices();
            \ShortPixel\Controller\adminNoticesController::resetQuotaNotices();
        }
        $this->_settings->quotaExceeded = 0;
    }
    public function generateCustomColumn($column_name, $id, $extended = false)
    {
        if ('wp-shortPixel' == $column_name) {
            if (!$this->isProcessable($id)) {
                $renderData['status'] = 'n/a';
                $this->view->renderCustomColumn($id, $renderData, $extended);
                return;
            }
            $fs = \wpSPIO()->filesystem();
            $file = $fs->getAttachedFile($id);
            $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
            $itemHandler = new ShortPixelMetaFacade($id);
            $meta = $itemHandler->getMeta();
            $fileExtension = strtolower($file->getExtension());
            $invalidKey = !$this->_settings->verifiedKey;
            $quotaExceeded = $this->_settings->quotaExceeded;
            $renderData = array("id" => $id, "showActions" => current_user_can('manage_options') || current_user_can('upload_files') || current_user_can('edit_posts'));
            if ($invalidKey) {
                $renderData['status'] = 'invalidKey';
                $this->view->renderCustomColumn($id, $renderData, $extended);
                return;
            } elseif (empty($data)) {
                if ($fileExtension == "pdf") {
                    $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
                    $renderData['message'] = __('PDF not processed.', 'shortpixel-image-optimiser');
                } else {
                    $renderData['status'] = 'n/a';
                }
                $this->view->renderCustomColumn($id, $renderData, $extended);
                return;
            }
            if (!isset($data['ShortPixelImprovement'])) {
                $data['ShortPixelImprovement'] = '';
            }
            if (is_numeric($data['ShortPixelImprovement']) && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing']))) {
                $thumbsOptList = isset($data['ShortPixel']['thumbsOptList']) ? $data['ShortPixel']['thumbsOptList'] : array();
                list($thumbsToOptimizeList, $sizesCount) = $this->getThumbsToOptimize($data, $file->getFullPath());
                $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
                $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
                $renderData['bonus'] = $data['ShortPixelImprovement'] < 5;
                $renderData['backup'] = $this->getBackupFolderAny($file->getFullPath(), $sizesCount ? $data['sizes'] : array());
                $renderData['type'] = isset($data['ShortPixel']['type']) ? $data['ShortPixel']['type'] : '';
                $renderData['invType'] = ShortPixelAPI::getCompressionTypeName($this->getOtherCompressionTypes(ShortPixelAPI::getCompressionTypeCode($renderData['type'])));
                $renderData['thumbsTotal'] = $sizesCount;
                $renderData['thumbsOpt'] = isset($data['ShortPixel']['thumbsOpt']) ? $data['ShortPixel']['thumbsOpt'] : $sizesCount;
                $renderData['thumbsToOptimize'] = is_array($thumbsToOptimizeList) ? count($thumbsToOptimizeList) : 0;
                $renderData['thumbsToOptimizeList'] = $thumbsToOptimizeList;
                $renderData['thumbsOptList'] = $thumbsOptList;
                $renderData['excludeSizes'] = isset($data['ShortPixel']['excludeSizes']) ? $data['ShortPixel']['excludeSizes'] : null;
                $renderData['thumbsMissing'] = isset($data['ShortPixel']['thumbsMissing']) ? $data['ShortPixel']['thumbsMissing'] : array();
                $renderData['retinasOpt'] = isset($data['ShortPixel']['retinasOpt']) ? $data['ShortPixel']['retinasOpt'] : null;
                $renderData['exifKept'] = isset($data['ShortPixel']['exifKept']) ? $data['ShortPixel']['exifKept'] : null;
                $renderData['png2jpg'] = isset($data['ShortPixelPng2Jpg']) ? $data['ShortPixelPng2Jpg'] : 0;
                $renderData['date'] = isset($data['ShortPixel']['date']) ? $data['ShortPixel']['date'] : null;
                $renderData['quotaExceeded'] = $quotaExceeded;
                $webP = 0;
                $avif = 0;
                if ($extended) {
                    if (file_exists(dirname($file->getFullPath()) . '/' . ShortPixelAPI::MB_basename($file->getFullPath(), '.' . $fileExtension) . '.webp')) {
                        $webP++;
                    } elseif (file_exists($file->getFullPath() . '.webp')) {
                        $webP++;
                    }
                    if (file_exists(dirname($file->getFullPath()) . '/' . ShortPixelAPI::MB_basename($file->getFullPath(), '.' . $fileExtension) . '.avif')) {
                        $avif++;
                    }
                    if (isset($data['sizes'])) {
                        foreach ($data['sizes'] as $key => $size) {
                            if (strpos($key, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) {
                                continue;
                            }
                            $sizeName = $size['file'];
                            if (file_exists(dirname($file->getFullPath()) . '/' . ShortPixelAPI::MB_basename($sizeName, '.' . $fileExtension) . '.webp')) {
                                $webP++;
                            } elseif (file_exists(dirname($file->getFullPath()) . '/' . $sizeName . '.webp')) {
                                $webP++;
                            }
                            if (file_exists(dirname($file->getFullPath()) . '/' . ShortPixelAPI::MB_basename($sizeName, '.' . $fileExtension) . '.avif')) {
                                $avif++;
                            }
                        }
                    }
                }
                $renderData['webpCount'] = $webP;
                $renderData['avifCount'] = $avif;
            } elseif (trim(strip_tags($data['ShortPixelImprovement'])) == __("Cannot write optimized file", 'shortpixel-image-optimiser')) {
                $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'retry';
                $renderData['message'] = __("Cannot write optimized file", 'shortpixel-image-optimiser') . " - <a href='https://shortpixel.com/faq#cannot-write-optimized-file' target='_blank'>" . __("Why?", 'shortpixel-image-optimiser') . "</a>";
            } elseif (strlen(trim(strip_tags($data['ShortPixelImprovement']))) > 0) {
                $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'retry';
                $renderData['message'] = $data['ShortPixelImprovement'];
                if (strpos($renderData['message'], __('The file(s) do not exist on disk: ', 'shortpixel-image-optimiser')) !== false) {
                    $renderData['cleanup'] = true;
                }
            } elseif (isset($data['ShortPixel']['NoFileOnDisk'])) {
                $renderData['status'] = 'notFound';
                $renderData['message'] = __('Image does not exist', 'shortpixel-image-optimiser');
            } elseif (isset($data['ShortPixel']['WaitingProcessing'])) {
                $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'waiting';
                $renderData['message'] = "<img src=\"" . plugins_url('res/img/loading.gif', SHORTPIXEL_PLUGIN_FILE) . "\" class='sp-loading-small'>&nbsp;" . __("Image waiting to be processed.", 'shortpixel-image-optimiser');
                if ($this->_settings->autoMediaLibrary && !$quotaExceeded && ($id > $this->prioQ->getFlagBulkId() || !$this->prioQ->bulkRunning())) {
                    $this->prioQ->unskip($id);
                    $this->prioQ->push($id);
                }
            } else {
                $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
                $sizes = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
                $renderData['thumbsTotal'] = $sizes;
                $renderData['message'] = ($fileExtension == "pdf" ? 'PDF' : __('Image', 'shortpixel-image-optimiser')) . __(' not processed.', 'shortpixel-image-optimiser') . ' (<a href="https://shortpixel.com/image-compression-test?site-url=' . urlencode(ShortPixelMetaFacade::safeGetAttachmentUrl($id)) . '" target="_blank">' . __('Test&nbsp;for&nbsp;free', 'shortpixel-image-optimiser') . '</a>)';
            }
            $this->view->renderCustomColumn($id, $renderData, $extended);
        }
    }
    function getThumbsToOptimize($data, $filepath)
    {
        return WpShortPixelMediaLbraryAdapter::getThumbsToOptimize($data, $filepath);
    }
    function columnRegisterSortable($columns)
    {
        $columns['wp-shortPixel'] = 'ShortPixel Compression';
        return $columns;
    }
    function columnOrderFilterBy($vars)
    {
        if (isset($vars['orderby']) && 'ShortPixel Compression' == $vars['orderby']) {
            $vars = array_merge($vars, array('meta_key' => '_shortpixel_status', 'orderby' => 'meta_value_num'));
        }
        if ('upload.php' == $GLOBALS['pagenow'] && isset($_GET['shortpixel_status'])) {
            $status = sanitize_text_field($_GET['shortpixel_status']);
            $metaKey = '_shortpixel_status';
            if ($status == 'all') {
                return $vars;
            }
            switch ($status) {
                case "opt":
                    $status = ShortPixelMeta::FILE_STATUS_SUCCESS;
                    $metaCompare = ">=";
                    break;
                case "unopt":
                    $status = ShortPixelMeta::FILE_STATUS_UNPROCESSED;
                    $metaCompare = "NOT EXISTS";
                    break;
                case "pending":
                    $status = ShortPixelMeta::FILE_STATUS_PENDING;
                    $metaCompare = "=";
                    break;
                case "error":
                    $status = -1;
                    $metaCompare = "<=";
                    break;
            }
            $vars = array_merge($vars, array('meta_query' => array(array('key' => $metaKey, 'value' => $status, 'compare' => $metaCompare))));
        }
        return $vars;
    }
    function mediaAddFilterDropdown()
    {
        $scr = get_current_screen();
        if ($scr->base !== 'upload') {
            return;
        }
        $status = filter_input(INPUT_GET, 'shortpixel_status', FILTER_SANITIZE_STRING);
        $options = array('all' => __('All Images', 'shortpixel-image-optimiser'), 'opt' => __('Optimized', 'shortpixel-image-optimiser'), 'unopt' => __('Unoptimized', 'shortpixel-image-optimiser'));
        echo "<select name='shortpixel_status' id='shortpixel_status'>\n";
        foreach ($options as $optname => $optval) {
            $selected = $status == $optname ? 'selected' : '';
            echo "<option value='" . $optname . "' {$selected}>" . $optval . "</option>\n";
        }
        echo "</select>";
    }
    function optimizationPercentIfPng2Jpg($meta)
    {
        $png2jpgPercent = isset($meta['ShortPixelPng2Jpg']['optimizationPercent']) ? $meta['ShortPixelPng2Jpg']['optimizationPercent'] : 0;
        return number_format(100.0 - (100.0 - $png2jpgPercent) * (100.0 - $meta['ShortPixelImprovement']) / 100.0, 2);
    }
    function shortpixelInfoBox()
    {
        if (get_post_type() == 'attachment') {
            add_meta_box('shortpixel_info_box', __('ShortPixel Info', 'shortpixel-image-optimiser'), array(&$this, 'shortpixelInfoBoxContent'), null, 'side');
        }
    }
    function shortpixelInfoBoxContent($post)
    {
        $this->generateCustomColumn('wp-shortPixel', $post->ID, true);
    }
    public function onDeleteImage($post_id)
    {
        Log::addDebug('onDeleteImage - Image Removal Detected ' . $post_id);
        $result = null;
        try {
            $imageObj = new ImageModel();
            $imageObj->setbyPostID($post_id);
            $result = $imageObj->delete();
        } catch (Exception $e) {
            Log::addError('OndeleteImage triggered an error. ' . $e->getMessage(), $e);
        }
        return $result;
    }
    public function deleteBackupsAndWebPs($paths)
    {
        if (apply_filters('shortpixel_skip_delete_backups_and_webps', false, $paths)) {
            return;
        }
        $fs = \wpSPIO()->filesystem();
        $backupFolder = trailingslashit($this->getBackupFolder($paths[0]));
        Log::addDebug('Removing from Backup Folder - ' . $backupFolder);
        foreach ($paths as $path) {
            $pos = strrpos($path, ".");
            $pathFile = $fs->getFile($path);
            if ($pos !== false) {
                $file = $fs->getFile(substr($path, 0, $pos) . ".webp");
                $file->delete();
                $file = $fs->getFile(substr($path, 0, $pos) . "@2x.webp");
                $file->delete();
                $file = $fs->getFile($path . '.webp');
                $file->delete();
                $file = $fs->getFile($path . '.@2xwebp');
                $file->delete();
                $file = $fs->getFile($path . '.avif');
                if ($file->exists()) {
                    $file->delete();
                }
            }
            $fileName = $pathFile->getFileName();
            $extension = $pathFile->getExtension();
            $backupFile = $fs->getFile($backupFolder . $fileName);
            if ($backupFile->exists()) {
                $backupFile->delete();
            }
            $backupFile = $fs->getFile($backupFolder . preg_replace("/\\." . $extension . "\$/i", '@2x.' . $extension, $fileName));
            if ($backupFile->exists() && $backupFile->is_file()) {
                $backupFile->delete();
            }
        }
    }
    public function columns($defaults)
    {
        $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
        if (current_user_can('manage_options')) {
            $defaults['wp-shortPixel'] .= '&nbsp;<a href="options-general.php?page=wp-shortpixel-settings&part=stats" title="' . __('ShortPixel Statistics', 'shortpixel-image-optimiser') . '"><span class="dashicons dashicons-dashboard"></span></a>';
        }
        return $defaults;
    }
    public function generatePluginLinks($links)
    {
        $in = '<a href="options-general.php?page=wp-shortpixel-settings">Settings</a>';
        array_unshift($links, $in);
        return $links;
    }
    public static function formatBytes($bytes, $precision = 2)
    {
        Log::addDebug('Deprecated function called: formatBytes');
        return \ShortPixelTools::formatBytes($bytes, $precision);
    }
    public function isProcessable($ID, $excludeExtensions = array())
    {
        $excludePatterns = $this->_settings->excludePatterns;
        return self::_isProcessable($ID, $excludeExtensions, $excludePatterns);
    }
    public function isProcessablePath($path, $excludeExtensions = array())
    {
        $excludePatterns = $this->_settings->excludePatterns;
        return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
    }
    public static function _isProcessable($ID, $excludeExtensions = array(), $excludePatterns = array(), $meta = false)
    {
        $file = \wpSPIO()->filesystem()->getAttachedFile($ID);
        $path = $file->getFullPath();
        if (isset($excludePatterns) && is_array($excludePatterns)) {
            foreach ($excludePatterns as $excludePattern) {
                $type = $excludePattern["type"];
                if ($type == "size") {
                    $meta = $meta ? $meta : wp_get_attachment_metadata($ID);
                    if (isset($meta["width"]) && isset($meta["height"]) && self::isProcessableSize($meta["width"], $meta["height"], $excludePattern["value"]) === false) {
                        return false;
                    }
                }
            }
        }
        return $path ? self::_isProcessablePath($path, $excludeExtensions, $excludePatterns) : false;
    }
    public static function _isProcessablePath($path, $excludeExtensions = array(), $excludePatterns = array())
    {
        $pathParts = pathinfo($path);
        $ext = isset($pathParts['extension']) ? $pathParts['extension'] : false;
        if ($ext && in_array(strtolower($ext), array_diff(self::$PROCESSABLE_EXTENSIONS, $excludeExtensions))) {
            if (!$excludePatterns || !is_array($excludePatterns)) {
                return true;
            }
            foreach ($excludePatterns as $item) {
                $type = trim($item["type"]);
                if (in_array($type, array("name", "path"))) {
                    $pattern = trim($item["value"]);
                    $target = $type == "name" ? ShortPixelAPI::MB_basename($path) : $path;
                    if (self::matchExcludePattern($target, $pattern)) {
                        return false;
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }
    public static function isProcessableSize($width, $height, $excludePattern)
    {
        $ranges = preg_split("/(x|×)/", $excludePattern);
        $widthBounds = explode("-", $ranges[0]);
        if (!isset($widthBounds[1])) {
            $widthBounds[1] = $widthBounds[0];
        }
        $heightBounds = isset($ranges[1]) ? explode("-", $ranges[1]) : false;
        if (!isset($heightBounds[1])) {
            $heightBounds[1] = $heightBounds[0];
        }
        if ($width >= 0 + $widthBounds[0] && $width <= 0 + $widthBounds[1] && ($heightBounds === false || $height >= 0 + $heightBounds[0] && $height <= 0 + $heightBounds[1])) {
            return false;
        }
        return true;
    }
    public static function matchExcludePattern($target, $pattern)
    {
        if (strlen($pattern) == 0) {
            return false;
        }
        $first = substr($pattern, 0, 1);
        if ($first == '/') {
            if (@preg_match($pattern, false) !== false) {
                $m = preg_match($pattern, $target);
                if ($m !== false && $m > 0) {
                    return true;
                }
            }
        } else {
            if (strpos($target, $pattern) !== false) {
                return true;
            }
        }
        return false;
    }
    public function getURLsAndPATHs($itemHandler, $meta = NULL, $onlyThumbs = false)
    {
        return $itemHandler->getURLsAndPATHs($this->_settings->processThumbnails, $onlyThumbs, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
    }
    public static function deleteDir($dirPath)
    {
        if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
            $dirPath .= '/';
        }
        $files = glob($dirPath . '*', GLOB_MARK);
        foreach ($files as $file) {
            if (is_dir($file)) {
                self::deleteDir($file);
                @rmdir($file);
            } else {
                @unlink($file);
            }
        }
    }
    public static function folderSize($path)
    {
        $total_size = 0;
        $fs = wpSPIO()->filesystem();
        $dir = $fs->getDirectory($path);
        $files = $subdirs = array();
        if ($dir->exists()) {
            $files = $dir->getFiles();
            $subdirs = $dir->getSubDirectories();
        } else {
            return $total_size;
        }
        if ($files) {
            foreach ($files as $file) {
                $total_size += $file->getFileSize();
            }
        }
        if ($subdirs) {
            foreach ($subdirs as $dir) {
                $total_size += self::folderSize($dir->getPath());
            }
        }
        return $total_size;
        return $total_size;
    }
    public function migrateBackupFolder()
    {
        $oldBackupFolder = WP_CONTENT_DIR . '/' . SHORTPIXEL_BACKUP;
        if (file_exists($oldBackupFolder)) {
            if (!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
                if (!ShortPixelFolder::createBackUpFolder()) {
                    return;
                }
            }
            $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
            foreach ($scannedDirectory as $file) {
                @rename($oldBackupFolder . '/' . $file, SHORTPIXEL_BACKUP_FOLDER . '/' . $file);
            }
            $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
            if (empty($scannedDirectory)) {
                @rmdir($oldBackupFolder);
            }
        }
        if (!is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . SHORTPIXEL_UPLOADS_NAME) && !is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR))) {
            @rename(SHORTPIXEL_BACKUP_FOLDER, SHORTPIXEL_BACKUP_FOLDER . "_tmp");
            ShortPixelFolder::createBackUpFolder();
            @rename(SHORTPIXEL_BACKUP_FOLDER . "_tmp", SHORTPIXEL_BACKUP_FOLDER . '/' . SHORTPIXEL_UPLOADS_NAME);
            if (!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
                @rename(SHORTPIXEL_BACKUP_FOLDER . "_tmp", SHORTPIXEL_BACKUP_FOLDER);
            }
        }
        if (!is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR))) {
            @rename(SHORTPIXEL_BACKUP_FOLDER, SHORTPIXEL_BACKUP_FOLDER . "_tmp");
            ShortPixelFolder::createBackUpFolder();
            @rename(SHORTPIXEL_BACKUP_FOLDER . "_tmp", SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR));
            if (!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
                @rename(SHORTPIXEL_BACKUP_FOLDER . "_tmp", SHORTPIXEL_BACKUP_FOLDER);
            }
        }
        if (!file_exists(trailingslashit(SHORTPIXEL_BACKUP_FOLDER) . '.htaccess')) {
            ShortPixelFolder::protectDirectoryListing(SHORTPIXEL_BACKUP_FOLDER);
        }
        return;
    }
    function getAllThumbnailSizes()
    {
        global $_wp_additional_image_sizes;
        $sizes_names = get_intermediate_image_sizes();
        $sizes = array();
        foreach ($sizes_names as $size) {
            $sizes[$size]['width'] = intval(get_option("{$size}_size_w"));
            $sizes[$size]['height'] = intval(get_option("{$size}_size_h"));
            $sizes[$size]['crop'] = get_option("{$size}_crop") ? get_option("{$size}_crop") : false;
        }
        if (function_exists('wp_get_additional_image_sizes')) {
            $sizes = array_merge($sizes, wp_get_additional_image_sizes());
        } elseif (is_array($_wp_additional_image_sizes)) {
            $sizes = array_merge($sizes, $_wp_additional_image_sizes);
        }
        $sizes = apply_filters('shortpixel/settings/image_sizes', $sizes);
        return $sizes;
    }
    function getMaxIntermediateImageSize()
    {
        global $_wp_additional_image_sizes;
        $width = 0;
        $height = 0;
        $get_intermediate_image_sizes = get_intermediate_image_sizes();
        if (is_array($get_intermediate_image_sizes)) {
            foreach ($get_intermediate_image_sizes as $_size) {
                if (in_array($_size, array('thumbnail', 'medium', 'large'))) {
                    $width = max($width, get_option($_size . '_size_w'));
                    $height = max($height, get_option($_size . '_size_h'));
                } elseif (isset($_wp_additional_image_sizes[$_size])) {
                    $width = max($width, $_wp_additional_image_sizes[$_size]['width']);
                    $height = max($height, $_wp_additional_image_sizes[$_size]['height']);
                }
            }
        }
        return array('width' => max(100, $width), 'height' => max(100, $height));
    }
    public function getOtherCompressionTypes($compressionType = false)
    {
        return array_values(array_diff(array(0, 1, 2), array(0 + $compressionType)));
    }
    public function validateFeedback($params)
    {
        if (isset($params['keep-settings'])) {
            $this->_settings->removeSettingsOnDeletePlugin = 1 - $params['keep-settings'];
        }
        return $params;
    }
    public function getApiKey()
    {
        return $this->_settings->apiKey;
    }
    public function getPrioQ()
    {
        return $this->prioQ;
    }
    public function backupImages()
    {
        return $this->_settings->backupImages;
    }
    public function processThumbnails()
    {
        return $this->_settings->processThumbnails;
    }
    public function getCMYKtoRGBconversion()
    {
        return $this->_settings->CMYKtoRGBconversion;
    }
    public function getSettings()
    {
        return $this->_settings;
    }
    public function getResizeImages()
    {
        return $this->_settings->resizeImages;
    }
    public function getResizeWidth()
    {
        return $this->_settings->resizeWidth;
    }
    public function getResizeHeight()
    {
        return $this->_settings->resizeHeight;
    }
    public static function getAffiliateSufix()
    {
        Log::addDebug('Function call - getAffiliateSufix should be removed');
        return "";
    }
    public function getVerifiedKey()
    {
        return $this->_settings->verifiedKey;
    }
    public function getCompressionType()
    {
        return $this->_settings->compressionType;
    }
    public function getSpMetaDao()
    {
        return $this->spMetaDao;
    }
    public function fetch_cloudflare_api_email()
    {
        return $this->_settings->cloudflareEmail;
    }
    public function fetch_cloudflare_api_key()
    {
        return $this->_settings->cloudflareAuthKey;
    }
    public function fetch_cloudflare_api_zoneid()
    {
        return $this->_settings->cloudflareZoneID;
    }
}