Changeset 3498073
- Timestamp:
- 04/03/2026 09:34:14 AM (15 hours ago)
- Location:
- podcast-player
- Files:
-
- 189 added
- 4 edited
-
tags/8.0.3 (added)
-
tags/8.0.3/LICENSE (added)
-
tags/8.0.3/README.txt (added)
-
tags/8.0.3/backend (added)
-
tags/8.0.3/backend/admin (added)
-
tags/8.0.3/backend/admin/admin-icons.svg (added)
-
tags/8.0.3/backend/admin/class-options.php (added)
-
tags/8.0.3/backend/admin/class-shortcodegen.php (added)
-
tags/8.0.3/backend/admin/templates (added)
-
tags/8.0.3/backend/admin/templates/help.php (added)
-
tags/8.0.3/backend/admin/templates/home.php (added)
-
tags/8.0.3/backend/admin/templates/main.php (added)
-
tags/8.0.3/backend/admin/templates/products.php (added)
-
tags/8.0.3/backend/admin/templates/settings.php (added)
-
tags/8.0.3/backend/admin/templates/shortcode.php (added)
-
tags/8.0.3/backend/admin/templates/sidebar.php (added)
-
tags/8.0.3/backend/admin/templates/toolkit (added)
-
tags/8.0.3/backend/admin/templates/toolkit.php (added)
-
tags/8.0.3/backend/admin/templates/toolkit/feed-review.php (added)
-
tags/8.0.3/backend/admin/templates/toolkit/feed-update.php (added)
-
tags/8.0.3/backend/class-register.php (added)
-
tags/8.0.3/backend/css (added)
-
tags/8.0.3/backend/css/admin-options-rtl.css (added)
-
tags/8.0.3/backend/css/admin-options.css (added)
-
tags/8.0.3/backend/css/partials (added)
-
tags/8.0.3/backend/css/podcast-player-admin-rtl.css (added)
-
tags/8.0.3/backend/css/podcast-player-admin.css (added)
-
tags/8.0.3/backend/inc (added)
-
tags/8.0.3/backend/inc/class-background-tasks.php (added)
-
tags/8.0.3/backend/inc/class-block.php (added)
-
tags/8.0.3/backend/inc/class-dashboard-widget.php (added)
-
tags/8.0.3/backend/inc/class-loader.php (added)
-
tags/8.0.3/backend/inc/class-misc.php (added)
-
tags/8.0.3/backend/inc/class-shortcode.php (added)
-
tags/8.0.3/backend/inc/class-widget.php (added)
-
tags/8.0.3/backend/js (added)
-
tags/8.0.3/backend/js/admin-options.build.js (added)
-
tags/8.0.3/backend/js/admin-options.js (added)
-
tags/8.0.3/backend/js/admin.build.js (added)
-
tags/8.0.3/backend/js/admin.js (added)
-
tags/8.0.3/backend/js/blocks.build.js (added)
-
tags/8.0.3/backend/js/blocks.js (added)
-
tags/8.0.3/backend/js/partials (added)
-
tags/8.0.3/backend/js/partials/blocks (added)
-
tags/8.0.3/backend/js/partials/blocks/ecc.js (added)
-
tags/8.0.3/backend/js/partials/blocks/edit.js (added)
-
tags/8.0.3/backend/js/partials/blocks/index.js (added)
-
tags/8.0.3/backend/js/partials/blocks/mcc.js (added)
-
tags/8.0.3/backend/js/partials/options (added)
-
tags/8.0.3/backend/js/partials/options/changeDetect.js (added)
-
tags/8.0.3/backend/js/partials/options/colorpicker.js (added)
-
tags/8.0.3/backend/js/partials/options/dom.js (added)
-
tags/8.0.3/backend/js/partials/options/feededit.js (added)
-
tags/8.0.3/backend/js/partials/options/feedmigrate.js (added)
-
tags/8.0.3/backend/js/partials/options/fetchMethod.js (added)
-
tags/8.0.3/backend/js/partials/options/reviews.js (added)
-
tags/8.0.3/backend/js/partials/options/shortgen.js (added)
-
tags/8.0.3/backend/js/partials/options/variables.js (added)
-
tags/8.0.3/backend/js/partials/widgets (added)
-
tags/8.0.3/backend/js/partials/widgets/changeDetect.js (added)
-
tags/8.0.3/backend/js/partials/widgets/colorpicker.js (added)
-
tags/8.0.3/backend/js/partials/widgets/fetchFilters.js (added)
-
tags/8.0.3/backend/js/partials/widgets/fetchMethod.js (added)
-
tags/8.0.3/backend/js/partials/widgets/imageupload.js (added)
-
tags/8.0.3/backend/js/partials/widgets/variables.js (added)
-
tags/8.0.3/backend/partials (added)
-
tags/8.0.3/backend/partials/pp-notifications.php (added)
-
tags/8.0.3/credits.txt (added)
-
tags/8.0.3/frontend (added)
-
tags/8.0.3/frontend/class-register.php (added)
-
tags/8.0.3/frontend/css (added)
-
tags/8.0.3/frontend/css/podcast-player-editor-rtl.css (added)
-
tags/8.0.3/frontend/css/podcast-player-editor.css (added)
-
tags/8.0.3/frontend/css/podcast-player-public-rtl.css (added)
-
tags/8.0.3/frontend/css/podcast-player-public.css (added)
-
tags/8.0.3/frontend/images (added)
-
tags/8.0.3/frontend/images/icons.svg (added)
-
tags/8.0.3/frontend/inc (added)
-
tags/8.0.3/frontend/inc/class-display.php (added)
-
tags/8.0.3/frontend/inc/class-feed.php (added)
-
tags/8.0.3/frontend/inc/class-general.php (added)
-
tags/8.0.3/frontend/inc/class-icon-loader.php (added)
-
tags/8.0.3/frontend/inc/class-icons-extend.php (added)
-
tags/8.0.3/frontend/inc/class-instance-counter.php (added)
-
tags/8.0.3/frontend/inc/class-loader.php (added)
-
tags/8.0.3/frontend/inc/class-render.php (added)
-
tags/8.0.3/frontend/js (added)
-
tags/8.0.3/frontend/js/mmerrorfix.js (added)
-
tags/8.0.3/frontend/js/partials (added)
-
tags/8.0.3/frontend/js/partials/editor.js (added)
-
tags/8.0.3/frontend/js/partials/header.js (added)
-
tags/8.0.3/frontend/js/partials/hooks.js (added)
-
tags/8.0.3/frontend/js/partials/load.js (added)
-
tags/8.0.3/frontend/js/partials/main.js (added)
-
tags/8.0.3/frontend/js/partials/media.js (added)
-
tags/8.0.3/frontend/js/partials/mediaelem.js (added)
-
tags/8.0.3/frontend/js/partials/modal.js (added)
-
tags/8.0.3/frontend/js/partials/play.js (added)
-
tags/8.0.3/frontend/js/partials/podcast.js (added)
-
tags/8.0.3/frontend/js/partials/pplib.js (added)
-
tags/8.0.3/frontend/js/partials/search.js (added)
-
tags/8.0.3/frontend/js/partials/variables.js (added)
-
tags/8.0.3/frontend/js/ppeditor.build.js (added)
-
tags/8.0.3/frontend/js/ppeditor.js (added)
-
tags/8.0.3/frontend/js/public.build.js (added)
-
tags/8.0.3/frontend/js/public.js (added)
-
tags/8.0.3/frontend/js/templates (added)
-
tags/8.0.3/frontend/js/templates/audioplayer.js (added)
-
tags/8.0.3/frontend/templates (added)
-
tags/8.0.3/frontend/templates/episode (added)
-
tags/8.0.3/frontend/templates/episode/featured.php (added)
-
tags/8.0.3/frontend/templates/episode/single.php (added)
-
tags/8.0.3/frontend/templates/header (added)
-
tags/8.0.3/frontend/templates/header/image.php (added)
-
tags/8.0.3/frontend/templates/header/menu (added)
-
tags/8.0.3/frontend/templates/header/menu/default-links.php (added)
-
tags/8.0.3/frontend/templates/header/menu/podcast-menu.php (added)
-
tags/8.0.3/frontend/templates/header/subscribe-buttons.php (added)
-
tags/8.0.3/frontend/templates/list (added)
-
tags/8.0.3/frontend/templates/list/entry-modern.php (added)
-
tags/8.0.3/frontend/templates/list/entry.php (added)
-
tags/8.0.3/frontend/templates/list/search-field.php (added)
-
tags/8.0.3/frontend/templates/misc (added)
-
tags/8.0.3/frontend/templates/misc/buttons (added)
-
tags/8.0.3/frontend/templates/misc/buttons/clear-search.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/launch.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/list.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/load-more.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/next.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/playpause.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/pod-menu.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/previous.php (added)
-
tags/8.0.3/frontend/templates/misc/buttons/single-close.php (added)
-
tags/8.0.3/frontend/templates/misc/js (added)
-
tags/8.0.3/frontend/templates/misc/js/addcontrols.php (added)
-
tags/8.0.3/frontend/templates/misc/js/auxmodal.php (added)
-
tags/8.0.3/frontend/templates/misc/js/controls.php (added)
-
tags/8.0.3/frontend/templates/misc/js/vshare.php (added)
-
tags/8.0.3/frontend/templates/subscribe (added)
-
tags/8.0.3/frontend/templates/subscribe/amazon.php (added)
-
tags/8.0.3/frontend/templates/subscribe/apple.php (added)
-
tags/8.0.3/frontend/templates/subscribe/breaker.php (added)
-
tags/8.0.3/frontend/templates/subscribe/bullhorn.php (added)
-
tags/8.0.3/frontend/templates/subscribe/castbox.php (added)
-
tags/8.0.3/frontend/templates/subscribe/castro.php (added)
-
tags/8.0.3/frontend/templates/subscribe/deezer.php (added)
-
tags/8.0.3/frontend/templates/subscribe/external.php (added)
-
tags/8.0.3/frontend/templates/subscribe/google.php (added)
-
tags/8.0.3/frontend/templates/subscribe/iheart.php (added)
-
tags/8.0.3/frontend/templates/subscribe/overcast.php (added)
-
tags/8.0.3/frontend/templates/subscribe/pandora.php (added)
-
tags/8.0.3/frontend/templates/subscribe/playerfm.php (added)
-
tags/8.0.3/frontend/templates/subscribe/pocketcasts.php (added)
-
tags/8.0.3/frontend/templates/subscribe/podbean.php (added)
-
tags/8.0.3/frontend/templates/subscribe/podcastaddict.php (added)
-
tags/8.0.3/frontend/templates/subscribe/podchaser.php (added)
-
tags/8.0.3/frontend/templates/subscribe/radiopublic.php (added)
-
tags/8.0.3/frontend/templates/subscribe/soundcloud.php (added)
-
tags/8.0.3/frontend/templates/subscribe/spotify.php (added)
-
tags/8.0.3/frontend/templates/subscribe/stitcher.php (added)
-
tags/8.0.3/frontend/templates/subscribe/tunein.php (added)
-
tags/8.0.3/frontend/templates/subscribe/youtube.php (added)
-
tags/8.0.3/helper (added)
-
tags/8.0.3/helper/core (added)
-
tags/8.0.3/helper/core/class-background-jobs.php (added)
-
tags/8.0.3/helper/core/class-singleton.php (added)
-
tags/8.0.3/helper/feed (added)
-
tags/8.0.3/helper/feed/class-add-external-link-attr.php (added)
-
tags/8.0.3/helper/feed/class-fetch-feed.php (added)
-
tags/8.0.3/helper/feed/class-get-feed.php (added)
-
tags/8.0.3/helper/feed/class-modify-feed-data.php (added)
-
tags/8.0.3/helper/feed/class-prepare-front-new.php (added)
-
tags/8.0.3/helper/feed/class-prepare-storage.php (added)
-
tags/8.0.3/helper/functions (added)
-
tags/8.0.3/helper/functions/class-country-codes.php (added)
-
tags/8.0.3/helper/functions/class-date-parser.php (added)
-
tags/8.0.3/helper/functions/class-getters.php (added)
-
tags/8.0.3/helper/functions/class-markup.php (added)
-
tags/8.0.3/helper/functions/class-utility.php (added)
-
tags/8.0.3/helper/functions/class-validation.php (added)
-
tags/8.0.3/helper/store (added)
-
tags/8.0.3/helper/store/class-feeddata.php (added)
-
tags/8.0.3/helper/store/class-itemdata.php (added)
-
tags/8.0.3/helper/store/class-storageregister.php (added)
-
tags/8.0.3/helper/store/class-storebase.php (added)
-
tags/8.0.3/helper/store/class-storemanager.php (added)
-
tags/8.0.3/lang (added)
-
tags/8.0.3/lang/podcast-player.pot (added)
-
tags/8.0.3/podcast-player.php (added)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/backend/inc/class-background-tasks.php (modified) (4 diffs)
-
trunk/helper/core/class-background-jobs.php (modified) (5 diffs)
-
trunk/podcast-player.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
podcast-player/trunk/README.txt
r3484403 r3498073 5 5 Tested up to: 6.9 6 6 Requires PHP: 5.6 7 Stable tag: 8.0. 27 Stable tag: 8.0.3 8 8 License: GPLv3 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 107 107 108 108 == Changelog == 109 = 8.0.3 = 110 * Bug Fix: Image download background jobs fix. 111 109 112 = 8.0.2 = 110 113 * Modify: Code cleanup and improvements -
podcast-player/trunk/backend/inc/class-background-tasks.php
r3462195 r3498073 32 32 */ 33 33 const MAX_TOTAL_IMAGE_DOWNLOADS = 1000; 34 35 /** 36 * Short-lived image lock TTL to reduce duplicate sideloads while a request is still processing. 37 */ 38 const IMAGE_DOWNLOAD_LOCK_TTL = 900; 34 39 35 40 /** … … 121 126 // Backfill normalized hash for future de-dupes if missing. 122 127 if ( $norm_hash ) { 123 add_post_meta( $matched_post_id, 'pp_featured_key_norm', $norm_hash, true);128 update_post_meta( $matched_post_id, 'pp_featured_key_norm', $norm_hash ); 124 129 } 125 130 $completed[ $key ] = array_merge( $item, array( 'post_id' => $matched_post_id ) ); … … 165 170 $completed[ $key ] = array_merge( $item, array( 'post_id' => false ) ); 166 171 } else { 172 $lookup = $this->get_image_lookup_data( $image_url ); 173 $existing_attachment_id = $this->find_existing_attachment_for_image( $lookup ); 174 if ( $existing_attachment_id ) { 175 $this->persist_image_lookup_meta( $existing_attachment_id, $lookup ); 176 $completed[ $key ] = array_merge( $item, array( 'post_id' => $existing_attachment_id ) ); 177 continue; 178 } 179 180 $lock_key = $this->get_image_lock_key( $lookup ); 181 if ( ! $this->acquire_image_download_lock( $lock_key ) ) { 182 // Another request is already working on this exact image. Leave it pending. 183 continue; 184 } 185 186 try { 187 $existing_attachment_id = $this->find_existing_attachment_for_image( $lookup ); 188 if ( $existing_attachment_id ) { 189 $this->persist_image_lookup_meta( $existing_attachment_id, $lookup ); 190 $completed[ $key ] = array_merge( $item, array( 'post_id' => $existing_attachment_id ) ); 191 continue; 192 } 193 167 194 // 1. Download the REAL URL (unchanged!) 168 $tmp = download_url( $image_url ); 169 170 if ( is_wp_error( $tmp ) ) { 171 return ( array( $tmp, false ) ); 195 $tmp = download_url( $image_url ); 196 197 if ( is_wp_error( $tmp ) ) { 198 return ( array( $tmp, false ) ); 199 } 200 201 // 2. Force a correct "filename" for WP regardless of the remote URL 202 // Try using MIME type to get correct extension 203 $headers = wp_remote_head( $image_url ); 204 $mime = wp_remote_retrieve_header( $headers, 'content-type' ); 205 $ext = Get_Fn::get_extension_from_mime( $mime ); 206 $filename = $lookup['filename_stem'] . '.' . $ext; 207 208 // 3. Build array as if it was a file upload 209 $file = array( 210 'name' => $filename, 211 'tmp_name' => $tmp, 212 ); 213 214 // 4. Let WP handle the upload 215 $attachment_id = media_handle_sideload( $file, 0, $title ); 216 217 if ( ! is_wp_error( $attachment_id ) ) { 218 219 // Count successful downloads. 220 $this->increment_image_download_count(); 221 222 $this->persist_image_lookup_meta( $attachment_id, $lookup ); 223 224 // Let's do post_meta verification to see if data is getting saved correctly. 225 $stored = get_post_meta( $attachment_id, 'pp_featured_key', true ); 226 if ( $stored !== $lookup['raw_hash'] ) { 227 wp_delete_attachment( $attachment_id, true ); 228 $this->disable_image_downloads(); 229 return array( 230 new \WP_Error( 231 'meta-write-failed', 232 esc_html__( 'Failed to persist image meta. Downloads stopped.', 'podcast-player' ) 233 ), 234 false 235 ); 236 } 237 238 $completed[ $key ] = array_merge( $item, array( 'post_id' => $attachment_id ) ); 239 } else { 240 wp_delete_file( $tmp ); 241 return ( array( $attachment_id, false ) ); 242 } 243 } finally { 244 $this->release_image_download_lock( $lock_key ); 172 245 } 173 174 // 2. Force a correct "filename" for WP regardless of the remote URL175 // Try using MIME type to get correct extension176 $headers = wp_remote_head( $image_url );177 $mime = wp_remote_retrieve_header( $headers, 'content-type' );178 $ext = Get_Fn::get_extension_from_mime( $mime );179 $filename = 'podcast-episode-image-' . md5( $image_url ) . '.' . $ext;180 181 // 3. Build array as if it was a file upload182 $file = [183 'name' => $filename,184 'tmp_name' => $tmp,185 ];186 187 // 4. Let WP handle the upload188 $attachment_id = media_handle_sideload( $file, 0, $title );189 190 if ( ! is_wp_error( $attachment_id ) ) {191 192 // Count successful downloads.193 $this->increment_image_download_count();194 195 add_post_meta( $attachment_id, 'pp_featured_key', md5( $image_url ), true );196 $normalized_url = Utility_Fn::normalize_media_url( $image_url );197 add_post_meta( $attachment_id, 'pp_featured_key_norm', md5( $normalized_url ), true );198 199 // Let's do post_meta verification to see if data is getting saved correctly.200 $stored = get_post_meta( $attachment_id, 'pp_featured_key', true );201 if ( $stored !== md5( $image_url ) ) {202 $this->disable_image_downloads();203 return array(204 new \WP_Error(205 'meta-write-failed',206 esc_html__( 'Failed to persist image meta. Downloads stopped.', 'podcast-player' )207 ),208 false209 );210 }211 212 $completed[ $key ] = array_merge( $item, array( 'post_id' => $attachment_id ) );213 } else {214 wp_delete_file( $tmp );215 return ( array( $attachment_id, false ) );216 }217 246 } 218 247 } … … 380 409 delete_option( 'pp_total_image_downloads' ); 381 410 } 411 412 /** 413 * Build stable lookup values used for de-dupe checks and locks. 414 * 415 * @param string $image_url Image URL. 416 * @return array 417 */ 418 private function get_image_lookup_data( $image_url ) { 419 $raw_hash = md5( $image_url ); 420 $normalized_url = Utility_Fn::normalize_media_url( $image_url ); 421 $norm_hash = md5( $normalized_url ); 422 423 return array( 424 'raw_hash' => $raw_hash, 425 'norm_hash' => $norm_hash, 426 'filename_stem' => 'podcast-episode-image-' . $raw_hash, 427 ); 428 } 429 430 /** 431 * Find a previously downloaded attachment for this image. 432 * 433 * Falls back to generated filename matching so we can recover from cases where the 434 * attachment was created but our custom de-dupe meta was never written. 435 * 436 * @param array $lookup Lookup data. 437 * @return int 438 */ 439 private function find_existing_attachment_for_image( $lookup ) { 440 global $wpdb; 441 442 $hashes = array_filter( 443 array_unique( 444 array( 445 isset( $lookup['raw_hash'] ) ? $lookup['raw_hash'] : '', 446 isset( $lookup['norm_hash'] ) ? $lookup['norm_hash'] : '', 447 ) 448 ) 449 ); 450 451 if ( ! empty( $hashes ) ) { 452 $in_clause = implode( 453 ',', 454 array_map( 455 function ( $hash ) use ( $wpdb ) { 456 return $wpdb->prepare( '%s', $hash ); 457 }, 458 $hashes 459 ) 460 ); 461 462 $post_id = (int) $wpdb->get_var( 463 "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key IN ( 'pp_featured_key', 'pp_featured_key_norm' ) AND meta_value IN ( $in_clause ) ORDER BY post_id ASC LIMIT 1" 464 ); 465 if ( $post_id ) { 466 return $post_id; 467 } 468 } 469 470 $filename_stem = isset( $lookup['filename_stem'] ) ? $lookup['filename_stem'] : ''; 471 if ( empty( $filename_stem ) ) { 472 return 0; 473 } 474 475 return (int) $wpdb->get_var( 476 $wpdb->prepare( 477 "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_wp_attached_file' AND meta_value LIKE %s ORDER BY post_id ASC LIMIT 1", 478 '%' . $wpdb->esc_like( $filename_stem ) . '%' 479 ) 480 ); 481 } 482 483 /** 484 * Persist de-dupe metadata for a downloaded attachment. 485 * 486 * @param int $attachment_id Attachment ID. 487 * @param array $lookup Lookup data. 488 */ 489 private function persist_image_lookup_meta( $attachment_id, $lookup ) { 490 $raw_hash = isset( $lookup['raw_hash'] ) ? $lookup['raw_hash'] : ''; 491 $norm_hash = isset( $lookup['norm_hash'] ) ? $lookup['norm_hash'] : ''; 492 493 if ( $raw_hash ) { 494 update_post_meta( $attachment_id, 'pp_featured_key', $raw_hash ); 495 } 496 497 if ( $norm_hash ) { 498 update_post_meta( $attachment_id, 'pp_featured_key_norm', $norm_hash ); 499 } 500 } 501 502 /** 503 * Get a transient key for a specific image download. 504 * 505 * @param array $lookup Lookup data. 506 * @return string 507 */ 508 private function get_image_lock_key( $lookup ) { 509 $hash = ! empty( $lookup['norm_hash'] ) ? $lookup['norm_hash'] : $lookup['raw_hash']; 510 return 'pp_img_lock_' . $hash; 511 } 512 513 /** 514 * Acquire a short-lived lock for a specific image download. 515 * 516 * @param string $lock_key Lock key. 517 * @return bool 518 */ 519 private function acquire_image_download_lock( $lock_key ) { 520 if ( get_transient( $lock_key ) ) { 521 return false; 522 } 523 524 return set_transient( $lock_key, 1, self::IMAGE_DOWNLOAD_LOCK_TTL ); 525 } 526 527 /** 528 * Release a short-lived image download lock. 529 * 530 * @param string $lock_key Lock key. 531 */ 532 private function release_image_download_lock( $lock_key ) { 533 delete_transient( $lock_key ); 534 } 382 535 } -
podcast-player/trunk/helper/core/class-background-jobs.php
r3462195 r3498073 38 38 39 39 /** 40 * Current task ID being processed. 41 * 42 * @var string 43 */ 44 private $current_task_id = ''; 45 46 /** 47 * Current task payload being processed. 48 * 49 * @var array 50 */ 51 private $current_task = array(); 52 53 /** 54 * Ensure the shutdown handler is only registered once per request. 55 * 56 * @var bool 57 */ 58 private $shutdown_registered = false; 59 60 /** 40 61 * Get the name of the queue. 41 62 * … … 217 238 */ 218 239 private function handle() { 240 $this->register_shutdown_handler(); 219 241 $this->lock_process(); 220 242 $queue = $this->get_tasks_queue(); 221 243 $current_task_id = array_key_first( $queue ); 222 244 $current_task = $queue[ $current_task_id ]; 245 $this->current_task_id = $current_task_id; 246 $this->current_task = $current_task; 223 247 $error = false; 224 248 … … 244 268 245 269 if ( $error ) { 246 $current_task['attempts'] = $current_task['attempts'] + 1; 247 $new_queue = $this->get_tasks_queue(); 248 if ( $current_task['attempts'] < 3 ) { 249 $new_queue[ $current_task_id ] = $current_task; 250 } else { 251 unset( $new_queue[ $current_task_id ] ); 252 253 // If error is in image download. Let's disable image download to prevent infinite loop. 254 if ( 'download_image' === $current_task['type'] ) { 255 $options = get_option( 'pp-common-options', array() ); 256 $options['img_save'] = 'no'; 257 update_option( 'pp-common-options', $options ); 258 } 259 } 260 $this->set_tasks_queue( $new_queue ); 261 $this->log_error( $error, $current_task['data'] ); 270 $this->handle_task_error( $current_task_id, $current_task, $error ); 262 271 } 263 272 … … 265 274 sleep( 5 ); 266 275 $this->unlock_process(); 276 $this->reset_current_task(); 267 277 268 278 // If import tasks are pending and current task didn't error, re-dispatch to prioritize completion. … … 270 280 $this->dispatch_internal( true ); 271 281 } 282 } 283 284 /** 285 * Register a shutdown handler to capture fatal processing failures. 286 */ 287 private function register_shutdown_handler() { 288 if ( $this->shutdown_registered ) { 289 return; 290 } 291 292 register_shutdown_function( array( $this, 'handle_fatal_shutdown' ) ); 293 $this->shutdown_registered = true; 294 } 295 296 /** 297 * Handle fatal shutdowns that bypass normal exception handling. 298 */ 299 public function handle_fatal_shutdown() { 300 $error = error_get_last(); 301 if ( empty( $this->current_task_id ) || empty( $this->current_task ) || ! $this->is_fatal_error( $error ) ) { 302 return; 303 } 304 305 $message = isset( $error['message'] ) ? $error['message'] : esc_html__( 'Unknown fatal error.', 'podcast-player' ); 306 $this->handle_task_error( $this->current_task_id, $this->current_task, $message ); 307 $this->unlock_process(); 308 $this->reset_current_task(); 309 } 310 311 /** 312 * Determine if the provided shutdown error should be treated as fatal. 313 * 314 * @param array|false $error Last PHP error. 315 * @return bool 316 */ 317 private function is_fatal_error( $error ) { 318 if ( empty( $error ) || ! is_array( $error ) || empty( $error['type'] ) ) { 319 return false; 320 } 321 322 return in_array( 323 (int) $error['type'], 324 array( E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ), 325 true 326 ); 327 } 328 329 /** 330 * Update queue state after a task failure. 331 * 332 * @param string $task_id Task ID. 333 * @param array $task Task payload. 334 * @param string $error Error message. 335 */ 336 private function handle_task_error( $task_id, $task, $error ) { 337 $lock_token = $this->acquire_queue_lock(); 338 $new_queue = $this->get_tasks_queue( false ); 339 340 if ( ! isset( $new_queue[ $task_id ] ) ) { 341 $this->release_queue_lock( $lock_token ); 342 return; 343 } 344 345 $new_queue[ $task_id ]['attempts'] = isset( $new_queue[ $task_id ]['attempts'] ) ? (int) $new_queue[ $task_id ]['attempts'] + 1 : 1; 346 $attempts = $new_queue[ $task_id ]['attempts']; 347 348 if ( $attempts >= 3 ) { 349 unset( $new_queue[ $task_id ] ); 350 351 // If error is in image download. Let's disable image download to prevent infinite loop. 352 if ( isset( $task['type'] ) && 'download_image' === $task['type'] ) { 353 $options = get_option( 'pp-common-options', array() ); 354 $options['img_save'] = 'no'; 355 update_option( 'pp-common-options', $options ); 356 } 357 } 358 359 $this->set_tasks_queue( $new_queue ); 360 $this->release_queue_lock( $lock_token ); 361 $this->log_error( $error, isset( $task['data'] ) ? $task['data'] : array() ); 362 } 363 364 /** 365 * Clear current task tracking after processing completes. 366 */ 367 private function reset_current_task() { 368 $this->current_task_id = ''; 369 $this->current_task = array(); 272 370 } 273 371 -
podcast-player/trunk/podcast-player.php
r3484403 r3498073 15 15 * Plugin URI: https://easypodcastpro.com 16 16 * Description: Host your podcast episodes anywhere, display them only using podcast feed url. Use custom widget or shortcode to display podcast player anywhere on your site. 17 * Version: 8.0. 217 * Version: 8.0.3 18 18 * Author: vedathemes 19 19 * Author URI: https://easypodcastpro.com … … 30 30 31 31 // Currently plugin version. 32 define( 'PODCAST_PLAYER_VERSION', '8.0. 2' );32 define( 'PODCAST_PLAYER_VERSION', '8.0.3' ); 33 33 34 34 // Define plugin constants.
Note: See TracChangeset
for help on using the changeset viewer.