# AWS POLLY 功能實作 ### 實作流程 ![image](https://hackmd.io/_uploads/r1duxh4Wll.png) ### 前置步驟 * 插件基本資訊 ```php= /** * Plugin Name: aws_polly_service(whatever you want) * Author: your name * Version: 1.0.0 */ ``` * 防止檔案被直接存取 ```php= if (!defined('ABSPATH')) { exit; } ``` > <font size="2">'ABSPATH' 是 WordPress 目錄的絕對路徑,若沒有被定義表示該檔案不是在 WordPress 環境中運行,直接結束程式防止安全問題。</font> <br> * 引入 Composer 自動載入器 ```php require_once dirname(__DIR__) . '/vendor/autoload.php'; ``` > <font size =2> 載入 AWS SDK for PHP >需要先在此資料夾上層(plugin)安裝 composer ></font> *** ### Function1: 載入必要的前端資源 ```php= add_action('wp_enqueue_scripts','polly_load_assets'); function polly_load_assets(){ wp_enqueue_style( 'bootstrap-css', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css', array(), '5.3.0', 'all' ); wp_enqueue_script('jquery'); wp_enqueue_script( 'bootstrap-js', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', array('jquery'), '5.3.0', true ); } ``` > <font size=2>在前端載入 Bootstrap 5 的 CSS 和 JS,還有 WordPress 內建的 jQuery >CSS: 負責設定網頁的外觀和版面 >JS: 讓網頁變得有互動功能的程式語言 ></font> * 引入 AWS Polly 所需的 class ```php= use Aws\Polly\PollyClient; use Aws\Exception\AwsException; ``` > <font size="2"> PollyClient 是用來與 Polly 服務互動的主要對象,AwsException 是例外處理用。</font> *** ### Function2: 建立上傳表單 > ```php= >function wp_polly_form() { > ob_start(); > ?> > HTML內容略 > <?php > return ob_get_clean(); >} >add_shortcode('wp_polly', 'wp_polly_form'); > // 建立轉語音前端表單 >``` > <font size="2"> 建立 HTML 表單(含輸入框、按鈕與 audio 播放器),並透過 add_shortcode 建立short code [wp_polly],可插入在文章或頁面中使用。</font> :::spoiler &nbsp;省略的HTML ```php= <div class="container mt-5"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card shadow-sm"> <div class="card-body"> <h2 class="card-title text-center mb-4">輸入文字轉語音</h2> <div class="mb-3"> <textarea id="polly-text" class="form-control" rows="4" placeholder="輸入要轉換的文字..."></textarea> </div> <div class="d-grid gap-2"> <button id="polly-convert" class="btn btn-primary">轉換為語音</button> </div> <audio id="polly-audio" controls style="display:none; width: 100%; margin-top: 15px;"></audio> </div> </div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> jQuery(document).ready(function($) { $("#polly-convert").click(function() { var text = $("#polly-text").val(); if (text.trim() === "") { alert("請輸入文字!"); return; } $.ajax({ url: "<?php echo admin_url('admin-ajax.php'); ?>", type: "POST", data: { action: "wp_polly_synthesize", text: text }, success: function(response) { if (response.success) { $("#polly-audio").attr("src", response.audio_url).show(); $("#polly-audio")[0].play(); } else { alert("語音轉換失敗:" + response.message); } }, error: function() { alert("請求失敗,請檢查伺服器!"); } }); }); }); </script> ``` ::: <br> 首先要建立使用者輸入要轉語音的文字方塊 ```php <textarea id="polly-text" class="form-control" rows="4" placeholder="輸入要轉換的文字..."></textarea> ``` 接著建立可以轉換語音的按鈕 ```php <button id="polly-convert">轉換為語音</button> ``` ><font size="2">點擊後會觸發 JS 向後端送出 AJAX 請求 ></font> 播放回傳的語音檔 ```php <audio id="polly-audio" controls style="display:none; width: 100%; margin-top: 15px;"></audio> ``` 使用jQuery來聯繫前端後端 ```php jQuery(document).ready(function($) ``` ><font size=2>等待前端表單建立完成 ></font> 取得使用者輸入的文字 ```php var text = $("#polly-text").val(); ``` ><font size=2>把 <textarea> 中輸入的文字讀取出來,準備送到後端。</font> 顯示語音播放器並播放聲音 ```php $("#polly-audio").attr("src", response.audio_url).show(); $("#polly-audio")[0].play(); ``` ><font size=2> 將後端回傳的語音檔 URL 設定給 audio 元件 >自動播放語音 >顯示原本隱藏的播放器(style="display:none") ></font> AJAX 非同步送出資料給後端 ```php= $.ajax({ url: "...", type: "POST", data: { action: "wp_polly_synthesize", text: text }, success: function(response) { ... } }); ``` ><font size=2>讓使用者不需要重新整理頁面 就能把資料送到後端並且後端回傳資料後,直接更新頁面內容</font> 前端 JavaScript 處理流程 ```php= $("#polly-convert").click(function() { var text = $("#polly-text").val(); if (text.trim() === "") { alert("請輸入文字!"); return; } $.ajax({ url: "<?php echo admin_url('admin-ajax.php'); ?>", type: "POST", data: { action: "wp_polly_synthesize", text: text }, success: function(response) { if (response.success) { $("#polly-audio").attr("src", response.audio_url).show(); $("#polly-audio")[0].play(); } else { alert("語音轉換失敗:" + response.message); } }, error: function() { alert("請求失敗,請檢查伺服器!"); } }); }); ``` ><font size=2>點擊按鈕後讀取文字 >if (text.trim() === "") 檢查是否有輸入 >發送 AJAX 請求到 WordPress 的 AJAX handler admin-ajax.php >指定 action 為 wp_polly_synthesize,這樣 WordPress 才會去執行對應的 PHP 函式 ></font> > [!Important] 完整程式碼 > > :::spoiler &nbsp;function wp_polly_form > ```php= >function wp_polly_form() { > ob_start(); > ?> > <div class="container mt-5"> > <div class="row justify-content-center"> > <div class="col-md-8"> > <div class="card shadow-sm"> > <div class="card-body"> > <h2 class="card-title >text-center mb-4">輸入文字轉語音</h2> > <div class="mb-3"> > <textarea id="polly->text" class="form-control" rows="4" placeholder="輸入要轉換的文字..."></textarea> > </div> > <div class="d-grid gap-2"> > <button id="polly->convert" class="btn btn-primary">轉換為語音</button> > </div> > <audio id="polly-audio" controls style="display:none; width: 100%; margin-top: 15px;"></audio> > </div> > </div> > </div> > </div> > </div> > > <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> > <script> > jQuery(document).ready(function($) { > $("#polly-convert").click(function() >{ > var text = $("#polly-text").val(); > if (text.trim() === "") { > alert("請輸入文字!"); > return; > } > > $.ajax({ > url: "<?php echo admin_url('admin-ajax.php'); ?>", > type: "POST", > data: { action: "wp_polly_synthesize", text: text }, > success: function(response) >{ > if (response.success) { > $("#polly-audio").attr("src", response.audio_url).show(); > $("#polly-audio")[0].play(); > } else { > alert("語音轉換失敗:" + response.message); > } > }, > error: function() { > alert("請求失敗,請檢查伺服器!"); > } > }); > }); > }); > </script> > <?php > return ob_get_clean(); >} > ``` > ::: <br> <br> *** ### Function3: 後端處理語音合成請求 ```php= function wp_polly_synthesize() { // 處理 AJAX 發送過來的資料(文字),並使用 AWS Polly 將文字轉換成語音 } ``` <br> 檢查是否有輸入內容,沒有就回傳錯誤 ```php= if (!isset($_POST['text']) || empty(trim($_POST['text']))) { wp_send_json(["success" => false, "message" => "請輸入文字"]); } ``` <br> 建立可選擇的聲音陣列方便切換使用 ```php= $voice_id_options = array( 'Zhiyu (Chinese Female)' => 'Zhiyu', 'Joanna (English US Female)' => 'Joanna', 'Matthew (English US Male)' => 'Matthew', 'Lupe (Spanish Female)' => 'Lupe', ); ``` <br> 準備好連接 AWS Polly 服務時需要用的「設定資料」 ```php= $authentication_method = 'api_key'; $region = 'us-west-2'; $accessKeyId = 'YOUR_KEY'; $secretAccessKey = 'YOUR_SECRET_KEY'; $VoiceId = $voice_id_options['Zhiyu (Chinese Female)']; ``` <br> 整理要轉換的資料 ```php= $text = trim($_POST['text']); ``` 建立 Polly 客戶端 ```php= $polly = new PollyClient([ 'region' => $region, 'version' => 'latest', 'credentials' => [ 'key' => $accessKeyId, 'secret' => $secretAccessKey, ] ]); ``` > <font size="2">建立 Polly 物件並認證 ></font> ```php= $result = $polly->synthesizeSpeech([ 'Text' => $text, 'OutputFormat' => 'mp3', 'VoiceId' => $VoiceId, ]); ``` ><font size="2">發送請求給 AWS Polly,把輸入文字轉為 MP3 音檔格式,語音使用指定 VoiceId ></font> > <br> 取得音檔內容 ```php $audioStream = $result['AudioStream']->getContents(); ``` 接著建立wordpress目錄,儲存上傳後的MP3檔案 ```php= $upload_dir = wp_upload_dir(); $file_path = $upload_dir['path'] . '/polly_audio_' . time() . '.mp3'; file_put_contents($file_path, $audioStream); ``` 取得公開的URL,來獲得音檔內容 ```php $audio_url = str_replace('http://', 'https://', $upload_dir['url']) . '/' . basename($file_path); ``` > <font size="2"> >把HTTP轉換成HTTPS,因為如果不是HTTPS無法存取到使用HTTPS網址進入的網站的內容 ></font> 將音檔的公開 URL 回傳給前端,讓 audio 標籤可以播放它 ```php wp_send_json(["success" => true, "audio_url" => $audio_url]); ``` <br> > [!Important] 完整程式碼 > > :::spoiler &nbsp;wp_polly_synthesize > ```php= >function wp_polly_synthesize() { > if (!isset($_POST['text']) || >empty(trim($_POST['text']))) { > wp_send_json(["success" => false, "message" => "請輸入文字"]); > } > $voice_id_options = array( > 'Zhiyu (Chinese Female)' => 'Zhiyu', > 'Joanna (English US Female)' => 'Joanna', > 'Matthew (English US Male)' => 'Matthew', > 'Lupe (Spanish Female)' => 'Lupe', > ); > $authentication_method = 'api_key'; > $region = 'us-west-2'; > $accessKeyId = 'YOUR_KEY'; > $secretAccessKey = 'YOUR_SECRET_KEY'; > $VoiceId = $voice_id_options['Zhiyu (Chinese Female)']; > //要轉換的文字 > $text = trim($_POST['text']); > error_log($text); > $polly = new PollyClient([ > 'region' => $region, > 'version' => 'latest', > 'credentials' => [ > 'key' => $accessKeyId, > 'secret' => $secretAccessKey, > ] > ]); > > try { > $result = $polly->synthesizeSpeech([ > 'Text' => $text, > 'OutputFormat' => 'mp3', > 'VoiceId' => $VoiceId, > ]); > > $audioStream = $result['AudioStream']->getContents(); > > $upload_dir = wp_upload_dir(); > $file_path = $upload_dir['path'] . '/polly_audio_' . time() . '.mp3'; > file_put_contents($file_path, $audioStream); > > // 取得公開 URL > error_log("Polly success"); > $audio_url = str_replace('http://', 'https://', $upload_dir['url']) . '/' . basename($file_path); > > wp_send_json(["success" => true, "audio_url" => $audio_url]); > > } catch (AwsException $e) { > error_log($e); > wp_send_json(["success" => false, "message" => $e->getMessage()]); > } >} > ``` > ::: <br> *** 最後綁定AJAX,讓有登入的跟沒登入的都可以使用 ```php= add_action('wp_ajax_wp_polly_synthesize', 'wp_polly_synthesize'); add_action('wp_ajax_nopriv_wp_polly_synthesize', 'wp_polly_synthesize'); ``` ><font size = 2> >wp_ajax_:登入使用者用的 >wp_ajax_nopriv_:未登入使用者用的(例如前台訪客) ></font> 這樣就完成 AWS POLLY插件的程式碼了! <font color = #1936C9>完整程式碼</font> :::spoiler &nbsp;aws_polly_service ```php= <?php /** * Plugin Name: AWS_polly * Author: Yungtunchi * Version: 1.0.0 */ if (!defined('ABSPATH')) { exit; } require_once dirname(__DIR__) . '/vendor/autoload.php'; add_action('wp_enqueue_scripts','polly_load_assets'); function polly_load_assets(){ wp_enqueue_style( 'bootstrap-css', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css', array(), '5.3.0', 'all' ); // 載入 jQuery(WordPress 內建 jQuery) wp_enqueue_script('jquery'); // 載入 Bootstrap JS wp_enqueue_script( 'bootstrap-js', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js', array('jquery'), '5.3.0', true ); } use Aws\Polly\PollyClient; use Aws\Exception\AwsException; // 產生 Shortcode,讓前端顯示表單,"shortcode 輸入[wp_polly]" function wp_polly_form() { ob_start(); ?> <div class="container mt-5"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card shadow-sm"> <div class="card-body"> <h2 class="card-title text-center mb-4">輸入文字轉語音</h2> <div class="mb-3"> <textarea id="polly-text" class="form-control" rows="4" placeholder="輸入要轉換的文字..."></textarea> </div> <div class="d-grid gap-2"> <button id="polly-convert" class="btn btn-primary">轉換為語音</button> </div> <audio id="polly-audio" controls style="display:none; width: 100%; margin-top: 15px;"></audio> </div> </div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> jQuery(document).ready(function($) { $("#polly-convert").click(function() { var text = $("#polly-text").val(); if (text.trim() === "") { alert("請輸入文字!"); return; } $.ajax({ url: "<?php echo admin_url('admin-ajax.php'); ?>", type: "POST", data: { action: "wp_polly_synthesize", text: text }, success: function(response) { if (response.success) { $("#polly-audio").attr("src", response.audio_url).show(); $("#polly-audio")[0].play(); } else { alert("語音轉換失敗:" + response.message); } }, error: function() { alert("請求失敗,請檢查伺服器!"); } }); }); }); </script> <?php return ob_get_clean(); } add_shortcode('wp_polly', 'wp_polly_form'); // WordPress AJAX 處理 Polly 轉語音 function wp_polly_synthesize() { if (!isset($_POST['text']) || empty(trim($_POST['text']))) { wp_send_json(["success" => false, "message" => "請輸入文字"]); } $voice_id_options = array( 'Zhiyu (Chinese Female)' => 'Zhiyu', 'Joanna (English US Female)' => 'Joanna', 'Matthew (English US Male)' => 'Matthew', 'Lupe (Spanish Female)' => 'Lupe', // Add more options as needed ); $authentication_method = 'api_key'; $region = 'us-west-2'; // Default region $accessKeyId = 'AKIAWIA4HWSFHJJFRCCJ'; $secretAccessKey = '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'; $VoiceId = $voice_id_options['Zhiyu (Chinese Female)']; //要轉換的文字 $text = trim($_POST['text']); error_log($text); // 建立 AWS Polly 物件 $polly = new PollyClient([ 'region' => $region, 'version' => 'latest', 'credentials' => [ 'key' => $accessKeyId, 'secret' => $secretAccessKey, ] ]); try { // Polly 轉語音 (可中文、英文) $result = $polly->synthesizeSpeech([ 'Text' => $text, 'OutputFormat' => 'mp3', 'VoiceId' => $VoiceId, //女聲 ]); // 取得音檔內容 $audioStream = $result['AudioStream']->getContents(); // 使用 WordPress 內建上傳目錄 $upload_dir = wp_upload_dir(); // error_log(print_r($upload_dir, true)); $file_path = $upload_dir['path'] . '/polly_audio_' . time() . '.mp3'; file_put_contents($file_path, $audioStream); // 取得公開 URL error_log("Polly success"); $audio_url = str_replace('http://', 'https://', $upload_dir['url']) . '/' . basename($file_path); wp_send_json(["success" => true, "audio_url" => $audio_url]); } catch (AwsException $e) { error_log($e); wp_send_json(["success" => false, "message" => $e->getMessage()]); } } add_action('wp_ajax_wp_polly_synthesize', 'wp_polly_synthesize'); add_action('wp_ajax_nopriv_wp_polly_synthesize', 'wp_polly_synthesize'); ?> ``` :::