add tampermonkey script

This commit is contained in:
Leo
2026-02-15 01:13:10 +01:00
parent bc2f96c463
commit 01d62df2fb
2 changed files with 170 additions and 0 deletions

133
tampermonkey/script.js Normal file
View File

@@ -0,0 +1,133 @@
// ==UserScript==
// @name YTM Remote Control
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Remote control YTM via local server
// @author Gemini
// @match *://music.youtube.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant GM_xmlhttpRequest
// @connect localhost
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
console.log("Remote Script Running...");
function poll() {
GM_xmlhttpRequest({
method: "GET",
url: "http://localhost:3000/poll",
onload: (res) => {
const data = JSON.parse(res.responseText);
if (data && data.videoId && data.title && data.action && data.artist) {
loadPage(data.title,data.artist,data.videoId,data.action);
}
}
});
}
function loadPage(title,artist,videoId,action) {
// 1. Find and open the search bar if it's closed
const searchOpenButton = document.querySelector('ytmusic-search-box') ||
document.querySelector('tp-yt-paper-icon-button[aria-label="Search"]');
if (searchOpenButton) {
searchOpenButton.click();
}
// 2. Wait a moment for the animation/DOM to catch up
setTimeout(() => {
const searchInput = document.querySelector('input.ytmusic-search-box') ||
document.querySelector('#input.ytmusic-search-box') ||
document.querySelector('input#input');
if (searchInput) {
// 3. Focus and set the value
searchInput.focus();
searchInput.value = title + " " + artist;
// 4. Force the app to "see" the new text
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
searchInput.dispatchEvent(new Event('change', { bubbles: true }));
// 5. Instead of pressing Enter, find the "Search" icon inside the bar and click it
// This is usually the glass icon that appears once you start typing
const searchSubmitButton = document.querySelector('.search-icon.ytmusic-search-box') ||
document.querySelector('ytmusic-search-box [icon="search"]');
if (searchSubmitButton) {
searchSubmitButton.click();
console.log("Search button clicked!");
} else {
// Fallback: If no button, try the "Enter" key again with a more complete event
const opts = { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter', keyCode: 13, which: 13 };
searchInput.dispatchEvent(new KeyboardEvent('keydown', opts));
searchInput.dispatchEvent(new KeyboardEvent('keypress', opts));
searchInput.dispatchEvent(new KeyboardEvent('keyup', opts));
console.log("Search button not found, tried full key sequence.");
}
}
}, 300);
setTimeout(() => {
addToQueue(videoId, action);
}, 1000);
}
function addToQueue(videoId, action) {
console.log("adding");
const actionLabel = action === 'next' ? 'play next' : 'add to queue';
function tryAddToQueue() {
// 1. Find the link for your video
const videoLink = document.querySelector(`a[href*="${videoId}"]`);
if (!videoLink) {
console.log("Waiting for search results...");
return false;
}
// 2. Find the Action Menu button near that link
let container = videoLink.parentElement;
let menuButton = null;
for (let i = 0; i < 10; i++) {
if (!container) break;
menuButton = container.querySelector('button[aria-label="Action menu"]');
if (menuButton) break;
container = container.parentElement;
}
if (menuButton) {
menuButton.click();
// 3. Find the EXCLUSIVE "Add to queue" option
setTimeout(() => {
const menuOptions = document.querySelectorAll('ytmusic-menu-service-item-renderer');
// We use a more strict filter to avoid "Play next"
const addToQueue = Array.from(menuOptions).find(el => {
const text = el.textContent.trim().toLowerCase();
return text === actionLabel;
});
if (addToQueue) {
addToQueue.click();
console.log("Success: Added to the end of the queue!");
} else {
console.error("Found the menu, but couldn't find the exact 'Add to queue' button.");
}
}, 400);
return true;
}
return false;
}
// Attempt to run, and if search results aren't ready, try again in 1 second
if (!tryAddToQueue()) {
setTimeout(tryAddToQueue, 1500);
}
}
setInterval(poll, 1500);
})();