133 lines
4.9 KiB
JavaScript
133 lines
4.9 KiB
JavaScript
// ==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);
|
|
})(); |