MATRIX RESORTED

ランキングの動画を「投稿が新しい順」に並び変える

  1. // ==UserScript==
  2. // @name MATRIX RESORTED
  3. // @namespace https://github.com/segabito/
  4. // @version 0.0.6
  5. // @description ランキングの動画を「投稿が新しい順」に並び変える
  6. // @author segabito macmoto
  7. // @match *://www.nicovideo.jp/ranking*
  8. // @grant none
  9. // @run-at document-start
  10. // @noframes
  11. // @license public domain
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16. const sortMatrix = app => {
  17. const data = JSON.parse(app.dataset.app);
  18. const lanes = data.lanes;
  19. const column = 'registeredAt';
  20. const ad = -1;
  21. const callback = (a, b) => (a[column] < b[column] ? -1 : 1) * ad;
  22.  
  23. lanes.forEach(lane => {
  24. lane.videoList.filter(v => !v._originalIndex).forEach((v, i) => {
  25. v._originalIndex = i + 1;
  26. v.title = `${(i + 1).toString().padStart(3, '0')}. ${v.title}`;
  27. });
  28. lane.videoList.sort(callback);
  29. });
  30. app.setAttribute('data-app', JSON.stringify(data, null, 2));
  31. }
  32.  
  33. const sortSingle = () => {
  34. const videos = Array.from(document.querySelectorAll('.MediaObject.RankingMainVideo'));
  35. videos.forEach(v => { v.date = v.querySelector('.RankingMainVideo-metaItem:last-child').textContent.trim()});
  36. videos.sort((a, b) => a.date < b.date ? 1 : -1);
  37. videos.forEach((v, i) => {
  38. const rank = i + 1;
  39. const originalRank = v.querySelector('.RankingRowRank').textContent * 1;
  40. const t = v.querySelector('.RankingMainVideo-title');
  41. const title = t.textContent;
  42. v.querySelector('.RankingRowRank').textContent = `${rank}`;
  43. t.textContent = `${originalRank.toString().padStart(3, '0')}. ${title}`;
  44. });
  45. const frag = document.createDocumentFragment();
  46. frag.append(...videos);
  47. document.querySelector('.RankingVideoListContainer').prepend(frag);
  48. };
  49.  
  50. const _ruler_ = `
  51. <style>
  52. .jump-ruler {
  53. position: fixed;
  54. top: 30vh;
  55. right: 24px;
  56. border: 1px solid #888;
  57. border-width: 0px 1px 0px 0;
  58. height: 40vh;
  59. min-width: 48px;
  60. white-space: nowrap;
  61. user-select: none;
  62. cursor: pointer;
  63. }
  64. .jump-ruler-label {
  65. position: absolute;
  66. width: 100%;
  67. text-align: right;
  68. font-size: 16px;
  69. font-weight: bold;
  70. font-style: italic;
  71. color: #888;
  72. cursor: pointer;
  73. transform: translateY(-50%);
  74. }
  75. .jump-ruler-label:hover {
  76. background: #888;
  77. color: #fff;
  78. }
  79.  
  80. .jump-ruler-label[data-param="1"] {
  81. }
  82. .jump-ruler-label[data-param="25"] {
  83. top: 25%;
  84. }
  85. .jump-ruler-label[data-param="50"] {
  86. top: 50%;
  87. }
  88. .jump-ruler-label[data-param="75"] {
  89. top: 75%;
  90. }
  91. .jump-ruler-label[data-param="100"] {
  92. top: 100%;
  93. }
  94. </style>
  95. <div class="jump-ruler" data-command="relative-scroll">
  96. <div class="jump-ruler-label"
  97. data-command="scrollTo"
  98. data-param="1">1 -</div>
  99. <div class="jump-ruler-label"
  100. data-command="scrollTo"
  101. data-param="25">25 -</div>
  102. <div class="jump-ruler-label"
  103. data-command="scrollTo"
  104. data-param="50">50 -</div>
  105. <div class="jump-ruler-label"
  106. data-command="scrollTo"
  107. data-param="75">75 -</div>
  108. <div class="jump-ruler-label"
  109. data-command="scrollTo"
  110. data-param="100">100 -</div>
  111. </div>
  112. `;
  113.  
  114. const scrollTo = rank => {
  115. const target = Array.from(document.querySelectorAll('.RankingRowRank'));
  116. if (!target.length || !target[rank - 1]) {
  117. return;
  118. }
  119.  
  120. target[rank - 1].scrollIntoView({behavior: 'instant', block: 'start', inline: 'center'});
  121. document.documentElement.scrollTop -= 120;
  122. };
  123.  
  124. const initRuler = () => {
  125. document.body.insertAdjacentHTML('beforeend', _ruler_);
  126. const ruler = document.querySelector('.jump-ruler');
  127.  
  128. ruler.addEventListener('click', e => {
  129. const target = e.target.closest('[data-command]');
  130. if (!target) {
  131. return;
  132. }
  133. const {command, param} = target.dataset;
  134. if (!command) {
  135. return;
  136. }
  137. switch (command) {
  138. case 'scrollTo':
  139. scrollTo(param * 1);
  140. break;
  141. case 'relative-scroll':
  142. const rank = Math.round(e.offsetY / target.getBoundingClientRect().height * 100);
  143. scrollTo(rank);
  144. break;
  145. }
  146. e.preventDefault();
  147. e.stopPropagation();
  148. });
  149. };
  150.  
  151. if (document.body.classList.contains('MatrixRanking-body') && !document.gi) {
  152. document.gi = document.getElementById;
  153. document.getElementById = function(id) {
  154. if (id === 'MatrixRanking-app') {
  155. console.info('%cMATRIX RESORTED',`
  156. font-size: 150%;
  157. letter-spacing: 120%;
  158. background: black;
  159. color: lightgreen;
  160. font-family: "Baskerville","Arial Black";
  161. padding: 4px 8px;
  162. text-shadow:
  163. 2px 0 4px rgba(0, 255, 0, 0.5),
  164. -2px 0 4px rgba(0, 255, 0, 0.5),
  165. 4px 0 4px rgba(0, 255, 0, 0.5),
  166. -4px 0 4px rgba(0, 255, 0, 0.5)
  167. `);
  168. const elm = document.gi(id);
  169. sortMatrix(elm);
  170. document.getElementById = document.gi;
  171. delete document.gi;
  172. return elm;c
  173. }
  174. return document.gi(id);
  175. };
  176. }
  177.  
  178. window.addEventListener('DOMContentLoaded', () => {
  179. if (document.querySelector('.RankingVideoListContainer')) {
  180. sortSingle();
  181. initRuler();
  182. window.dispatchEvent(new CustomEvent('MatrixResorted'));
  183. } else
  184. if (document.body.classList.contains('MatrixRanking-body')) {
  185. initRuler();
  186. }
  187. });
  188. })();
  189.  
  190.  

QingJ © 2025

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