true ) ); $safe_post_types = array_intersect( $saved_post_types, array_keys( $all_post_types ) ); if ( empty( $safe_post_types ) ) { $safe_post_types = array( '__aat_no_content_type__' ); } return array_values( $safe_post_types ); } /** * Fetch generated posts from database with pagination. * * @param int $per_page Number of posts per page. * @param int $offset Offset for pagination. * @return array Array of WP_Post objects. */ public function fetch_generated_posts( $per_page, $offset ) { return get_posts( array( 'numberposts' => $per_page, 'offset' => $offset, 'post_status' => 'publish', 'post_type' => $this->get_safe_post_types(), 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- required for cron stats. array( 'key' => 'accessibility_audio_tts_generated', 'value' => '1', 'compare' => '=', ), ), 'orderby' => 'date', 'order' => 'DESC', // Return full WP_Post objects for renderer compatibility. 'fields' => 'all', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); } /** One paged query keeps filtering, totals and pagination in agreement. */ public function get_recordings_page( int $page, int $per_page, string $filter = 'all', string $target_model = '' ): array { $models = \Accessibility_Audio_TTS\Core\ElevenLabs_Models::ids(); $meta_query = array( array( 'key' => 'accessibility_audio_tts_generated', 'value' => '1', 'compare' => '=' ) ); if ( 'different' === $filter ) { $meta_query[] = array( 'key' => 'accessibility_audio_tts_model', 'value' => $models, 'compare' => 'IN' ); $meta_query[] = array( 'key' => 'accessibility_audio_tts_model', 'value' => $target_model, 'compare' => '!=' ); } elseif ( 'unknown' === $filter ) { $meta_query[] = array( 'relation' => 'OR', array( 'key' => 'accessibility_audio_tts_model', 'compare' => 'NOT EXISTS' ), array( 'key' => 'accessibility_audio_tts_model', 'value' => $models, 'compare' => 'NOT IN' ), ); } $page = max( 1, $page ); $per_page = max( 1, min( 50, $per_page ) ); $query = new \WP_Query( array( 'post_type' => $this->get_safe_post_types(), 'post_status' => 'publish', 'posts_per_page' => $per_page, 'paged' => $page, 'meta_query' => $meta_query, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Recording model filters are bounded and paginated. 'orderby' => array( 'date' => 'DESC', 'ID' => 'DESC' ), 'no_found_rows' => false, 'update_post_term_cache' => false, ) ); return array( 'posts' => $query->posts, 'total' => (int) $query->found_posts, 'page' => $page, 'per_page' => $per_page ); } /** * Count total number of generated posts. * * @return int Total number of posts with generated audio. */ public function count_total_generated_posts() { $posts = get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => $this->get_safe_post_types(), 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- required for cron stats. array( 'key' => 'accessibility_audio_tts_generated', 'value' => '1', 'compare' => '=', ), ), 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); return count( $posts ); } /** * Get posts by specific criteria. * * @param array $criteria Search criteria. * @return array Array of WP_Post objects. */ public function get_posts_by_criteria( $criteria ) { $default_args = array( 'post_status' => 'publish', 'post_type' => $this->get_safe_post_types(), 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- required for cron filters. array( 'key' => 'accessibility_audio_tts_generated', 'value' => '1', 'compare' => '=', ), ), 'orderby' => 'date', 'order' => 'DESC', ); $args = array_merge( $default_args, $criteria ); if ( ! empty( $args['meta_key'] ) || ! empty( $args['meta_value'] ) ) { $meta_key = isset( $args['meta_key'] ) ? $args['meta_key'] : 'accessibility_audio_tts_generated'; $meta_value = isset( $args['meta_value'] ) ? $args['meta_value'] : '1'; unset( $args['meta_key'], $args['meta_value'] ); $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- normalized. array( 'key' => $meta_key, 'value' => $meta_value, 'compare' => '=', ), ); } $args['fields'] = isset( $args['fields'] ) ? $args['fields'] : 'ids'; $args['no_found_rows'] = true; $args['update_post_meta_cache'] = false; $args['update_post_term_cache'] = false; return get_posts( $args ); } /** * Count posts generated in the last 24 hours. * * @return int Number of recently generated posts. */ public function count_recent_generated_posts() { $yesterday = gmdate( 'Y-m-d H:i:s', strtotime( '-24 hours' ) ); $posts = get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => $this->get_safe_post_types(), 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- required for cron stats. array( 'key' => 'accessibility_audio_tts_generated', 'value' => '1', 'compare' => '=', ), ), 'date_query' => array( array( 'after' => $yesterday, 'inclusive' => true, ), ), 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); return count( $posts ); } /** * Get the date of the last generated post. * * @return string|null Date of last generated post or null if none. */ public function get_last_generated_post_date() { $posts = get_posts( array( 'numberposts' => 1, 'post_status' => 'publish', 'post_type' => $this->get_safe_post_types(), 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- required for cron stats. array( 'key' => 'accessibility_audio_tts_generated', 'value' => '1', 'compare' => '=', ), ), 'orderby' => 'date', 'order' => 'DESC', 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); if ( empty( $posts ) ) { return null; } $post = get_post( $posts[0] ); return $post ? $post->post_date : null; } /** * Check if post has generated audio. * * @param int $post_id Post ID to check. * @return bool True if post has generated audio. */ public function post_has_generated_audio( $post_id ) { return '1' === get_post_meta( $post_id, 'accessibility_audio_tts_generated', true ); } /** * Get audio URL for post. * * @param int $post_id Post ID. * @return string|null Audio URL or null if not found. */ public function get_post_audio_url( $post_id ) { $url = get_post_meta( $post_id, 'accessibility_audio_tts_url', true ); return $url ? $url : null; } }