YouTube Uploads Sorter Button

Adds a button to a YouTube channel's videos page which sorts recent uploads by views

目前為 2025-02-23 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name YouTube Uploads Sorter Button
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.15.0
  5. // @description Adds a button to a YouTube channel's videos page which sorts recent uploads by views
  6. // @author Lex
  7. // @match *://*.youtube.com/@*
  8. // @match *://*.youtube.com/*/featured
  9. // @match *://*.youtube.com/*/videos
  10. // @exclude-match *://*.youtube.com/watch
  11. // @require https://code.jquery.com/jquery-3.2.1.min.js
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. (function($) {
  16. 'use strict';
  17.  
  18. function addButton() {
  19. if ($("#sortViewButton").length == 0) {
  20. const chip = $("<button>").attr("id", "sortViewButton").click(sortByViews);
  21. chip.css({"border": "none", "border-radius": "8px", "padding": "8px 15px", "margin-left": "1em", "cursor": "pointer"})
  22. const container = $("#chips-wrapper iron-selector");
  23. chip.appendTo(container);
  24. setTimeout(() => {
  25. chip[0].innerText = "Sort by Views";
  26. }, 0)
  27. }
  28. }
  29.  
  30. function parseViewNumber(str) {
  31. /* Parses 405K, 1.5M etc */
  32. let multiplier = 1;
  33. if (str.endsWith("K")) {
  34. multiplier = 1000;
  35. } else if (str.endsWith("M")) {
  36. multiplier = 1000000;
  37. }
  38. return parseFloat(str) * multiplier;
  39. }
  40.  
  41. function getViewsShort(e) {
  42. // viewsText = "405K views"
  43. const viewsText = $(e).find(".inline-metadata-item")[0].textContent;
  44. const viewsStr = viewsText.split(" ")[0];
  45. return parseViewNumber(viewsStr);
  46. }
  47.  
  48. function getViewsVideo(e) {
  49. const viewsTitle = $(e).find('a#video-title-link')[0].getAttribute("aria-label");
  50. //console.log(`Found title: ${viewsTitle}`);
  51. if (viewsTitle.search(/No views$/) > -1) // video has no views yet
  52. return 0;
  53. else {
  54. const views = parseInt(/([\d,]+) views( - play Short)?$/.exec(viewsTitle)[1].replace(/,/g, ""));
  55. return views;
  56. }
  57. }
  58.  
  59. function getViews(e) {
  60. // Try to get the views of a regular video, and if that fails try to get views as a Short
  61. try {
  62. return getViewsVideo(e);
  63. } catch(err) {
  64. try {
  65. return getViewsShort(e);
  66. } catch(err) {
  67. return 0;
  68. }
  69. }
  70. }
  71.  
  72. function sortByViews() {
  73. console.log("Sorting...");
  74. const items = $("ytd-rich-item-renderer");
  75. console.log(`Found ${items.length} videos on the page.`);
  76. //console.log(items);
  77. //console.log(getViews(items[0]));
  78.  
  79. // Array of each parent for a given index.
  80. // e.g. if there are 4 videos in the first row container, the first 4 indexes are that first row
  81. const parents = [...items].map(e => e.parentNode);
  82. //console.log(parents);
  83. const sorted = items.toArray().sort(function(a, b) {
  84. return getViews(b) - getViews(a);
  85. });
  86. const infiniteScrollItem = document.getElementsByTagName("ytd-continuation-item-renderer")[0]
  87. console.log(infiniteScrollItem)
  88. infiniteScrollItem.parentNode.removeChild(infiniteScrollItem)
  89. for (let item of sorted) {
  90. // Remove item from its parent and append it to the ordered parent
  91. const parent = parents.shift();
  92. //console.log("Parent: ", parent);
  93. //console.log("Removing", item, "from its parent");
  94. item.parentNode.removeChild(item);
  95. parent.append(item);
  96. }
  97. sorted.slice(-1)[0].parentNode.append(infiniteScrollItem)
  98. }
  99.  
  100. function waitForLoad(query, callback) {
  101. if (document.querySelector(query)) {
  102. callback();
  103. } else {
  104. setTimeout(waitForLoad.bind(null, query, callback), 100);
  105. }
  106. }
  107.  
  108. waitForLoad("#chips-wrapper", addButton);
  109. })(window.jQuery);