Автоматически пропускает лайкнутые/дизлайкнутые песни. Клавиша 'D' для дизлайка. Стабильная и отлаженная версия.
当前为
// ==UserScript== // @name [v4.6] YouTube Music - Skip Liked/Disliked & Pause (with Libraries) // @namespace http://tampermonkey.net/ // @version 4.6 // @description Автоматически пропускает лайкнутые/дизлайкнутые песни. Клавиша 'D' для дизлайка. Стабильная и отлаженная версия. // @author torch // @match https://music.youtube.com/* // @require https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dayjs.min.js // @grant GM_addStyle // @license MIT // ==/UserScript== (function() { 'use strict'; // Helper для логгирования с временной меткой const logWithTimestamp = (message, isWarning = false) => { const timestamp = dayjs().format('HH:mm:ss'); const logFunction = isWarning ? console.warn : console.log; logFunction(`[${timestamp}] ${message}`); }; logWithTimestamp("[YT Music Skipper] Version 4.6 (with Lodash & Day.js) loaded."); let isScriptPaused = false; let currentSongTitle = ""; let isSkipping = false; // Флаг "иммунитета" после пропуска // ================================================= // ЧАСТЬ 0: УПРАВЛЕНИЕ СОСТОЯНИЕМ СКРИПТА // ================================================= function createPauseButton() { try { const controlsContainer = document.createElement('div'); controlsContainer.id = 'userscript-controls'; const statusSpan = document.createElement('span'); statusSpan.id = 'script-status'; statusSpan.textContent = 'Статус: Активен'; const toggleButton = document.createElement('button'); toggleButton.id = 'toggle-script-btn'; toggleButton.textContent = 'Пауза'; controlsContainer.append(statusSpan, toggleButton); document.body.appendChild(controlsContainer); toggleButton.addEventListener('click', () => { isScriptPaused = !isScriptPaused; statusSpan.textContent = `Статус: ${isScriptPaused ? 'Пауза' : 'Активен'}`; toggleButton.textContent = isScriptPaused ? 'Возобновить' : 'Пауза'; logWithTimestamp(`Скрипт ${isScriptPaused ? 'приостановлен' : 'возобновлен'}.`); if (!isScriptPaused) checkCurrentTrack(); }); } catch (e) { logWithTimestamp(`Не удалось создать кнопку управления: ${e}`, true); } } GM_addStyle(` #userscript-controls { position: fixed; bottom: 15px; right: 15px; z-index: 10001; background-color: rgba(28, 28, 28, 0.95); border: 1px solid #555; border-radius: 8px; padding: 10px; display: flex; align-items: center; font-family: 'Roboto', 'Arial', sans-serif; color: white; box-shadow: 0 4px 12px rgba(0,0,0,0.6); } #script-status { margin-right: 12px; font-size: 14px; } #toggle-script-btn { background-color: #f44336; color: white; border: none; padding: 8px 12px; font-size: 14px; border-radius: 5px; cursor: pointer; transition: background-color 0.3s; } #toggle-script-btn:hover { background-color: #d32f2f; } `); // ================================================= // ЧАСТЬ 1: АВТОМАТИЧЕСКИЙ ПРОПУСК ПЕСЕН // ================================================= function activateImmunityAndSkip(songTitle, status) { const skipButton = document.querySelector('ytmusic-player-bar .next-button'); if (skipButton && skipButton.offsetParent !== null) { logWithTimestamp(`Пропускаем трек: "${songTitle}". Статус: ${status}.`); isSkipping = true; // Активируем "иммунитет" skipButton.click(); // Используем _.delay вместо setTimeout для консистентности _.delay(() => { logWithTimestamp("Период 'иммунитета' завершен."); isSkipping = false; checkCurrentTrack.cancel(); checkCurrentTrack(); }, 1500); } } function skipTrackIfNecessary(songTitle) { if (isScriptPaused || isSkipping) return; let attempts = 0; const maxAttempts = 15; const checkInterval = setInterval(() => { if (isScriptPaused || isSkipping || attempts++ >= maxAttempts) { clearInterval(checkInterval); return; } const likeStatus = document.querySelector('ytmusic-player-bar ytmusic-like-button-renderer')?.getAttribute('like-status'); if (!likeStatus) return; if (likeStatus === 'LIKE' || likeStatus === 'DISLIKE') { clearInterval(checkInterval); activateImmunityAndSkip(songTitle, likeStatus); } else if (likeStatus === 'INDIFFERENT') { clearInterval(checkInterval); } }, 100); } const checkCurrentTrack = _.debounce(() => { if (isScriptPaused || isSkipping) return; const titleElement = document.querySelector('ytmusic-player-bar .title.style-scope.ytmusic-player-bar'); const newTitle = titleElement?.getAttribute('title'); if (newTitle && newTitle !== currentSongTitle) { currentSongTitle = newTitle; logWithTimestamp(`Новый трек: "${newTitle}". Начинаем проверку статуса...`); skipTrackIfNecessary(newTitle); } }, 250, { leading: false, trailing: true }); // ================================================= // ЧАСТЬ 2: ДИЗЛАЙК ПО КЛАВИШЕ "D" // ================================================= document.addEventListener('keydown', (e) => { if (isScriptPaused || isSkipping) return; const activeElement = document.activeElement; const isInput = activeElement.tagName === 'INPUT' || activeElement.closest('ytmusic-search-box') || activeElement.isContentEditable; if (e.key.toUpperCase() === 'D' && !isInput) { e.preventDefault(); const songTitleForLog = document.querySelector('ytmusic-player-bar .title')?.getAttribute('title') || "Неизвестный трек"; const likeButtonRenderer = document.querySelector('ytmusic-player-bar ytmusic-like-button-renderer'); const currentStatus = likeButtonRenderer?.getAttribute('like-status'); if (currentStatus === 'DISLIKE') { logWithTimestamp(`Трек "${songTitleForLog}" уже дизлайкнут. Действие пропущено.`); return; } const dislikeButton = document.querySelector('ytmusic-player-bar #button-shape-dislike button'); if (dislikeButton) { logWithTimestamp(`Дизлайк и пропуск трека: "${songTitleForLog}"`); isSkipping = true; dislikeButton.click(); // Используем _.delay вместо setTimeout для консистентности _.delay(() => { logWithTimestamp("Период 'иммунитета' (после дизлайка) завершен."); isSkipping = false; checkCurrentTrack.cancel(); checkCurrentTrack(); }, 1500); } else { logWithTimestamp(`Не удалось найти кнопку дизлайка.`, true); } } }); // ================================================= // ЗАПУСК СКРИПТА // ================================================= function initialize() { createPauseButton(); const targetNode = document.querySelector('ytmusic-player-bar .title.style-scope.ytmusic-player-bar'); if (targetNode) { logWithTimestamp("Плеер обнаружен. Запускаем MutationObserver."); const observer = new MutationObserver(checkCurrentTrack); observer.observe(targetNode, { attributes: true, attributeFilter: ['title'] }); checkCurrentTrack(); } else { // Используем _.delay вместо setTimeout для консистентности _.delay(initialize, 500); } } if (document.readyState === 'complete') { initialize(); } else { window.addEventListener('load', initialize); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址