diff --git a/apps/tools.js b/apps/tools.js index 3c84440..cea5897 100644 --- a/apps/tools.js +++ b/apps/tools.js @@ -269,25 +269,27 @@ export class tools extends plugin { // 加载 BBDown 的CDN配置 this.biliCDN = this.toolsConfig.biliCDN; // 加载网易云Cookie - this.neteaseCookie = this.toolsConfig.neteaseCookie + this.neteaseCookie = this.toolsConfig.neteaseCookie; // 加载是否转化群语音 - this.isSendVocal = this.toolsConfig.isSendVocal + this.isSendVocal = this.toolsConfig.isSendVocal; // 加载是否自建服务器 - this.useLocalNeteaseAPI = this.toolsConfig.useLocalNeteaseAPI + this.useLocalNeteaseAPI = this.toolsConfig.useLocalNeteaseAPI; // 加载自建服务器API - this.neteaseCloudAPIServer = this.toolsConfig.neteaseCloudAPIServer + this.neteaseCloudAPIServer = this.toolsConfig.neteaseCloudAPIServer; // 加载网易云解析最高音质 - this.neteaseCloudAudioQuality = this.toolsConfig.neteaseCloudAudioQuality + this.neteaseCloudAudioQuality = this.toolsConfig.neteaseCloudAudioQuality; // 加载哔哩哔哩是否使用Aria2 this.biliDownloadMethod = this.toolsConfig.biliDownloadMethod; // 加载哔哩哔哩最高分辨率 this.biliResolution = this.toolsConfig.biliResolution; // 加载youtube的截取时长 - this.youtubeClipTime = this.toolsConfig.youtubeClipTime + this.youtubeClipTime = this.toolsConfig.youtubeClipTime; // 加载youtube的解析时长 - this.youtubeDuration = this.toolsConfig.youtubeDuration + this.youtubeDuration = this.toolsConfig.youtubeDuration; // 加载油管下载画质选项 - this.youtubeGraphicsOptions = this.toolsConfig.youtubeGraphicsOptions + this.youtubeGraphicsOptions = this.toolsConfig.youtubeGraphicsOptions; + // 加载youtube的Cookie + this.youtubeCookiePath = this.toolsConfig.youtubeCookiePath; // 加载抖音Cookie this.douyinCookie = this.toolsConfig.douyinCookie; // 加载抖音是否压缩 @@ -1981,8 +1983,8 @@ export class tools extends plugin { await checkAndRemoveFile(path + "/temp.mp4") await checkAndRemoveFile(path + "/temp.mp3") await checkAndRemoveFile(path + "/thumbnail.png") - await ytDlpGetThumbnail(path, url, isOversea, this.myProxy) - const title = ytDlpGetTilt(url, isOversea, this.myProxy).toString().replace(/\n/g, ''); + await ytDlpGetThumbnail(path, url, isOversea, this.myProxy, this.youtubeCookiePath) + const title = ytDlpGetTilt(url, isOversea, this.myProxy, this.youtubeCookiePath).toString().replace(/\n/g, ''); // 音频逻辑 if (url.includes("music")) { @@ -1990,7 +1992,7 @@ export class tools extends plugin { segment.image(`${path}/thumbnail.png`), `${this.identifyPrefix}识别:油管音乐\n视频标题:${title}` ]); - await ytDlpHelper(path, url, isOversea, this.myProxy, this.videoDownloadConcurrency, true, graphics, timeRange); + await ytDlpHelper(path, url, isOversea, this.myProxy, this.videoDownloadConcurrency, true, graphics, timeRange, this.youtubeCookiePath); e.reply(segment.record(`${ path }/temp.mp3`)); this.uploadGroupFile(e, `${ path }/temp.mp3`); // 发送完就截断 @@ -2010,11 +2012,11 @@ export class tools extends plugin { segment.image(`${path}/thumbnail.png`), `${this.identifyPrefix}识别:油管,视频截取中请耐心等待 \n视频标题:${title}\n✂️${DIVIDING_LINE.replace('{}', '截取说明').replace(/\n/g, '')}✂️\n视频时长:${(Duration / 60).toFixed(2).replace(/\.00$/, '')} 分钟\n大于管理员限定截取时长:${(this.youtubeClipTime / 60).toFixed(2).replace(/\.00$/, '')} 分钟\n将截取视频片段` ]); - await ytDlpHelper(path, url, isOversea, this.myProxy, this.videoDownloadConcurrency, true, graphics, timeRange); + await ytDlpHelper(path, url, isOversea, this.myProxy, this.videoDownloadConcurrency, true, graphics, timeRange, this.youtubeCookiePath); this.sendVideoToUpload(e, `${path}/temp.mp4`); } else { e.reply([segment.image(`${path}/thumbnail.png`), `${this.identifyPrefix}识别:油管,视频下载中请耐心等待 \n视频标题:${title}\n视频时长:${(Duration / 60).toFixed(2).replace(/\.00$/, '')} 分钟`]); - await ytDlpHelper(path, url, isOversea, this.myProxy, this.videoDownloadConcurrency, true, graphics, timeRange); + await ytDlpHelper(path, url, isOversea, this.myProxy, this.videoDownloadConcurrency, true, graphics, timeRange, this.youtubeCookiePath); this.sendVideoToUpload(e, `${path}/temp.mp4`); } } catch (error) { diff --git a/config/tools.yaml b/config/tools.yaml index 6feb8aa..9246736 100644 --- a/config/tools.yaml +++ b/config/tools.yaml @@ -33,6 +33,7 @@ neteaseCloudAudioQuality: exhigh # 网易云解析最高音质 默认exhigh(极 youtubeGraphicsOptions: 720 # YouTobe的下载画质,0为原画,1080,720,480,自定义画面高度(默认为720) youtubeClipTime: 0 # YouTobe限制的最大视频时长(默认不开启),单位:秒 最好不要超过5分钟,否则截取效率非常低 youtubeDuration: 480 # YouTobe限制的最大视频时长(默认8分钟),单位:秒 最好不要超过30分钟,否则截取效率非常低 +youtubeCookiePath: '' # YouTobe的Cookie.txt所在的路径 douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;passport_fe_beating_status=xxx;sid_guard=xxx;uid_tt=xxx;uid_tt_ss=xxx;sid_tt=xxx;sessionid=xxx;sessionid_ss=xxx;sid_ucp_v1=xxx;ssid_ucp_v1=xxx;passport_assist_user=xxx;ttwid=xxx; douyinCompression: true # true-压缩,false-不压缩;是否使用压缩视频格式的抖音(默认使用),使用后加速视频发送 diff --git a/guoba.support.js b/guoba.support.js index 422e7f8..6191f23 100644 --- a/guoba.support.js +++ b/guoba.support.js @@ -222,7 +222,7 @@ export function supportGuoba() { }, { field: "tools.youtubeDuration", - label: "YouTuBe最大解析时长", + label: "油管最大解析时长", bottomHelpMessage: "超过时长不解析(单位:秒),保护魔法的流量,计算公式:8分钟 x 60秒 = 480秒,默认8分钟,最好不超过30分钟", component: "InputNumber", @@ -233,7 +233,7 @@ export function supportGuoba() { }, { field: "tools.youtubeClipTime", - label: "YouTuBe截取时长", + label: "油管截取时长", bottomHelpMessage: "超过时长会截取指定时间(单位:秒),保护魔法的流量,计算公式:3分钟 x 60秒 = 180秒,默认不开启,最好不超过5分钟,0表无限or不开启", component: "InputNumber", @@ -244,7 +244,7 @@ export function supportGuoba() { }, { field: "tools.youtubeGraphicsOptions", - label: "YouTube最高分辨率", + label: "油管最高分辨率", bottomHelpMessage: "油管下载的最高分辨率(默认720p,请根据自己魔法流量和服务器承载能力进行调整)", component: "Select", @@ -252,6 +252,17 @@ export function supportGuoba() { options: YOUTUBE_GRAPHICS_LIST, } }, + { + field: "tools.youtubeCookiePath", + label: "油管Cookie", + bottomHelpMessage: + "【!重要:这里填写的是路径,例如/path/to/cookies.txt】如果无法解析油管就填写这个Cookie", + component: "Input", + required: false, + componentProps: { + placeholder: "请输入Youtube Cookie所在的路径,例如:/path/to/cookies.txt", + }, + }, { field: "tools.useLocalNeteaseAPI", label: "使用自建网易云API", diff --git a/package.json b/package.json index a61b729..a7e9978 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "dependencies": { "axios": "^1.3.4", "qrcode": "^1.5.3", - "p-queue": "^8.0.1", - "ws": "^8.17.0" + "p-queue": "^8.0.1" } } diff --git a/utils/yt-dlp-util.js b/utils/yt-dlp-util.js index 791cbe1..d00e75b 100644 --- a/utils/yt-dlp-util.js +++ b/utils/yt-dlp-util.js @@ -10,6 +10,17 @@ function constructProxyParam(isOversea, proxy) { return isOversea ? "" : `--proxy ${proxy}`; } +/** + * 构造cookie参数 + * 目前只支持YouTube构造cookie,否则就必须修改`url.includes("youtu")` + * @param url + * @param cookiePath + * @returns {string} + */ +function constructCookiePath(url, cookiePath) { + return (cookiePath !== "" && url.includes("youtu")) ? `--cookies ${ cookiePath }` : ""; +} + /** * 获取时长 @@ -27,10 +38,12 @@ export function ytDlpGetDuration(url, isOversea, proxy) { * @param url * @param isOversea * @param proxy + * @param cookiePath * @returns string */ -export function ytDlpGetTilt(url, isOversea, proxy) { - return execSync(`yt-dlp --get-title --skip-download ${constructProxyParam(isOversea, proxy)} ${url} --encoding utf8`); +export function ytDlpGetTilt(url, isOversea, proxy, cookiePath = "") { + const cookieParam = constructCookiePath(url, cookiePath); + return execSync(`yt-dlp --get-title --skip-download ${cookieParam} ${ constructProxyParam(isOversea, proxy) } ${ url } --encoding utf8`); } /** @@ -39,9 +52,11 @@ export function ytDlpGetTilt(url, isOversea, proxy) { * @param url * @param isOversea * @param proxy + * @param cookiePath */ -export function ytDlpGetThumbnail(path, url, isOversea, proxy) { - return execSync(`yt-dlp --write-thumbnail --convert-thumbnails png --skip-download ${constructProxyParam(isOversea, proxy)} ${url} -P ${path} -o "thumbnail.%(ext)s"`); +export function ytDlpGetThumbnail(path, url, isOversea, proxy, cookiePath= "") { + const cookieParam = constructCookiePath(url, cookiePath); + return execSync(`yt-dlp --write-thumbnail --convert-thumbnails png --skip-download ${cookieParam} ${constructProxyParam(isOversea, proxy)} ${url} -P ${path} -o "thumbnail.%(ext)s"`); } /** @@ -55,17 +70,19 @@ export function ytDlpGetThumbnail(path, url, isOversea, proxy) { * @param graphics YouTube画质参数 * @param timeRange 截取时间段 * @param maxThreads 最大并发 + * @param cookiePath Cookie所在位置 */ -export async function ytDlpHelper(path, url, isOversea, proxy, maxThreads, merge = false, graphics, timeRange) { +export async function ytDlpHelper(path, url, isOversea, proxy, maxThreads, merge = false, graphics, timeRange, cookiePath = "") { return new Promise((resolve, reject) => { let command = ""; + const cookieParam = constructCookiePath(url, cookiePath); if (url.includes("music")) { // e.g yt-dlp -x --audio-format mp3 https://youtu.be/5wEtefq9VzM -o test.mp3 - command = `yt-dlp -x --audio-format mp3 ${constructProxyParam(isOversea, proxy)} -P ${path} -o "temp.mp3" ${url}`; + command = `yt-dlp -x --audio-format mp3 ${cookieParam} ${constructProxyParam(isOversea, proxy)} -P ${path} -o "temp.mp3" ${url}`; } else { const fParam = url.includes("youtu") ? `--download-sections "*${timeRange}" -f "bv${graphics}[ext=mp4]+ba[ext=m4a]" ` : ""; - command = `yt-dlp -N ${maxThreads} ${fParam} --concurrent-fragments ${maxThreads} ${constructProxyParam(isOversea, proxy)} -P ${path} -o "temp.%(ext)s" ${url}`; + command = `yt-dlp -N ${maxThreads} ${fParam} --concurrent-fragments ${maxThreads} ${cookieParam} ${constructProxyParam(isOversea, proxy)} -P ${path} -o "temp.%(ext)s" ${url}`; } logger.info(`[R插件][yt-dlp审计] ${command}`);