├── .vercelignore ├── .dockerignore ├── Dockerfile ├── vercel.json ├── package.json ├── LICENSE ├── .github └── workflows │ └── docker.yml ├── api └── tts.js ├── downloadvoicesjson.js ├── README.md ├── .gitignore ├── index.js ├── cf_worker.js └── public └── index.html /.vercelignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .cache/ 4 | temp/ 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *node_modules 2 | npm-debug.log 3 | .git 4 | README.md 5 | .idea 6 | *card.json 7 | *.idea 8 | *.vscode 9 | *.iml 10 | *.DS_Store -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm ci --only=production 8 | 9 | COPY . . 10 | 11 | EXPOSE 3035 12 | 13 | CMD [ "npm", "start" ] 14 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "builds": [ 3 | { 4 | "src": "index.js", 5 | "use": "@vercel/node" 6 | } 7 | ], 8 | "routes": [ 9 | { 10 | "src": "/(.*)", 11 | "dest": "index.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tts", 3 | "version": "1.0.0", 4 | "description": "微软azure文本转语音 音频下载", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "nodemon index.js", 9 | "start": "node index.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/x-dr/tts.git" 14 | }, 15 | "keywords": [ 16 | "tts" 17 | ], 18 | "author": "x-dr", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/x-dr/tts/issues" 22 | }, 23 | "homepage": "https://github.com/x-dr/tts#readme", 24 | "devDependencies": { 25 | "nodemon": "^3.0.1" 26 | }, 27 | "dependencies": { 28 | "axios": "^1.4.0", 29 | "express": "^4.18.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 x-dr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: "docker build release" 2 | 3 | 4 | on: 5 | push: 6 | branches: 7 | - 'main' 8 | # on: 9 | # workflow_dispatch: 10 | # inputs: 11 | # project: 12 | # description: 'Project:VERSION' 13 | # required: true 14 | # default: 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | 23 | - name: Set tag 24 | id: tag 25 | run: | 26 | echo "tag=$(date +%Y)-$(date +%m)-$(date +%d)" >> $GITHUB_ENV 27 | - name: Set up QEMU 28 | uses: docker/setup-qemu-action@v2 29 | 30 | - name: Set up Docker Buildx 31 | uses: docker/setup-buildx-action@v2 32 | 33 | - name: Login Docker Hub 34 | uses: docker/login-action@v2 35 | with: 36 | username: ${{ secrets.DOCKER_USERNAME }} 37 | password: ${{ secrets.DOCKERHUB_TOKEN }} 38 | 39 | - name: Build and push to docker hub 40 | uses: docker/build-push-action@v3 41 | with: 42 | context: . 43 | platforms: linux/amd64,linux/arm64 44 | push: true 45 | build-args: VERSION=${{ env.tag }} 46 | tags: | 47 | ${{ secrets.DOCKER_USERNAME }}/tts-azure:latest 48 | ${{ secrets.DOCKER_USERNAME }}/tts-azure:${{ env.tag }} -------------------------------------------------------------------------------- /api/tts.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | function generateUUID() { 4 | let uuid = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function(c) { 5 | let r = Math.random() * 16 | 0, 6 | v = c === 'x' ? r : (r & 0x3 | 0x8); 7 | return v.toString(16); 8 | }); 9 | return uuid; 10 | } 11 | 12 | const speechApi = (ssml) => { 13 | var data = JSON.stringify({ 14 | ssml, 15 | ttsAudioFormat: "audio-24khz-160kbitrate-mono-mp3", 16 | offsetInPlainText: 0, 17 | properties: { 18 | SpeakTriggerSource: "AccTuningPagePlayButton", 19 | }, 20 | }); 21 | 22 | var config = { 23 | method: "post", 24 | url: "https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak", 25 | responseType: "arraybuffer", 26 | headers: { 27 | authority: "southeastasia.api.speech.microsoft.com", 28 | accept: "*/*", 29 | "accept-language": "zh-CN,zh;q=0.9", 30 | customvoiceconnectionid: generateUUID(), 31 | origin: "https://speech.microsoft.com", 32 | "sec-ch-ua": 33 | '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', 34 | "sec-ch-ua-mobile": "?0", 35 | "sec-ch-ua-platform": '"Windows"', 36 | "sec-fetch-dest": "empty", 37 | "sec-fetch-mode": "cors", 38 | "sec-fetch-site": "same-site", 39 | "user-agent": 40 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", 41 | "content-type": "application/json", 42 | }, 43 | 44 | data: data, 45 | }; 46 | return new Promise((resolve, reject) => { 47 | axios(config) 48 | .then(function (response) { 49 | resolve(response.data); 50 | }) 51 | .catch(function (error) { 52 | reject(error); 53 | }); 54 | }); 55 | }; 56 | 57 | 58 | 59 | export default speechApi; 60 | 61 | -------------------------------------------------------------------------------- /downloadvoicesjson.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import fs from "fs"; 3 | const speechApi = () => { 4 | const data = JSON.stringify({ 5 | "queryCondition": { 6 | "items": [ 7 | { 8 | "name": "VoiceTypeList", 9 | "value": "StandardVoice", 10 | "operatorKind": "Contains" 11 | } 12 | ] 13 | } 14 | }); 15 | // https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/voices 16 | const config = { 17 | method: 'post', 18 | url: 'https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/voices', 19 | headers: { 20 | 'authority': 'southeastasia.api.speech.microsoft.com', 21 | 'accept': 'application/json, text/plain, */*', 22 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 23 | 'customvoiceconnectionid': '97130be0-f304-11ed-b81e-274ad6e5de17', 24 | 'origin': 'https://speech.microsoft.com', 25 | 'sec-ch-ua': '"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"', 26 | 'sec-ch-ua-mobile': '?0', 27 | 'sec-ch-ua-platform': '"Windows"', 28 | 'sec-fetch-dest': 'empty', 29 | 'sec-fetch-mode': 'cors', 30 | 'sec-fetch-site': 'same-site', 31 | 'speechstudio-session-id': '951910a0-f304-11ed-b81e-274ad6e5de17', 32 | 'speechstudio-subscriptionsession-id': 'undefined', 33 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.42', 34 | 'x-ms-useragent': 'SpeechStudio/2021.05.001', 35 | 'content-type': 'application/json' 36 | }, 37 | data: data, 38 | // timeout: 1500, 39 | }; 40 | 41 | return new Promise((resolve, reject) => { 42 | axios(config) 43 | .then(function (response) { 44 | fs.writeFileSync("voices.json", JSON.stringify(response.data)); 45 | resolve(response.data); 46 | }) 47 | .catch(function (error) { 48 | reject(error); 49 | }); 50 | }); 51 | }; 52 | 53 | 54 | speechApi() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 已失效 3 | 4 | 5 | ### 微软azure文本转语音 音频下载 6 | *Demo* : [https://tts.131213.xyz/](https://tts.131213.xyz/) 7 | 8 | 9 | > *本文由GitHub Copilot 生成* 10 | 11 | ### 1. 介绍 12 | 13 | 本项目是基于微软azure的文本转语音服务,通过调用微软azure的api接口,将文本转换为语音,然后下载到本地。 14 | 15 | ### 2. 安装说明 16 | 17 | #### 利用Cloudflare Workers部署 18 | 19 | 1. 新建一个 Cloudflare Worker 20 | 21 | 2. 将 [cf_worker.js](https://github.com/x-dr/tts/blob/main/cf_worker.js) 中的代码复制到 Cloudflare Worker 中并部署即可 22 | 23 | 24 | 25 | > 修改前端 26 | 27 | `https://raw.githubusercontent.com/x-dr/cf_pages/main/tts.html` 是前端代码 ,想修改的自己修改cf_worker.js中的下面行就行 28 | 29 | ```javascript 30 | const html = await fetch("https://raw.githubusercontent.com/x-dr/cf_pages/main/tts.html") 31 | ``` 32 | 33 | *** 34 | 35 | #### 利用docker部署 36 | 37 | 1. 下载docker镜像 38 | 39 | ```bash 40 | docker pull gindex/tts-azure:latest 41 | ``` 42 | 43 | 2. 运行容器 44 | 45 | ```bash 46 | docker run -itd \ 47 | --name tts \ 48 | -p 3035:3035 \ 49 | --restart=always \ 50 | gindex/tts-azure:latest 51 | ``` 52 | 53 | 3. 访问地址 54 | 55 | ```bash 56 | http://ip:3035/ 57 | ``` 58 | 59 | 60 | *** 61 | 62 | #### 利用Vercel部署 63 | 64 | 65 | [![Deploy with Vercel](https://vercel.com/button?utm_source=busiyi&utm_campaign=oss)](https://vercel.com/new/clone?utm_source=busiyi&utm_campaign=oss&repository-url=https://github.com/x-dr/tts) 66 | 67 | #### 利用Linux服务器部署 68 | 69 | 1. 安装nodejs (如果已经安装过nodejs则跳过此步骤) 70 | 71 | ```bash 72 | curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - 73 | sudo apt-get install -y nodejs 74 | ``` 75 | 76 | 2. 安装git (如果已经安装过git则跳过此步骤) 77 | 78 | ```bash 79 | sudo apt-get install git 80 | ``` 81 | 82 | 3. 下载项目 83 | 84 | ```bash 85 | git clone https://github.com/x-dr/tts.git 86 | ``` 87 | 88 | 4. 安装依赖 89 | 90 | ```bash 91 | cd tts 92 | npm install 93 | ``` 94 | 95 | 5. 运行项目 96 | 97 | ```bash 98 | node index.js 99 | ``` 100 | 101 | 6. 访问地址 102 | 103 | ```bash 104 | http://ip:3035/ 105 | ``` 106 | 107 | ### 3. 使用说明 108 | 109 | > iOS源阅读tts复制链接网络导入即可 110 | 111 | 112 | 113 | ### 4. 更新日志 114 | 115 | + 2023-11-27 : 添加阅读3.0和iOS源阅读 tts 116 | 117 | ### Star History 118 | 119 | [![Star History Chart](https://api.star-history.com/svg?repos=x-dr/tts&type=Timeline)](https://star-history.com/#x-dr/tts&Timeline) 120 | 121 | 122 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // const express = require("express"); 2 | import Express from "express"; 3 | const app = Express(); 4 | 5 | import speechApi from "./api/tts.js" // 导入 speechApi 函数 6 | import path from 'path'; 7 | const __dirname = path.resolve(); 8 | 9 | app.set('x-powered-by', false) 10 | app.use(Express.json()) 11 | app.use(Express.urlencoded({ extended: false })) 12 | app.use(Express.static(__dirname+'/public')); 13 | 14 | 15 | 16 | 17 | app.get("/audio", async (req, res) => { 18 | try { 19 | 20 | const { voice, rate, pitch, text,voiceStyle } = req.query; 21 | const ssml = ` 22 | 23 | 24 | 25 | ${text} 26 | 27 | 28 | 29 | `; 30 | 31 | // 调用 speechApi 函数,获取音频数据 32 | const audioData = await speechApi(ssml); 33 | const nowtime = new Date().getTime(); 34 | // 设置响应头为 audio/mp3 35 | res.set("Content-Type", "audio/mpeg"); 36 | res.set("Content-Disposition", `attachment; filename=${nowtime}.mp3`); 37 | 38 | // 将音频数据发送给客户端 39 | res.send(audioData); 40 | } catch (error) { 41 | console.error("Failed to generate audio:",error.message); 42 | const errorJson = { 43 | error: error.message, 44 | }; 45 | res.status(500).json(errorJson); 46 | } 47 | }); 48 | 49 | app.get("/sourcereader", async (req, res) => { 50 | try{ 51 | 52 | const { voice, rate, pitch, voiceStyle } = req.query; 53 | 54 | // const hostname = req.hostname; 55 | const hostname = req.rawHeaders[1]; 56 | console.log(req.rawHeaders[1]); 57 | 58 | const dataJson = { 59 | "concurrentRate": "",//并发率 60 | "contentType": "audio/mpeg", 61 | "header": "", 62 | "id": Date.now(), 63 | "lastUpdateTime": Date.now(), 64 | "loginCheckJs": "", 65 | "loginUi": "", 66 | "loginUrl": "", 67 | "name": `Azure ${voice} ${voiceStyle} pitch: ${pitch} rate:${rate}`, 68 | "url": `https://${hostname}/audio?text={{speakText}}&rate=${rate}&pitch=${pitch}&voice=${voice}&voiceStyle=${voiceStyle},{"method":"GET"}`, 69 | } 70 | 71 | res.send(dataJson); 72 | 73 | }catch(error){ 74 | 75 | } 76 | 77 | }); 78 | app.get("/legado", async (req, res) => { 79 | try{ 80 | 81 | const { voice, rate, pitch, voiceStyle } = req.query; 82 | 83 | // const hostname = req.hostname; 84 | const hostname = req.rawHeaders[1]; 85 | console.log(req.rawHeaders[1]); 86 | 87 | const dataJson = [{ 88 | "customOrder": 100, 89 | "id": Date.now(), 90 | "lastUpdateTime": Date.now(), 91 | "name": ` ${voice} ${voiceStyle} pitch: ${pitch} rate:${rate}`, 92 | "url": `https://${hostname}/audio?text={{speakText}}&rate=${rate}&pitch=${pitch}&voice=${voice}&voiceStyle=${voiceStyle},{"method":"GET"}`, 93 | }] 94 | 95 | res.send(dataJson); 96 | 97 | }catch(error){ 98 | 99 | } 100 | 101 | }); 102 | 103 | // 启动服务器,监听在指定端口上 104 | const port = process.env.PORT || 3035; 105 | app.listen(port, () => { 106 | console.log('Start service success! listening port: http://127.0.0.1:' + port); 107 | }); 108 | -------------------------------------------------------------------------------- /cf_worker.js: -------------------------------------------------------------------------------- 1 | addEventListener('fetch', event => { 2 | event.respondWith(handleRequest(event.request)) 3 | }) 4 | 5 | 6 | function generateUUID() { 7 | let uuid = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c) { 8 | let r = Math.random() * 16 | 0, 9 | v = c === 'x' ? r : (r & 0x3 | 0x8); 10 | return v.toString(16); 11 | }); 12 | return uuid; 13 | } 14 | 15 | const API_URL = "https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak"; 16 | const DEFAULT_HEADERS = { 17 | authority: "southeastasia.api.speech.microsoft.com", 18 | accept: "*/*", 19 | "accept-language": "zh-CN,zh;q=0.9", 20 | customvoiceconnectionid: generateUUID(), 21 | origin: "https://speech.microsoft.com", 22 | "sec-ch-ua": 23 | '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', 24 | "sec-ch-ua-mobile": "?0", 25 | "sec-ch-ua-platform": '"Windows"', 26 | "sec-fetch-dest": "empty", 27 | "sec-fetch-mode": "cors", 28 | "sec-fetch-site": "same-site", 29 | "user-agent": 30 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", 31 | "content-type": "application/json", 32 | }; 33 | 34 | const speechApi = async (ssml) => { 35 | const data = JSON.stringify({ 36 | ssml, 37 | ttsAudioFormat: "audio-24khz-160kbitrate-mono-mp3", 38 | offsetInPlainText: 0, 39 | properties: { 40 | SpeakTriggerSource: "AccTuningPagePlayButton", 41 | }, 42 | }); 43 | 44 | try { 45 | const response = await fetch(API_URL, { 46 | method: "POST", 47 | responseType: "arraybuffer", 48 | headers: DEFAULT_HEADERS, 49 | body: data 50 | }); 51 | 52 | if (!response.ok) { 53 | throw new Error(`Request failed with status ${response.status}`); 54 | } 55 | 56 | return response.arrayBuffer(); 57 | } catch (error) { 58 | console.error("Error during API request:", error); 59 | throw error; 60 | } 61 | }; 62 | 63 | const handleRequest = async (request) => { 64 | // 解析请求 URL 65 | const url = new URL(request.url); 66 | 67 | const clientIP = request.headers.get("CF-Connecting-IP") 68 | 69 | if (url.pathname == "/") { 70 | const html = await fetch("https://raw.githubusercontent.com/x-dr/tts/main/public/index.html") 71 | 72 | const page = await html.text() 73 | return new Response(page, { 74 | headers: { 75 | "content-type": "text/html;charset=UTF-8", 76 | "Access-Control-Allow-Origin": "*", 77 | "Access-Control-Allow-Credentials": "true", 78 | "Access-Control-Allow-Headers": "*", 79 | "Access-Control-Allow-Methods": "*", 80 | "ip": `Access cloudflare's ip:${clientIP}` 81 | }, 82 | }) 83 | } else if (url.pathname == "/audio") { 84 | // 解析查询参数 85 | const params = new URLSearchParams(url.search); 86 | // 获取查询参数中的文本 87 | const text = params.get("text"); 88 | // 获取查询参数中的语速 89 | const rate = params.get("rate"); 90 | // 获取查询参数中的音高 91 | const pitch = params.get("pitch"); 92 | // 获取查询参数中的音色 93 | const voice = params.get("voice"); 94 | // 获取查询参数中的音色风格 95 | const voiceStyle = params.get("voiceStyle"); 96 | const ssml = ` 97 | 98 | 99 | 100 | ${text} 101 | 102 | 103 | 104 | `; 105 | 106 | const audio = await speechApi(ssml); 107 | const nowtime = new Date().getTime(); 108 | return new Response(audio, { 109 | headers: { 110 | "Content-Type": "audio/mpeg", 111 | "Content-Disposition": `attachment; filename=${nowtime}.mp3`, 112 | }, 113 | }); 114 | } else if (url.pathname == "/legado") { 115 | const origin = url.origin 116 | const params = new URLSearchParams(url.search); 117 | // 获取查询参数中的文本 118 | // const text = params.get("text"); 119 | // 获取查询参数中的语速 120 | const rate = params.get("rate"); 121 | // 获取查询参数中的音高 122 | const pitch = params.get("pitch"); 123 | // 获取查询参数中的音色 124 | const voice = params.get("voice"); 125 | // 获取查询参数中的音色风格 126 | const voiceStyle = params.get("voiceStyle"); 127 | 128 | const dataJson = { 129 | "concurrentRate": "",//并发率 130 | "contentType": "audio/mpeg", 131 | "header": "", 132 | "id": Date.now(), 133 | "lastUpdateTime": Date.now(), 134 | "loginCheckJs": "", 135 | "loginUi": "", 136 | "loginUrl": "", 137 | "name": `Azure ${voice} ${voiceStyle} pitch: ${pitch} rate:${rate}`, 138 | "url": `${origin}/audio?text={{speakText}}&rate=${rate}&pitch=${pitch}&voice=${voice}&voiceStyle=${voiceStyle},{"method":"GET"}`, 139 | } 140 | 141 | return new Response(JSON.stringify(dataJson), { 142 | headers: { 143 | "content-type": "application/json;charset=UTF-8", 144 | "Access-Control-Allow-Origin": "*", 145 | "Access-Control-Allow-Credentials": "true", 146 | "Access-Control-Allow-Headers": "*", 147 | "Access-Control-Allow-Methods": "*", 148 | "ip": `Access cloudflare's ip:${clientIP}` 149 | }, 150 | }) 151 | 152 | 153 | 154 | } else if (url.pathname == "/sourcereader") { 155 | const origin = url.origin 156 | const params = new URLSearchParams(url.search); 157 | // 获取查询参数中的文本 158 | // const text = params.get("text"); 159 | // 获取查询参数中的语速 160 | const rate = params.get("rate"); 161 | // 获取查询参数中的音高 162 | const pitch = params.get("pitch"); 163 | // 获取查询参数中的音色 164 | const voice = params.get("voice"); 165 | // 获取查询参数中的音色风格 166 | const voiceStyle = params.get("voiceStyle"); 167 | 168 | const dataJson = [{ 169 | "customOrder": 100, 170 | "id": Date.now(), 171 | "lastUpdateTime": Date.now(), 172 | "name": ` ${voice} ${voiceStyle} pitch: ${pitch} rate:${rate}`, 173 | "url": `${origin}/audio?text={{speakText}}&rate=${rate}&pitch=${pitch}&voice=${voice}&voiceStyle=${voiceStyle},{"method":"GET"}`, 174 | }] 175 | return new Response(JSON.stringify(dataJson), { 176 | headers: { 177 | "content-type": "application/json;charset=UTF-8", 178 | "Access-Control-Allow-Origin": "*", 179 | "Access-Control-Allow-Credentials": "true", 180 | "Access-Control-Allow-Headers": "*", 181 | "Access-Control-Allow-Methods": "*", 182 | "ip": `Access cloudflare's ip:${clientIP}` 183 | }, 184 | }) 185 | } 186 | else { 187 | return new Response("page", { 188 | headers: { 189 | "content-type": "text/html;charset=UTF-8", 190 | "Access-Control-Allow-Origin": "*", 191 | "Access-Control-Allow-Credentials": "true", 192 | "Access-Control-Allow-Headers": "*", 193 | "Access-Control-Allow-Methods": "*", 194 | "ip": `Access cloudflare's ip:${clientIP}` 195 | }, 196 | }) 197 | } 198 | 199 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 语音合成 12 | 138 | 139 | 140 | 141 |
142 |

语音合成

143 |
144 | 147 | 150 | 153 | 156 | 157 | 158 | 159 |
160 | 161 | 162 | 163 |
164 |
165 | 166 | 167 |
168 |
169 | 170 |
171 | 172 | 173 | 174 | 179 | 180 | 280 | 281 | 282 | --------------------------------------------------------------------------------