首页 / 应用软件 / WordPress插件开发教程,实现网站内容自动生成语音并转换为播客

WordPress插件开发教程,实现网站内容自动生成语音并转换为播客

WordPress插件开发教程:实现网站内容自动生成语音并转换为播客

引言:当文字遇见声音

在信息爆炸的数字时代,内容呈现方式的多样性变得至关重要。据统计,全球播客听众数量已从2019年的3.32亿增长到2023年的4.64亿,预计到2024年将超过5亿。与此同时,语音技术的发展使得文字转语音的质量达到了前所未有的自然度。对于WordPress网站运营者而言,将文字内容自动转换为语音并生成播客,不仅能提升用户体验,还能扩大内容传播渠道,吸引更多受众。

本教程将深入讲解如何通过WordPress插件开发,实现网站内容自动生成语音并转换为播客的功能。我们将从基础概念讲起,逐步深入到代码实现,最终打造一个功能完整的插件。无论您是WordPress开发者还是有一定技术基础的内容创作者,都能通过本教程掌握这一实用技能。

第一部分:准备工作与环境搭建

1.1 理解WordPress插件架构

WordPress插件系统基于PHP构建,采用事件驱动的钩子(Hooks)机制。插件通过动作钩子(Actions)和过滤器钩子(Filters)与WordPress核心交互。理解这一机制是开发任何WordPress插件的基础。

一个标准的WordPress插件通常包含以下结构:

  • 主插件文件(plugin-name.php):包含插件元信息
  • 功能类文件:实现核心功能
  • 资源文件:CSS、JavaScript、图像等
  • 语言文件:用于国际化

1.2 开发环境配置

在开始开发前,需要搭建合适的开发环境:

  1. 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel
  2. 代码编辑器:VS Code、PHPStorm或Sublime Text
  3. WordPress安装:最新版本的WordPress
  4. 调试工具:安装Query Monitor、Debug Bar等调试插件
  5. 版本控制:使用Git进行代码管理

1.3 创建插件基础结构

首先,在WordPress的wp-content/plugins/目录下创建新文件夹auto-podcast-generator,然后创建主插件文件:

<?php
/**
 * Plugin Name: Auto Podcast Generator
 * Plugin URI: https://yourwebsite.com/auto-podcast-generator
 * Description: 自动将WordPress文章转换为语音播客
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL v2 or later
 * Text Domain: auto-podcast-generator
 */

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('APG_VERSION', '1.0.0');
define('APG_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('APG_PLUGIN_URL', plugin_dir_url(__FILE__));
define('APG_PLUGIN_BASENAME', plugin_basename(__FILE__));

// 初始化插件
require_once APG_PLUGIN_DIR . 'includes/class-auto-podcast-generator.php';

function run_auto_podcast_generator() {
    $plugin = new Auto_Podcast_Generator();
    $plugin->run();
}
run_auto_podcast_generator();

第二部分:核心功能设计与实现

2.1 语音生成引擎选择与集成

目前市场上有多种文字转语音(TTS)服务可供选择:

  1. Google Cloud Text-to-Speech:质量高,支持多种语言和声音
  2. Amazon Polly:提供逼真的语音合成
  3. Microsoft Azure Cognitive Services:语音自然度高
  4. IBM Watson Text to Speech:企业级解决方案
  5. 开源方案:如eSpeak、Festival(质量较低)

本教程将使用Google Cloud Text-to-Speech API作为示例,因为它提供了高质量的语音合成和相对友好的免费额度。

2.1.1 配置Google Cloud TTS API

首先,需要在Google Cloud Console中创建项目并启用Text-to-Speech API,然后创建服务账号密钥。

在插件中创建API配置类:

<?php
// includes/class-tts-engine.php

class APG_TTS_Engine {
    
    private $api_key;
    private $language_code;
    private $voice_name;
    
    public function __construct() {
        $options = get_option('apg_settings');
        $this->api_key = isset($options['google_tts_api_key']) ? $options['google_tts_api_key'] : '';
        $this->language_code = isset($options['tts_language']) ? $options['tts_language'] : 'en-US';
        $this->voice_name = isset($options['tts_voice']) ? $options['tts_voice'] : 'en-US-Wavenet-D';
    }
    
    /**
     * 将文本转换为语音
     * 
     * @param string $text 要转换的文本
     * @param int $post_id 文章ID
     * @return string|bool 返回音频文件路径或false
     */
    public function text_to_speech($text, $post_id) {
        
        if (empty($this->api_key)) {
            error_log('Auto Podcast Generator: Google TTS API key not configured');
            return false;
        }
        
        // 清理文本,移除HTML标签
        $clean_text = wp_strip_all_tags($text);
        
        // 限制文本长度(Google TTS API限制为5000字符)
        if (strlen($clean_text) > 5000) {
            $clean_text = $this->truncate_text($clean_text, 5000);
        }
        
        // 准备API请求
        $url = 'https://texttospeech.googleapis.com/v1/text:synthesize?key=' . $this->api_key;
        
        $data = array(
            'input' => array(
                'text' => $clean_text
            ),
            'voice' => array(
                'languageCode' => $this->language_code,
                'name' => $this->voice_name
            ),
            'audioConfig' => array(
                'audioEncoding' => 'MP3',
                'speakingRate' => 1.0,
                'pitch' => 0,
                'volumeGainDb' => 0
            )
        );
        
        $args = array(
            'body' => json_encode($data),
            'headers' => array(
                'Content-Type' => 'application/json'
            ),
            'timeout' => 30
        );
        
        // 发送请求
        $response = wp_remote_post($url, $args);
        
        if (is_wp_error($response)) {
            error_log('Auto Podcast Generator: TTS API request failed - ' . $response->get_error_message());
            return false;
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (isset($data['error'])) {
            error_log('Auto Podcast Generator: TTS API error - ' . $data['error']['message']);
            return false;
        }
        
        if (!isset($data['audioContent'])) {
            error_log('Auto Podcast Generator: No audio content in response');
            return false;
        }
        
        // 解码base64音频数据
        $audio_data = base64_decode($data['audioContent']);
        
        // 保存音频文件
        $upload_dir = wp_upload_dir();
        $podcast_dir = $upload_dir['basedir'] . '/apg-podcasts/';
        
        if (!file_exists($podcast_dir)) {
            wp_mkdir_p($podcast_dir);
        }
        
        $filename = 'podcast-' . $post_id . '-' . time() . '.mp3';
        $filepath = $podcast_dir . $filename;
        
        if (file_put_contents($filepath, $audio_data)) {
            // 保存文件信息到文章元数据
            $file_url = $upload_dir['baseurl'] . '/apg-podcasts/' . $filename;
            update_post_meta($post_id, '_apg_audio_file', $filepath);
            update_post_meta($post_id, '_apg_audio_url', $file_url);
            update_post_meta($post_id, '_apg_audio_generated', current_time('mysql'));
            
            return $filepath;
        }
        
        return false;
    }
    
    /**
     * 截断文本,尽量在句子结束处截断
     */
    private function truncate_text($text, $max_length) {
        if (strlen($text) <= $max_length) {
            return $text;
        }
        
        $truncated = substr($text, 0, $max_length);
        $last_period = strrpos($truncated, '.');
        $last_question = strrpos($truncated, '?');
        $last_exclamation = strrpos($truncated, '!');
        
        $last_sentence_end = max($last_period, $last_question, $last_exclamation);
        
        if ($last_sentence_end > 0) {
            return substr($text, 0, $last_sentence_end + 1);
        }
        
        return $truncated . '...';
    }
    
    /**
     * 获取可用的语音列表
     */
    public function get_available_voices() {
        // 这里可以缓存语音列表以提高性能
        $voices = get_transient('apg_google_voices');
        
        if (false === $voices) {
            $url = 'https://texttospeech.googleapis.com/v1/voices?key=' . $this->api_key;
            
            $response = wp_remote_get($url);
            
            if (!is_wp_error($response)) {
                $body = wp_remote_retrieve_body($response);
                $data = json_decode($body, true);
                
                if (isset($data['voices'])) {
                    $voices = $data['voices'];
                    set_transient('apg_google_voices', $voices, WEEK_IN_SECONDS);
                }
            }
        }
        
        return $voices;
    }
}

2.2 文章内容处理与优化

直接使用文章原始内容生成语音可能不是最佳体验,我们需要对内容进行处理:

<?php
// includes/class-content-processor.php

class APG_Content_Processor {
    
    /**
     * 处理文章内容,准备用于语音合成
     */
    public function prepare_content_for_tts($post_id) {
        $post = get_post($post_id);
        
        if (!$post || $post->post_status !== 'publish') {
            return false;
        }
        
        $content = $post->post_content;
        
        // 应用the_content过滤器,处理短代码等
        $content = apply_filters('the_content', $content);
        
        // 移除不需要的元素
        $content = $this->clean_content($content);
        
        // 添加文章标题作为介绍
        $title = get_the_title($post_id);
        $final_content = "文章标题: " . $title . ". " . $content;
        
        // 添加结尾语
        $final_content .= " 本文由自动播客生成器为您朗读。访问我们的网站获取更多内容。";
        
        return $final_content;
    }
    
    /**
     * 清理HTML内容,提取纯文本
     */
    private function clean_content($content) {
        // 移除脚本和样式标签
        $content = preg_replace('/<scriptb[^>]*>(.*?)</script>/is', '', $content);
        $content = preg_replace('/<styleb[^>]*>(.*?)</style>/is', '', $content);
        
        // 移除注释
        $content = preg_replace('/<!--(.*?)-->/', '', $content);
        
        // 替换HTML标签为适当的停顿
        $content = preg_replace('/</h[1-6]>/', '. ', $content);
        $content = preg_replace('/</p>/', '. ', $content);
        $content = preg_replace('/</div>/', '. ', $content);
        $content = preg_replace('/<brs*/?>/', '. ', $content);
        
        // 移除所有剩余HTML标签
        $content = wp_strip_all_tags($content);
        
        // 规范化空格和标点
        $content = preg_replace('/s+/', ' ', $content);
        $content = preg_replace('/s*.s*/', '. ', $content);
        $content = preg_replace('/s*,s*/', ', ', $content);
        $content = preg_replace('/s*!s*/', '! ', $content);
        $content = preg_replace('/s*?s*/', '? ', $content);
        
        // 解码HTML实体
        $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        
        return trim($content);
    }
    
    /**
     * 估算朗读时间
     */
    public function estimate_reading_time($content) {
        // 平均阅读速度:每分钟150-200字
        $word_count = str_word_count(strip_tags($content));
        $minutes = ceil($word_count / 180); // 使用180字/分钟作为平均值
        
        return $minutes;
    }
}

2.3 播客RSS Feed生成

播客的核心是RSS Feed,它使音频内容能够被播客客户端订阅:

<?php
// includes/class-podcast-feed.php

class APG_Podcast_Feed {
    
    private $feed_slug = 'podcast-feed';
    
    public function __construct() {
        add_action('init', array($this, 'add_podcast_feed'));
        add_action('do_feed_' . $this->feed_slug, array($this, 'generate_podcast_feed'), 10, 1);
    }
    
    /**
     * 注册自定义RSS Feed
     */
    public function add_podcast_feed() {
        add_feed($this->feed_slug, array($this, 'generate_podcast_feed'));
    }
    
    /**
     * 生成播客RSS Feed
     */
    public function generate_podcast_feed() {
        // 设置内容类型为XML
        header('Content-Type: ' . feed_content_type('rss2') . '; charset=' . get_option('blog_charset'), true);
        
        // 获取播客设置
        $options = get_option('apg_settings');
        
        // 查询有音频的文章
        $args = array(
            'post_type' => 'post',
            'post_status' => 'publish',
            'meta_key' => '_apg_audio_url',
            'meta_compare' => 'EXISTS',
            'posts_per_page' => 20,
            'orderby' => 'date',
            'order' => 'DESC'
        );
        
        $podcast_posts = new WP_Query($args);
        
        echo '<?xml version="1.0" encoding="' . get_option('blog_charset') . '"?>';
        ?>
<rss version="2.0" 
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
     xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
     xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0">
     
    <channel>
        <title><?php echo esc_xml(get_bloginfo('name') . ' Podcast'); ?></title>
        <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
        <link><?php echo esc_url(home_url('/')); ?></link>
        <description><?php echo esc_xml(get_bloginfo('description')); ?></description>
        <lastBuildDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_lastpostmodified('GMT'), false); ?></lastBuildDate>
        <language><?php echo get_option('rss_language'); ?></language>
        <sy:updatePeriod><?php echo apply_filters('rss_update_period', 'hourly'); ?></sy:updatePeriod>
        <sy:updateFrequency><?php echo apply_filters('rss_update_frequency', '1'); ?></sy:updateFrequency>
        <generator>Auto Podcast Generator for WordPress</generator>
        
        <!-- iTunes播客特定标签 -->
        <itunes:subtitle><?php echo esc_xml($options['podcast_subtitle'] ?? get_bloginfo('description')); ?></itunes:subtitle>
        <itunes:summary><?php echo esc_xml($options['podcast_summary'] ?? get_bloginfo('description')); ?></itunes:summary>
        <itunes:author><?php echo esc_xml($options['podcast_author'] ?? get_bloginfo('name')); ?></itunes:author>
        
        <?php if (!empty($options['podcast_image'])): ?>
        <itunes:image href="<?php echo esc_url($options['podcast_image']); ?>" />
        <image>
            <url><?php echo esc_url($options['podcast_image']); ?></url>
            <title><?php echo esc_xml(get_bloginfo('name') . ' Podcast'); ?></title>
            <link><?php echo esc_url(home_url('/')); ?></link>
        </image>
        <?php endif; ?>
        
        <itunes:explicit><?php echo (!empty($options['podcast_explicit']) && $options['podcast_explicit'] === 'yes') ? 'yes' : 'no'; ?></itunes:explicit>
        <itunes:category text="<?php echo esc_attr($options['podcast_category'] ?? 'Technology'); ?>">
            <?php if (!empty($options['podcast_subcategory'])): ?>
            <itunes:category text="<?php echo esc_attr($options['podcast_subcategory']); ?>" />
            <?php endif; ?>
        </itunes:category>
        

): ?>

        <?php while ($podcast_posts->have_posts()): $podcast_posts->the_post(); ?>
            <?php
            $audio_url = get_post_meta(get_the_ID(), '_apg_audio_url', true);
            $audio_file = get_post_meta(get_the_ID(), '_apg_audio_file', true);
            $audio_duration = get_post_meta(get_the_ID(), '_apg_audio_duration', true);
            
            if (empty($audio_url)) {
                continue;
            }
            
            // 获取音频文件大小
            $file_size = 0;
            if (file_exists($audio_file)) {
                $file_size = filesize($audio_file);
            }
            
            // 获取文章特色图像
            $thumbnail_url = get_the_post_thumbnail_url(get_the_ID(), 'full');
            ?>
            
            <item>
                <title><?php echo esc_xml(get_the_title()); ?></title>
                <link><?php echo esc_url(get_permalink()); ?></link>
                <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_post_time('Y-m-d H:i:s', true), false); ?></pubDate>
                <dc:creator><![CDATA[<?php the_author(); ?>]]></dc:creator>
                <guid isPermaLink="false"><?php the_guid(); ?></guid>
                <description><![CDATA[<?php echo esc_xml(get_the_excerpt()); ?>]]></description>
                <content:encoded><![CDATA[<?php echo esc_xml(get_the_content()); ?>]]></content:encoded>
                
                <!-- 播客特定元素 -->
                <enclosure 
                    url="<?php echo esc_url($audio_url); ?>" 
                    length="<?php echo esc_attr($file_size); ?>" 
                    type="audio/mpeg" />
                
                <itunes:author><?php the_author(); ?></itunes:author>
                <itunes:subtitle><?php echo esc_xml(wp_trim_words(get_the_excerpt(), 20)); ?></itunes:subtitle>
                <itunes:summary><![CDATA[<?php echo esc_xml(get_the_excerpt()); ?>]]></itunes:summary>
                
                <?php if (!empty($thumbnail_url)): ?>
                <itunes:image href="<?php echo esc_url($thumbnail_url); ?>" />
                <?php endif; ?>
                
                <?php if (!empty($audio_duration)): ?>
                <itunes:duration><?php echo esc_attr($audio_duration); ?></itunes:duration>
                <?php endif; ?>
                
                <itunes:explicit><?php echo (!empty($options['podcast_explicit']) && $options['podcast_explicit'] === 'yes') ? 'yes' : 'no'; ?></itunes:explicit>
            </item>
        <?php endwhile; ?>
        <?php wp_reset_postdata(); ?>
    <?php endif; ?>
</channel>

</rss>

    <?php
}

/**
 * 获取播客Feed URL
 */
public function get_feed_url() {
    return home_url('/feed/' . $this->feed_slug);
}

}


## 第三部分:插件核心类与集成

### 3.1 主插件类实现

<?php
// includes/class-auto-podcast-generator.php

class Auto_Podcast_Generator {


private $loader;
private $tts_engine;
private $content_processor;
private $podcast_feed;
private $admin;

public function __construct() {
    $this->load_dependencies();
    $this->define_admin_hooks();
    $this->define_public_hooks();
}

private function load_dependencies() {
    require_once APG_PLUGIN_DIR . 'includes/class-tts-engine.php';
    require_once APG_PLUGIN_DIR . 'includes/class-content-processor.php';
    require_once APG_PLUGIN_DIR . 'includes/class-podcast-feed.php';
    require_once APG_PLUGIN_DIR . 'admin/class-admin.php';
    
    $this->tts_engine = new APG_TTS_Engine();
    $this->content_processor = new APG_Content_Processor();
    $this->podcast_feed = new APG_Podcast_Feed();
    $this->admin = new APG_Admin();
}

private function define_admin_hooks() {
    add_action('admin_menu', array($this->admin, 'add_admin_menu'));
    add_action('admin_init', array($this->admin, 'register_settings'));
    add_action('add_meta_boxes', array($this, 'add_podcast_meta_box'));
    add_action('save_post', array($this, 'save_post_handler'), 10, 2);
    add_action('admin_enqueue_scripts', array($this->admin, 'enqueue_admin_scripts'));
}

private function define_public_hooks() {
    // 在文章内容后添加音频播放器
    add_filter('the_content', array($this, 'add_audio_player_to_content'));
    
    // 添加播客订阅链接到页面
    add_action('wp_footer', array($this, 'add_podcast_subscription_links'));
    
    // 短代码支持
    add_shortcode('apg_podcast_player', array($this, 'podcast_player_shortcode'));
}

/**
 * 添加播客元数据框
 */
public function add_podcast_meta_box() {
    $post_types = apply_filters('apg_supported_post_types', array('post'));
    
    foreach ($post_types as $post_type) {
        add_meta_box(
            'apg_podcast_meta',
            __('播客设置', 'auto-podcast-generator'),
            array($this, 'render_podcast_meta_box'),
            $post_type,
            'side',
            'default'
        );
    }
}

/**
 * 渲染播客元数据框
 */
public function render_podcast_meta_box($post) {
    wp_nonce_field('apg_podcast_meta', 'apg_podcast_meta_nonce');
    
    $audio_url = get_post_meta($post->ID, '_apg_audio_url', true);
    $audio_generated = get_post_meta($post->ID, '_apg_audio_generated', true);
    $generate_audio = get_post_meta($post->ID, '_apg_generate_audio', true);
    
    if (empty($generate_audio)) {
        $generate_audio = 'auto';
    }
    ?>
    <div class="apg-meta-box">
        <p>
            <label for="apg_generate_audio">
                <strong><?php _e('生成音频', 'auto-podcast-generator'); ?></strong>
            </label>
        </p>
        <p>
            <select name="apg_generate_audio" id="apg_generate_audio" style="width:100%;">
                <option value="auto" <?php selected($generate_audio, 'auto'); ?>>
                    <?php _e('自动(发布时生成)', 'auto-podcast-generator'); ?>
                </option>
                <option value="manual" <?php selected($generate_audio, 'manual'); ?>>
                    <?php _e('手动生成', 'auto-podcast-generator'); ?>
                </option>
                <option value="disabled" <?php selected($generate_audio, 'disabled'); ?>>
                    <?php _e('不生成', 'auto-podcast-generator'); ?>
                </option>
            </select>
        </p>
        
        <?php if (!empty($audio_url)): ?>
        <p>
            <strong><?php _e('音频状态:', 'auto-podcast-generator'); ?></strong><br>
            <?php _e('已生成', 'auto-podcast-generator'); ?>
            <?php if ($audio_generated): ?>
                <br><small><?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($audio_generated)); ?></small>
            <?php endif; ?>
        </p>
        <p>
            <audio controls style="width:100%;">
                <source src="<?php echo esc_url($audio_url); ?>" type="audio/mpeg">
                <?php _e('您的浏览器不支持音频播放。', 'auto-podcast-generator'); ?>
            </audio>
        </p>
        <p>
            <a href="<?php echo esc_url($audio_url); ?>" target="_blank" class="button button-small">
                <?php _e('下载音频', 'auto-podcast-generator'); ?>
            </a>
            <button type="button" class="button button-small button-secondary" id="apg_regenerate_audio">
                <?php _e('重新生成', 'auto-podcast-generator'); ?>
            </button>
        </p>
        <?php else: ?>
        <p>
            <strong><?php _e('音频状态:', 'auto-podcast-generator'); ?></strong><br>
            <?php _e('未生成', 'auto-podcast-generator'); ?>
        </p>
        <?php endif; ?>
        
        <div id="apg_audio_generation_message" style="display:none; margin-top:10px; padding:5px; background:#f5f5f5; border-left:4px solid #46b450;"></div>
    </div>
    
    <script>
    jQuery(document).ready(function($) {
        $('#apg_regenerate_audio').on('click', function(e) {
            e.preventDefault();
            
            var button = $(this);
            var messageDiv = $('#apg_audio_generation_message');
            
            button.prop('disabled', true).text('<?php _e("生成中...", "auto-podcast-generator"); ?>');
            messageDiv.hide().removeClass('notice-success notice-error');
            
            $.ajax({
                url: ajaxurl,
                type: 'POST',
                data: {
                    action: 'apg_generate_audio',
                    post_id: <?php echo $post->ID; ?>,
                    nonce: '<?php echo wp_create_nonce("apg_generate_audio_" . $post->ID); ?>'
                },
                success: function(response) {
                    if (response.success) {
                        messageDiv.addClass('notice-success').html('<p><?php _e("音频生成成功!页面将重新加载...", "auto-podcast-generator"); ?></p>').show();
                        setTimeout(function() {
                            location.reload();
                        }, 2000);
                    } else {
                        messageDiv.addClass('notice-error').html('<p>' + response.data + '</p>').show();
                        button.prop('disabled', false).text('<?php _e("重新生成", "auto-podcast-generator"); ?>');
                    }
                },
                error: function() {
                    messageDiv.addClass('notice-error').html('<p><?php _e("生成失败,请重试。", "auto-podcast-generator"); ?></p>').show();
                    button.prop('disabled', false).text('<?php _e("重新生成", "auto-podcast-generator"); ?>');
                }
            });
        });
    });
    </script>
    <?php
}

/**
 * 保存文章时的处理
 */
public function save_post_handler($post_id, $post) {
    // 检查权限
    if (!current_user_can('edit_post', $post_id)) {
        return;
    }
    
    // 验证nonce
    if (!isset($_POST['apg_podcast_meta_nonce']) || !wp_verify_nonce($_POST['apg_podcast_meta_nonce'], 'apg_podcast_meta')) {
        return;
    }
    
    // 防止自动保存时处理
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    
    // 保存生成选项
    if (isset($_POST['apg_generate_audio'])) {
        update_post_meta($post_id, '_apg_generate_audio', sanitize_text_field($_POST['apg_generate_audio']));
    }
    
    // 如果是发布状态且设置为自动生成,则生成音频
    if ($post->post_status === 'publish' && isset($_POST['apg_generate_audio']) && $_POST['apg_generate_audio'] === 'auto') {
        $this->generate_audio_for_post($post_id);
    }
}

/**
 * 为文章生成音频
 */
public function generate_audio_for_post($post_id) {
    // 检查是否已生成
    $existing_audio = get_post_meta($post_id, '_apg_audio_url', true);
    if (!empty($existing_audio)) {
        return true;
    }
    
    // 准备内容
    $content = $this->content_processor->prepare_content_for_tts($post_id);
    
    if (empty($content)) {
        return false;
    }
    
    // 生成音频
    $audio_file = $this->tts_engine->text_to_speech($content, $post_id);
    
    if ($audio_file) {
        // 计算音频时长
        $this->calculate_audio_duration($post_id, $audio_file);
        
        // 触发动作,允许其他插件响应
        do_action('apg_audio_generated', $post_id, $audio_file);
        
        return true;
    }
    
    return false;
}

/**
 * 计算音频时长
 */
private function calculate_audio_duration($post_id, $audio_file) {
    if (!file_exists($audio_file)) {
        return;
    }
    
    // 使用getID3库获取音频信息
    if (!class_exists('getID3')) {
        require_once(ABSPATH . 'wp-admin/includes/media.php');
    }
    
    try {
        $id3 = new getID3();
        $file_info = $id3->analyze($audio_file);
        
        if (isset($file_info['playtime_seconds'])) {
            $duration_seconds = floor($file_info['playtime_seconds']);
            $duration_formatted = sprintf('%02d:%02d:%02d', 
                floor($duration_seconds / 3600),
                floor(($duration_seconds % 3600) / 60),
                $duration_seconds % 60
            );
            
            update_post_meta($post_id, '_apg_audio_duration', $duration_formatted);
            update_post_meta($post_id, '_apg_audio_duration_seconds', $duration_seconds);
        }
    } catch (Exception $e) {
        error_log('Auto Podcast Generator: Could not calculate audio duration - ' . $e->getMessage());
    }
}

/**
 * 在文章内容后添加音频播放器
 */
public function add_audio_player_to_content($content) {
    if (!is_single() || !in_the_loop() || !is_main_query()) {
        return $content;
    }
    
    $post_id = get_the_ID();
    $audio_url = get_post_meta($post_id, '_apg_audio_url', true);
    
    if (empty($audio_url)) {
        return $content;
    }
    
    $options = get_option('apg_settings');
    $show_player = isset($options['show_player_in_content']) ? $options['show_player_in_content'] : 'yes';
    
    if ($show_player !== 'yes') {
        return $content;
    }
    
    $player_position = isset($options['player_position']) ? $options['player_position'] : 'after';
    
    $player_html = $this->get_audio_player_html($post_id);
    
    if ($player_position === 'before') {
        return $player_html . $content;
    } else {
        return $content . $player_html;
    }
}

/**
 * 获取音频播放器HTML
 */
private function get_audio_player_html($post_id) {
    $audio_url = get_post_meta($post_id, '_apg_audio_url', true);
    $audio_duration = get_post_meta($post_id, '_apg_audio_duration', true);
    
    ob_start();
    ?>
    <div class="apg-audio-player" style="margin: 30px 0; padding: 20px; background: #f9f9f9; border-radius: 8px; border-left: 4px solid #0073aa;">
        <h3 style="margin-top: 0; color: #333;">
            <?php _e('收听本文音频版', 'auto-podcast-generator'); ?>
            <?php if ($audio_duration): ?>
                <small style="font-size: 14px; color: #666; font-weight: normal;">
                    (<?php echo esc_html($audio_duration); ?>)
                </small>
            <?php endif; ?>
        </h3>
        <audio controls style="width:100%;">
            <source src="<?php echo esc_url($audio_url); ?>" type="audio/mpeg">
            <?php _e('您的浏览器不支持音频播放。', 'auto-podcast-generator'); ?>
        </audio>
        <div style="margin-top: 15px; font-size: 14px; color: #666;">
            <p style="margin: 5px 0;">
                <?php _e('您也可以:', 'auto-podcast-generator'); ?>
                <a href="<?php echo esc_url($audio_url); ?>" download style="margin-left: 10px;">
                    <?php _e('下载音频', 'auto-podcast-generator'); ?>
                </a>
                <a href="<?php echo esc_url($this->podcast_feed->get_feed_url()); ?>" style="margin-left: 10px;" target="_blank">
                    <?php _e('订阅播客', 'auto-podcast-generator'); ?>
                </a>
            </p>
        </div>
    </div>
    <?php
    return ob_get_clean();
}

/**
 * 添加播客订阅链接
 */
public function add_podcast_subscription_links() {
    $options = get_option('apg_settings');
    $show_subscription_links = isset($options['show_subscription_links']) ? $options['show_subscription_links'] : 'yes';
    
    if ($show
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5240.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部