Changeset 3479631
- Timestamp:
- 03/11/2026 12:29:35 AM (3 weeks ago)
- Location:
- natural-text-to-speech/trunk
- Files:
-
- 13 added
- 12 deleted
- 3 edited
-
components/rest/podcast.php (modified) (10 diffs)
-
natural-text-to-speech.php (modified) (2 diffs)
-
public/images/podcast.png (added)
-
public/js/ntts-admin-v2.6.3-main.js (deleted)
-
public/js/ntts-admin-v2.6.3-vendor.js (deleted)
-
public/js/ntts-admin-v2.6.4-main.js (added)
-
public/js/ntts-admin-v2.6.4-vendor.js (added)
-
public/js/ntts-public-v2.6.3-main.js (deleted)
-
public/js/ntts-public-v2.6.3-vendor.js (deleted)
-
public/js/ntts-public-v2.6.4-main.js (added)
-
public/js/ntts-public-v2.6.4-vendor.js (added)
-
public/js/nttsa-0c373cd5.js (added)
-
public/js/nttsa-1019e62d.js (deleted)
-
public/js/nttsa-3b46a0f2.js (added)
-
public/js/nttsa-412b44b9.js (deleted)
-
public/js/nttsa-4e3123f9.js (added)
-
public/js/nttsa-58b2dca4.js (deleted)
-
public/js/nttsa-85278bfb.js (added)
-
public/js/nttsa-9826f7c8.js (deleted)
-
public/js/nttsa-aa8aff1c.js (added)
-
public/js/nttsa-ac25bf23.js (deleted)
-
public/js/nttsa-b96542c0.js (added)
-
public/js/nttsa-d3ae8f15.js (added)
-
public/js/nttsa-d4018bd6.js (deleted)
-
public/js/nttsa-e31f66e6.js (added)
-
public/js/nttsa-f53ceef8.js (deleted)
-
public/js/nttsa-f5d4fd0f.js (deleted)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
natural-text-to-speech/trunk/components/rest/podcast.php
r3478726 r3479631 13 13 const NATUTETO_PODCAST_META_PATH = '_natuteto_podcast_audio_path'; 14 14 const NATUTETO_PODCAST_META_GENERATED_AT = '_natuteto_podcast_generated_at'; 15 const NATUTETO_PODCAST_META_HASH = '_natuteto_podcast_content_hash'; 15 16 const NATUTETO_PODCAST_META_ERROR = '_natuteto_podcast_error'; 16 17 const NATUTETO_PODCAST_QUEUE_OPTION = 'natuteto_podcast_queue_state'; … … 76 77 77 78 /** 79 * Build a stable hash for the rendered post content. 80 * 81 * @param int $post_id Post ID. 82 * @param string|null $content Optional rendered content to hash. 83 * @return string 84 */ 85 function natuteto_get_podcast_content_hash( $post_id, $content = null ) { 86 if ( null === $content ) { 87 $post = get_post( $post_id ); 88 if ( ! $post ) { 89 return ''; 90 } 91 92 $content = apply_filters( 'the_content', $post->post_content ); 93 } 94 95 return hash( 'sha256', (string) $content ); 96 } 97 98 /** 78 99 * Build the final podcast file info for a post. 79 100 * 80 * @param int $post_id Post ID. 101 * @param int $post_id Post ID. 102 * @param string|null $content_hash Optional content hash override. 81 103 * @return array 82 104 */ 83 function natuteto_get_podcast_file_info( $post_id ) {105 function natuteto_get_podcast_file_info( $post_id, $content_hash = null ) { 84 106 $post_id = absint( $post_id ); 85 107 $storage_info = natuteto_get_podcast_storage_info(); … … 91 113 } 92 114 93 $slug = sanitize_title( $slug_source ); 94 $filename = $post_id . '-' . $slug . '.mp3'; 95 $path = $storage_info['basedir'] . '/' . $filename; 96 $url = natuteto_proxy_file_get_url( 'podcast' ) . rawurlencode( $filename ); 115 $slug = sanitize_title( $slug_source ); 116 117 if ( null === $content_hash ) { 118 $content_hash = get_post_meta( $post_id, NATUTETO_PODCAST_META_HASH, true ); 119 } 120 121 $content_hash = is_string( $content_hash ) ? trim( $content_hash ) : ''; 122 $hash_suffix = '' !== $content_hash ? '-' . substr( $content_hash, 0, 16 ) : ''; 123 $filename = $post_id . '-' . $slug . $hash_suffix . '.mp3'; 124 $path = $storage_info['basedir'] . '/' . $filename; 125 $url = natuteto_proxy_file_get_url( 'podcast' ) . rawurlencode( $filename ); 97 126 98 127 return array( 128 'hash' => $content_hash, 99 129 'filename' => $filename, 100 130 'path' => $path, 101 131 'url' => $url, 102 132 ); 133 } 134 135 /** 136 * Remove stale podcast files for a post, keeping only the latest file. 137 * 138 * @param int $post_id Post ID. 139 * @param string $keep_path Path to preserve. 140 * @return void 141 */ 142 function natuteto_cleanup_podcast_files_for_post( $post_id, $keep_path = '' ) { 143 global $wp_filesystem; 144 145 $post_id = absint( $post_id ); 146 if ( $post_id <= 0 ) { 147 return; 148 } 149 150 if ( ! $wp_filesystem ) { 151 require_once ABSPATH . '/wp-admin/includes/file.php'; 152 WP_Filesystem(); 153 } 154 155 $storage_info = natuteto_get_podcast_storage_info(); 156 $pattern = $storage_info['basedir'] . '/' . $post_id . '-*.mp3'; 157 $candidates = glob( $pattern ); 158 $normalized_keep = is_string( $keep_path ) && '' !== $keep_path ? wp_normalize_path( $keep_path ) : ''; 159 $normalized_base = wp_normalize_path( $storage_info['basedir'] . '/' ); 160 161 if ( ! is_array( $candidates ) ) { 162 return; 163 } 164 165 foreach ( $candidates as $candidate ) { 166 if ( ! is_string( $candidate ) || '' === $candidate ) { 167 continue; 168 } 169 170 $normalized_candidate = wp_normalize_path( $candidate ); 171 if ( '' !== $normalized_keep && $normalized_candidate === $normalized_keep ) { 172 continue; 173 } 174 175 if ( 0 !== strpos( $normalized_candidate, $normalized_base ) ) { 176 continue; 177 } 178 179 $wp_filesystem->delete( $candidate ); 180 } 103 181 } 104 182 … … 825 903 */ 826 904 function natuteto_podcast_generation_needed( $post_id ) { 827 $file_info = natuteto_get_podcast_file_info( $post_id ); 905 $stored_hash = get_post_meta( $post_id, NATUTETO_PODCAST_META_HASH, true ); 906 $stored_hash = is_string( $stored_hash ) ? trim( $stored_hash ) : ''; 907 $file_info = natuteto_get_podcast_file_info( $post_id, $stored_hash ); 828 908 $generated_at = absint( get_post_meta( $post_id, NATUTETO_PODCAST_META_GENERATED_AT, true ) ); 829 909 $post_modified = strtotime( (string) get_post_field( 'post_modified_gmt', $post_id ) ); … … 837 917 } 838 918 839 if ( false !== $post_modified && $post_modified > $generated_at ) { 919 if ( '' !== $stored_hash ) { 920 if ( false !== $post_modified && $post_modified <= $generated_at ) { 921 return false; 922 } 923 924 $current_hash = natuteto_get_podcast_content_hash( $post_id ); 925 if ( '' !== $current_hash && $stored_hash !== $current_hash ) { 926 return true; 927 } 928 929 return false; 930 } 931 932 if ( '' === $stored_hash && false !== $post_modified && $post_modified > $generated_at ) { 840 933 return true; 841 934 } … … 1049 1142 } 1050 1143 1051 $file_info = natuteto_get_podcast_file_info( $post_id ); 1144 $content_hash = get_post_meta( $post_id, NATUTETO_PODCAST_META_HASH, true ); 1145 $content_hash = is_string( $content_hash ) ? trim( $content_hash ) : ''; 1146 if ( '' === $content_hash ) { 1147 $content_hash = natuteto_get_podcast_content_hash( $post_id ); 1148 } 1149 1150 $file_info = natuteto_get_podcast_file_info( $post_id, $content_hash ); 1052 1151 $storage_info = natuteto_get_podcast_storage_info(); 1053 1152 … … 1068 1167 update_post_meta( $post_id, NATUTETO_PODCAST_META_PATH, $file_info['path'] ); 1069 1168 update_post_meta( $post_id, NATUTETO_PODCAST_META_GENERATED_AT, time() ); 1169 if ( '' !== $content_hash ) { 1170 update_post_meta( $post_id, NATUTETO_PODCAST_META_HASH, $content_hash ); 1171 } 1070 1172 delete_post_meta( $post_id, NATUTETO_PODCAST_META_ERROR ); 1173 1174 natuteto_cleanup_podcast_files_for_post( $post_id, $file_info['path'] ); 1071 1175 1072 1176 return array( … … 1096 1200 if ( '' === trim( wp_strip_all_tags( $content ) ) ) { 1097 1201 return new WP_Error( 'podcast_empty_content', 'Post content is empty after rendering.', array( 'status' => 400 ) ); 1202 } 1203 1204 $content_hash = natuteto_get_podcast_content_hash( $post_id, $content ); 1205 if ( '' !== $content_hash ) { 1206 update_post_meta( $post_id, NATUTETO_PODCAST_META_HASH, $content_hash ); 1098 1207 } 1099 1208 … … 1357 1466 $file_info = natuteto_get_podcast_file_info( $post_id ); 1358 1467 $meta_path = get_post_meta( $post_id, NATUTETO_PODCAST_META_PATH, true ); 1359 1360 $path = is_string( $meta_path ) && '' !== $meta_path ? $meta_path : $file_info['path']; 1361 $url = $file_info['url']; 1362 1363 if ( ! file_exists( $path ) ) { 1364 return null; 1468 $meta_url = get_post_meta( $post_id, NATUTETO_PODCAST_META_URL, true ); 1469 1470 if ( is_string( $meta_path ) && '' !== $meta_path && file_exists( $meta_path ) ) { 1471 $path = $meta_path; 1472 $url = is_string( $meta_url ) && '' !== $meta_url ? $meta_url : $file_info['url']; 1473 } else { 1474 $path = $file_info['path']; 1475 $url = $file_info['url']; 1476 if ( ! file_exists( $path ) ) { 1477 return null; 1478 } 1365 1479 } 1366 1480 … … 1385 1499 if ( ! $feed || empty( $feed['enabled'] ) ) { 1386 1500 status_header( 404 ); 1387 echo 'Podcast feed not found. Goto plugin setting -> Enable podcast RSS';1501 echo 'Podcast feed not found. Goto plugin setting -> Podcast & Export Audio -> Enable podcast RSS'; 1388 1502 exit; 1389 1503 } -
natural-text-to-speech/trunk/natural-text-to-speech.php
r3478732 r3479631 4 4 * Plugin URI: https://wordpress.org/plugins/natural-text-to-speech 5 5 * Description: Read aloud your posts using natural, human-like voices and highlights sentences and words as they are spoken. Available in Free and Pro versions! Start now with 20,000 free characters / month. 6 * Version: 2.6. 36 * Version: 2.6.4 7 7 * Author: Reinvent WP 8 8 * Author URI: https://reinventwp.com … … 20 20 * serious issues with the options, so no if ( ! defined() ).}} 21 21 */ 22 define( 'NATUTETO_VERSION', '2.6. 3' );22 define( 'NATUTETO_VERSION', '2.6.4' ); 23 23 24 24 if ( ! defined( 'NATUTETO_PLUGIN_DIR' ) ) { -
natural-text-to-speech/trunk/readme.txt
r3478732 r3479631 4 4 Requires at least: 5.0 5 5 Tested up to: 6.9 6 Stable tag: 2.6. 37 Version: 2.6. 36 Stable tag: 2.6.4 7 Version: 2.6.4 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 479 479 == Changelog == 480 480 481 = 2.6.4 = 482 * Enhance Podcast setting UI 483 481 484 = 2.6.3 = 482 485 * Improve Post to Podcast feature
Note: See TracChangeset
for help on using the changeset viewer.