soundcloud shuffle likes

Adds a shuffle play button to "Likes" and playlists

  1. // ==UserScript==
  2. // @name soundcloud shuffle likes
  3. // @version 1.7
  4. // @description Adds a shuffle play button to "Likes" and playlists
  5. // @author bhackel
  6. // @match https://soundcloud.com/*
  7. // @grant none
  8. // @run-at document-end
  9. // @license MIT
  10. // @noframes
  11. // @namespace https://gf.qytechs.cn/en/users/324178-bhackel
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. /* Injects Button into the page once it has loaded,
  18. then tries to re-add it if it disappears due to page change
  19. */
  20. function insertButtonLoop() {
  21. let url = window.location.href;
  22. url = url.split('?')[0];
  23. let btnShuffle = document.querySelector('.bhackel-shuffle-likes');
  24.  
  25. // Check if button does not exist already, and that user is on likes or a playlist
  26. if (!btnShuffle && (url.includes("/likes") || url.includes("/sets/") || url.includes("/discover/"))) {
  27. btnShuffle = document.createElement('Button');
  28. btnShuffle.innerHTML = 'Shuffle Play';
  29. btnShuffle.onclick = function(){ setupLoad(this); };
  30. btnShuffle.scrolling = false;
  31. btnShuffle.interval = 0;
  32.  
  33. // Case for likes
  34. if (url.includes("you/likes")) {
  35. btnShuffle.className = 'bhackel-shuffle-likes sc-button sc-button-large';
  36. btnShuffle.pageType = "Likes";
  37. // Check if top bar has loaded
  38. let collectionTop = document.querySelector('.collectionSection__top');
  39. if (collectionTop) {
  40. // Insert the button above the grid of tracks
  41. collectionTop.insertBefore(btnShuffle, collectionTop.children[2]);
  42. } else {
  43. setTimeout(insertButtonLoop, 1000);
  44. }
  45. }
  46. // Case for generic user likes
  47. else if (url.includes("/likes") && !url.includes("you/likes")) {
  48. btnShuffle.className = 'bhackel-shuffle-likes sc-button sc-button-medium';
  49. btnShuffle.pageType = "GenericLikes";
  50. // Check if top bar has loaded
  51. let titleBar = document.querySelector(".userNetworkTabs");
  52. if (titleBar) {
  53. // Insert the button above the list of tracks
  54. titleBar.appendChild(btnShuffle);
  55. } else {
  56. setTimeout(insertButtonLoop, 1000);
  57. }
  58. }
  59. // Case for a playlist
  60. else if (url.includes("/sets/") && !url.includes("/discover/")) {
  61. btnShuffle.className = 'bhackel-shuffle-likes sc-button sc-button-medium';
  62. btnShuffle.pageType = "Playlist";
  63. // Check if action bar has loaded
  64. let soundActions = document.querySelector('.soundActions');
  65. if (soundActions) {
  66. // Insert the button after other action buttons
  67. soundActions.children[0].appendChild(btnShuffle);
  68. } else {
  69. setTimeout(insertButtonLoop, 1000);
  70. }
  71. }
  72. // Case for discover playlists
  73. else if (url.includes("/discover/sets/")) {
  74. btnShuffle.className = 'bhackel-shuffle-likes sc-button sc-button-medium';
  75. btnShuffle.pageType = "Discover";
  76. // Check if action bar has loaded
  77. let playlistControls = document.querySelector('.systemPlaylistDetails__controls');
  78. if (playlistControls) {
  79. // Insert the button after other action buttons
  80. playlistControls.appendChild(btnShuffle);
  81. } else {
  82. setTimeout(insertButtonLoop, 1000);
  83. }
  84. }
  85. }
  86. // Perform another check in 3 seconds, in the case button has been removed
  87. setTimeout(insertButtonLoop, 3000);
  88. }
  89.  
  90. /* Changes the text of the button, resets the queue to have the user's
  91. likes, then starts the scrolling loop. Or it stops the loop from running.
  92. */
  93. function setupLoad(btn) {
  94. // Check whether the loop is running or not
  95. if (btn.scrolling === false) {
  96. btn.innerHTML = 'Click to Stop Loading';
  97. btn.scrolling = true;
  98. // The list of tracks visible on screen, which changes for a playlist or likes
  99. let tracks;
  100. if (btn.pageType === "Likes") {
  101. tracks = document.querySelector('.lazyLoadingList__list');
  102. } else if (btn.pageType === "GenericLikes") {
  103. tracks = document.querySelector('.lazyLoadingList__list');
  104. } else if (btn.pageType === "Playlist") {
  105. tracks = document.querySelector('.trackList__list');
  106. } else if (btn.pageType === "Discover") {
  107. tracks = document.querySelector('.systemPlaylistTrackList__list');
  108. }
  109. if (tracks.childElementCount > 2) {
  110. // Reset the queue to the beginning of the list of tracks
  111. let firstTrack = tracks.children[0];
  112. let secondTrack = tracks.children[1];
  113.  
  114. let firstPlayButton = firstTrack.querySelector(".playButton");
  115. let secondPlayButton = secondTrack.querySelector(".playButton");
  116. // Reset by playing 2, playing 1, then pausing playback
  117. secondPlayButton.click();
  118. setTimeout(function(){ firstPlayButton.click(); }, 150);
  119. setTimeout(function(){
  120. let playButton = document.querySelector('.playControl');
  121. if (playButton.classList.contains('playing')) {
  122. playButton.click();
  123. }
  124. }, 500);
  125.  
  126. // Add the first track to the queue so it gets shuffled
  127. tracks.getElementsByClassName("sc-button-more")[0].click()
  128. document.getElementsByClassName("moreActions__button addToNextUp")[0].click()
  129.  
  130. // Open the queue to load it
  131. toggleQueue('open');
  132.  
  133. // Setup the scrolling loop - Needs time before running so the queue loads
  134. btn.timeout = setTimeout(function(){
  135. btn.interval = setInterval(function() { scrollQueue(btn); }, 500);
  136. }, 3000);
  137. } else {
  138. // The list has two or less tracks - cannot shuffle play
  139. btn.innerHTML = 'Error: Too Few Tracks';
  140. }
  141. } else {
  142. clearInterval(btn.interval);
  143. clearTimeout(btn.timeout);
  144. btn.interval = 0;
  145. btn.scrolling = false;
  146. btn.innerHTML = 'Shuffle Play';
  147. }
  148. }
  149.  
  150. /* Scrolls the queue down, ensuring that the queue is open by opening it
  151. */
  152. function scrollQueue(btn) {
  153. let queue = document.querySelector('.queue');
  154. // Check if the queue is open
  155. if (queue.classList.contains('m-visible')) {
  156. // Scroll the queue to the bottom, loading new tracks below
  157. let scrollableQueue = document.querySelector('.queue__scrollableInner');
  158. let queueContainer = document.querySelector('.queue__itemsHeight');
  159. let scrollToHeight = parseInt(queueContainer.style.height);
  160. scrollableQueue.scroll(0,scrollToHeight);
  161.  
  162. // Check if all tracks are loaded, then play
  163. let autoplayDiv = document.querySelector('.queue__fallback');
  164. if (autoplayDiv) {
  165. clearInterval(btn.interval);
  166. btn.scrolling = false;
  167. btn.interval = 0;
  168. play(btn);
  169. }
  170. } else {
  171. // Open the queue if it is closed
  172. toggleQueue('open');
  173. }
  174. }
  175.  
  176. /* Shuffles the queue, skips the first track, then plays it
  177. */
  178. function play(btn) {
  179. btn.innerHTML = 'Shuffle Play';
  180. let playButton = document.querySelector('.playControl');
  181. let shuffleButton = document.querySelector('.shuffleControl');
  182. let skipButton = document.querySelector(".skipControl__next");
  183.  
  184. // Re-Shuffle tracks if shuffle is enabled, and enable shuffle if it is disabled
  185. if (shuffleButton.classList.contains('m-shuffling')) {
  186. shuffleButton.click();
  187. shuffleButton.click();
  188. } else if (!shuffleButton.classList.contains('m-shuffling')) {
  189. shuffleButton.click();
  190. }
  191.  
  192. // Skip the duplicate first track that was added previously
  193. // This also begins playback
  194. skipButton.click();
  195.  
  196. // Close the queue if it is open
  197. toggleQueue('close');
  198.  
  199. // Add focus back to the play/pause button so keybinds work
  200. playButton.focus()
  201. }
  202.  
  203. /* Opens or closes the song queue
  204. */
  205. function toggleQueue(changeToState) {
  206. let queue = document.querySelector('.queue');
  207. let isQueueOpen = queue.classList.contains('m-visible');
  208. // Toggle queue if the queue is open and it should be closed, or if it's closed and should be open
  209. if ((isQueueOpen && changeToState === 'close') || (!isQueueOpen && changeToState === 'open')) {
  210. let queueTrigger = document.querySelector('.playbackSoundBadge__queueCircle');
  211. queueTrigger.click();
  212. }
  213. }
  214.  
  215. insertButtonLoop();
  216.  
  217. })();

QingJ © 2025

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