Youtube: expand description and long comments; show all the replies

Video description, long comments and list of subscriptions are expanded automatically; all the replies are shown after pressing "View all N replies" button

当前为 2022-12-21 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube: expand description and long comments; show all the replies
  3. // @description Video description, long comments and list of subscriptions are expanded automatically; all the replies are shown after pressing "View all N replies" button
  4. // @author MK
  5. // @namespace max44
  6. // @homepage https://greasyfork.org/en/users/309172-max44
  7. // @match *://*.youtube.com/*
  8. // @match *://*.youtu.be/*
  9. // @icon https://cdn.icon-icons.com/icons2/1488/PNG/512/5295-youtube-i_102568.png
  10. // @version 1.2.1
  11. // @license MIT
  12. // @grant none
  13. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  14. // @run-at document-idle
  15. // ==/UserScript==
  16.  
  17. (function () {
  18. 'use strict';
  19.  
  20. var videoIdAtLastCheck = "";
  21. var btnClick = null;
  22. var waitVideo;
  23. var i;
  24.  
  25. var observerBody = null;
  26. var observerSubs = null;
  27. var flgSubsDone = false;
  28. var observerDesc = null;
  29. var observerComments = null;
  30. var observerCommentsNotif = null;
  31. var observerComPost = null;
  32.  
  33. //---
  34. //--- Listen to global page changes
  35. //---
  36. const callbackBody = function (mutationsList, observer) {
  37.  
  38. var pathArray = window.location.pathname.split('/');
  39. var firstPath = pathArray[1];
  40. var lastPath = pathArray[pathArray.length - 1];
  41.  
  42. //Check whether video is new to expand description
  43. if (firstPath === "watch") {
  44. var player = $( "div#content ytd-watch-flexy" );
  45. if (player != null && player.length > 0) {
  46. clearInterval(waitVideo); //Stop waiting for video
  47. var videoId = player[0].getAttribute("video-id");
  48. player = null;
  49.  
  50. if (videoIdAtLastCheck != videoId) {
  51. videoIdAtLastCheck = videoId;
  52. //console.log("new video");
  53. expandDesc();
  54. }
  55. }
  56. }
  57.  
  58. /* //---
  59. //--- Listen to description and expand it
  60. //---
  61. if (observerDesc == null) {
  62. const callbackDesc = function (mutationsList, observer) {
  63. //Check whether video is new to expand description
  64. if (firstPath === "watch") {
  65. var player = $( "div#content ytd-watch-flexy" );
  66. if (player != null && player.length > 0) {
  67. clearInterval(waitVideo); //Stop waiting for video
  68. var videoId = player[0].getAttribute("video-id");
  69. player = null;
  70.  
  71. if (videoIdAtLastCheck != videoId) {
  72. videoIdAtLastCheck = videoId;
  73. console.log("new video");
  74. expandDesc();
  75. }
  76. }
  77. }
  78. }
  79. let nodeDesc = document.querySelector("#player");//document.querySelector(".watch-active-metadata");
  80. if (nodeDesc != null) {
  81. observerDesc = new MutationObserver(callbackDesc);
  82. observerDesc.observe(nodeDesc, {childList: true, subtree: true, attributes: true, characterData: true});
  83. console.log("desc observer added");
  84. }
  85. }*/
  86.  
  87. //Remove subscriptions observer after subscriptions have been expanded
  88. if (flgSubsDone && observerSubs != null) {
  89. observerSubs.disconnect(); //Expand subscriptions only once
  90. observerSubs = null;
  91. //console.log("subs observer removed");
  92. }
  93.  
  94. //---
  95. //--- Listen to community posts and expand them
  96. //---
  97. if (observerComPost == null && lastPath === "community") {
  98. const callbackComPost = function (mutationsList, observer) {
  99. expandComPost();
  100. //console.log("post expanded");
  101. }
  102. let nodeComPost = document.querySelector("div#content");
  103. if (nodeComPost != null) {
  104. observerComPost = new MutationObserver(callbackComPost);
  105. observerComPost.observe(nodeComPost, {childList: true, subtree: true, attributes: true, characterData: true});
  106. //console.log("post observer added");
  107. }
  108. }
  109. //Remove community post observer on non-community pages
  110. if (observerComPost != null && lastPath != "community") {
  111. observerComPost.disconnect();
  112. observerComPost = null;
  113. //console.log("post observer removed");
  114. }
  115.  
  116. //---
  117. //--- Listen to comments and expand them
  118. //---
  119. if (observerComments == null && (firstPath === "watch" || firstPath === "post" || lastPath === "community")) {
  120. const callbackComments = function (mutationsList, observer) {
  121. expandComments();
  122. //console.log("comments expanded");
  123. }
  124. let nodeComments = null;
  125. if (firstPath === "watch") {
  126. nodeComments = document.querySelector("ytd-comments");
  127. if (nodeComments != null) {
  128. observerComments = new MutationObserver(callbackComments);
  129. observerComments.observe(nodeComments, {childList: true, subtree: true, attributes: true, characterData: false});
  130. //console.log("comments observer added");
  131. }
  132. } else {
  133. nodeComments = document.querySelector("body");
  134. if (nodeComments != null) {
  135. observerComments = new MutationObserver(callbackComments);
  136. observerComments.observe(nodeComments, {childList: true, subtree: true, attributes: true, characterData: false});
  137. //console.log("comments observer added");
  138. }
  139. }
  140. }
  141. //Remove comments observer
  142. if (observerComments != null && firstPath != "watch" && firstPath != "post" && lastPath != "community") {
  143. observerComments.disconnect();
  144. observerComments = null;
  145. //console.log("comments observer removed");
  146. }
  147.  
  148. //---
  149. //--- Listen to comments in notification submenu and expand them
  150. //---
  151. if (observerCommentsNotif == null) {
  152. const callbackCommentsNotif = function (mutationsList, observer) {
  153. expandCommentsNotif();
  154. //console.log("notif comments expanded");
  155. }
  156. let nodeCommentsNotif = null;
  157. nodeCommentsNotif = document.querySelector("ytd-popup-container #contentWrapper");
  158. if (nodeCommentsNotif != null) {
  159. observerCommentsNotif = new MutationObserver(callbackCommentsNotif);
  160. observerCommentsNotif.observe(nodeCommentsNotif, {childList: true, subtree: true, attributes: true, characterData: false});
  161. //console.log("notif comments observer added");
  162. }
  163. }
  164. }
  165.  
  166. let nodeBody = document.querySelector("body");
  167. if (nodeBody != null) {
  168. const observerBody = new MutationObserver(callbackBody);
  169. observerBody.observe(nodeBody, {childList: true, subtree: true, attributes: true, characterData: false});
  170. //console.log("body observer added");
  171. }
  172.  
  173. //---
  174. //--- Listen to subscriptions and expand them
  175. //---
  176. const callbackSubs = function (mutationsList, observer) {
  177. expandSubs();
  178. }
  179. let nodeSubs = document.querySelector("div#guide-wrapper");
  180. if (nodeSubs != null) {
  181. observerSubs = new MutationObserver(callbackSubs);
  182. observerSubs.observe(nodeSubs, {childList: true, subtree: true, attributes: true, characterData: false});
  183. //console.log("subs observer added");
  184. }
  185.  
  186. /*//Check what buttons to click after each scroll of main window
  187. $( window ).scroll(function() {
  188. //expandComPost();
  189. expandComments();
  190. });*/
  191.  
  192.  
  193. //---------------------------------------
  194. // Expand description
  195. //---------------------------------------
  196. function expandDesc() {
  197. //Expand description
  198. btnClick = $( "div#description ytd-text-inline-expander tp-yt-paper-button#expand[role='button']:not([hidden=''])" );
  199. if (btnClick != null && btnClick.length > 0) {// && isVisible(btnClick)) {
  200. btnClick[0].click();
  201. }
  202.  
  203. //Expand description after 7ktTube | 2016 REDUX script
  204. btnClick = $( "div#meta-contents ytd-video-secondary-info-renderer div ytd-expander tp-yt-paper-button#more:not([hidden=''])" );
  205. if (btnClick != null && btnClick.length > 0) {// && isVisible(btnClick)) {
  206. btnClick[0].click();
  207. }
  208. }
  209.  
  210. //---------------------------------------
  211. // Expand post in community section
  212. //---------------------------------------
  213. function expandComPost() {
  214. btnClick = $( "div#post ytd-expander#contentTextExpander tp-yt-paper-button#more[role='button']:not([hidden=''])" );
  215. if (btnClick != null && btnClick.length > 0) {
  216. btnClick[0].click();
  217. //btnClick[0].setAttribute("clicked-by-script", "true"); //Do not click it again
  218. }
  219. }
  220.  
  221. //---------------------------------------
  222. // Expand comments
  223. //---------------------------------------
  224. function expandComments() {
  225. //Expand long comments and hide "show less" button in comments section
  226. btnClick = $( "#primary ytd-comment-renderer #comment-content tp-yt-paper-button#more:not([hidden='']) > span.more-button[slot='more-button']" );
  227. if (btnClick != null) {
  228. for (i = 0; i < btnClick.length; i++) {
  229. btnClick[i].click();
  230. btnClick[i].setAttribute("clicked-by-script", "true"); //Do not click it again
  231. btnClick[i].parentNode.previousElementSibling.setAttribute("style", "display:none;"); //Hide "Show less" button
  232. }
  233. }
  234.  
  235. //Show all replies upon pressing "Show more replies" button
  236. btnClick = $( "#primary div#replies div#expander div#expander-contents:not([hidden]) div#button.ytd-continuation-item-renderer ytd-button-renderer.ytd-continuation-item-renderer button.yt-spec-button-shape-next:not([clicked-by-script='true'])" );
  237. if (btnClick != null) {
  238. for (i = 0; i < btnClick.length; i++) {
  239. btnClick[i].click();
  240. btnClick[i].setAttribute("clicked-by-script", "true"); //Do not click it again
  241. }
  242. }
  243.  
  244. //Show all replies upon pressing "View all N replies" button (7ktTube | 2016 REDUX script)
  245. btnClick = $( "#primary div#replies div#expander div#expander-contents:not([hidden]) div#button.ytd-continuation-item-renderer ytd-button-renderer tp-yt-paper-button#button[role='button']:not([clicked-by-script='true'])" );
  246. if (btnClick != null) {
  247. for (i = 0; i < btnClick.length; i++) {
  248. btnClick[i].click();
  249. btnClick[i].setAttribute("clicked-by-script", "true"); //Do not click it again
  250. }
  251. }
  252. }
  253.  
  254. //---------------------------------------
  255. // Expand comments in notification submenu
  256. //---------------------------------------
  257. function expandCommentsNotif() {
  258. //Expand long comments and hide "show less" button in notification submenu
  259. btnClick = $( "#submenu ytd-comment-renderer #comment-content tp-yt-paper-button#more:not([hidden='']) > span.more-button[slot='more-button']:not([clicked-by-script='true'])" );
  260. if (btnClick != null) {
  261. for (i = 0; i < btnClick.length; i++) {
  262. btnClick[i].click();
  263. btnClick[i].setAttribute("clicked-by-script", "true"); //Do not click it again
  264. btnClick[i].parentNode.previousElementSibling.setAttribute("style", "display:none;"); //Hide "Show less" button
  265. }
  266. }
  267. }
  268.  
  269. //---------------------------------------
  270. // Show all subscriptions
  271. //---------------------------------------
  272. function expandSubs() {
  273. btnClick = $( "#guide div#sections div#items ytd-guide-collapsible-entry-renderer.ytd-guide-section-renderer[can-show-more=''] #expander-item" );
  274. if (btnClick != null) {
  275. for (i = 0; i < btnClick.length; i++) {
  276. if (isVisible(btnClick[i])) {
  277. btnClick[i].click();
  278. flgSubsDone = true;
  279. }
  280. }
  281. }
  282. }
  283.  
  284. //---------------------------------------
  285. // Check all the parents of element to find whether it is visible or not
  286. //---------------------------------------
  287. function isVisible(pObj) {
  288. if (pObj != null) {
  289. var checkNext = true;
  290. var vObj = pObj;
  291.  
  292. while (checkNext) {
  293. checkNext = false;
  294. //console.log("checking element " + vObj.tagName + "#" + vObj.id + ": '" + document.defaultView.getComputedStyle(vObj,null)['display'] + "'");
  295. if (document.defaultView.getComputedStyle(vObj,null)['display'] != "none") {
  296. if (vObj.parentElement != null) {
  297. vObj = vObj.parentElement;
  298. checkNext = true;
  299. }
  300. } else {
  301. return false;
  302. }
  303. }
  304. return true;
  305. }
  306. return false;
  307. }
  308.  
  309. })();
  310.  
  311. /*//Detect spinner at main comments
  312. var spinnerMain = $( "#primary div#replies div#expander tp-yt-paper-spinner#spinner[active]" );
  313. if (spinnerMain != null && spinnerMain.length > 0) {
  314. console.log("main active spinner detected");
  315. spinnerActive = true;
  316.  
  317. //Listen to spinner changes
  318. const spinnerCallback = function (mutationsList, observer) {
  319. expandComments();
  320.  
  321. //spinnerMain = $( "#primary div#replies div#expander tp-yt-paper-spinner#spinner[active]" );
  322. if (spinnerMain[0].getAttribute("active") == null || spinnerMain[0].getAttribute("active") == "") {
  323. console.log("main spinner deactivated");
  324. spinnerObserver.disconnet();
  325. }
  326. }
  327.  
  328. var spinnerNode = document.querySelector("#primary div#replies div#expander tp-yt-paper-spinner#spinner[active]");
  329. if (spinnerNode != null) {
  330. const spinnerObserver = new MutationObserver(spinnerCallback);
  331. spinnerObserver.observe(spinnerNode, {childList: true, subtree: true, attributes: true, characterData: true});
  332. }
  333.  
  334. } else if (spinnerActive) {
  335. console.log("spinner stopped");
  336. spinnerActive = false;
  337. expandComments();
  338. }*/