在线视频转音频 https://yttomp3.media/ 和 https://youtubetomp3converter.media/ 两个工具开始是根据Cobalt提供的接口实现的,在这个项目升级之后关闭了免费的接口,所以需要自己拉取Git项目搭建一个后端服务。
一、服务平台选择
对比了下各个平台的费用,搭建后端服务的平台直接使用赞助Cobalt项目的RoyaleHosting,相比其他平台性价比最高,需要注意的是,在RoyaleHosting平台注册账号的时候不要使用iCloud的邮箱,iCloud屏蔽了这个平台的邮件发送服务,无法使用iCloud的邮箱激活账号,如果不慎使用iCloud邮箱,可以提一个Ticket(https://royalehosting.net/dashboard/tickets)给服务支持人员。
服务器的地址最好是选美国区域,如果选错了,也可以提一个Ticket去修改。
服务器的系统选择Ubuntu 22.04版本。
考虑到后续的访问量,选择4核4G内存的配置。
二、环境搭建配置
在创建服务钱,先购买一个域名,阿里云或namecheap都可以,配置下二级域名,比如购买的域名是xxx.com,二级域名参数配置成api.xxx.com,添加二级域名的A记录到购买的服务器IP地址。
按照Cobalt的文档操作:https://github.com/imputnet/cobalt/blob/main/docs/run-an-instance.md
1. 安装Docker
根据文章教程操作:How to Install and Use Docker
2. 安装Docker Compose
根据文章教程操作:How To Install Docker Compose
3. 创建Cobalt项目目录
mkdir cobalt
4. 进入目录添加docker-compose.yml配置文件
进入cobalt目录
cd cobalt
添加配置文件,执行如下命令进入编辑模式
sudo vim docker-compose.yml
拷贝如下内容,替换API_URL参数为自己的二级域名,如果cookies.json文件还没配置,就先注释掉
COOKIE_PATH: "/cookies.json"
和
volumes:
- ./cookies.json:/cookies.json
Ctrl+V 粘贴,之后按ESC,输入 :wq 保存退出。
services:
cobalt-api:
image: ghcr.io/imputnet/cobalt:10
init: true
read_only: true
restart: unless-stopped
container_name: cobalt-api
ports:
#- 9000:9000/tcp
# if you use a reverse proxy (such as nginx),
# uncomment the next line and remove the one above (9000:9000/tcp):
- 127.0.0.1:9000:9000
environment:
# replace https://api.url.example/ with your instance's url
# or else tunneling functionality won't work properly
API_URL: "https://api.XXX.com/"
# if you want to use cookies for fetching data from services,
# uncomment the next line & volumes section
COOKIE_PATH: "/cookies.json"
# it's recommended to configure bot protection or api keys if the instance is public,
# see /docs/protect-an-instance.md for more info
# see /docs/run-an-instance.md for more variables that you can use here
labels:
- com.centurylinklabs.watchtower.scope=cobalt
# uncomment only if you use the COOKIE_PATH variable
volumes:
- ./cookies.json:/cookies.json
# watchtower updates the cobalt image automatically
watchtower:
image: ghcr.io/containrrr/watchtower
restart: unless-stopped
command: --cleanup --scope cobalt --interval 900 --include-restarting
volumes:
- /var/run/docker.sock:/var/run/docker.sock
cookies.json文件主要是配置Youtube的Token参数。如果不需要支持Youtube平台的转换和下载可以不配置。
如果还没配置HTTPS的环境,可以先注释掉- 127.0.0.1:9000:9000,恢复- 9000:9000/tcp,等配置nginx代理之后再恢复回来。
5. 启动Cobalt项目容器
docker compose up -d
三、配置HTTPS代理
1. 安装 Certbot
Certbot 是用于获取和管理 SSL 证书的工具,您可以通过以下命令安装(Ubuntu/Debian环境):
sudo apt update
sudo apt install certbot python3-certbot-nginx
2. 获取 SSL 证书
使用 Certbot 为您的域名获取 SSL 证书,yourdomain
替换为自己的域名:
sudo certbot --nginx -d api.yourdomain.com
这将自动为您配置 Nginx 以支持 HTTPS,并获取 Let’s Encrypt 提供的 SSL 证书。Certbot 会提示您输入电子邮件地址以及是否同意服务条款。
3. 配置自动续期
Certbot 会自动配置证书续期,但您可以手动运行以下命令以测试续期:
sudo certbot renew --dry-run
4. 检查 Nginx 配置
Certbot 会自动更新 Nginx 配置,您可以检查 /etc/nginx/sites-available/api.yourdomain.com
是否包含类似以下内容的配置:
server {
listen 80;
server_name api.yourdomain.com;
# Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:9000; # 替换为 Cobalt 使用的端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
5. 重新加载 Nginx
完成配置后,重新加载 Nginx 以使更改生效:
sudo systemctl reload nginx
6. 测试 HTTPS
上面都配置好之后,恢复- 127.0.0.1:9000:9000的配置,注释掉- 9000:9000/tcp,执行下面两条命令,重启下Docker:
docker-compose down
docker-compose up -d
在浏览器中访问 https://api.yourdomain.com
,确认 HTTPS 是否可以正常访问。
通过这些步骤,您就可以为 api.yourdomain.com
配置 HTTPS,使其安全访问。
四、配置Youtube Token
1. 先clone项目到本地电脑
git clone https://github.com/imputnet/cobalt.git
2. 进入项目目录,安装下环境配置
cd cobalt/api
pnpm install
3. 打开Chrome无痕浏览模式,避免账号关联
打开 https://www.google.com/device 地址
用你专门申请的Google账号登录(比如使用网易邮箱申请一个Google Gmail账号),账号激活如果需要美区电话号码认证,可以使用接码平台(https://sms-activate.guru/)购买一个十几分钟使用期限的美区电话号码。
从Cobalt项目的issue信息来看,当账号调用Youtube接口次数比较大的时候,账号容易被封,所以为了避免账号损失,最好不要使用私人账号,额外申请一个或多个账号用于这个项目的使用。
4. 执行生成Token命令
pnpm -C api token:youtube
把控制台输出的类似如下的信息拷贝出来:
"access_token=xxxxxxx; refresh_token=xxxxxxx; scope=https://www.googleapis.com/auth/youtube-paid-content https://www.googleapis.com/auth/youtube; token_type=Bearer; expiry_date=2024-11-11T02:19:43.303Z"
配置到cookies.json文件,配置示例如下:
{
"youtube_oauth": [
"access_token=xxxxxxx; refresh_token=xxxxxxx; scope=https://www.googleapis.com/auth/youtube-paid-content https://www.googleapis.com/auth/youtube; token_type=Bearer; expiry_date=2024-11-11T02:19:43.303Z"
]
}
如果有多个Token可以用英文字符逗号隔开,之后打开docker-compose.yml配置文件里面关于cookies.json的两个配置,重启下Docker实例就可以立即生效。
Token的有效期是一天,Cobalt会自动申请更新Token,在第一次配置完成之后就无需关注,除非账号被封需要重新执行下以上步骤。
最后这个步骤是最难搞的,个人测试,即使开了“科学上网”,打开了系统全局代理,也没法访问Youtube的API域名地址,ping这个地址直接出现超时异常。后面谷歌搜索了其他生成Youtube Token的方法,虽然可以生成Token,但是不能用在这个项目的配置上。最后只好找在美国的大学同学帮操作了下,顺利生成了Token,同学说可以搞个Mac Mini,直接把路由配置成“科学上网”的方式,未做测试,这个不知道是否可以行。
除了上面这些,也可以增加下保护实例的配置:https://github.com/imputnet/cobalt/blob/main/docs/protect-an-instance.md
五、刷新Token的JS脚本
如果需要使用其他方式获取Token,可以参考以下这两篇文章,这边的方式获取的Token不能用在Cobalt项目上:
Retrieve YouTube API v3 key with Node.js
结合下面的JS脚本来获取和刷新cookies.json的Token内容。
var fs = require('fs');
var readline = require('readline');
var { google } = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var TOKEN_PATH = './cookies.json';
// Load client secrets from a local file (client_secret.json).
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file:', err);
return;
}
// Authorize a client with the loaded credentials, then call the YouTube API.
authorize(JSON.parse(content), getNewTokenAndUpdateFile);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* @param {Object} credentials The authorization client credentials.
* @param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.web.client_secret;
var clientId = credentials.web.client_id;
var redirectUrl = credentials.web.redirect_uris[0];
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function (err, tokenData) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
var token = JSON.parse(tokenData).youtube_oauth[0];
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization.
*
* @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* @param {function} callback The callback to call with the authorized client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: ['https://www.googleapis.com/auth/youtube.readonly'],
prompt: 'consent' // Ensures that a refresh token is returned
});
console.log('Authorize this app by visiting this url:', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function (code) {
rl.close();
oauth2Client.getToken(code, function (err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store the token in the cookies.json file under the youtube_oauth field.
*
* @param {Object} token The token to store.
*/
function storeToken(token) {
fs.readFile(TOKEN_PATH, (err, data) => {
var updatedData = { youtube_oauth: [] };
if (!err) {
try {
updatedData = JSON.parse(data);
} catch (e) {
console.error('Error parsing cookies.json:', e);
}
}
// Replace the youtube_oauth field with the new token.
updatedData.youtube_oauth = [JSON.stringify(token)];
fs.writeFile(TOKEN_PATH, JSON.stringify(updatedData, null, 2), (err) => {
if (err) {
console.error('Error writing token to file:', err);
} else {
console.log('Token stored to', TOKEN_PATH);
}
});
});
}
/**
* Refreshes the access token and updates the cookies.json file.
*
* @param {google.auth.OAuth2} oauth2Client The OAuth2 client.
*/
function getNewTokenAndUpdateFile(oauth2Client) {
oauth2Client.refreshAccessToken((err, tokens) => {
if (err) {
console.error('Error refreshing access token:', err);
return;
}
console.log('New access token obtained:', tokens.access_token);
oauth2Client.credentials = tokens;
storeToken(tokens);
});
}
在 VPS 服务器上,你可以使用 cron
作业来配置定时任务,以每 55 分钟执行一次 generateNewToken.js
脚本来获取新的 Token。以下是具体步骤:
1. 检查 Node.js 可执行路径
确保在 cron
中可以正确调用 node
。运行以下命令获取 Node.js 的可执行路径:
which node
通常输出类似 /usr/bin/node
或 /usr/local/bin/node
。这个路径将在 cron
作业中使用。
2. 使用 pwd
命令查看当前路径
如果你在终端中处于 generateNewToken.js
文件所在的文件夹中,运行以下命令可以查看当前路径:
pwd
3. 编辑 crontab
文件
使用以下命令编辑 crontab
文件:
crontab -e
4. 添加 cron
作业
在 crontab
文件中,添加以下行,每 55 分钟运行一次 generateNewToken.js
脚本。将 <path-to-your-script>
替换为第2步中获取的 generateNewToken.js
文件的绝对路径,<path-to-node>
替换为第 1 步中获取的 Node.js 路径。
*/55 * * * * <path-to-node> <path-to-your-script>/generateNewToken.js >> <path-to-your-script>/generateNewToken.log 2>&1
解释
*/55 * * * *
:每 55 分钟运行一次任务。<path-to-node>
:Node.js 的绝对路径。<path-to-your-script>/generateNewToken.js
:脚本的绝对路径。>> <path-to-your-script>/generateNewToken.log 2>&1
:将输出和错误日志重定向到generateNewToken.log
文件。
5. 保存和退出 crontab
编辑完成后,保存并退出编辑器。cron
将自动加载和应用新的配置。
6. 检查 cron
作业是否已配置
运行以下命令,验证 cron
作业已成功添加:
crontab -l
7. 查看日志文件(可选)
检查 generateNewToken.log
文件,确保任务按预期运行,并调试任何潜在的问题:
tail -f <path-to-your-script>/generateNewToken.log
补充建议
- 权限:确保
generateNewToken.js
文件具有可执行权限,可以使用chmod +x generateNewToken.js
赋予权限。 - 环境变量:如果
generateNewToken.js
依赖特定的环境变量,确保cron
作业的环境中设置了这些变量,或在脚本中加载.env
文件。
通过上述步骤,你可以在 VPS 上配置一个定时任务,每 55 分钟自动运行 generateNewToken.js
脚本,以获取新的 YouTube API 访问令牌。
扩展阅读:
微信公众号
转载请注明出处:陈文管的博客 – 在线视频转音频后端服务搭建
发表回复