05-当下不杂

封面图拍摄于 21 年的 10 月 06 日，是我第一次进去夜场。和朋友要了个卡座，周边形形色色，我在其中格格不入。

这周去了南昌出差，每次出差都满是碳水和风土人情的款待。我话比较少，往往在聚餐的时候是一个观察者，每一次都能发觉许多有意思的人，和有意思的点。真好呀～

赖声川的创意学

这周在飞机上粗读了这本书，非常好的一本书。看似在讲 “创意”，实质却是大道万千，殊途同归。分享下我最喜欢的这段话：

我们每一个人内心深处似乎都具有一个个人创意泉源。同时，存在一种更广大、超越个人、属于全人类的共同泉源，里面储存着各种原始、深奥的集体智慧。这个庞大泉源或许在我们体内，或许我们有管道可以通到它。

回来后，我在朋友圈分享了自己的感受，只有寥寥的 3 行文字：

v0 by vercel

看见 Vercel 也推出了自己的生成式 UI 系统，输入文本，就可以绘制一个简单的 UI 界面。与之前分享过的 screenshot-to-code 比较类似。

GPT Crawler

这周自己测试了下，用这个爬虫框架，来爬取自己博客的内容，并将其喂给在训练的自己的分身，效果还不错。
Your browser does not support the video tag.
感受

这周在 AI 上的实践，是基于开源项目：tts，借助其提供的方案，花了不到 2 分钟，在 cloudflare 上就部署好了这个用 Azure tts 来做语音合成的网站：语音合成。

整个方案非常简单，在 cloudflare 上新建一个 workers，写入以下代码就可以实现：

Copy
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})

function generateUUID() {
let uuid = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c) {
let r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return uuid;
}

const API_URL = "https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak";
const DEFAULT_HEADERS = {
authority: "southeastasia.api.speech.microsoft.com",
accept: "*/*",
"accept-language": "zh-CN,zh;q=0.9",
customvoiceconnectionid: generateUUID(),
origin: "https://speech.microsoft.com",
"sec-ch-ua":
'"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
"content-type": "application/json",
};

const speechApi = async (ssml) => {
const data = JSON.stringify({
ssml,
ttsAudioFormat: "audio-24khz-160kbitrate-mono-mp3",
offsetInPlainText: 0,
properties: {
SpeakTriggerSource: "AccTuningPagePlayButton",
},
});

try {
const response = await fetch(API_URL, {
method: "POST",
responseType: "arraybuffer",
headers: DEFAULT_HEADERS,
body: data
});

if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}

return response.arrayBuffer();
} catch (error) {
console.error("Error during API request:", error);
throw error;
}
};

const handleRequest = async (request) => {
// 解析请求 URL
const url = new URL(request.url);

const clientIP = request.headers.get("CF-Connecting-IP")

if (url.pathname == "/") {
const html = await fetch("https://files.chiloh.net/azure-tts.html")

const page = await html.text()
return new Response(page, {
headers: {
"content-type": "text/html;charset=UTF-8",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
"ip": `Access cloudflare's ip:${clientIP}`
},
})
} else if (url.pathname == "/audio") {
// 解析查询参数
const params = new URLSearchParams(url.search);
// 获取查询参数中的文本
const text = params.get("text");
// 获取查询参数中的语速
const rate = params.get("rate");
// 获取查询参数中的音高
const pitch = params.get("pitch");
// 获取查询参数中的音色
const voice = params.get("voice");
// 获取查询参数中的音色风格
const voiceStyle = params.get("voiceStyle");
const ssml = `<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">
<voice name="${voice}">
<mstts:express-as style="${voiceStyle}">
<prosody rate="${rate}%" pitch="${pitch}%">
${text}
</prosody>
</mstts:express-as>
</voice>
</speak>`;

const audio = await speechApi(ssml);
const nowtime = new Date().getTime();
return new Response(audio, {
headers: {
"Content-Type": "audio/mpeg",
"Content-Disposition": `attachment; filename=${nowtime}.mp3`,
},
});
} else if (url.pathname == "/legado") {
const origin = url.origin
const params = new URLSearchParams(url.search);
// 获取查询参数中的文本
// const text = params.get("text");
// 获取查询参数中的语速
const rate = params.get("rate");
// 获取查询参数中的音高
const pitch = params.get("pitch");
// 获取查询参数中的音色
const voice = params.get("voice");
// 获取查询参数中的音色风格
const voiceStyle = params.get("voiceStyle");

const dataJson = {
"concurrentRate": "",//并发率
"contentType": "audio/mpeg",
"header": "",
"id": Date.now(),
"lastUpdateTime": Date.now(),
"loginCheckJs": "",
"loginUi": "",
"loginUrl": "",
"name": `Azure ${voice} ${voiceStyle} pitch: ${pitch} rate:${rate}`,
"url": `${origin}/audio?text={{speakText}}&rate=${rate}&pitch=${pitch}&voice=${voice}&voiceStyle=${voiceStyle},{"method":"GET"}`,
}

return new Response(JSON.stringify(dataJson), {
headers: {
"content-type": "application/json;charset=UTF-8",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
"ip": `Access cloudflare's ip:${clientIP}`
},
})

} else if (url.pathname == "/sourcereader") {
const origin = url.origin
const params = new URLSearchParams(url.search);
// 获取查询参数中的文本
// const text = params.get("text");
// 获取查询参数中的语速
const rate = params.get("rate");
// 获取查询参数中的音高
const pitch = params.get("pitch");
// 获取查询参数中的音色
const voice = params.get("voice");
// 获取查询参数中的音色风格
const voiceStyle = params.get("voiceStyle");

const dataJson = [{
"customOrder": 100,
"id": Date.now(),
"lastUpdateTime": Date.now(),
"name": ` ${voice} ${voiceStyle} pitch: ${pitch} rate:${rate}`,
"url": `${origin}/audio?text={{speakText}}&rate=${rate}&pitch=${pitch}&voice=${voice}&voiceStyle=${voiceStyle},{"method":"GET"}`,
}]
return new Response(JSON.stringify(dataJson), {
headers: {
"content-type": "application/json;charset=UTF-8",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
"ip": `Access cloudflare's ip:${clientIP}`
},
})
}
else {
return new Response("page", {
headers: {
"content-type": "text/html;charset=UTF-8",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
"ip": `Access cloudflare's ip:${clientIP}`
},
})
}

}

如果想要修改页面的样式，可以更改下面这行代码中的 URL，修改为自己调整后的前端页面 URL 地址即可：

Copy
const html = await fetch("https://files.chiloh.net/azure-tts.html")

创造正在变得越来越简单，如果你有好的想法，Just do it !!