# AWS POLLY 功能實作
### 實作流程

### 前置步驟
* 插件基本資訊
```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 省略的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 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 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 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');
?>
```
:::