Created
March 26, 2025 16:31
-
-
Save t7ko/8ae14612d53195d70fbec1fc98a25bc1 to your computer and use it in GitHub Desktop.
Adds 3 more 'like' level to Yandex Music (so instead of like/not you can classify to 4 levels of song coolness). For UI version up to 2024.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name YM multiple likes | |
| // @namespace http://tampermonkey.net/ | |
| // @version 2024-01-10 | |
| // @author AB | |
| // @match https://music.yandex.ru/* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=yandex.ru | |
| // @grant none | |
| // @description Adds likes buttons for playlist named StarN to player controls | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| function onRemove(element, callback) { | |
| const parent = element.parentNode; | |
| if (!parent) throw new Error("The node must already be attached"); | |
| const obs = new MutationObserver(mutations => { | |
| for (const mutation of mutations) { | |
| for (const el of mutation.removedNodes) { | |
| if (el === element) { | |
| obs.disconnect(); | |
| callback(); | |
| } | |
| } | |
| } | |
| }); | |
| obs.observe(parent, { | |
| childList: true, | |
| }); | |
| } | |
| const PLAYLIST_NAMES = { | |
| 2: "Star2", | |
| 3: "3 Stars", | |
| 4: "4 Stars", | |
| 5: "5 Stars", | |
| // 6: "Stoned" // custom playlist | |
| 6: "Hang Drum" // custom playlist | |
| } | |
| const SELECTORS = { | |
| controls: ".player-controls__track-controls", | |
| prevTrack: ".player-controls__btn_prev", | |
| nextTrack: ".player-controls__btn_next", | |
| castButton: ".player-controls__btn-cast", | |
| //addToPlaylistButton: ".d-addition__opener", | |
| addToPlaylistButton: ".player-controls__btn_add > .d-addition__opener", | |
| checkedPlaylist: ".d-icon_checkmark", | |
| customControls: "custom-like-controls-container", | |
| trackTitle: ".track__title" | |
| } | |
| const likeButtonTemplate = (num, liked = false) => { | |
| const elem = document.createElement("span") | |
| elem.className = 'd-like d-like_on player-controls__btn deco-player-controls__button d-like_theme-player d-icon-star-' + num | |
| elem.innerHTML = '<span class="d-icon d-icon_heart' + (liked ? '-full' : '') + ' d-icon-star-inner-' + num + ' ">' + num + '</span>' | |
| return elem | |
| } | |
| const clickOnUI = (sel) => { | |
| const elem = document.querySelector(sel) | |
| if (elem) { | |
| elem.click(); | |
| } | |
| } | |
| const showCustomControls = () => { | |
| clickOnUI(SELECTORS.addToPlaylistButton) | |
| setTimeout(() => { | |
| const tmpControls = document.querySelector('.' + SELECTORS.customControls); | |
| tmpControls?.remove(); | |
| const controls = document.querySelector(SELECTORS.controls); | |
| const innerControls = document.createElement("i") | |
| innerControls.className = SELECTORS.customControls | |
| for (let i = 3; i <= 6; i++) { | |
| const playlist = document.querySelector('[title="' + PLAYLIST_NAMES[i] + '"].deco-popup-menu__item-text') | |
| let liked = false | |
| if (playlist?.parentElement.querySelector(SELECTORS.checkedPlaylist)) { | |
| liked = true | |
| } | |
| const star = likeButtonTemplate(i, liked) | |
| star.replaceWith(star.cloneNode(true)); | |
| star.addEventListener("click", () => { | |
| clickOnUI(SELECTORS.addToPlaylistButton) | |
| setTimeout(() => { | |
| const pl = document.querySelector('[title="' + PLAYLIST_NAMES[i] + '"].deco-popup-menu__item-text') | |
| if (pl) { | |
| let ld = false | |
| if (pl?.parentElement.querySelector(SELECTORS.checkedPlaylist)) { | |
| ld = true | |
| } | |
| pl?.parentElement.click() | |
| const lb = document.querySelector('.d-icon-star-inner-' + i) | |
| if (!ld) { | |
| lb.classList.add('d-icon_heart-full') | |
| lb.classList.remove('d-icon_heart') | |
| } | |
| } | |
| clickOnUI(SELECTORS.addToPlaylistButton) | |
| }, 500) | |
| }) | |
| innerControls.appendChild(star) | |
| } | |
| innerControls.style.marginRight = '-250px'; | |
| const tmpElem = document.createElement("span") | |
| tmpElem.id = "tmpElem" | |
| innerControls?.appendChild(tmpElem) | |
| //const refreshButton = document.createElement("div") | |
| //refreshButton.className = 'player-controls__btn deco-player-controls__button player-controls__btn_repeat' | |
| //refreshButton.title = 'Перезагрузить кнопки лайков' | |
| //refreshButton.innerHTML = '<div class="d-icon d-icon_repeat"></div>' | |
| //refreshButton.addEventListener("click", () => { | |
| // showCustomControls() | |
| //}) | |
| //innerControls?.appendChild(refreshButton) | |
| const refreshButtonR = likeButtonTemplate("R", false) | |
| refreshButtonR.replaceWith(refreshButtonR.cloneNode(true)); | |
| refreshButtonR.title = 'Перезагрузить кнопки лайков' | |
| refreshButtonR.addEventListener("click", () => { | |
| showCustomControls() | |
| }) | |
| innerControls?.appendChild(refreshButtonR) | |
| controls?.appendChild(innerControls) | |
| onRemove(tmpElem, showCustomControls) | |
| clickOnUI(SELECTORS.addToPlaylistButton) | |
| }, 500) | |
| } | |
| var lastTrack = ''; | |
| const getTrackHref = () => { | |
| document.querySelector(SELECTORS.trackTitle)?.href | |
| } | |
| const watcher = () => { | |
| const tmpControls = document.querySelector('.' + SELECTORS.customControls); | |
| if (tmpControls == null) { | |
| showCustomControls(); | |
| } else { | |
| const currTrack = getTrackHref(); | |
| if (currTrack != lastTrack) { | |
| lastTrack = currTrack; | |
| setTimeout(showCustomControls, 2000) | |
| } | |
| } | |
| startWatcher() | |
| } | |
| const startWatcher = () => { | |
| setTimeout(watcher, 1000) | |
| } | |
| const setup = () => { | |
| const prevButton = document.querySelector(SELECTORS.prevTrack); | |
| const nextButton = document.querySelector(SELECTORS.nextTrack); | |
| prevButton?.addEventListener("click", () => { | |
| setTimeout(showCustomControls, 1000) | |
| }) | |
| nextButton?.addEventListener("click", () => { | |
| setTimeout(showCustomControls, 1000) | |
| }) | |
| const cast = document.querySelector(SELECTORS.castButton); | |
| if (cast) cast.remove(); | |
| setTimeout(startWatcher, 2000); | |
| setTimeout(showCustomControls, 500) | |
| } | |
| setTimeout(setup, 3000); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment