YouTube Check Out Updated Channels

Open channels with updates in new tabs

  1. // ==UserScript==
  2. // @name YouTube Check Out Updated Channels
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.8
  5. // @description Open channels with updates in new tabs
  6. // @author Nathaniel Wu
  7. // @include *://www.youtube.com/*
  8. // @include *://www.youtube.com
  9. // @license Apache-2.0
  10. // @supportURL https://gist.github.com/Nathaniel-Wu/fabd62df2d6146121fa4b9cfcae08763
  11. // @run-at document-idle
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. function is_channel_updated(channel) {
  18. return window.getComputedStyle(channel.querySelector('div#newness-dot')).display == "block";
  19. }
  20. function is_channel_broadcasting(channel) {
  21. return window.getComputedStyle(channel.querySelector('div#newness-dot')).display == "none" && Boolean(channel.querySelector('yt-icon.guide-entry-badge.style-scope.ytd-guide-entry-renderer svg'));
  22. }
  23. function click_videos_tab() {
  24. let ret = false;
  25. document.querySelectorAll('#tabsContent > tp-yt-paper-tab > div').forEach(e => {
  26. if (/[Vv][Ii][Dd][Ee][Oo][Ss]/g.test(e.innerText)) {
  27. e.click();
  28. ret = true;
  29. }
  30. });
  31. return ret;
  32. }
  33. let accumulated_delay = 0;
  34. function check_for_updates(subscriptions) {
  35. subscriptions.querySelectorAll('ytd-guide-entry-renderer').forEach(channel => {
  36. console.info(channel);
  37. if (is_channel_updated(channel)) {
  38. setTimeout(() => {
  39. channel.querySelector('a').click();
  40. }, accumulated_delay += 500);
  41. }
  42. });
  43. }
  44. function checkout_updated_channels() {
  45. let subscriptions = document.querySelector('div#content > tp-yt-app-drawer#guide div#sections > ytd-guide-section-renderer:nth-of-type(2) > div#items');//subject to change
  46. if (subscriptions == null)
  47. return false;
  48. let channels = subscriptions.querySelectorAll(':scope > ytd-guide-entry-renderer.style-scope.ytd-guide-section-renderer');
  49. if (is_channel_updated(channels[channels.length - 1]) || is_channel_broadcasting(channels[channels.length - 1])) {
  50. let active_DOMNodeInsertion = 0;
  51. const observer = new MutationObserver(mutationList =>
  52. mutationList.filter(m => m.type === 'childList').forEach(m => {
  53. m.addedNodes.forEach(() => {
  54. active_DOMNodeInsertion++;
  55. setTimeout(() => {
  56. active_DOMNodeInsertion--;
  57. if (active_DOMNodeInsertion == 0) {
  58. check_for_updates(subscriptions);
  59. }
  60. }, 2000);
  61. });
  62. }));
  63. observer.observe(subscriptions, { childList: true, subtree: true });
  64. subscriptions.querySelector('ytd-guide-collapsible-entry-renderer > ytd-guide-entry-renderer#expander-item').click();
  65. } else
  66. check_for_updates(subscriptions);
  67. return true;
  68. }
  69. function repeat_until_successful(function_ptr, interval) {
  70. if (!function_ptr())
  71. setTimeout(() => {
  72. repeat_until_successful(function_ptr, interval);
  73. }, interval);
  74. }
  75. function in_iframe() {
  76. try {
  77. return window.self !== window.top;
  78. } catch (e) {
  79. return true;
  80. }
  81. }
  82. function on_subscription_page() {
  83. return /^https?:\/\/((www|m)\.)?youtube\.com\/feed\/subscriptions\/?$/.test(window.location.href);
  84. }
  85. if (!in_iframe()) {
  86. if (on_subscription_page())
  87. repeat_until_successful(checkout_updated_channels, 1000);
  88. document.addEventListener('transitionend', (e) => {
  89. if (e.target.id === 'progress' && on_subscription_page())
  90. repeat_until_successful(checkout_updated_channels, 1000);
  91. });
  92. }
  93. })();