牙叔教程 简单易懂
我想学习某个人的文案, 怎么把它的文案全下载下来?
- 批量下载视频和音频
- 批量音频转文字
下载视频和音频
我在github找到的是这个仓库
https://github.com/Johnserf-Seed/TikTokDownload
经过实际测试, 可以使用, 只是失败的频率略高.
获取用户信息的时候, 尝试了5次才正确获取到用户信息,
只有获取到用户信息, 才能下载到视频;
虽然失败的概率有点高, 但是也仅是要多按几下 ↑ 和 回车键, 也不费啥事.
如果, 填写了cookie, 那么成功率大大提高, 不懂的话就看仓库的 issue.
这是下载好的视频和音频, 可以看到mp4和mp3的后缀名,
音频是直接下载下来的, 不是后期提取的.
文件 conf.ini 默认是不下载音频的, 所以要改一下.
music后面默认是no, 改成yes
[music]
# 视频原声保存(yes|no)
music = yes
其他的就照着README.md做就可以了
语音转文字
一共三种方案
- 某讯
- 微软
- 有道
某讯
我看了一下某讯的价格, 70块30个小时,
微软
微软也有语音转文字的服务, 就是要绑卡, 绑卡的话, 某宝是几十块左右;
网易
网易, 他是2块钱一个小时, 这个就很人性化,
某讯开始就是30个小时, 我又用不了那么多, 至少我现在用不了.
批量自动化的时候, 你才需要买它, 你单独转的话, 可以直接使用
网易见外 https://jianwai.youdao.com/index/0
网易见外不用花一分钱, 但是依然很好用
那么今天给大家试试两种方案
- 微软
- 网易 - 长语音转写服务
微软
如果你没有卡, 可以去某宝搞;
如果你不会创建微软的服务, 可以看这个教程
文本转语音-微软Azure-一步一步教你从注册到使用
https://mp.weixin.qq.com/s/ycc35s51W0_4-raoF-fsMw
我们要把音频转文字, 所以选择第二个, 脱机字幕
他的文档有代码, 直接复制黏贴就可以使用了
代码在此
下面是我的代码例子
语音转文字, 分两步
- mp3转wav
- wav转文字
// 此示例支持最多 30 秒的音频。
const fs = require("fs");
const sdk = require("microsoft-cognitiveservices-speech-sdk");
const { exec } = require("child_process");
const { secretKey: YourSubscriptionKey, region: YourServiceRegion, mp3FilePath } = require("./config.js");
let wavFilePath = mp3FilePath.replace(".mp3", ".wav");
let mp3ToWavCmd = `ffmpeg -i "${mp3FilePath}" "${wavFilePath}"`;
const speechConfig = sdk.SpeechConfig.fromSubscription(YourSubscriptionKey, YourServiceRegion);
speechConfig.speechRecognitionLanguage = "zh-CN";
async function recognizeSpeechFromFile() {
let speechRecognizer;
try {
// Convert MP3 to WAV
await new Promise((resolve, reject) => {
exec(mp3ToWavCmd, (error) => {
if (error) {
console.error(`Error converting MP3 to WAV: ${error.message}`);
reject(error);
} else {
console.log("MP3 file converted to WAV successfully.");
resolve();
}
});
});
const audioConfig = sdk.AudioConfig.fromWavFileInput(fs.readFileSync(wavFilePath));
speechRecognizer = new sdk.SpeechRecognizer(speechConfig, audioConfig);
await new Promise((resolve, reject) => {
speechRecognizer.recognizeOnceAsync((result) => {
switch (result.reason) {
case sdk.ResultReason.RecognizedSpeech:
console.log(`RECOGNIZED: Text=${result.text}`);
resolve(result.text);
break;
case sdk.ResultReason.NoMatch:
console.log("NOMATCH: Speech could not be recognized.");
reject("NOMATCH");
break;
case sdk.ResultReason.Canceled: {
const cancellation = sdk.CancellationDetails.fromResult(result);
console.log(`CANCELED: Reason=${cancellation.reason}`);
if (cancellation.reason === sdk.CancellationReason.Error) {
console.log(`CANCELED: ErrorCode=${cancellation.ErrorCode}`);
console.log(`CANCELED: ErrorDetails=${cancellation.errorDetails}`);
console.log("CANCELED: Did you set the speech resource key and region values?");
}
reject("CANCELED");
break;
}
}
});
});
} catch (error) {
console.error("Error during recognition:", error);
} finally {
if (speechRecognizer) {
speechRecognizer.close();
}
}
}
recognizeSpeechFromFile();
不过, 还是应该看看文档再动手,
这是微软文字转语音的文档
上面的代码只支持30s, 如果改成这种订阅式的代码, 就支持更长时间了
我们将订阅从 SpeechRecognizer 发送的事件:
recognizing:事件信号,包含中间识别结果。
recognized:包含最终识别结果的事件信号,指示成功的识别尝试。
sessionStopped:事件信号,指示识别会话的结束(操作)。
canceled:事件信号,包含已取消的识别结果。 这些结果指示因直接取消请求而取消的识别尝试。 或者,它们指示传输或协议失败。
speechRecognizer.recognizing = (s, e) => {
console.log(`RECOGNIZING: Text=${e.result.text}`);
};
speechRecognizer.recognized = (s, e) => {
if (e.result.reason == sdk.ResultReason.RecognizedSpeech) {
console.log(`RECOGNIZED: Text=${e.result.text}`);
}
else if (e.result.reason == sdk.ResultReason.NoMatch) {
console.log("NOMATCH: Speech could not be recognized.");
}
};
speechRecognizer.canceled = (s, e) => {
console.log(`CANCELED: Reason=${e.reason}`);
if (e.reason == sdk.CancellationReason.Error) {
console.log(`"CANCELED: ErrorCode=${e.errorCode}`);
console.log(`"CANCELED: ErrorDetails=${e.errorDetails}`);
console.log("CANCELED: Did you set the speech resource key and region values?");
}
speechRecognizer.stopContinuousRecognitionAsync();
};
speechRecognizer.sessionStopped = (s, e) => {
console.log("\n Session stopped event.");
speechRecognizer.stopContinuousRecognitionAsync();
};
下载的数据
mp4 + mp3 + txt
语音转的文字
这是一个5分钟的电影解说, 1412个字,
如果是小说, 估计字数得翻倍, 这个是电影, 有一些时间是放的电影片段, 没有台词.
ffmpge
由于微软默认支持wav, 所以我们需要转换语音格式, 把mp3转成wav,
因此, 需要在电脑上安装 FFMPEG, 不会装的可以百度 Windows 10系统下安装FFmpeg教程详解
ffmpeg使用注意事项
- 如果文件存在, 那么命令会卡住
网易
长语音转写文档
创建应用时, 选择 长语音转写
网易文档中的错误
这个sliceId是 大写的i (爱),
但是他这个文档里是小写的l (矮楼)
文档中的响应结果, 是写文档的人复制黏贴的.
比如, 这几张响应结果, msg都是sucess
实际上, msg的值都是 null
文件上传的时候, 这里是字节数组, 而不是text
你就说这样写文档快不快吧, 类型全是text
网易长语音转文本的代码
const fs = require("fs");
const axios = require("axios");
const path = require("path");
const crypto = require("crypto");
const FormData = require("form-data");
const util = require("util");
const { wangyi } = require("./config.js");
const stat = util.promisify(fs.stat);
async function getFileSize(filePath) {
try {
const stats = await stat(filePath);
const fileSizeInBytes = stats.size;
console.log(`文件大小: ${fileSizeInBytes} 字节`);
return fileSizeInBytes;
} catch (err) {
console.error("发生错误:", err);
}
}
async function wangYiRcognizeSpeechFromFile(audioFilePath) {
let fileName = path.basename(audioFilePath);
// 请在这里设置您的应用ID和应用密钥
const appKey = wangyi.appKey;
const appSecret = wangyi.secretKey;
let fileSize = await getFileSize(audioFilePath);
let salt = uuidv4();
let curtime = Math.floor(Date.now() / 1000);
let sign = crypto
.createHash("sha256")
.update(appKey + salt + curtime + appSecret)
.digest("hex");
let formData = new FormData();
formData.append("salt", salt);
formData.append("type", 1);
formData.append("appKey", appKey);
formData.append("sliceNum", "1");
formData.append("name", fileName);
formData.append("fileSize", fileSize);
formData.append("curtime", curtime);
formData.append("langType", "zh-CHS");
formData.append("sign", sign);
formData.append("signType", "v4");
formData.append("format", "mp3");
formData.append("noitn", 1);
const prepareResponse = await axios.post("http://openapi.youdao.com/api/audio/prepare", formData, { headers: formData.getHeaders() });
if (prepareResponse.data.errorCode !== "0") {
throw new Error(`Prepare failed: ${prepareResponse.data.msg}`);
}
console.log("准备上传");
console.log(prepareResponse.data);
const taskid = prepareResponse.data.result;
formData = new FormData();
formData.append("q", taskid);
formData.append("appKey", appKey);
salt = uuidv4();
formData.append("salt", salt);
formData.append("curtime", curtime);
sign = crypto
.createHash("sha256")
.update(appKey + salt + curtime + appSecret)
.digest("hex");
formData.append("sign", sign);
formData.append("signType", "v4");
formData.append("sliceId", "1");
// 判断文件是否存在
if (!fs.existsSync(audioFilePath)) {
throw new Error(`File not found: ${audioFilePath}`);
}
// 打印文件大小
console.log(`文件大小: ${fs.statSync(audioFilePath).size} 字节`);
formData.append("file", fs.readFileSync(audioFilePath), { contentType: "audio/mp3", filename: fileName });
formData.append("type", "1");
const uploadResponse = await axios.post("http://openapi.youdao.com/api/audio/upload", formData, { headers: formData.getHeaders() });
if (uploadResponse.data.errorCode !== "0") {
throw new Error(`Upload failed: ${uploadResponse.data.msg}`);
}
console.log("上传文件");
console.log(uploadResponse.data);
/* -------------------------------------------------------------------------- */
formData = new FormData();
formData.append("q", taskid);
formData.append("appKey", appKey);
salt = uuidv4();
formData.append("salt", salt);
curtime = Math.floor(Date.now() / 1000);
formData.append("curtime", curtime);
sign = crypto
.createHash("sha256")
.update(appKey + salt + curtime + appSecret)
.digest("hex");
formData.append("sign", sign);
formData.append("signType", "v4");
const mergeResponse = await axios.post("http://openapi.youdao.com/api/audio/merge", formData, { headers: formData.getHeaders() });
if (mergeResponse.data.errorCode !== "0") {
throw new Error(`Merge failed: ${mergeResponse.data.msg}`);
}
console.log("合并文件");
console.log(mergeResponse.data);
// process.exit(0);
let progressResponse;
do {
await new Promise((resolve) => setTimeout(resolve, 60 * 1000));
formData = new FormData();
formData.append("q", taskid);
formData.append("appKey", appKey);
salt = uuidv4();
formData.append("salt", salt);
curtime = Math.floor(Date.now() / 1000);
formData.append("curtime", curtime);
sign = crypto
.createHash("sha256")
.update(appKey + salt + curtime + appSecret)
.digest("hex");
formData.append("sign", sign);
formData.append("signType", "v4");
progressResponse = await axios.post("http://openapi.youdao.com/api/audio/get_progress", formData, { headers: formData.getHeaders() });
console.log("获取进度");
console.log(progressResponse.data);
} while (progressResponse.data.result[0].status !== "9");
formData = new FormData();
formData.append("q", taskid);
formData.append("appKey", appKey);
salt = uuidv4();
formData.append("salt", salt);
curtime = Math.floor(Date.now() / 1000);
formData.append("curtime", curtime);
sign = crypto
.createHash("sha256")
.update(appKey + salt + curtime + appSecret)
.digest("hex");
formData.append("sign", sign);
formData.append("signType", "v4");
const resultResponse = await axios.post("http://openapi.youdao.com/api/audio/get_result", formData, { headers: formData.getHeaders() });
if (resultResponse.data.errorCode !== "0") {
throw new Error(`Get result failed: ${resultResponse.data.msg}`);
}
console.log("获取结果");
console.log(resultResponse.data);
const text = resultResponse.data.result.map((item) => item.sentence).join("\n");
fs.writeFileSync(fileName, text);
}
// 生成 UUID v4
function uuidv4() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
网易与微软对比
微软 |
网易 |
|
获取结果需要轮询 |
不需要 |
需要 |
准确度 |
略高 |
略低 |
接口方便程度 |
一个订阅模式, 每秒钟都会给你返回信息, 直到任务完成, 感觉比网易方便一些 |
四个步骤: 准备, 上传, 获取状态, 获取结果 |
需要mp3转wav |
需要 |
不需要 |
建议用哪个
都可以, 都差不多
写代码利器
本文的代码大部分是 ChatGPT4 写的,
你如果不想动手, 可以像我一样, 直接复制黏贴网易有道云的文档, 然后叫chatgpt给你代码
就像这样
我用的 ChatGPT4, 是这个
ChatGPT联网版, Stable Diffusion画图, 这个星球全都有, 低调使用, 别外传
声明
本文仅供学习研究使用, 禁止用于其他用途