Greasy Fork is available in English.

【自用】YoutubeTools

1.恢复页面布局,2.增加按钮,方便IDM下载(配合IDM使用)【需配合Local YouTube Downloader使用】,3.增加按钮,一键重绘下载png格式的cover,4.增加按钮生成合并命令行,5.用clipboard,火狐v99之后抽风

As of 2022-05-08. See the latest version.

  1. // ==UserScript==
  2.  
  3. // @icon https://github.com/favicon.ico
  4. // @name 【自用】YoutubeTools
  5. // @namespace Violentmonkey Scripts
  6. // @match *://*.youtube.com/*
  7. // @grant none
  8. // @version 2022.05.08-1
  9. // @author heckles
  10. // @description 1.恢复页面布局,2.增加按钮,方便IDM下载(配合IDM使用)【需配合Local YouTube Downloader使用】,3.增加按钮,一键重绘下载png格式的cover,4.增加按钮生成合并命令行,5.用clipboard,火狐v99之后抽风
  11. // @Homepage URL https://greasyfork.org/zh-CN/scripts/431488-%E8%87%AA%E7%94%A8-youtubetools
  12. // ==/UserScript==
  13.  
  14.  
  15. //1.设定加载条件
  16. if (document.getElementById("browser-app") || document.getElementById("masthead")) { //这两个元素,有一个是true,就往下执行
  17. var sx = setInterval(function () { //间隔执行
  18. //console.log(">>>>>>>>>>> 【YoutubeTools】 interval开始 <<<<<<<<<<<");
  19. if (window.location.href.indexOf("watch?v=") < 0) { //如果网址不匹配
  20. return false; //就不执行 【这里只能匹配域名,然后筛,直接用watch的网址,从首页点进去会不触发】
  21. }else{
  22. if (document.getElementById("meta-contents") && document.getElementById("punisher") === null) { //网址匹配的话,punisher没有被添加
  23. StartJS(); //就执行函数,添加punisher
  24. console.log(">>>>>>>>>>> 【YoutubeTools】 已加载 <<<<<<<<<<<");
  25. }
  26. }
  27. }, 3000); //间隔时间,毫秒
  28. //return;
  29. }
  30.  
  31. //2.条件触发后加载
  32. function StartJS() {
  33. //2.1新增按钮的样式
  34. const btncss = `
  35. color: #F97D00;
  36. font-weight: bold;
  37. /*text-transform: uppercase;*/
  38. padding: 0px 3px;
  39. background-color: transparent;
  40. border-color: transparent;
  41. font-family: sans-serif !important;//这个得加,要不字体不一样
  42. `
  43. //2.2开始添加按钮
  44. var buttonDiv = document.createElement("span");
  45. buttonDiv.id = "punisher";
  46. buttonDiv.style.width = "100%";
  47. buttonDiv.style.marginTop = "3px";
  48. buttonDiv.style.padding = "1px 0";
  49. var addButtonV = document.createElement("button");
  50. var addButtonA = document.createElement("button");
  51. var addButtonM = document.createElement("button");
  52. var aCover = document.createElement("a");
  53. var aTitle = document.createElement("a");
  54. var aSrt = document.createElement("a");
  55. addButtonV.appendChild(document.createTextNode("Vcode"));
  56. addButtonA.appendChild(document.createTextNode("Acode"));
  57. addButtonM.appendChild(document.createTextNode("Merge"));
  58. aCover.appendChild(document.createTextNode("Cover"));
  59. aTitle.appendChild(document.createTextNode("F-name"));
  60. aSrt.appendChild(document.createTextNode("Srt"));
  61. addButtonV.style.cssText = btncss;
  62. addButtonA.style.cssText = btncss;
  63. addButtonM.style.cssText = btncss;
  64. aCover.style.cssText = btncss;
  65. aTitle.style.cssText = btncss;
  66. aSrt.style.cssText = btncss;
  67. buttonDiv.appendChild(addButtonV);
  68. buttonDiv.appendChild(addButtonA);
  69. buttonDiv.appendChild(addButtonM);
  70. buttonDiv.appendChild(aCover);
  71. buttonDiv.appendChild(aTitle);
  72. buttonDiv.appendChild(aSrt);
  73.  
  74.  
  75. var targetElement = document.querySelectorAll("[id='info-text']"); //youtube故意的,很多元素id重复,这里够绝,直接全选中,然后按class筛,再加
  76. if (targetElement) {
  77. for (var i = 0; i < targetElement.length; i++) {
  78. if (targetElement[i].className.indexOf("style-scope ytd-video-primary-info-renderer") > -1) {
  79. targetElement[i].appendChild(buttonDiv);
  80. }
  81. }
  82. }
  83.  
  84. //3.创建一个input,但是不显示(通过移位),作为复制的中介
  85. var nMInput = document.createElement('input');
  86. nMInput.style.cssText = "position:absolute; top:-200px;"; //火狐实测隐藏的话不能选,oInput.style.display='none';
  87. document.body.appendChild(nMInput);
  88. //4.生成文件名
  89. var refreshvar = function(){//设置全局变量,随时准备刷新
  90. nMo = document.querySelector("#container h1 yt-formatted-string").innerText;//获取视频名称,下面再把不能作为文件名的符号替换
  91. /*油管应该是禁了,cmd方式调用IDM下载会报错,这里就没必要筛选韩文了
  92. if(nMo.match(/[\uac00-\ud7ff]/gi)){
  93. nMo = "【名称包含韩文,根据视频编号自行修改】" +window.location.href.split("watch?v=")[1];
  94. }
  95. */
  96. nM = nMo.replace(/[|\\|\/|\:|\*|\?|\"|\<|\>|\|]/g, function (a) {//每一个符号前面都加|\,/[]/g表示全文匹配
  97. switch (a) {//就是换成全角的标点,全角标点是用输入法找出来的
  98. case '\\':
  99. return '\';
  100. case '/':
  101. return '/';
  102. case ':':
  103. return ':';
  104. case '*':
  105. return '·';
  106. case '?':
  107. return ' ?';
  108. case '\"':
  109. return '"';
  110. case '<':
  111. return '〈';
  112. case '>':
  113. return '〉';
  114. case '|':
  115. return '|';
  116. }
  117. });
  118. nM_V = '"' + nM + ' - DASH_V' + '"' + '.mp4';
  119. nM_A = '"' + nM + ' - DASH_A' + '"' + '.m4a';
  120. //5.生成封面图的地址和名称
  121. src_J = document.querySelector("#container div.ytp-cued-thumbnail-overlay-image").style.cssText.slice(23, -3);
  122. nM_J = document.querySelector("#container h1 yt-formatted-string").innerText + src_J.split("default")[1];
  123. if(document.querySelector("div#info-strings #dot").nextSibling.innerText.split(" ")[1]){
  124. nM_dateXX = document.querySelector("div#info-strings #dot").nextSibling.innerText.split(" ")[1];//避免出现“首播开始于”
  125. }else{
  126. nM_dateXX = document.querySelector("div#info-strings #dot").nextSibling.innerText;
  127. }
  128. nM_date = nM_dateXX.split("年")[0] +"-"+ nM_dateXX.split("月")[0].split("年")[1].padStart(2,'0') +"-"+ nM_dateXX.split("日")[0].split("月")[1].padStart(2,'0');
  129. }
  130. aCover.target = "_blank";
  131. //6.IDM下载命令行所需
  132. const ds1 = `"D:\\Programs\\Internet Download Manager"\\idman.exe /n /d "`
  133. const ds2 = `"/p "D:\\下载\\IDM\\00.合并油管" /f `
  134. const mg1 = `"D:\\Programs\\视频编辑\\YouTube 音视频分离合并\\64 位\\ffmpeg" -i "D:\\下载\\IDM\\00.合并油管\\`
  135. const mg2 = ` - DASH_A".m4a -i "D:\\下载\\IDM\\00.合并油管\\`
  136. const mg3 = ` - DASH_V".mp4 -acodec copy -vcodec copy "D:\\下载\\IDM\\00.合并油管\\`
  137. const mg4 = `".mp4`
  138. //7.1视频
  139. addButtonV.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM
  140. refreshvar();//刷新全局变量
  141. nMInput.value = "D:\\下载\\IDM\\00.合并油管\\" + nM_date + '\ ' + nM + ' - DASH_V'+ '.mp4';
  142. nMInput.select(); // 选择对象
  143. document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动
  144. //只能先复制,要不打开新窗口容易失去焦点,然后没法复制,V99开始
  145. var xroot = document.getElementById("hahahazijijiade");//找这里const shadowHost = $el('div'),然后加一句 shadowHost.setAttribute("id","hahahazijijiade"); <<<<<<<<<<<<<<<<<<<<<<<<<
  146. var linkss = xroot.shadowRoot.children[0].children[2].children[1].children[1];
  147. var ku = linkss.querySelectorAll("a");
  148. if (ku) {
  149. for (var i = 0; i < ku.length; i++) {
  150. 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,先下1080p
  151. //nMInput.value = ds1 + linkss.children[i].href + ds2 + nM_V;
  152. var downlink = linkss.children[i].href;
  153. console.log(downlink);
  154. }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
  155. //nMInput.value = ds1 + linkss.children[i].href + ds2 + nM_V;
  156. var downlink = linkss.children[i].href;
  157. console.log(downlink);
  158. }
  159. }
  160. }
  161. window.open(downlink,"_blank");
  162. };
  163. //7.2音频
  164. addButtonA.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM // 找这里const shadow = shadowHost.attachShadow ? shadowHost.attachShadow({ mode: 'closed' }) <<<<<<<<<<<<<<<<<<<<<<<<<
  165. refreshvar();//刷新全局变量
  166. nMInput.value = "D:\\下载\\IDM\\00.合并油管\\" + nM_date + '\ ' + nM + ' - DASH_A'+ '.m4a';
  167. nMInput.select(); // 选择对象
  168. document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动
  169. //只能先复制,要不打开新窗口容易失去焦点,然后没法复制,V99开始
  170. var xroot = document.getElementById("hahahazijijiade");
  171. var linkss = xroot.shadowRoot.children[0].children[2].children[1].children[1];
  172. var ku = linkss.querySelectorAll("a");
  173. if (ku) {
  174. for (var i = 0; i < ku.length; i++) {
  175. if (linkss.children[i].innerText.indexOf("audio/mp4") > -1) { //用=0就不行,用>-1就行...,如果没有,就是-1
  176. //nMInput.value = ds1 + linkss.children[i].href + ds2 + nM_A;
  177. var downlink = linkss.children[i].href;
  178. console.log(downlink);
  179. }
  180. }
  181. }
  182. window.open(downlink,"_blank");
  183. };
  184. //7.3合并
  185. addButtonM.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM
  186. refreshvar();//刷新全局变量
  187. nMInput.value = mg1 + nM_date + '\ ' + nM + mg2 + nM_date + '\ ' + nM + mg3 + nM_date + '\ ' + nM + mg4;
  188. nMInput.select(); // 选择对象
  189. document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动
  190. };
  191. //7.4封面
  192. //网上找的点击下载图片的,原理是canvas重绘
  193. aCover.onclick = function(){
  194. refreshvar();//刷新全局变量
  195. function dIamge(xhref, name) {
  196. var image = new Image();
  197. image.setAttribute('crossOrigin', 'anonymous'); // 解决跨域 Canvas 污染问题
  198. image.src = xhref;
  199. image.onload = function () {
  200. var canvas = document.createElement('canvas');
  201. canvas.width = image.width;
  202. canvas.height = image.height;
  203. var context = canvas.getContext('2d');
  204. context.drawImage(image, 0, 0, image.width, image.height);
  205. var url = canvas.toDataURL('image/png');
  206. var a = document.createElement('a'); // 生成一个a元素
  207. var event = new MouseEvent('click'); // 创建一个单击事件
  208. a.download = name || '下载图片名称'; // 将a的download属性设置为我们想要下载的图片名称,若name不存在则使用‘下载图片名称’作为默认名称
  209. a.href = url; // 将生成的URL设置为a.href属性
  210. a.dispatchEvent(event); // 触发a的单击事件
  211. }
  212. };
  213. nMInput.value = nM_date +'\ '+ document.querySelector("#container h1 yt-formatted-string").innerText.replace(/[|\\|\/|\:|\*|\?|\"|\<|\>|\|]/g, function (a) {//每一个符号前面都加|\,/[]/g表示全文匹配
  214. switch (a) {
  215. case '\\':
  216. return ' ';
  217. case '/':
  218. return ' ';
  219. case ':':
  220. return ':';
  221. case '*':
  222. return '·';
  223. case '?':
  224. return ' ?';
  225. case '\"':
  226. return '\'';
  227. case '<':
  228. return '《';
  229. case '>':
  230. return '》';
  231. case '|':
  232. return '-';
  233. }
  234. });;
  235. //dIamge(src_J, nM);
  236. var thumbJ = nMInput.value + '-thumb' ;
  237. //alert(thumbJ);
  238. dIamge(src_J, thumbJ);
  239. };
  240. //7.5名称
  241. aTitle.onclick = function () {//按钮加event //shadowroot的mode必须是open,否则没有ShadowDOM
  242. refreshvar();//刷新全局变量
  243. nMInput.value = nM_date +'\ '+ document.querySelector("#container h1 yt-formatted-string").innerText.replace(/[|\\|\/|\:|\*|\?|\"|\<|\>|\|]/g, function (a) {//每一个符号前面都加|\,/[]/g表示全文匹配
  244. switch (a) {
  245. case '\\':
  246. return ' ';
  247. case '/':
  248. return ' ';
  249. case ':':
  250. return ':';
  251. case '*':
  252. return '·';
  253. case '?':
  254. return ' ?';
  255. case '\"':
  256. return '\'';
  257. case '<':
  258. return '《';
  259. case '>':
  260. return '》';
  261. case '|':
  262. return '-';
  263. }
  264. });;
  265. nMInput.select(); // 选择对象
  266. document.execCommand("Copy"); // 执行浏览器复制命令,火狐里面这个command只能是用户触发,不能自动
  267. };
  268. //7.6字幕
  269. /*
  270. aSrt.href = window.location.href.split("www.")[0]+"www.subtitle.to/" + window.location.href.split("www.")[1]; //这样会有下划线
  271. aSrt.target = "_blank";
  272. */
  273. aSrt.onclick = function () {
  274. window.open(window.location.href.split("www.")[0]+"www.subtitle.to/" + window.location.href.split("www.")[1],"_blank");
  275. };
  276. if(!document.getElementById("hahahazijijiade")){
  277. addButtonV.prepend(document.createTextNode(">>需更新<< "));
  278. }
  279. }