YouTube 訂閱通知影片加入稍後觀看清單按鈕

增加可將通知影片加入稍後觀看清單的按鈕。

  1. // ==UserScript==
  2.  
  3. // @name YouTube Button As Add Notify Video To WL
  4. // @name:zh-TW YouTube 訂閱通知影片加入稍後觀看清單按鈕
  5. // @name:zh-CN YouTube 订阅通知影片加入稍后观看清单按钮
  6. // @name:ja YouTube 登録通知ビデオを後で見るボタンに追加
  7. // @description Add button that join Video to watch later playlist from notify.
  8. // @description:zh-TW 增加可將通知影片加入稍後觀看清單的按鈕。
  9. // @description:zh-CN 添加可将通知视频加入稍后观看清单的按钮。
  10. // @description:ja 通知から後でプレイリストを視聴するビデオに参加するボタンを追加します。
  11. // @copyright 2023, HrJasn (https://gf.qytechs.cn/zh-TW/users/142344-jasn-hr)
  12. // @license MIT
  13. // @icon https://www.google.com/s2/favicons?domain=www.youtube.com
  14. // @homepageURL https://gf.qytechs.cn/zh-TW/users/142344-jasn-hr
  15. // @supportURL https://gf.qytechs.cn/zh-TW/users/142344-jasn-hr
  16. // @version 1.5
  17. // @namespace https://gf.qytechs.cn/zh-TW/users/142344-jasn-hr
  18. // @grant none
  19. // @match http*://www.youtube.com/*
  20. // @exclude http*://www.google.com/*
  21.  
  22. // ==/UserScript==
  23.  
  24. (() => {
  25. console.log('YouTube Button As Add Notify Video To WL is loading.');
  26. let YBAANVTWobserver;
  27. YBAANVTWobserver = new MutationObserver( (mutations) => {
  28. mutations.forEach((adNds)=>{
  29. adNds.addedNodes.forEach((adNde)=>{
  30. if( (adNde) && (adNde.querySelector) && (adNde.querySelector('ytd-notification-renderer button[aria-label]')) ){
  31. let ynrbe = adNde.querySelector('ytd-notification-renderer button[aria-label]');
  32. if( (ynrbe) && (ynrbe.parentNode) && !(ynrbe.parentNode.querySelector('button[name="addtowl"]')) ){
  33. ynrbe.parentNode.insertAdjacentHTML("beforeend",`
  34. <button id="button" class="style-scope yt-icon-button" name="addtowl">
  35. <div style="width: 100%; height: 100%; fill: currentcolor;">
  36. <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" focusable="false" style="pointer-events: none; display: block; width: 100%; height: 100%;color: white;">
  37. <path d="M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM12 3c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9m0-1c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2z"></path>
  38. </svg>
  39. </div>
  40. </button>`);
  41. let awlbe = ynrbe.parentNode.querySelector('button[name="addtowl"]');
  42. if(awlbe){
  43. console.log('YouTube Button As Add Notify Video To WL is loaded.');
  44. awlbe.addEventListener('click', async (evnt)=>{
  45. evnt.preventDefault();
  46. evnt.stopPropagation();
  47. evnt.stopImmediatePropagation();
  48. let evne = evnt.target;
  49. console.log(evne);
  50. let tgVideoId = null;
  51. try{
  52. tgVideoId = evne.parentNode.parentNode.parentNode.parentNode.parentNode.querySelector('a[href *= "/watch?v="]').href.match(/watch\?v=([^=&\?]+)&?/)[1];
  53. }catch(err){
  54. try{
  55. tgVideoId = evne.parentNode.parentNode.parentNode.parentNode.parentNode.querySelector('a[href *= "/shorts/"]').href.match(/shorts\/([^\/\?]+)\/?/)[1];
  56. }catch(err2){
  57. console.log(err2);
  58. };
  59. console.log(err);
  60. };
  61. if(tgVideoId){
  62. let ytactsjson = null;
  63. let evnesp = evne.querySelector('svg path');
  64. if(evnesp.getAttribute('d') == 'M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM12 3c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9m0-1c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2z'){
  65. ytactsjson = [{
  66. "action": "ACTION_ADD_VIDEO",
  67. "addedVideoId": tgVideoId
  68. }];
  69. } else if(evnesp.getAttribute('d') == 'M11 17H9V8h2v9zm4-9h-2v9h2V8zm4-4v1h-1v16H6V5H5V4h4V3h6v1h4zm-2 1H7v15h10V5z'){
  70. ytactsjson = [{
  71. "action": "ACTION_REMOVE_VIDEO_BY_VIDEO_ID",
  72. "removedVideoId": tgVideoId
  73. }];
  74. };
  75. if(ytactsjson){
  76. let res = await fetchYTAddVideoAPI(ytactsjson,'WL');
  77. console.log(res);
  78. if(res.status === 200){
  79. evnesp.setAttribute('d', 'M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zM9.8 17.3l-4.2-4.1L7 11.8l2.8 2.7L17 7.4l1.4 1.4-8.6 8.5z');
  80. if(ytactsjson[0].action == "ACTION_ADD_VIDEO"){
  81. evnesp.setAttribute('d', 'M11 17H9V8h2v9zm4-9h-2v9h2V8zm4-4v1h-1v16H6V5H5V4h4V3h6v1h4zm-2 1H7v15h10V5z');
  82. } else if(ytactsjson[0].action == "ACTION_REMOVE_VIDEO_BY_VIDEO_ID"){
  83. evnesp.setAttribute('d', 'M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM12 3c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9m0-1c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2z');
  84. };
  85. } else {
  86. evnesp.setAttribute('d', 'M 12 2 C 6.5 2 2 6.5 2 12 s 4.5 10 10 10 s 10 -4.5 10 -10 S 17.5 2 12 2 z M 9 12 l -4 -4 L 8 5 l 4 4 L 16 5 l 3 3 l -4 4 L 19 16 L 16 19 L 12 15 L 8 19 L 5 16 z');
  87. setTimeout(()=>{
  88. evnesp.setAttribute('d', 'M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM12 3c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9m0-1c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2z');
  89. },3000);
  90. };
  91. }
  92. };
  93. async function getSApiSidHash(SAPISID, origin) {
  94. function sha1(str) {
  95. return window.crypto.subtle
  96. .digest("SHA-1", new TextEncoder().encode(str))
  97. .then((buf) => {
  98. return Array.prototype.map
  99. .call(new Uint8Array(buf), (x) => ("00" + x.toString(16)).slice(-2))
  100. .join("")
  101. });
  102. };
  103. const TIMESTAMP_MS = Date.now();
  104. const digest = await sha1(`${TIMESTAMP_MS} ${SAPISID} ${origin}`);
  105. return `${TIMESTAMP_MS}_${digest}`;
  106. };
  107. async function fetchYTAddVideoAPI(actions,playlistId){
  108. return fetch("https://www.youtube.com/youtubei/v1/browse/edit_playlist?key=" + ytcfg.data_.INNERTUBE_API_KEY + "&prettyPrint=false", {
  109. "headers": {
  110. "accept": "*/*",
  111. "authorization": "SAPISIDHASH " + await getSApiSidHash(document.cookie.split("SAPISID=")[1].split("; ")[0], window.origin),
  112. "content-type": "application/json"
  113. },
  114. "body": JSON.stringify({
  115. "context": {
  116. "client": {
  117. clientName: "WEB",
  118. clientVersion: ytcfg.data_.INNERTUBE_CLIENT_VERSION
  119. }
  120. },
  121. "actions": actions,
  122. "playlistId": "WL"
  123. }),
  124. "method": "POST"
  125. });
  126. };
  127.  
  128. });
  129. };
  130. };
  131. };
  132. });
  133. });
  134. });
  135. YBAANVTWobserver.observe(document, {attributes:true, childList:true, subtree:true});
  136. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址