Last active
September 25, 2024 10:32
-
-
Save Chaosmeister/918a8b75b1c43e2ef9daa5cab4d347b7 to your computer and use it in GitHub Desktop.
more efficient rvrb layout
This file contains 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 RVRB Relayout | |
// @namespace https://app.rvrb.one/ | |
// @version 1.12 | |
// @description RVRB relayout | |
// @author Tomas Dittmann, Nicola Bosco | |
// @match https://app.rvrb.one/* | |
// @icon https://app.rvrb.one/favicon-32x32.png | |
// @grant none | |
// ==/UserScript== | |
///// CHANGELOG | |
//// 1.12 | |
// - add sound notifications when user's get tagged or mentioned | |
// - add DJ auto step-up | |
//// 1.11 | |
// autodope after 1 and song-length/2 seconds have passed | |
//// 1.10 | |
// use mutationobserver for autodope | |
//// 1.9 | |
// fix autodope loop | |
//// 1.8 | |
// adjust to rvrb v1.10 | |
// move menu text below buttons | |
// use visibility attribute instead of display to fix menu button positions | |
// add setting to hide menutitles | |
// fix custommenu position | |
// use rvrb icon | |
// use rvrb styles | |
//// 1.7 | |
// adjust to rvrb v1.9 | |
//// 1.6 | |
// call colorizeBackground on start-up | |
//// 1.5 | |
// cleanup | |
// refactor menuitem code into separate function | |
// add background styling by Kino | |
//// 1.4 | |
// add Changelog | |
// move checkboxes into separate menu | |
// this will allow adding features more easily | |
(function() { | |
'use strict'; | |
// INTERNAL DATA / FUNCTIONS // | |
var rvrbAccount; | |
var Style1; | |
var Style2; | |
var Style3; | |
var pingSound = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU="); | |
var tagObserver; | |
var replyObserver; | |
var djStepUpObserver; | |
function convert(input) { | |
var parts = input.split(':'), | |
minutes = +parts[0], | |
seconds = +parts[1]; | |
return (minutes * 60 + seconds); | |
} | |
function waitForEl(selector) { | |
return new Promise((resolve) => { | |
if (document.querySelector(selector)) { | |
return resolve(document.querySelector(selector)); | |
} | |
const observer = new MutationObserver(() => { | |
if (document.querySelector(selector)) { | |
resolve(document.querySelector(selector)); | |
observer.disconnect(); | |
} | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true, | |
attributes: true, | |
}); | |
}); | |
} | |
function RegisterTrackChange() { | |
const observer = new MutationObserver(() => { | |
dope(false); | |
}); | |
observer.observe(document.body.querySelector("#trackTitle"), { | |
childList: true, | |
subtree: true, | |
attributes: true, | |
}); | |
} | |
function dope(immediately) | |
{ | |
if (typeof (Storage) !== "undefined") { | |
if (localStorage.getItem("autodope")) { | |
const btn = document.body.querySelector(".section-content.floating.reactions > .section-actions > .action"); | |
if (immediately) | |
{ | |
if (btn.className.indexOf("active") < 0){ | |
btn.click(); | |
} | |
} | |
else | |
{ | |
// dope sometime after half the tracklength + 1 seconds | |
var halfLengthInMilliseconds = ((convert(document.body.querySelector(".trackLength").innerText)/2)+1)*1000; | |
setTimeout(() => { | |
if (btn.className.indexOf("active") < 0){ | |
btn.click(); | |
} | |
}, Math.floor(Math.random() * halfLengthInMilliseconds)); | |
} | |
} | |
} | |
} | |
function colorizeScrollbar() | |
{ | |
if (typeof (Storage) !== "undefined") { | |
if (localStorage.getItem("colorizescrollbar")) { | |
Style1 = addGlobalStyle(` | |
div ::-webkit-scrollbar-thumb { | |
background: linear-gradient(to bottom, var(--secondary-album-palette), var(--primary-album-palette)) | |
}`); | |
Style2 = addGlobalStyle(` | |
div ::-webkit-scrollbar-thumb:window-inactive { | |
background: linear-gradient(to bottom, var(--secondary-album-palette), var(--primary-album-palette)) | |
}`); | |
} else { | |
if (Style1) | |
{ | |
Style1.remove(); | |
} | |
if (Style2) | |
{ | |
Style2.remove(); | |
} | |
} | |
} | |
} | |
function colorizeBackground() | |
{ | |
if (typeof (Storage) !== "undefined") { | |
if (localStorage.getItem("colorizebackground")) { | |
Style3 = addGlobalStyle(` | |
.section-content.fixed:hover, .section-content.scrollable:hover { | |
background-color: rgba(0,0,0,.4)!important; | |
} | |
@property --albumColor { | |
syntax: '<color>'; | |
initial-value: black; | |
inherits: false; | |
} | |
@property --albumColorSecondary { | |
syntax: '<color>'; | |
initial-value: black; | |
inherits: false; | |
} | |
@property --black { | |
syntax: '<color>'; | |
initial-value: black; | |
inherits: false; | |
} | |
#App, #MobileApp { | |
--albumColorSecondary: var(--secondary-album-palette); | |
background-image: linear-gradient(-45deg, var(--albumColorSecondary), var(--black) 85%)!important; | |
transition: --albumColorSecondary 1s; | |
}`); | |
} else { | |
if (Style3) | |
{ | |
Style3.remove(); | |
} | |
} | |
} | |
} | |
function hidemenutitles() | |
{ | |
if (typeof (Storage) !== "undefined") { | |
var SideMenu = document.querySelector('#SideMenu'); | |
if (localStorage.getItem("hidemenutitles")) { | |
SideMenu.querySelectorAll(".nav-label").forEach(item=>{ | |
item.style.visibility = "hidden"; | |
}); | |
UpdateHeight(50); | |
} | |
else | |
{ | |
SideMenu.querySelectorAll(".nav-label").forEach(item=>{ | |
item.style.visibility = "visible"; | |
}); | |
UpdateHeight(65); | |
} | |
} | |
} | |
function beep() { | |
pingSound.play(); | |
} | |
function pingUserOnTag() { | |
if (typeof (Storage) !== "undefined" && rvrbAccount !== undefined) { | |
if (localStorage.getItem("pingUserOnTag")) { | |
var handler = function ([record]) { | |
if (record.type === "childList") { | |
if (record.addedNodes[0].querySelector(".username." + rvrbAccount.userName)) return beep(); | |
} | |
}; | |
tagObserver = new MutationObserver((...r) => handler(...r)); | |
tagObserver.observe(document.getElementById("ChatHistory"), {childList: true, subtree: true}); | |
} else { | |
if (tagObserver !== undefined) { | |
tagObserver.disconnect(); | |
} | |
} | |
} | |
} | |
function pingUserOnReply() { | |
if (typeof (Storage) !== "undefined" && rvrbAccount !== undefined) { | |
if (localStorage.getItem("pingUserOnReply")) { | |
var handler = function ([record]) { | |
if (record.type === "childList") { | |
const replyNode = record.addedNodes[0].querySelector(".replyTo"); | |
if (replyNode && replyNode.textContent.includes(rvrbAccount.displayName)) { | |
beep(); | |
} | |
} | |
}; | |
replyObserver = new MutationObserver((...r) => handler(...r)); | |
replyObserver.observe(document.getElementById("ChatHistory"), {childList: true, subtree: true}); | |
} else { | |
if (replyObserver !== undefined) { | |
replyObserver.disconnect(); | |
} | |
} | |
} | |
} | |
function autoStepUp() { | |
if (typeof (Storage) !== "undefined") { | |
if (localStorage.getItem("autoStepUp")) { | |
var tryToStepUp = function () { | |
const stepUpButton = document.querySelector(".rvrb-dj-step-up"); | |
if (stepUpButton) { | |
stepUpButton.click(); | |
} | |
} | |
var handler = function ([record]) { | |
if (record.type === "childList") { | |
tryToStepUp(); | |
} | |
}; | |
djStepUpObserver = new MutationObserver((...r) => handler(...r)); | |
var djListActionSection = document.querySelector("#DjList .section-actions"); | |
if (djListActionSection !== undefined) { | |
djStepUpObserver.observe(djListActionSection, {childList: true, subtree: true}); | |
} | |
tryToStepUp(); | |
} else { | |
if (djStepUpObserver !== undefined) { | |
djStepUpObserver.disconnect(); | |
} | |
} | |
} | |
} | |
function addGlobalStyle(css) | |
{ | |
var node = document.createElement("style"); | |
node.type = "text/css"; | |
node.appendChild(document.createTextNode(css)); | |
var heads = document.getElementsByTagName("head"); | |
if (heads.length > 0) { | |
heads[0].appendChild(node); | |
} else { | |
// no head yet, stick it whereever | |
document.documentElement.appendChild(node); | |
} | |
return node; | |
} | |
function addCheckbox(Title, Id, OnChange) | |
{ | |
var Element = document.createElement("div"); | |
Element.classList.add("nav-button"); | |
Element.style.display = "flex"; | |
Element.style.justifyContent = "space-between"; | |
Element.style.flexDirection = "row"; | |
Element.innerHTML = '<label for="ID' + Id + '">' + Title + '</label>'; | |
var CheckBox = document.createElement("input"); | |
CheckBox.type = "checkbox"; | |
CheckBox.id = "ID" + Id; | |
CheckBox.style.marginLeft = "5px" | |
if (typeof (Storage) !== "undefined") { | |
if (localStorage.getItem(Id)) { | |
CheckBox.checked = true; | |
} | |
} | |
CheckBox.addEventListener('change', (event) => { | |
if (typeof (Storage) !== "undefined") { | |
if (event.currentTarget.checked) { | |
localStorage.setItem(Id, 'on'); | |
} else { | |
localStorage.removeItem(Id); | |
} | |
} | |
}); | |
CheckBox.addEventListener('change', OnChange); | |
Element.appendChild(CheckBox); | |
return Element; | |
} | |
function UpdateHeight(Height) | |
{ | |
var App = document.querySelector('#App'); | |
App.style.height = 'calc(100% - ' + Height + 'px)'; | |
var SideMenu = document.querySelector('#SideMenu'); | |
SideMenu.style.height = Height + "px"; | |
var CustomMenu = SideMenu.querySelector('#CustomMenu'); | |
CustomMenu.style.marginTop = Height - CustomMenu.previousElementSibling.clientHeight + "px"; | |
} | |
// Main functions | |
function updateLayout() { | |
///// Re-Layout ///// | |
// remove the SideMenu open button | |
document.querySelector('.toggle-button').remove() | |
// move SideMenu to top | |
var App = document.querySelector('#App'); | |
var SideMenu = document.querySelector('#SideMenu'); | |
App.before(SideMenu); | |
// fix SideMenu Layout | |
SideMenu.style.flexDirection = 'row'; | |
SideMenu.style.width = 'auto'; | |
// remove Account button margins and fix to the right border | |
var Account = SideMenu.querySelector('[title="Account"]'); | |
Account.style.marginTop = 'unset'; | |
Account.style.marginLeft = 'auto'; | |
// show complete text in SideMenu; | |
document.querySelectorAll('.ellipses').forEach(item=>{ | |
item.style.overflow = 'unset'; | |
}); | |
SideMenu.querySelectorAll(".nav-button").forEach(item=>{ | |
item.style.flexDirection = 'column'; | |
item.style.alignItems = 'center'; | |
}); | |
// move the ChatView to center | |
var Chatview = document.querySelector('#ChatView'); | |
var UserView = document.querySelector('#UserView'); | |
Chatview.after(UserView); | |
} | |
function CreateCustomMenu(){ | |
var Menu = document.createElement("div"); | |
Menu.style.visibility = "hidden"; | |
Menu.style.background = "#000" | |
Menu.style.zIndex = "100"; | |
Menu.style.flexDirection = "column"; | |
Menu.id = "CustomMenu"; | |
var MenuButtonText = document.createElement("div"); | |
MenuButtonText.innerHTML = "RE-LAYOUT" | |
MenuButtonText.classList.add("nav-label"); | |
var MenuButtonBox = document.createElement("div"); | |
MenuButtonBox.classList.add("nav-icon"); | |
var MenuButtonIcon = document.createElement("i"); | |
MenuButtonIcon.classList.add("icon"); | |
MenuButtonIcon.classList.add("rvrb-settings"); | |
MenuButtonBox.appendChild(MenuButtonIcon); | |
var MenuButton = document.createElement("div"); | |
MenuButton.appendChild(MenuButtonBox); | |
MenuButton.appendChild(MenuButtonText); | |
MenuButton.classList.add("nav-button"); | |
MenuButton.style.flexDirection = "column"; | |
MenuButton.style.alignItems = "center"; | |
MenuButton.addEventListener("click", function() { | |
if (event.currentTarget !== event.target) { | |
if (Menu.style.visibility === "hidden") { | |
Menu.style.visibility = "visible"; | |
} | |
else | |
{ | |
Menu.style.visibility = "hidden"; | |
} | |
} | |
}); | |
var CustomMenu = document.createElement("div"); | |
CustomMenu.style.display = "flex"; | |
CustomMenu.style.flexDirection = "column"; | |
CustomMenu.appendChild(MenuButton); | |
CustomMenu.appendChild(Menu); | |
var SideMenu = document.querySelector('#SideMenu'); | |
var Account = SideMenu.querySelector('[title="Account"]'); | |
Account.before(CustomMenu); | |
Menu.appendChild(addCheckbox("Autodope", "autodope", (event) => { | |
dope(true); | |
})); | |
Menu.appendChild(addCheckbox("Colorize scrollbar","colorizescrollbar", (event) => { | |
colorizeScrollbar(); | |
})); | |
Menu.appendChild(addCheckbox("Colorize background","colorizebackground", (event) => { | |
colorizeBackground(); | |
})); | |
Menu.appendChild(addCheckbox("Hide menu titles","hidemenutitles", (event) => { | |
hidemenutitles(); | |
})); | |
Menu.appendChild(addCheckbox("Ping user on tag","pingUserOnTag", (event) => { | |
pingUserOnTag(); | |
})); | |
Menu.appendChild(addCheckbox("Ping user on reply","pingUserOnReply", (event) => { | |
pingUserOnReply(); | |
})); | |
Menu.appendChild(addCheckbox("Enable DJ auto step-up","autoStepUp", (event) => { | |
autoStepUp(); | |
})); | |
} | |
function loadRvrbAccout () { | |
if (typeof (Storage) !== "undefined") { | |
const rvrbAccountRaw = localStorage.getItem("rvrb-account"); | |
rvrbAccount = JSON.parse(rvrbAccountRaw); | |
} | |
} | |
loadRvrbAccout(); | |
// wait for all elements to load before applying all the patches | |
waitForEl("#ChatView").then(() => { | |
updateLayout(); | |
CreateCustomMenu(); | |
// apply custom features | |
dope(true); | |
colorizeScrollbar(); | |
colorizeBackground(); | |
hidemenutitles(); | |
pingUserOnTag(); | |
pingUserOnReply(); | |
autoStepUp(); | |
// register autodope on trackchange | |
RegisterTrackChange(); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment