您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Truly sort songs from an artist's page by play count from highest to lowest.
当前为
// ==UserScript== // @name YouTube Music: sort by play count // @match https://music.youtube.com/* // @grant none // @version 1.0.7 // @license MIT // @description Truly sort songs from an artist's page by play count from highest to lowest. // @namespace https://github.com/KenKaneki73985 // @author Ken Kaneki // ==/UserScript== // user_script = "moz-extension://762e4395-b145-4620-8dd9-31bf09e052de/options.html#nav=b41e56cb-722b-4b62-8e6a-304d16fa6f20+editor" <--- this line is very important. Do not delete this at all cost. (function() { 'use strict'; // ===== CONFIGURATION OPTIONS ===== const NOTIFICATION_CONFIG = { IN_PROGRESS_Notification: { top: '80%', // Vertical position (can use %, px, etc.) right: '38%', // Horizontal position (can use %, px, etc.) fontSize: '17px' }, SORTING_COMPLETE_Notification: { top: '80%', // Different vertical position right: '45%', // Horizontal position fontSize: '17px' }, ALREADY_SORTED_Notification: { top: '80%', // Different vertical position right: '42%', // Horizontal position fontSize: '17px' } }; if (document.readyState === 'complete' || document.readyState === 'interactive') { // Create a style for the notification const style = document.createElement('style'); style.textContent = ` #auto-dismiss-notification { position: fixed; color: white; padding: 15px; border-radius: 5px; z-index: 9999; transition: opacity 0.5s ease-out; } #auto-dismiss-notification.sorting-in-progress { background-color: rgba(0, 100, 0, 0.7); /* Green */ } #auto-dismiss-notification.sorting-complete { background-color: rgba(82, 82, 255, 0.7); /* Blue */ } #auto-dismiss-notification.already-sorted { // background-color: rgba(255, 165, 0, 0.7); /* Orange */ background-color: rgba(82, 82, 255, 0.7); /* Blue */ }`; document.head.appendChild(style); let SORT_SONGS_BTN = document.createElement('button') SORT_SONGS_BTN.innerHTML ='<svg width="30px" height="30px" fill="#0080ff" viewBox="0 0 24 24" id="sort-ascending" data-name="Flat Line" xmlns="http://www.w3.org/2000/svg" class="icon flat-line" stroke="#0080ff"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><polyline id="primary" points="10 15 6 19 2 15" style="fill: none; stroke: #0080ff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline><path id="primary-2" data-name="primary" d="M6,19V4M20,16H15m5-5H13m7-5H10" style="fill: none; stroke: #0080ff; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path></g></svg>' SORT_SONGS_BTN.style.border = "none" SORT_SONGS_BTN.style.position = 'absolute' SORT_SONGS_BTN.style.left = '89%' // works in 125/150% SORT_SONGS_BTN.style.top = '2.5%' SORT_SONGS_BTN.style.padding = '0px' SORT_SONGS_BTN.style.background = "none" SORT_SONGS_BTN.style.zIndex = '9999' SORT_SONGS_BTN.addEventListener('click', () => { // Check if playlist is already sorted if (IS_PLAYLIST_SORTED()) { MESSAGE_ALREADY_SORTED(); return; } // Show message immediately MESSAGE_SORTING_IN_PROCESS(); // Delay sorting to ensure message is visible setTimeout(() => { SORT_SONGS(); // Show sorting complete message after sorting setTimeout(() => { MESSAGE_SORTING_COMPLETE(); }, 500); // Small delay to ensure sorting is visually complete }, 50); // Small delay to ensure message rendering }); document.body.appendChild(SORT_SONGS_BTN) } // Function to convert play count string to number function parsePlayCount(playString) { playString = playString.replace(' plays', '').trim(); const multipliers = { 'B': 1000000000, 'M': 1000000, 'K': 1000 }; const match = playString.match(/^(\d+(?:\.\d+)?)\s*([BMK])?$/); if (!match) return 0; const number = parseFloat(match[1]); const multiplier = match[2] ? multipliers[match[2]] : 1; return number * multiplier; } // Check if playlist is already sorted by play count function IS_PLAYLIST_SORTED() { const PLAYLIST_SHELF_DIV = document.querySelector('div.ytmusic-playlist-shelf-renderer:nth-child(3)'); if (PLAYLIST_SHELF_DIV) { const children = Array.from(PLAYLIST_SHELF_DIV.children); const playCounts = children.map(child => { const playsElement = child.querySelector('div:nth-child(5) > div:nth-child(3) > yt-formatted-string:nth-child(2)'); return playsElement ? parsePlayCount(playsElement.textContent.trim()) : 0; }); // Check if play counts are in descending order for (let i = 1; i < playCounts.length; i++) { if (playCounts[i] > playCounts[i - 1]) { return false; // Not sorted } } return true; // Already sorted } return false; } function SORT_SONGS(){ const PLAYLIST_SHELF_DIV = document.querySelector('div.ytmusic-playlist-shelf-renderer:nth-child(3)'); if (PLAYLIST_SHELF_DIV) { // Clone the original children to preserve event listeners const topLevelChildren = Array.from(PLAYLIST_SHELF_DIV.children); const songInfo = []; topLevelChildren.forEach((child, index) => { const titleElement = child.querySelector('div:nth-child(5) > div:nth-child(1) > yt-formatted-string:nth-child(1) > a:nth-child(1)'); const playsElement = child.querySelector('div:nth-child(5) > div:nth-child(3) > yt-formatted-string:nth-child(2)'); const songDetails = { element: child, id: `${index + 1}`, title: titleElement ? titleElement.textContent.trim() : 'Title not found', plays: playsElement ? playsElement.textContent.trim() : 'Plays not found', playCount: playsElement ? parsePlayCount(playsElement.textContent.trim()) : 0 }; songInfo.push(songDetails); }); // Sort songs by play count (highest to lowest) songInfo.sort((a, b) => b.playCount - a.playCount); // Use replaceChildren to preserve original event listeners PLAYLIST_SHELF_DIV.replaceChildren(...songInfo.map(song => song.element)); // Modify song ranks without recreating elements songInfo.forEach((song, index) => { song.element.id = `${index + 1}`; }); console.log("Success: Sorted By Play Count"); } else { alert('error: Playlist shelf div not found'); } } function MESSAGE_SORTING_IN_PROCESS(){ // Remove any existing notification const EXISTING_NOTIFICATION = document.getElementById('auto-dismiss-notification'); if (EXISTING_NOTIFICATION) { EXISTING_NOTIFICATION.remove(); } // Create new notification element const notification = document.createElement('div'); notification.id = 'auto-dismiss-notification'; notification.classList.add('sorting-in-progress'); notification.textContent = "Sorting in Progress... Wait a few seconds" // Apply configuration notification.style.top = NOTIFICATION_CONFIG.IN_PROGRESS_Notification.top; notification.style.right = NOTIFICATION_CONFIG.IN_PROGRESS_Notification.right; notification.style.fontSize = NOTIFICATION_CONFIG.IN_PROGRESS_Notification.fontSize; // Append to body document.body.appendChild(notification); // Auto-dismiss after 3 seconds setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => { notification.remove(); }, 500); // matches transition time }, 3000); } function MESSAGE_SORTING_COMPLETE(){ // Remove any existing notification const EXISTING_NOTIFICATION = document.getElementById('auto-dismiss-notification'); if (EXISTING_NOTIFICATION) { EXISTING_NOTIFICATION.remove(); } // Create new notification element const notification = document.createElement('div'); notification.id = 'auto-dismiss-notification'; notification.classList.add('sorting-complete'); notification.textContent = "Sorting Complete" // Apply configuration notification.style.top = NOTIFICATION_CONFIG.SORTING_COMPLETE_Notification.top; notification.style.right = NOTIFICATION_CONFIG.SORTING_COMPLETE_Notification.right; notification.style.fontSize = NOTIFICATION_CONFIG.SORTING_COMPLETE_Notification.fontSize; // Append to body document.body.appendChild(notification); // Auto-dismiss after 3 seconds setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => { notification.remove(); }, 500); // matches transition time }, 3000); } function MESSAGE_ALREADY_SORTED(){ // Remove any existing notification const EXISTING_NOTIFICATION = document.getElementById('auto-dismiss-notification'); if (EXISTING_NOTIFICATION) { EXISTING_NOTIFICATION.remove(); } // Create new notification element const notification = document.createElement('div'); notification.id = 'auto-dismiss-notification'; notification.classList.add('already-sorted'); // notification.textContent = "Playlist Already Sorted by Play Count" notification.textContent = "Already Sorted by Play Count" // Apply configuration notification.style.top = NOTIFICATION_CONFIG.ALREADY_SORTED_Notification.top; notification.style.right = NOTIFICATION_CONFIG.ALREADY_SORTED_Notification.right; notification.style.fontSize = NOTIFICATION_CONFIG.ALREADY_SORTED_Notification.fontSize; // Append to body document.body.appendChild(notification); // Auto-dismiss after 3 seconds setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => { notification.remove(); }, 500); // matches transition time }, 3000); } })();