// ==UserScript== // @icon https://github.com/favicon.ico // @name 【自用】YoutubeTools // @namespace Violentmonkey Scripts // @match *://*.youtube.com/* // @grant none // @version 2022.10.27((with LYD v0.9.50)) // @author heckles // @description 1.恢复页面布局,2.增加按钮,方便IDM下载(配合IDM使用)【需配合Local YouTube Downloader使用】,3.增加按钮,一键重绘下载png格式的cover,4.增加按钮生成合并命令行,5.用clipboard,火狐v99之后抽风 // @Homepage URL https://greasyfork.org/zh-CN/scripts/431488-%E8%87%AA%E7%94%A8-youtubetools // @downloadURL none // ==/UserScript== //1.设定加载条件 if (document.getElementById("browser-app") || document.getElementById("masthead")) { //这两个元素,有一个是true,就往下执行 var sx = setInterval(function () { //间隔执行 //console.log(">>>>>>>>>>> 【YoutubeTools】 interval开始 <<<<<<<<<<<"); if (window.location.href.indexOf("watch?v=") < 0) { //如果网址不匹配 return false; //就不执行 【这里只能匹配域名,然后筛,直接用watch的网址,从首页点进去会不触发】 }else{ if (document.getElementById("meta-contents") && document.getElementById("punisher") === null) { //网址匹配的话,punisher没有被添加 StartJS(); //就执行函数,添加punisher console.log(">>>>>>>>>>> 【YoutubeTools】 已加载 <<<<<<<<<<<"); } } }, 3000); //间隔时间,毫秒 //return; } //2.条件触发后加载 function StartJS() { //2.1新增按钮的样式 const btncss = ` color: #F97D00; font-weight: bold; /*text-transform: uppercase;*/ padding: 0px 3px; background-color: transparent; border-color: transparent; font-family: sans-serif !important;//这个得加,要不字体不一样 ` //2.2开始添加按钮 var buttonDiv = document.createElement("span"); buttonDiv.id = "punisher"; buttonDiv.style.width = "100%"; buttonDiv.style.marginTop = "3px"; buttonDiv.style.padding = "1px 0"; var addButtonV = document.createElement("button"); var addButtonA = document.createElement("button"); var addButtonM = document.createElement("button"); var aCover = document.createElement("a"); var aTitle = document.createElement("a"); var aSrt = document.createElement("a"); addButtonV.appendChild(document.createTextNode("Vcode")); addButtonA.appendChild(document.createTextNode("Acode")); addButtonM.appendChild(document.createTextNode("Merge")); aCover.appendChild(document.createTextNode("Cover")); aTitle.appendChild(document.createTextNode("F-name")); aSrt.appendChild(document.createTextNode("Srt")); addButtonV.style.cssText = btncss; addButtonA.style.cssText = btncss; addButtonM.style.cssText = btncss; aCover.style.cssText = btncss; aTitle.style.cssText = btncss; aSrt.style.cssText = btncss; buttonDiv.appendChild(addButtonV); buttonDiv.appendChild(addButtonA); buttonDiv.appendChild(addButtonM); buttonDiv.appendChild(aCover); buttonDiv.appendChild(aTitle); buttonDiv.appendChild(aSrt); var targetElement = document.querySelectorAll("[id='info-text']"); //youtube故意的,很多元素id重复,这里够绝,直接全选中,然后按class筛,再加 if (targetElement) { for (var i = 0; i < targetElement.length; i++) { if (targetElement[i].className.indexOf("style-scope ytd-video-primary-info-renderer") > -1) { targetElement[i].appendChild(buttonDiv); } } } //3.创建一个input,但是不显示(通过移位),作为复制的中介 var nMInput = document.createElement('input'); nMInput.style.cssText = "position:absolute; top:-200px;"; //火狐实测隐藏的话不能选,oInput.style.display='none'; document.body.appendChild(nMInput); //4.生成文件名 var refreshvar = function(){//设置全局变量,随时准备刷新 nMo = document.querySelector("#container h1 yt-formatted-string").innerText;//获取视频名称,下面再把不能作为文件名的符号替换 /*油管应该是禁了,cmd方式调用IDM下载会报错,这里就没必要筛选韩文了 if(nMo.match(/[\uac00-\ud7ff]/gi)){ nMo = "【名称包含韩文,根据视频编号自行修改】" +window.location.href.split("watch?v=")[1]; } */ nM = nMo.replace(/[|\\|\/|\:|\*|\?|\"|\<|\>|\|]/g, function (a) {//每一个符号前面都加|\,/[]/g表示全文匹配 switch (a) {//就是换成全角的标点,全角标点是用输入法找出来的 case '\\': return '\'; case '/': return '/'; case ':': return ':'; case '*': return '·'; case '?': return ' ?'; case '\"': return '"'; case '<': return '〈'; case '>': return '〉'; case '|': return '|'; } }); nM_V = '"' + nM + ' - DASH_V' + '"' + '.mp4'; nM_A = '"' + nM + ' - DASH_A' + '"' + '.m4a'; //5.生成封面图的地址和名称 src_J = document.querySelector("#container div.ytp-cued-thumbnail-overlay-image").style.cssText.slice(23, -3); nM_J = document.querySelector("#container h1 yt-formatted-string").innerText + src_J.split("default")[1]; if(document.querySelector("div#info-strings #dot").nextSibling.innerText.split(" ")[1]){ nM_dateXX = document.querySelector("div#info-strings #dot").nextSibling.innerText.split(" ")[1];//避免出现“首播开始于” }else{ nM_dateXX = document.querySelector("div#info-strings #dot").nextSibling.innerText; } nM_date = nM_dateXX.split("年")[0] +"-"+ nM_dateXX.split("月")[0].split("年")[1].padStart(2,'0') +"-"+ nM_dateXX.split("日")[0].split("月")[1].padStart(2,'0'); } aCover.target = "_blank"; //6.IDM下载命令行所需 const ds1 = `"D:\\Programs\\Internet Download Manager"\\idman.exe /n /d "` const ds2 = `"/p "D:\\下载\\IDM\\00.合并油管" /f ` const mg1 = `"D:\\Programs\\视频编辑\\YouTube 音视频分离合并\\64 位\\ffmpeg" -i "D:\\下载\\IDM\\00.合并油管\\` const mg2 = ` - DASH_A".m4a -i "D:\\下载\\IDM\\00.合并油管\\` const mg3 = ` - DASH_V".mp4 -acodec copy -vcodec copy "D:\\下载\\IDM\\00.合并油管\\` const mg4 = `".mp4` //7.1视频 addButtonV.onclick = function () { //按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM refreshvar(); //刷新全局变量 nMInput.value = "D:\\下载\\IDM\\00.合并油管\\" + nM_date + '\ ' + nM + ' - DASH_V' + '.mp4'; nMInput.select(); // 选择对象 document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动 //只能先复制,要不打开新窗口容易失去焦点,然后没法复制,V99开始 var xroot = document.getElementById("hahahazijijiade"); //找这里const shadowHost = $el('div'),然后加一句 shadowHost.setAttribute("id","hahahazijijiade"); <<<<<<<<<<<<<<<<<<<<<<<<< var linkss = xroot.shadowRoot.children[0].children[2].children[1].children[1]; var ku = linkss.querySelectorAll("a"); if (ku) { for (var i = 0; i < ku.length; i++) { if (linkss.innerHTML.indexOf("1080p") > -1) { //先看看1080p有没有,不能用linkss.children[i].innerText.indexOf("1080p") > -1作为条件判断 if (linkss.children[i].innerText.indexOf("1080p") > -1 && linkss.children[i].innerText.indexOf("video/mp4") > -1 && linkss.children[i].innerText.indexOf("avc1.") > -1) { //用=0就不行,用>-1就行...,如果没有,就是-1, //nMInput.value = ds1 + linkss.children[i].href + ds2 + nM_V; var downlink = linkss.children[i].href; console.log('下面是1080p的链接'); console.log(downlink); } } else if (linkss.children[i].innerText.indexOf("720p") > -1 && linkss.children[i].innerText.indexOf("video/mp4") > -1 && linkss.children[i].innerText.indexOf("avc1.") > -1) { //用=0就不行,用>-1就行...,如果没有,就是-1,没有再下720p //nMInput.value = ds1 + linkss.children[i].href + ds2 + nM_V; var downlink = linkss.children[i].href; console.log('下面是720p的链接'); console.log(downlink); } } } window.open(downlink, "_blank"); }; //7.2音频 addButtonA.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM // 找这里const shadow = shadowHost.attachShadow ? shadowHost.attachShadow({ mode: 'closed' }) <<<<<<<<<<<<<<<<<<<<<<<<< refreshvar();//刷新全局变量 nMInput.value = "D:\\下载\\IDM\\00.合并油管\\" + nM_date + '\ ' + nM + ' - DASH_A'+ '.m4a'; nMInput.select(); // 选择对象 document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动 //只能先复制,要不打开新窗口容易失去焦点,然后没法复制,V99开始 var xroot = document.getElementById("hahahazijijiade"); var linkss = xroot.shadowRoot.children[0].children[2].children[1].children[1]; var ku = linkss.querySelectorAll("a"); if (ku) { for (var i = 0; i < ku.length; i++) { if (linkss.children[i].innerText.indexOf("audio/mp4") > -1) { //用=0就不行,用>-1就行...,如果没有,就是-1 //nMInput.value = ds1 + linkss.children[i].href + ds2 + nM_A; var downlink = linkss.children[i].href; console.log(downlink); } } } window.open(downlink,"_blank"); }; //7.3合并 addButtonM.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM refreshvar();//刷新全局变量 nMInput.value = mg1 + nM_date + '\ ' + nM + mg2 + nM_date + '\ ' + nM + mg3 + nM_date + '\ ' + nM + mg4; nMInput.select(); // 选择对象 document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动 }; //7.4封面 //网上找的点击下载图片的,原理是canvas重绘 aCover.onclick = function(){ refreshvar();//刷新全局变量 function dIamge(xhref, name) { var image = new Image(); image.setAttribute('crossOrigin', 'anonymous'); // 解决跨域 Canvas 污染问题 image.src = xhref; image.onload = function () { var canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext('2d'); context.drawImage(image, 0, 0, image.width, image.height); var url = canvas.toDataURL('image/png'); var a = document.createElement('a'); // 生成一个a元素 var event = new MouseEvent('click'); // 创建一个单击事件 a.download = name || '下载图片名称'; // 将a的download属性设置为我们想要下载的图片名称,若name不存在则使用‘下载图片名称’作为默认名称 a.href = url; // 将生成的URL设置为a.href属性 a.dispatchEvent(event); // 触发a的单击事件 } }; nMInput.value = nM_date +'\ '+ document.querySelector("#container h1 yt-formatted-string").innerText.replace(/[|\\|\/|\:|\*|\?|\"|\<|\>|\|]/g, function (a) {//每一个符号前面都加|\,/[]/g表示全文匹配 switch (a) { case '\\': return ' '; case '/': return ' '; case ':': return ':'; case '*': return '·'; case '?': return ' ?'; case '\"': return '\''; case '<': return '《'; case '>': return '》'; case '|': return '-'; } });; //dIamge(src_J, nM); var thumbJ = nMInput.value + '-thumb' ; //alert(thumbJ); dIamge(src_J, thumbJ); }; //7.5名称 aTitle.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM refreshvar();//刷新全局变量 nMInput.value = nM_date +'\ '+ document.querySelector("#container h1 yt-formatted-string").innerText.replace(/[|\\|\/|\:|\*|\?|\"|\<|\>|\|]/g, function (a) {//每一个符号前面都加|\,/[]/g表示全文匹配 switch (a) { case '\\': return ' '; case '/': return ' '; case ':': return ':'; case '*': return '·'; case '?': return ' ?'; case '\"': return '\''; case '<': return '《'; case '>': return '》'; case '|': return '-'; } });; nMInput.select(); // 选择对象 document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动 }; //7.6字幕 /* aSrt.href = window.location.href.split("www.")[0]+"www.subtitle.to/" + window.location.href.split("www.")[1]; //这样会有下划线 aSrt.target = "_blank"; */ aSrt.onclick = function () { window.open(window.location.href.split("www.")[0]+"www.subtitle.to/" + window.location.href.split("www.")[1],"_blank"); }; if(!document.getElementById("hahahazijijiade")){ addButtonV.prepend(document.createTextNode(">>需更新<< ")); } }