From be0f1d70272ea36f38857622fd5612983f6ad0df Mon Sep 17 00:00:00 2001 From: aljosa Date: Wed, 22 Oct 2025 22:25:23 +0200 Subject: [PATCH 1/4] Fixed youtube support and added odysee support --- userscript/mpvOdysee.js | 78 ++++++++++++++++++++++++++++ userscript/{mpv.js => mpvYouTube.js} | 32 ++++-------- 2 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 userscript/mpvOdysee.js rename userscript/{mpv.js => mpvYouTube.js} (73%) diff --git a/userscript/mpvOdysee.js b/userscript/mpvOdysee.js new file mode 100644 index 0000000..bf1911c --- /dev/null +++ b/userscript/mpvOdysee.js @@ -0,0 +1,78 @@ +// ==UserScript== +// @name Odysee MPV Player +// @version 0.2 +// @description This little script opens any Odysee video in MPV with a simple button click +// @author TibixDev +// @match https://odysee.com/* +// @icon https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Odysee_Logo.svg/1200px-Odysee_Logo.svg.png +// @grant GM_addStyle +// ==/UserScript== + +(async function () { + 'use strict'; + console.log("[YTMPV] Odysee MPV player script loaded"); + let urlRegex = /^https:\/\/(www\.)?odysee\.com\/@[^:]+:[^\/]+\/[^:]+:[^\/]*$/ + + function setButtonInterval() { + return setInterval(() => { + if (document.querySelector(".card__body>.media__subtitle--between>.media__actions")) { + console.log("[YTMPV] Menu container found, executing..."); + addMpvButton() + } + }, 2000); + } + + // let waitForButtons = setButtonInterval(); + let waitForButtons = null; + + let location = window.location.href;; + if (location.match(urlRegex)) { + waitForButtons = setButtonInterval(); + console.log("[YTMPV] Starting video link correct: " + location); + } + + let waitForUrlChange = setInterval(() => { + if (location !== window.location.href && window.location.href.match(urlRegex) && !waitForButtons) { + console.log("[YTMPV] Video URL detected, toggling waitForButtons..."); + waitForButtons = setButtonInterval(); + location = window.location.href; + } + }, 2000); + + function addMpvButton() { + clearInterval(waitForButtons); + waitForButtons = null; + const ytButtons = document.querySelector(".card__body>.media__subtitle--between>.media__actions"); + const ytButton = document.createElement("button"); + ytButton.id = "mpv-button"; + ytButton.classList.add("button", "button--no-style", "button--file-action"); + const mpvBtnStyle = ` + #mpv-button { + margin-right: 10px; + }` + + const styleElem = document.createElement("style"); + if (styleElem.styleSheet) { + styleElem.styleSheet.cssText = mpvBtnStyle; + } else { + styleElem.appendChild(document.createTextNode(mpvBtnStyle)); + } + document.getElementsByTagName('head')[0].appendChild(styleElem); + //ytButton.style.cssText = "margin-right: 10px;"; + ytButton.textContent = "▶ MPV"; + ytButton.addEventListener("click", () => { + document.querySelector("video").pause(); + document.location = "mpv://" + document.location.href; + ytButton.textContent = "⌛ Opening..."; + ytButton.style.cssText = "background-color: #0a6dab;"; + setTimeout(() => { + ytButton.textContent = "▶ MPV"; + ytButton.style.cssText = ""; + }, 3000); + }); + + ytButtons.appendChild(ytButton); + console.log("[YTMPV] MPV button added"); + console.log(ytButton, ytButtons); + } +})(); diff --git a/userscript/mpv.js b/userscript/mpvYouTube.js similarity index 73% rename from userscript/mpv.js rename to userscript/mpvYouTube.js index b60305a..f1f8f69 100644 --- a/userscript/mpv.js +++ b/userscript/mpvYouTube.js @@ -14,11 +14,11 @@ function setButtonInterval() { return setInterval(() => { - if (document.querySelector("#info>#menu-container>#menu>ytd-menu-renderer>#top-level-buttons-computed>ytd-toggle-button-renderer")) { + if (document.querySelector("#actions>#actions-inner>#menu>ytd-menu-renderer>#top-level-buttons-computed>yt-button-view-model")) { console.log("[YTMPV] Menu container found, executing..."); addMpvButton() } - }, 1000); + }, 2000); } // let waitForButtons = setButtonInterval(); @@ -27,6 +27,7 @@ let location = window.location.href;; if (location.match(/^https:\/\/www\.youtube\.com\/watch\?v=([^&]*)/)) { waitForButtons = setButtonInterval(); + console.log("[YTMPV] Video link correct: " + location); } let waitForUrlChange = setInterval(() => { @@ -35,35 +36,20 @@ waitForButtons = setButtonInterval(); location = window.location.href; } - }, 1000); + }, 2000); function addMpvButton() { clearInterval(waitForButtons); waitForButtons = null; - const ytButtons = document.querySelector("#info>#menu-container>#menu>ytd-menu-renderer>#top-level-buttons-computed"); + const ytButtons = document.querySelector("#actions>#actions-inner>#menu>ytd-menu-renderer>#top-level-buttons-computed"); const ytButton = document.createElement("button"); ytButton.id = "mpv-button"; + ytButton.classList.add("ytSpecButtonViewModelHost", "style-scope", "ytd-menu-renderer", "yt-spec-button-shape-next", "yt-spec-button-shape-next--tonal", "yt-spec-button-shape-next--mono", "yt-spec-button-shape-next--size-m", "yt-spec-button-shape-next--icon-leading", "yt-spec-button-shape-next--enable-backdrop-filter-experiment"); const mpvBtnStyle = ` #mpv-button { - color: white; - cursor: pointer; - background-color: #043565; - border-radius: 10px; margin-left: 10px; - margin-right: 10px; - padding-left: 10px; - padding-right: 10px; - font-size: var(--ytd-tab-system-font-size); - font-weight: var(--ytd-tab-system-font-weight); - font-family: Roboto, Arial, sans-serif; - border: 0; - transition: all 0.2s ease-in-out; - } - - #mpv-button:hover { - background-color: #0a6dab; }` - + const styleElem = document.createElement("style"); if (styleElem.styleSheet) { styleElem.styleSheet.cssText = mpvBtnStyle; @@ -71,7 +57,7 @@ styleElem.appendChild(document.createTextNode(mpvBtnStyle)); } document.getElementsByTagName('head')[0].appendChild(styleElem); - // ytButton.style.cssText = mpvBtnStyle; + //ytButton.style.cssText = "margin-left: 10px;"; ytButton.textContent = "▶ MPV"; ytButton.addEventListener("click", () => { document.querySelector("video").pause(); @@ -83,7 +69,7 @@ ytButton.style.cssText = ""; }, 3000); }); - + ytButtons.appendChild(ytButton); console.log("[YTMPV] MPV button added"); console.log(ytButton, ytButtons); From 45534c51fd0e10849a2c171b6fe2696f395fcf2d Mon Sep 17 00:00:00 2001 From: aljosa Date: Sun, 26 Oct 2025 23:19:47 +0100 Subject: [PATCH 2/4] Speed/eficiency optimizations --- userscript/mpvOdysee.js | 12 ++---------- userscript/mpvYouTube.js | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/userscript/mpvOdysee.js b/userscript/mpvOdysee.js index bf1911c..ba4ae7f 100644 --- a/userscript/mpvOdysee.js +++ b/userscript/mpvOdysee.js @@ -12,7 +12,7 @@ 'use strict'; console.log("[YTMPV] Odysee MPV player script loaded"); let urlRegex = /^https:\/\/(www\.)?odysee\.com\/@[^:]+:[^\/]+\/[^:]+:[^\/]*$/ - + function setButtonInterval() { return setInterval(() => { if (document.querySelector(".card__body>.media__subtitle--between>.media__actions")) { @@ -22,7 +22,6 @@ }, 2000); } - // let waitForButtons = setButtonInterval(); let waitForButtons = null; let location = window.location.href;; @@ -31,13 +30,6 @@ console.log("[YTMPV] Starting video link correct: " + location); } - let waitForUrlChange = setInterval(() => { - if (location !== window.location.href && window.location.href.match(urlRegex) && !waitForButtons) { - console.log("[YTMPV] Video URL detected, toggling waitForButtons..."); - waitForButtons = setButtonInterval(); - location = window.location.href; - } - }, 2000); function addMpvButton() { clearInterval(waitForButtons); @@ -75,4 +67,4 @@ console.log("[YTMPV] MPV button added"); console.log(ytButton, ytButtons); } -})(); +})(); \ No newline at end of file diff --git a/userscript/mpvYouTube.js b/userscript/mpvYouTube.js index f1f8f69..9844ad8 100644 --- a/userscript/mpvYouTube.js +++ b/userscript/mpvYouTube.js @@ -21,22 +21,29 @@ }, 2000); } - // let waitForButtons = setButtonInterval(); let waitForButtons = null; - let location = window.location.href;; + let location = window.location.href; if (location.match(/^https:\/\/www\.youtube\.com\/watch\?v=([^&]*)/)) { waitForButtons = setButtonInterval(); console.log("[YTMPV] Video link correct: " + location); } - let waitForUrlChange = setInterval(() => { - if (location !== window.location.href && window.location.href.includes("watch?v=") && !waitForButtons) { + function handleVideoUrlChange(newUrl) { + if (location !== newUrl && newUrl.includes("watch?v=") && !waitForButtons) { console.log("[YTMPV] Video URL detected, toggling waitForButtons..."); waitForButtons = setButtonInterval(); - location = window.location.href; + location = newUrl; } - }, 2000); + } + + // Listen for YouTube's navigation finish event + window.addEventListener('yt-navigate-finish', () => { + // Small delay to ensure URL and content are settled + setTimeout(() => { + handleVideoUrlChange(window.location.href); + }, 100); + }); function addMpvButton() { clearInterval(waitForButtons); From f3f1cfbbed9eb9cdc57d27b5b607dc3fe0fcee99 Mon Sep 17 00:00:00 2001 From: aljosa Date: Sun, 26 Oct 2025 23:23:12 +0100 Subject: [PATCH 3/4] Added youtube downloader --- .../mpv-opener.desktop | 0 desktopFiles/ytdlp-downloader.desktop | 9 ++ userscript/youtubeDownloader.js | 84 +++++++++++++++++++ 3 files changed, 93 insertions(+) rename mpv-opener.desktop => desktopFiles/mpv-opener.desktop (100%) create mode 100755 desktopFiles/ytdlp-downloader.desktop create mode 100644 userscript/youtubeDownloader.js diff --git a/mpv-opener.desktop b/desktopFiles/mpv-opener.desktop similarity index 100% rename from mpv-opener.desktop rename to desktopFiles/mpv-opener.desktop diff --git a/desktopFiles/ytdlp-downloader.desktop b/desktopFiles/ytdlp-downloader.desktop new file mode 100755 index 0000000..8e56910 --- /dev/null +++ b/desktopFiles/ytdlp-downloader.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=Video Downloader +Exec=bash -c 'arg=$0; arg=${arg/"https//"/"https://"}; clean_url=${arg:19}; echo "Download URL: $clean_url"; yt-dlp -f "best[height<=1080]/best" --output "$HOME/Downloads/%(title)s.%(ext)s" "$clean_url" || (echo "ERROR: Download failed for URL: $clean_url" && echo "Raw arg: $arg"; echo "Raw link:" $0 && sleep 5)' "%u" +StartupNotify=true +MimeType=x-scheme-handler/ytdlp-downloader +Terminal=true # Change to false after testing for background runs +Hidden=false +Icon=download diff --git a/userscript/youtubeDownloader.js b/userscript/youtubeDownloader.js new file mode 100644 index 0000000..a1822a4 --- /dev/null +++ b/userscript/youtubeDownloader.js @@ -0,0 +1,84 @@ +// ==UserScript== +// @name YouTube Downloader +// @version 0.3 +// @description This little script downloads any YouTube video +// @author TibixDev +// @match https://www.youtube.com/* +// @grant GM_addStyle +// ==/UserScript== + +(async function () { + 'use strict'; + console.log("[YTDL] YouTube downloader script loaded"); + + function setButtonInterval() { + return setInterval(() => { + if (document.querySelector("#actions>#actions-inner>#menu>ytd-menu-renderer>#top-level-buttons-computed>yt-button-view-model")) { + console.log("[YTDL] Menu container found, executing..."); + addDlButton() + } + }, 2000); + } + + let waitForButtons = null; + + let location = window.location.href; + if (location.match(/^https:\/\/www\.youtube\.com\/watch\?v=([^&]*)/)) { + waitForButtons = setButtonInterval(); + console.log("[YTDL] Video link correct: " + location); + } + + function handleVideoUrlChange(newUrl) { + if (location !== newUrl && newUrl.includes("watch?v=") && !waitForButtons) { + console.log("[YTDL] Video URL detected, toggling waitForButtons..."); + waitForButtons = setButtonInterval(); + location = newUrl; + } + } + + // Listen for YouTube's navigation finish event + window.addEventListener('yt-navigate-finish', () => { + // Small delay to ensure URL and content are settled + setTimeout(() => { + handleVideoUrlChange(window.location.href); + }, 100); + }); + + function addDlButton() { + clearInterval(waitForButtons); + waitForButtons = null; + const ytButtons = document.querySelector("#actions>#actions-inner>#menu>ytd-menu-renderer>#top-level-buttons-computed"); + const ytButton = document.createElement("button"); + ytButton.id = "ytdlp-button"; + ytButton.classList.add("ytSpecButtonViewModelHost", "style-scope", "ytd-menu-renderer", "yt-spec-button-shape-next", "yt-spec-button-shape-next--tonal", "yt-spec-button-shape-next--mono", "yt-spec-button-shape-next--size-m", "yt-spec-button-shape-next--icon-leading", "yt-spec-button-shape-next--enable-backdrop-filter-experiment"); + const mpvBtnStyle = ` + #ytdlp-button { + margin-left: 10px; + padding: 0px 40px; + }` + + const styleElem = document.createElement("style"); + if (styleElem.styleSheet) { + styleElem.styleSheet.cssText = mpvBtnStyle; + } else { + styleElem.appendChild(document.createTextNode(mpvBtnStyle)); + } + document.getElementsByTagName('head')[0].appendChild(styleElem); + //ytButton.style.cssText = "margin-left: 10px;"; + ytButton.textContent = "📥 DOWNLOAD"; + ytButton.addEventListener("click", () => { + document.querySelector("video").pause(); + document.location = "ytdlp-downloader://" + document.location.href; + ytButton.textContent = "📥 DOWNLOAD"; + //ytButton.style.cssText = "background-color: #0a6dab;"; + setTimeout(() => { + ytButton.textContent = "Downloading..."; + ytButton.style.cssText = ""; + }, 3000); + }); + + ytButtons.appendChild(ytButton); + console.log("[YTDL] DOWNLOAD button added"); + console.log(ytButton, ytButtons); + } +})(); \ No newline at end of file From 621ff130ea8b766df843f1a10c23369bea610cf6 Mon Sep 17 00:00:00 2001 From: aljosa Date: Mon, 27 Oct 2025 00:27:56 +0100 Subject: [PATCH 4/4] Odysee fix --- userscript/mpvOdysee.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/userscript/mpvOdysee.js b/userscript/mpvOdysee.js index ba4ae7f..f7de8ee 100644 --- a/userscript/mpvOdysee.js +++ b/userscript/mpvOdysee.js @@ -15,6 +15,7 @@ function setButtonInterval() { return setInterval(() => { + //console.log("pooling menu container"); if (document.querySelector(".card__body>.media__subtitle--between>.media__actions")) { console.log("[YTMPV] Menu container found, executing..."); addMpvButton() @@ -30,6 +31,25 @@ console.log("[YTMPV] Starting video link correct: " + location); } + function handleVideoUrlChange(newUrl) { + if (location !== newUrl && window.location.href.match(urlRegex) && !waitForButtons) { + console.log("[YTMPV] Video URL detected, toggling waitForButtons..."); + waitForButtons = setButtonInterval(); + location = newUrl; + } + } + + document.addEventListener('click', (e) => { + //console.log("click"); + // Optional: Skip if clicking your button or specific elements + + if (e.target.id === "mpv-button") { + return; // Don't refresh on MPV button clicks + } + setTimeout(() => { + handleVideoUrlChange(window.location.href); + }, 100); + }, true); function addMpvButton() { clearInterval(waitForButtons);