Skip to content

Instantly share code, notes, and snippets.

@t7ko
Created March 26, 2025 16:31
Show Gist options
  • Select an option

  • Save t7ko/8ae14612d53195d70fbec1fc98a25bc1 to your computer and use it in GitHub Desktop.

Select an option

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.
// ==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