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

View File

@@ -0,0 +1,37 @@
# Setup
```
python -m venv .venv
```
## Windows
```
.venv\Scripts\activate
pip install -r requirements.txt
ytmusicapi browser
```
## Linux
```
source .venv/bin/activate
pip install -r requirements.txt
ytmusicapi browser
```
Add the script in [tampermonkey](tampermonkey) as new userscript in tampermonkey
# Usage
```
flask run
```
```
node server/server.cjs
```
# Credits
Python code and javascript is written by me
The bridge server was also written by me
CSS was generated by Gemini
The tampermonkey script was also generated by Gemini since my first idea was to use the YTM-Desktop app as player until i realised that it doesn't support adding songs to the queue.

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);
})();