| @@ -78,8 +78,66 @@ | ||
| 78 | 78 | return $files; |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | 81 | /** |
| 82 | + * Get all optimized file IDs that have a backup file available for restore. | |
| 83 | + * | |
| 84 | + * @return array A list of file IDs. | |
| 85 | + */ | |
| 86 | + public function get_optimized_media_ids(): array { | |
| 87 | + global $wpdb; | |
| 88 | + | |
| 89 | + $this->set_no_time_limit(); | |
| 90 | + | |
| 91 | + $files_table = Imagify_Files_DB::get_instance()->get_table_name(); | |
| 92 | + $folders_table = Imagify_Folders_DB::get_instance()->get_table_name(); | |
| 93 | + $files = $wpdb->get_results( | |
| 94 | + $wpdb->prepare( // WPCS: unprepared SQL ok. | |
| 95 | + " | |
| 96 | + SELECT fi.file_id, fi.path | |
| 97 | + FROM $files_table AS fi | |
| 98 | + INNER JOIN $folders_table AS fo | |
| 99 | + ON ( fi.folder_id = fo.folder_id ) | |
| 100 | + WHERE | |
| 101 | + fi.status IN ( 'success', 'already_optimized' ) | |
| 102 | + ORDER BY fi.file_id DESC | |
| 103 | + LIMIT 0, %d", | |
| 104 | + imagify_get_unoptimized_attachment_limit() | |
| 105 | + ) | |
| 106 | + ); | |
| 107 | + | |
| 108 | + $wpdb->flush(); | |
| 109 | + unset( $files_table, $folders_table ); | |
| 110 | + | |
| 111 | + if ( ! $files ) { | |
| 112 | + return []; | |
| 113 | + } | |
| 114 | + | |
| 115 | + $data = []; | |
| 116 | + | |
| 117 | + foreach ( $files as $file ) { | |
| 118 | + $file_id = absint( $file->file_id ); | |
| 119 | + | |
| 120 | + if ( empty( $file->path ) ) { | |
| 121 | + // Problem. | |
| 122 | + continue; | |
| 123 | + } | |
| 124 | + | |
| 125 | + $file_path = Imagify_Files_Scan::remove_placeholder( $file->path ); | |
| 126 | + $backup_path = Imagify_Custom_Folders::get_file_backup_path( $file_path ); | |
| 127 | + | |
| 128 | + if ( ! $this->filesystem->exists( $backup_path ) ) { | |
| 129 | + // No backup, cannot restore. | |
| 130 | + continue; | |
| 131 | + } | |
| 132 | + | |
| 133 | + $data[] = $file_id; | |
| 134 | + } | |
| 135 | + | |
| 136 | + return $data; | |
| 137 | + } | |
| 138 | + | |
| 139 | + /** | |
| 82 | 140 | * Get ids of all optimized media without Next gen versions. |
| 83 | 141 | * |
| 84 | 142 | * @since 2.2 |
| 85 | 143 | * |
| @@ -113,9 +171,10 @@ | ||
| 113 | 171 | } |
| 114 | 172 | if ( ! isset( $mime ) && empty( $mime ) ) { |
| 115 | 173 | $mime = 'image/webp'; |
| 116 | 174 | } |
| 117 | - $mime_types = str_replace( ",'" . $mime . "'", '', $mime_types ); | |
| 175 | + $mime = trim( $mime ); | |
| 176 | + $mime_types = str_replace( [ ", '" . $mime . "'", ",'" . $mime . "'" ], '', $mime_types ); | |
| 118 | 177 | $nextgen_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::' . strtoupper( $format ) . '_SUFFIX' ); |
| 119 | 178 | $files = $wpdb->get_results( |
| 120 | 179 | $wpdb->prepare( // WPCS: unprepared SQL ok. |
| 121 | 180 | " |
| @@ -121,15 +180,25 @@ | ||
| 121 | 180 | " |
| 122 | 181 | SELECT fi.file_id, fi.path |
| 123 | 182 | FROM $files_table as fi |
| 124 | 183 | INNER JOIN $folders_table AS fo |
| 125 | - ON ( fi.folder_id = fo.folder_id ) | |
| 184 | + ON ( fi.folder_id = fo.folder_id AND fo.active = 1 ) | |
| 126 | 185 | WHERE |
| 127 | 186 | fi.mime_type IN ( $mime_types ) |
| 128 | 187 | AND ( fi.status = 'success' OR fi.status = 'already_optimized' ) |
| 129 | - AND ( fi.data NOT LIKE %s OR fi.data IS NULL ) | |
| 188 | + AND fi.data NOT LIKE %s | |
| 189 | + AND fi.data NOT LIKE %s | |
| 130 | 190 | ORDER BY fi.file_id DESC", |
| 131 | - '%' . $wpdb->esc_like( $nextgen_suffix . '";a:4:{s:7:"success";b:1;' ) . '%' | |
| 191 | + '%' . $wpdb->esc_like( $nextgen_suffix . '";a:4:{s:7:"success";b:1;' ) . '%', | |
| 192 | + /** | |
| 193 | + * Second predicate: skip files the API permanently refused to convert (the next-gen file | |
| 194 | + * would be heavier than the original, or the file is already compressed). Those are stored | |
| 195 | + * as `<size><suffix>";a:3:{s:15:"permanent_error";b:1;s:7:"success";b:0;s:5:"error";…`. | |
| 196 | + * Transient failures serialize as a 2-element array without the `permanent_error` key, so | |
| 197 | + * they keep being retried. Matching serialized data with LIKE is fragile: the key order | |
| 198 | + * written in Optimization\Data\CustomFolders must not change. | |
| 199 | + */ | |
| 200 | + '%' . $wpdb->esc_like( $nextgen_suffix . '";a:3:{s:15:"permanent_error";b:1;' ) . '%' | |
| 132 | 201 | ) |
| 133 | 202 | ); |
| 134 | 203 | |
| 135 | 204 | $wpdb->flush(); |
| @@ -155,9 +224,16 @@ | ||
| 155 | 224 | $data['errors']['no_file_path'][] = $file_id; |
| 156 | 225 | continue; |
| 157 | 226 | } |
| 158 | 227 | |
| 159 | - $file_path = Imagify_Files_Scan::remove_placeholder( $file->path ); | |
| 228 | + $file_path = Imagify_Files_Scan::remove_placeholder( $file->path ); | |
| 229 | + | |
| 230 | + // Skip files whose extension already matches the target format | |
| 231 | + // (e.g. a .webp file stored with incorrect post_mime_type). | |
| 232 | + if ( strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) ) === $format ) { | |
| 233 | + continue; | |
| 234 | + } | |
| 235 | + | |
| 160 | 236 | $backup_path = Imagify_Custom_Folders::get_file_backup_path( $file_path ); |
| 161 | 237 | |
| 162 | 238 | if ( ! $this->filesystem->exists( $backup_path ) ) { |
| 163 | 239 | // No backup, no WebP. |