コミュニティ主導の無料AIツール、TTS、Twitch配信者向けAPI
コミュニティ主導の無料AIツール、TTS、Twitch配信者向けAPI

Streamer.botでカスタムスクリプトを使って無料のTTS音声を再生する方法

Streamer.bot 内部で直接、カスタムの Execute Code スクリプトを追加することで、Twitch 配信用の無料 AI TTS(テキスト読み上げ)音声を再生する方法を学びます。Speaker.bot は不要です。
このページは、利便性のために、私たちのやる気に満ちたAIインターンが英語から翻訳しました。まだ学習中のため、いくつか誤りがあるかもしれません。最も正確な情報については、英語版をご覧ください。
使い方ガイド Streamer.botでカスタムスクリプトを使って無料のTTS音声を再生する方法

PotatoHotDog は、Speaker.bot や他のサードパーティ製ミドルウェアを必要とせず、Streamer.bot から直接呼び出せる無料の TTS(Text-to-Speech)API を提供します。このガイドでは、音声を生成し、音声ファイルをダウンロードして自動的に再生する、Streamer.bot の Execute Code サブアクションに、小さなカスタム C# スクリプトを追加する方法を説明します。

コピペですぐ使える2つの用意済みスクリプトを用意しています。1つは静的な Piper TTS の声を再生するスクリプト、もう1つは自分のクローン声を再生するスクリプトです。どちらのスクリプトも、PotatoHotDog APIキー、ボイスID、そして話してほしいテキストだけが必要です。

前提となる引数

テキストを音声合成(TTS)するには、Execute Code サブアクションが実行される前に、Streamer.bot で3つの引数を指定する必要があります。つまり、以下の3つを指定する必要があります:

  • APIキー。
  • 生成したい声のIDです。静的な声またはクローン声のどちらかを指定します。
  • テキスト。

これらは以下の Streamer.bot の引数に対応します。

  • potatoKey
  • potatoVoiceID
  • potatoText

アクション内で引数を指定するには、Sub Actions を右クリックして、Add、Core、Arguments を順に選択し、最後に Set Argument を選択します。

Streamer.bot で Set Argument サブアクションを追加

Streamer.bot で Execute Code アクションを追加する

Streamer.bot を開き、Actions タブに移動します。新しいアクションを作成するか、既存のものを開いて、後で自分が認識できる名前を付けてください。例としては「PotatoHotDog TTS」のような名前です。

アクションを選択した状態で、サブアクションの下にある Add をクリックし、次に Core を選択してから Execute C# Code を選びます。これにより、Streamer.bot がその場でコンパイルして実行する生の C# コードを貼り付けられるサブアクションが追加されます。

Streamer.bot で C# コードを実行するサブアクションを追加

以下のスクリプトのいずれかをコードエディタに貼り付けてください。静的な声を再生したい場合とクローン声を再生したい場合で、使うスクリプトが異なります。どちらのスクリプトも、アクション内にすでにいくつかの引数が存在することを前提としています。これらを設定する方法については、下記の前提引数を参照してください。

スクリプトを貼り付けた後は、Find Refs と Compile をクリックして、スクリプトが正しくコンパイルされていることを確認してください。

スクリプトを貼り付けて、Streamer.bot でコンパイルしてください。

PotatoHotDog の API キーはダッシュボードから生成できます。アクションを保存してから、手動で実行するか、コマンドやチャンネルポイント報酬に紐づけて、音声が正しく再生されるかをテストしてください。

静的な声を再生するスクリプト

このスクリプトは固定の Piper TTS ボイスを再生します。音声を生成し、処理が完了するまで待機し、生成された音声ファイルをダウンロードして Streamer.bot で再生します。利用可能な固定ボイスIDの一覧は Static Voices ページで確認できます。

スタティックボイスを閲覧する using System; using System.IO; using System.Net.Http; using System.Threading; using Newtonsoft.Json.Linq; public class CPHInline { // Tracks the version of this script for troubleshooting and support. private const string VERSION = "2 (static)"; // Base URL for the endpoint that creates a new TTS item. private const string BASE_URL_GENERATE = "https://api-generate-static-voice-tts-item-v1.potatohotdog.com/"; // Base URL for the endpoint that polls a TTS item's status. private const string BASE_URL_STATUS = "https://api-fetch-static-voice-tts-item-v1.potatohotdog.com/"; // Tracks the next numbered log argument. private int logIndex = 0; // Reuses one HTTP client for all API requests. private static readonly HttpClient httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; // Adds a log message as a numbered action argument. private void Log(string message) { // Stores the message under a numbered key. CPH.SetArgument($"potatoLog{logIndex}", message); // Advances the log index for the next message. logIndex++; // Keeps the total log count in sync. CPH.SetArgument("potatoLogCount", logIndex); } // Ensures a required action argument exists and is not empty. private bool RequireArgument(string name, out string value) { // Fails when the argument is missing or blank. if (!CPH.TryGetArg(name, out value) || string.IsNullOrWhiteSpace(value)) { value = ""; return false; } return true; } // URL-encodes a value so it can safely be used in a query string. private string EncodeUrlValue(string value) { // Escapes reserved characters for safe use in a URL. return Uri.EscapeDataString(value); } // Performs an HTTP GET request and returns the response body. private string HttpGet(string url) { try { Log($"GET {url}"); // Sends the HTTP request synchronously. HttpResponseMessage response = httpClient.GetAsync(url).GetAwaiter().GetResult(); // Reads the complete response body. string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); Log($"HTTP status: {(int)response.StatusCode} {response.ReasonPhrase}"); // Stops when the server returns an unsuccessful status code. if (!response.IsSuccessStatusCode) { Log($"Error: HTTP request failed with status {(int)response.StatusCode} {response.ReasonPhrase}."); // Logs the server error response when available. if (!string.IsNullOrWhiteSpace(responseBody)) { Log($"Error response: {responseBody}"); } return null; } Log($"Received {responseBody.Length} characters."); return responseBody; } catch (Exception exception) { // Logs network, timeout, and other HTTP-related exceptions. Log($"Error: HTTP request failed: {exception.Message}"); return null; } } // Downloads a file from a URL and saves it to disk. private bool DownloadFile(string url, string destinationPath) { try { Log($"Downloading audio from \"{url}\"."); Log($"Saving audio to \"{destinationPath}\"."); // Sends the audio download request. HttpResponseMessage response = httpClient.GetAsync(url).GetAwaiter().GetResult(); Log($"Audio download HTTP status: {(int)response.StatusCode} {response.ReasonPhrase}"); // Reads the complete audio response into memory. byte[] fileData = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); // Stops when the audio server returns an unsuccessful status. if (!response.IsSuccessStatusCode) { Log($"Error: Audio download failed with status {(int)response.StatusCode} {response.ReasonPhrase}."); return false; } // Rejects an empty audio response. if (fileData == null || fileData.Length == 0) { Log("Error: The downloaded audio file is empty."); return false; } // Writes the downloaded audio bytes to the destination file. File.WriteAllBytes(destinationPath, fileData); Log($"Downloaded {fileData.Length} audio bytes."); // Confirms that the audio file now exists on disk. if (!File.Exists(destinationPath)) { Log("Error: The audio download completed, but the file does not exist on disk."); return false; } // Reports the final file size from disk. FileInfo fileInfo = new FileInfo(destinationPath); Log($"Audio file saved successfully. File size: {fileInfo.Length} bytes."); return true; } catch (Exception exception) { // Logs download, filesystem, and permission errors. Log($"Error: Could not download the audio file: {exception.Message}"); return false; } } // Extracts a required string value from a JSON response. private bool TryGetJsonString(string json, string propertyName, out string value) { value = ""; try { // Parses the response body as a JSON object. JObject jsonObject = JObject.Parse(json); // Retrieves the requested JSON property. JToken property = jsonObject[propertyName]; if (property == null) { Log($"Error: JSON response does not contain \"{propertyName}\"."); return false; } value = property.ToString(); // Rejects empty JSON string values. if (string.IsNullOrWhiteSpace(value)) { Log($"Error: JSON property \"{propertyName}\" is empty."); value = ""; return false; } return true; } catch (Exception exception) { // Logs malformed JSON and property conversion errors. Log($"Error: Could not parse JSON property \"{propertyName}\": {exception.Message}"); return false; } } // Extracts a required integer value from a JSON response. private bool TryGetJsonInteger(string json, string propertyName, out int value) { value = 0; try { // Parses the response body as a JSON object. JObject jsonObject = JObject.Parse(json); // Retrieves the requested JSON property. JToken property = jsonObject[propertyName]; if (property == null) { Log($"Error: JSON response does not contain \"{propertyName}\"."); return false; } // Converts the JSON property to an integer. if (!int.TryParse(property.ToString(), out value)) { Log($"Error: JSON property \"{propertyName}\" is not a valid integer."); value = 0; return false; } return true; } catch (Exception exception) { // Logs malformed JSON and property conversion errors. Log($"Error: Could not parse JSON property \"{propertyName}\": {exception.Message}"); return false; } } // Creates a unique PotatoHotdog WAV filename. private string CreateAudioFileName() { // Uses eight GUID characters to avoid overwriting previous files. string uniqueID = Guid.NewGuid().ToString("N").Substring(0, 8).ToUpperInvariant(); return $"potatoAudio{uniqueID}.wav"; } // Creates and returns the dedicated PotatoAudio folder. private string GetAudioFolder() { // Uses Streamer.bot's application directory instead of the process working directory. string streamerBotFolder = AppDomain.CurrentDomain.BaseDirectory; // Places generated audio files inside a dedicated subfolder. string audioFolder = Path.Combine(streamerBotFolder, "temp_potatohotdog_audio"); Log($"Streamer.bot folder: \"{streamerBotFolder}\"."); Log($"Audio folder: \"{audioFolder}\"."); // Creates the audio folder if it does not already exist. if (!Directory.Exists(audioFolder)) { Log("Audio folder does not exist. Creating it..."); Directory.CreateDirectory(audioFolder); Log("Audio folder created."); } else { Log("Audio folder already exists."); } return audioFolder; } // Runs the main PotatoHotdog TTS action. public bool Execute() { // Resets log numbering for this execution. logIndex = 0; CPH.SetArgument("potatoLogCount", 0); Log("Starting script..."); // Logs the script version so runs can be tracked over time. Log($"Version: {VERSION}"); // Retrieves the PotatoHotdog API key. if (!RequireArgument("potatoKey", out string potatoKey)) { Log("Error: potatoKey variable is missing. Please login to potatohotdog.com, generate an API key, create a set variable action and save the variable \"potatoKey\" with your key."); return false; } // Retrieves the requested voice ID. if (!RequireArgument("potatoVoiceID", out string potatoVoiceID)) { Log("Error: potatoVoiceID variable is missing. Please login to potatohotdog.com, copy a voice ID, create a set variable action and save the variable \"potatoVoiceID\" with your voice ID."); return false; } // Retrieves the text that should be converted to speech. if (!RequireArgument("potatoText", out string potatoText)) { Log("Error: potatoText variable is missing. Create a set variable action and save the variable \"potatoText\" with the text you want the TTS to say."); return false; } // Logs the non-secret input values. Log("potatoKey was provided."); Log($"potatoVoiceID=\"{potatoVoiceID}\""); Log($"potatoText=\"{potatoText}\""); // Builds the API URL used to create the TTS item. string createUrl = BASE_URL_GENERATE + "?key=" + EncodeUrlValue(potatoKey) + "&voice=" + EncodeUrlValue(potatoVoiceID) + "&text=" + EncodeUrlValue(potatoText); // Requests creation of the TTS item. string createJson = HttpGet(createUrl); if (createJson == null) { Log("Error: Failed to create the TTS item."); return false; } Log($"Create response: {createJson}"); // Extracts the created item's ID from the API response. if (!TryGetJsonString(createJson, "itemID", out string itemID)) { Log("Error: Could not retrieve itemID from the create response."); return false; } Log($"itemID=\"{itemID}\""); // Builds the API URL used to poll the TTS item status. string fetchUrl = BASE_URL_STATUS + "?key=" + EncodeUrlValue(potatoKey) + "&item=" + EncodeUrlValue(itemID); // Tracks whether the TTS item finishes processing. bool completed = false; // Stores the final fetch response containing the audio URL. string completedFetchJson = null; // Polls the item status up to 60 times. for (int attempt = 1; attempt <= 60; attempt++) { Log($"Checking item status. Attempt {attempt} of 60."); // Fetches the latest TTS item status. string fetchJson = HttpGet(fetchUrl); if (fetchJson == null) { Log($"Error: Failed to fetch the TTS item on attempt {attempt}."); return false; } Log($"Fetch response: {fetchJson}"); // Extracts the current processing status from the response. if (!TryGetJsonInteger(fetchJson, "statusID", out int statusID)) { Log("Error: Could not retrieve statusID from the fetch response."); return false; } Log($"statusID={statusID}"); // Stops polling when the API reports that processing is complete. if (statusID == 0) { completed = true; completedFetchJson = fetchJson; Log("statusID is 0. TTS processing is complete."); break; } // Waits five seconds before the next polling request. if (attempt < 60) { Log("TTS processing is not complete. Waiting five seconds..."); Thread.Sleep(5000); } } // Fails when the item has not completed after all polling attempts. if (!completed) { Log("Error: TTS processing did not complete after 60 attempts."); return false; } // Extracts the generated audio URL from the completed item response. if (!TryGetJsonString(completedFetchJson, "audioURL", out string audioURL)) { Log("Error: Could not retrieve audioURL from the completed fetch response."); return false; } Log($"audioURL=\"{audioURL}\""); string audioFolder; try { // Resolves and creates the dedicated PotatoAudio directory. audioFolder = GetAudioFolder(); } catch (Exception exception) { Log($"Error: Could not create or access the audio folder: {exception.Message}"); return false; } // Creates a unique local filename for the generated WAV file. string audioFileName = CreateAudioFileName(); // Combines the audio folder and unique filename. string audioFilePath = Path.Combine(audioFolder, audioFileName); Log($"Generated audio filename: \"{audioFileName}\"."); Log($"Local audio path: \"{audioFilePath}\"."); // Downloads the generated audio file to the PotatoAudio folder. if (!DownloadFile(audioURL, audioFilePath)) { Log("Error: Failed to download the generated TTS audio."); return false; } try { Log($"Starting playback of \"{audioFilePath}\"."); // Plays the WAV file and waits until playback has finished. CPH.PlaySound(audioFilePath, 1.0f, true, audioFileName, true); Log("Audio playback finished."); } catch (Exception exception) { // Logs Streamer.bot audio playback errors. Log($"Error: Could not play the audio file: {exception.Message}"); return false; } Log("Finished."); return true; } }

クローンボイスを再生するスクリプト

このスクリプトは上の静的ボイススクリプトと同じように動作しますが、代わりに自分のクローンボイスのひとつを使って音声を生成します。PotatoHotDog のダッシュボード内の Cloned Voices ページで、クローンボイスのIDを見つけることができます。

クローンした声を表示する using System; using System.IO; using System.Net.Http; using System.Threading; using Newtonsoft.Json.Linq; public class CPHInline { // Tracks the version of this script for troubleshooting and support. private const string VERSION = "2 (cloned)"; // Base URL for the endpoint that creates a new TTS item. private const string BASE_URL_GENERATE = "https://api-generate-cloned-voice-tts-item-v1.potatohotdog.com/"; // Base URL for the endpoint that polls a TTS item's status. private const string BASE_URL_STATUS = "https://api-fetch-cloned-voice-tts-item-v1.potatohotdog.com/"; // Tracks the next numbered log argument. private int logIndex = 0; // Reuses one HTTP client for all API requests. private static readonly HttpClient httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; // Adds a log message as a numbered action argument. private void Log(string message) { // Stores the message under a numbered key. CPH.SetArgument($"potatoLog{logIndex}", message); // Advances the log index for the next message. logIndex++; // Keeps the total log count in sync. CPH.SetArgument("potatoLogCount", logIndex); } // Ensures a required action argument exists and is not empty. private bool RequireArgument(string name, out string value) { // Fails when the argument is missing or blank. if (!CPH.TryGetArg(name, out value) || string.IsNullOrWhiteSpace(value)) { value = ""; return false; } return true; } // URL-encodes a value so it can safely be used in a query string. private string EncodeUrlValue(string value) { // Escapes reserved characters for safe use in a URL. return Uri.EscapeDataString(value); } // Performs an HTTP GET request and returns the response body. private string HttpGet(string url) { try { Log($"GET {url}"); // Sends the HTTP request synchronously. HttpResponseMessage response = httpClient.GetAsync(url).GetAwaiter().GetResult(); // Reads the complete response body. string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); Log($"HTTP status: {(int)response.StatusCode} {response.ReasonPhrase}"); // Stops when the server returns an unsuccessful status code. if (!response.IsSuccessStatusCode) { Log($"Error: HTTP request failed with status {(int)response.StatusCode} {response.ReasonPhrase}."); // Logs the server error response when available. if (!string.IsNullOrWhiteSpace(responseBody)) { Log($"Error response: {responseBody}"); } return null; } Log($"Received {responseBody.Length} characters."); return responseBody; } catch (Exception exception) { // Logs network, timeout, and other HTTP-related exceptions. Log($"Error: HTTP request failed: {exception.Message}"); return null; } } // Downloads a file from a URL and saves it to disk. private bool DownloadFile(string url, string destinationPath) { try { Log($"Downloading audio from \"{url}\"."); Log($"Saving audio to \"{destinationPath}\"."); // Sends the audio download request. HttpResponseMessage response = httpClient.GetAsync(url).GetAwaiter().GetResult(); Log($"Audio download HTTP status: {(int)response.StatusCode} {response.ReasonPhrase}"); // Reads the complete audio response into memory. byte[] fileData = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); // Stops when the audio server returns an unsuccessful status. if (!response.IsSuccessStatusCode) { Log($"Error: Audio download failed with status {(int)response.StatusCode} {response.ReasonPhrase}."); return false; } // Rejects an empty audio response. if (fileData == null || fileData.Length == 0) { Log("Error: The downloaded audio file is empty."); return false; } // Writes the downloaded audio bytes to the destination file. File.WriteAllBytes(destinationPath, fileData); Log($"Downloaded {fileData.Length} audio bytes."); // Confirms that the audio file now exists on disk. if (!File.Exists(destinationPath)) { Log("Error: The audio download completed, but the file does not exist on disk."); return false; } // Reports the final file size from disk. FileInfo fileInfo = new FileInfo(destinationPath); Log($"Audio file saved successfully. File size: {fileInfo.Length} bytes."); return true; } catch (Exception exception) { // Logs download, filesystem, and permission errors. Log($"Error: Could not download the audio file: {exception.Message}"); return false; } } // Extracts a required string value from a JSON response. private bool TryGetJsonString(string json, string propertyName, out string value) { value = ""; try { // Parses the response body as a JSON object. JObject jsonObject = JObject.Parse(json); // Retrieves the requested JSON property. JToken property = jsonObject[propertyName]; if (property == null) { Log($"Error: JSON response does not contain \"{propertyName}\"."); return false; } value = property.ToString(); // Rejects empty JSON string values. if (string.IsNullOrWhiteSpace(value)) { Log($"Error: JSON property \"{propertyName}\" is empty."); value = ""; return false; } return true; } catch (Exception exception) { // Logs malformed JSON and property conversion errors. Log($"Error: Could not parse JSON property \"{propertyName}\": {exception.Message}"); return false; } } // Extracts a required integer value from a JSON response. private bool TryGetJsonInteger(string json, string propertyName, out int value) { value = 0; try { // Parses the response body as a JSON object. JObject jsonObject = JObject.Parse(json); // Retrieves the requested JSON property. JToken property = jsonObject[propertyName]; if (property == null) { Log($"Error: JSON response does not contain \"{propertyName}\"."); return false; } // Converts the JSON property to an integer. if (!int.TryParse(property.ToString(), out value)) { Log($"Error: JSON property \"{propertyName}\" is not a valid integer."); value = 0; return false; } return true; } catch (Exception exception) { // Logs malformed JSON and property conversion errors. Log($"Error: Could not parse JSON property \"{propertyName}\": {exception.Message}"); return false; } } // Creates a unique PotatoHotdog WAV filename. private string CreateAudioFileName() { // Uses eight GUID characters to avoid overwriting previous files. string uniqueID = Guid.NewGuid().ToString("N").Substring(0, 8).ToUpperInvariant(); return $"potatoAudio{uniqueID}.wav"; } // Creates and returns the dedicated PotatoAudio folder. private string GetAudioFolder() { // Uses Streamer.bot's application directory instead of the process working directory. string streamerBotFolder = AppDomain.CurrentDomain.BaseDirectory; // Places generated audio files inside a dedicated subfolder. string audioFolder = Path.Combine(streamerBotFolder, "temp_potatohotdog_audio"); Log($"Streamer.bot folder: \"{streamerBotFolder}\"."); Log($"Audio folder: \"{audioFolder}\"."); // Creates the audio folder if it does not already exist. if (!Directory.Exists(audioFolder)) { Log("Audio folder does not exist. Creating it..."); Directory.CreateDirectory(audioFolder); Log("Audio folder created."); } else { Log("Audio folder already exists."); } return audioFolder; } // Runs the main PotatoHotdog TTS action. public bool Execute() { // Resets log numbering for this execution. logIndex = 0; CPH.SetArgument("potatoLogCount", 0); Log("Starting script..."); // Logs the script version so runs can be tracked over time. Log($"Version: {VERSION}"); // Retrieves the PotatoHotdog API key. if (!RequireArgument("potatoKey", out string potatoKey)) { Log("Error: potatoKey variable is missing. Please login to potatohotdog.com, generate an API key, create a set variable action and save the variable \"potatoKey\" with your key."); return false; } // Retrieves the requested voice ID. if (!RequireArgument("potatoVoiceID", out string potatoVoiceID)) { Log("Error: potatoVoiceID variable is missing. Please login to potatohotdog.com, copy a voice ID, create a set variable action and save the variable \"potatoVoiceID\" with your voice ID."); return false; } // Retrieves the text that should be converted to speech. if (!RequireArgument("potatoText", out string potatoText)) { Log("Error: potatoText variable is missing. Create a set variable action and save the variable \"potatoText\" with the text you want the TTS to say."); return false; } // Logs the non-secret input values. Log("potatoKey was provided."); Log($"potatoVoiceID=\"{potatoVoiceID}\""); Log($"potatoText=\"{potatoText}\""); // Builds the API URL used to create the TTS item. string createUrl = BASE_URL_GENERATE + "?key=" + EncodeUrlValue(potatoKey) + "&voice=" + EncodeUrlValue(potatoVoiceID) + "&text=" + EncodeUrlValue(potatoText); // Requests creation of the TTS item. string createJson = HttpGet(createUrl); if (createJson == null) { Log("Error: Failed to create the TTS item."); return false; } Log($"Create response: {createJson}"); // Extracts the created item's ID from the API response. if (!TryGetJsonString(createJson, "itemID", out string itemID)) { Log("Error: Could not retrieve itemID from the create response."); return false; } Log($"itemID=\"{itemID}\""); // Builds the API URL used to poll the TTS item status. string fetchUrl = BASE_URL_STATUS + "?key=" + EncodeUrlValue(potatoKey) + "&item=" + EncodeUrlValue(itemID); // Tracks whether the TTS item finishes processing. bool completed = false; // Stores the final fetch response containing the audio URL. string completedFetchJson = null; // Polls the item status up to 60 times. for (int attempt = 1; attempt <= 60; attempt++) { Log($"Checking item status. Attempt {attempt} of 60."); // Fetches the latest TTS item status. string fetchJson = HttpGet(fetchUrl); if (fetchJson == null) { Log($"Error: Failed to fetch the TTS item on attempt {attempt}."); return false; } Log($"Fetch response: {fetchJson}"); // Extracts the current processing status from the response. if (!TryGetJsonInteger(fetchJson, "statusID", out int statusID)) { Log("Error: Could not retrieve statusID from the fetch response."); return false; } Log($"statusID={statusID}"); // Stops polling when the API reports that processing is complete. if (statusID == 0) { completed = true; completedFetchJson = fetchJson; Log("statusID is 0. TTS processing is complete."); break; } // Waits five seconds before the next polling request. if (attempt < 60) { Log("TTS processing is not complete. Waiting five seconds..."); Thread.Sleep(5000); } } // Fails when the item has not completed after all polling attempts. if (!completed) { Log("Error: TTS processing did not complete after 60 attempts."); return false; } // Extracts the generated audio URL from the completed item response. if (!TryGetJsonString(completedFetchJson, "audioURL", out string audioURL)) { Log("Error: Could not retrieve audioURL from the completed fetch response."); return false; } Log($"audioURL=\"{audioURL}\""); string audioFolder; try { // Resolves and creates the dedicated PotatoAudio directory. audioFolder = GetAudioFolder(); } catch (Exception exception) { Log($"Error: Could not create or access the audio folder: {exception.Message}"); return false; } // Creates a unique local filename for the generated WAV file. string audioFileName = CreateAudioFileName(); // Combines the audio folder and unique filename. string audioFilePath = Path.Combine(audioFolder, audioFileName); Log($"Generated audio filename: \"{audioFileName}\"."); Log($"Local audio path: \"{audioFilePath}\"."); // Downloads the generated audio file to the PotatoAudio folder. if (!DownloadFile(audioURL, audioFilePath)) { Log("Error: Failed to download the generated TTS audio."); return false; } try { Log($"Starting playback of \"{audioFilePath}\"."); // Plays the WAV file and waits until playback has finished. CPH.PlaySound(audioFilePath, 1.0f, true, audioFileName, true); Log("Audio playback finished."); } catch (Exception exception) { // Logs Streamer.bot audio playback errors. Log($"Error: Could not play the audio file: {exception.Message}"); return false; } Log("Finished."); return true; } }

トラブルシューティング

スクリプトが期待通りに動作しない場合、トラブルシューティングの最善の方法はアクションログを確認することです。

アクション履歴からアクション変数を確認することで、これを実行できます。

Streamer.bot のアクション履歴からアクション変数を確認する

このガイドは2026-07-11に初公開され、最終更新は2026-07-11です。

ほかの類似ガイド

ほかにも、役に立つ使い方ガイドをいくつかご紹介します。

Streamer.bot でカスタムスクリプトを使って AI 画像を生成する方法
Streamer.bot でカスタムスクリプトを使って AI 画像を生成する方法
Streamer.bot 内で直接、カスタム Execute Code スクリプトを追加するだけで、Twitch 配信向けの無料AI画像を生成する方法を学びます。サードパーティ製ミドルウェアは不要です。
ガイドを読む
Streamer.botをAIに接続する方法
Streamer.botをAIに接続する方法
Streamer.bot で Fetch URL サブアクションと PotatoHotDog Generate AI Response API を使って、Twitch 配信の無料 AI(LLM)レスポンスを生成する方法を学びましょう。カスタムスクリプトは不要です。
ガイドを読む
Streamer.bot で 動的ブラウザソースを使って 無料の TTS 音声を再生する方法
Streamer.bot で 動的ブラウザソースを使って 無料の TTS 音声を再生する方法
Streamer.bot を使って、Dynamic Browser Source とシンプルな Fetch URL webhook リクエストを活用し、カスタムスクリプト不要で、あなたの Twitch 配信向けの無料 AI TTS(Text-to-Speech)音声を再生する方法を学びましょう。
ガイドを読む