From 67bf469c5cc33d5d34426451870c8dd36586917d Mon Sep 17 00:00:00 2001 From: Akamaru Date: Fri, 5 Dec 2025 21:28:00 +0100 Subject: [PATCH] Neu: DoubleDouble Tools --- README.md | 19 ++ doubledouble-tools.user.js | 594 +++++++++++++++++++++++++++++++++++++ 2 files changed, 613 insertions(+) create mode 100644 doubledouble-tools.user.js diff --git a/README.md b/README.md index 822cebb..ea0aecd 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,25 @@ Stellt die Navigation-Links für "Abos" und "Verlauf" in der YouTube-Sidebar wie --- +### 17. `doubledouble-tools.user.js` +**[Installieren](https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/doubledouble-tools.user.js)** + +**Beschreibung:** +Erweiterte Einstellungen und Features für DoubleDouble Music Downloader (doubledouble.top). Bietet eine zentrale Settings-Verwaltung und verhindert automatische Downloads. + +**Funktionen:** +- Settings-Icon (⚙️) oben rechts öffnet Modal mit allen Einstellungen +- **Funktionale Settings:** Upload to external service (pixeldrain/litterbox/sendcm), Hide my download, Spotify Format (OGG/MP3), Add metadata +- **Kosmetische Settings:** Hide QQDL notice, Hide "Recently Downloaded" (blockiert auch `/recent` Requests), Hide Footer +- **Server-Wahl:** Auto-Redirect von doubledouble.top zu EU/US Server +- **Download-Link anzeigen:** Zeigt Modal mit Download-Link statt Auto-Download +- Modal mit drei Buttons: "Download Now", "Copy Link" (kopiert URL), "Close" +- Alle Einstellungen werden automatisch auf die Seiten-Checkboxen angewendet +- Persistent gespeichert mit GM_getValue/GM_setValue +- Injection in Page Context für zuverlässige window.open Interception + +--- + ## Übersicht der enthaltenen UserStyles ### 1. `myanimelist-tweaks.user.css` diff --git a/doubledouble-tools.user.js b/doubledouble-tools.user.js new file mode 100644 index 0000000..c5f1f34 --- /dev/null +++ b/doubledouble-tools.user.js @@ -0,0 +1,594 @@ +// ==UserScript== +// @name DoubleDouble Tools +// @namespace https://git.ponywave.de/Akamaru/Userscripts +// @version 1.0 +// @description Enhanced settings and features for DoubleDouble music downloader +// @author Akamaru +// @match https://doubledouble.top/* +// @match https://eu.doubledouble.top/* +// @match https://us.doubledouble.top/* +// @icon https://www.google.com/s2/favicons?domain=eu.doubledouble.top&sz=32 +// @grant GM_getValue +// @grant GM_setValue +// @grant GM_addStyle +// @grant GM_setClipboard +// @run-at document-start +// @updateURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/doubledouble-tools.user.js +// @downloadURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/doubledouble-tools.user.js +// ==/UserScript== + +(function() { + 'use strict'; + + console.log('[DoubleDouble Tools] Script loaded'); + + // Default settings + const DEFAULT_SETTINGS = { + uploadExternal: true, + uploadService: 'pdrain', + hideDownload: false, + spotifyFormat: 'ogg', + spotifyMetadata: true, + hideQQDL: false, + hideRecent: false, + hideFooter: false, + serverChoice: 'auto', // 'auto', 'eu', 'us' + showDownloadLink: false + }; + + // Settings management + function getSettings() { + try { + const stored = GM_getValue('doubledouble_settings', '{}'); + const settings = JSON.parse(stored); + return { ...DEFAULT_SETTINGS, ...settings }; + } catch (e) { + console.error('[DoubleDouble Tools] Error loading settings:', e); + return DEFAULT_SETTINGS; + } + } + + function saveSettings(settings) { + GM_setValue('doubledouble_settings', JSON.stringify(settings)); + } + + // Apply cosmetic settings (CSS) + function applyCosmetics(settings) { + let css = ''; + + if (settings.hideQQDL) { + css += '.lucida-link { display: none !important; }\n'; + } + + if (settings.hideRecent) { + css += '#recently-downloaded { display: none !important; }\n'; + } + + if (settings.hideFooter) { + css += '.footer { display: none !important; }\n'; + } + + // Settings icon styles + css += ` + #dd-tools-settings-icon { + position: fixed; + top: 20px; + right: 20px; + z-index: 10000; + background: #333; + color: white; + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + font-size: 20px; + cursor: pointer; + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + transition: background 0.2s; + } + + #dd-tools-settings-icon:hover { + background: #555; + } + + #dd-tools-modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.7); + z-index: 10001; + justify-content: center; + align-items: center; + } + + #dd-tools-modal.active { + display: flex; + } + + #dd-tools-modal-content { + background: #1a1a1a; + color: white; + padding: 30px; + border-radius: 10px; + max-width: 500px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 4px 20px rgba(0,0,0,0.5); + } + + #dd-tools-modal h2 { + margin-top: 0; + margin-bottom: 20px; + font-size: 24px; + } + + .dd-tools-section { + margin-bottom: 25px; + padding-bottom: 20px; + border-bottom: 1px solid #333; + } + + .dd-tools-section:last-child { + border-bottom: none; + } + + .dd-tools-section h3 { + margin-top: 0; + margin-bottom: 15px; + font-size: 16px; + color: #aaa; + text-transform: uppercase; + letter-spacing: 1px; + } + + .dd-tools-option { + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 10px; + } + + .dd-tools-option label { + flex: 1; + cursor: pointer; + } + + .dd-tools-option input[type="checkbox"] { + cursor: pointer; + } + + .dd-tools-option select { + background: #333; + color: white; + border: 1px solid #555; + border-radius: 4px; + padding: 5px 10px; + cursor: pointer; + } + + #dd-tools-close { + background: #d9534f; + color: white; + border: none; + border-radius: 5px; + padding: 10px 20px; + cursor: pointer; + font-size: 14px; + margin-top: 20px; + transition: background 0.2s; + } + + #dd-tools-close:hover { + background: #c9302c; + } + + #dd-tools-download-link-display { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: #1a1a1a; + color: white; + padding: 30px; + border-radius: 10px; + z-index: 10002; + box-shadow: 0 4px 20px rgba(0,0,0,0.5); + min-width: 400px; + display: none; + } + + #dd-tools-download-link-display.active { + display: block; + } + + #dd-tools-download-link-display h3 { + margin-top: 0; + } + + #dd-tools-download-link-display input { + width: 100%; + background: #333; + color: white; + border: 1px solid #555; + border-radius: 4px; + padding: 8px; + margin: 10px 0; + font-family: monospace; + } + + #dd-tools-download-link-display button { + background: #5cb85c; + color: white; + border: none; + border-radius: 5px; + padding: 10px 20px; + cursor: pointer; + margin-right: 10px; + transition: background 0.2s; + } + + #dd-tools-download-link-display button:hover { + background: #449d44; + } + + #dd-tools-copy-link { + background: #f0ad4e !important; + } + + #dd-tools-copy-link:hover { + background: #ec971f !important; + } + + #dd-tools-download-link-display button.secondary { + background: #777; + } + + #dd-tools-download-link-display button.secondary:hover { + background: #555; + } + `; + + GM_addStyle(css); + } + + // Server redirect + function handleServerRedirect(settings) { + if (window.location.hostname === 'doubledouble.top' && settings.serverChoice !== 'auto') { + const newUrl = window.location.href.replace('doubledouble.top', `${settings.serverChoice}.doubledouble.top`); + window.location.href = newUrl; + } + } + + // Block /recent requests + function blockRecentRequests(settings) { + if (!settings.hideRecent) return; + + // Intercept fetch + const originalFetch = window.fetch; + window.fetch = function(...args) { + const url = args[0]; + if (typeof url === 'string' && url.includes('/recent')) { + console.log('[DoubleDouble Tools] Blocked fetch to /recent'); + return Promise.resolve(new Response('[]', { + status: 200, + headers: { 'Content-Type': 'application/json' } + })); + } + return originalFetch.apply(this, args); + }; + + // Intercept XMLHttpRequest + const originalOpen = XMLHttpRequest.prototype.open; + XMLHttpRequest.prototype.open = function(method, url, ...rest) { + if (typeof url === 'string' && url.includes('/recent')) { + console.log('[DoubleDouble Tools] Blocked XMLHttpRequest to /recent'); + // Replace with dummy request + return originalOpen.call(this, method, 'data:text/plain,[]', ...rest); + } + return originalOpen.call(this, method, url, ...rest); + }; + } + + // Inject download interceptor into page context + function interceptDownloads(settings) { + if (!settings.showDownloadLink) return; + + const injectScript = () => { + const script = document.createElement('script'); + script.textContent = ` + (function() { + console.log('[DoubleDouble Tools] Injecting window.open interceptor into page context'); + + const originalWindowOpen = window.open; + let isIntercepting = false; + + window.open = function(url, target, ...rest) { + console.log('[DoubleDouble Tools] window.open called:', url, target); + + // Check if this is a download from statusHandoff (target="_self" and url starts with ./) + if (!isIntercepting && target === '_self' && typeof url === 'string' && url.startsWith('./')) { + console.log('[DoubleDouble Tools] Intercepted download:', url); + isIntercepting = true; + + // Dispatch custom event to show modal + const fullUrl = new URL(url, window.location.href).href; + window.dispatchEvent(new CustomEvent('dd-tools-show-download', { detail: fullUrl })); + + setTimeout(() => { isIntercepting = false; }, 100); + return null; + } + + return originalWindowOpen.call(this, url, target, ...rest); + }; + + console.log('[DoubleDouble Tools] window.open interceptor installed'); + })(); + `; + + (document.head || document.documentElement).appendChild(script); + script.remove(); + }; + + // Inject immediately or wait for head to exist + if (document.head || document.documentElement) { + injectScript(); + } else { + const observer = new MutationObserver(() => { + if (document.head || document.documentElement) { + injectScript(); + observer.disconnect(); + } + }); + observer.observe(document, { childList: true, subtree: true }); + } + + // Listen for the custom event to show the modal + window.addEventListener('dd-tools-show-download', (e) => { + console.log('[DoubleDouble Tools] Showing download modal for:', e.detail); + showDownloadLinkModal(e.detail); + }); + } + + // Show download link modal + function showDownloadLinkModal(url) { + const modal = document.getElementById('dd-tools-download-link-display'); + const input = document.getElementById('dd-tools-download-url-input'); + + const fullUrl = new URL(url, window.location.href).href; + input.value = fullUrl; + modal.classList.add('active'); + } + + // Copy link to clipboard + function copyLinkToClipboard(url) { + GM_setClipboard(url); + + // Visual feedback + const button = document.getElementById('dd-tools-copy-link'); + const originalText = button.textContent; + button.textContent = 'Copied!'; + button.style.background = '#5cb85c'; + + setTimeout(() => { + button.textContent = originalText; + button.style.background = ''; + }, 2000); + } + + // Apply functional settings to page controls + function applyFunctionalSettings(settings) { + const externalCheckbox = document.getElementById('external'); + const siteSelect = document.getElementById('site'); + const privateCheckbox = document.getElementById('private'); + const formatSelect = document.getElementById('format'); + const metadataCheckbox = document.getElementById('metadata'); + + if (externalCheckbox) externalCheckbox.checked = settings.uploadExternal; + if (siteSelect) siteSelect.value = settings.uploadService; + if (privateCheckbox) privateCheckbox.checked = settings.hideDownload; + if (formatSelect) formatSelect.value = settings.spotifyFormat; + if (metadataCheckbox) metadataCheckbox.checked = settings.spotifyMetadata; + } + + // Create settings UI + function createSettingsUI() { + const settings = getSettings(); + + // Settings icon + const icon = document.createElement('button'); + icon.id = 'dd-tools-settings-icon'; + icon.innerHTML = '⚙️'; + icon.title = 'DoubleDouble Tools Settings'; + icon.addEventListener('click', () => { + document.getElementById('dd-tools-modal').classList.add('active'); + }); + document.body.appendChild(icon); + + // Settings modal + const modal = document.createElement('div'); + modal.id = 'dd-tools-modal'; + modal.innerHTML = ` +
+

DoubleDouble Tools Settings

+ +
+

Functional Settings

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+

Cosmetic Settings

+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+

Advanced Settings

+ +
+ + +
+ +
+ + +
+
+ + +
+ `; + document.body.appendChild(modal); + + // Close modal on background click + modal.addEventListener('click', (e) => { + if (e.target === modal) { + saveSettingsFromUI(); + modal.classList.remove('active'); + } + }); + + // Close button + document.getElementById('dd-tools-close').addEventListener('click', () => { + saveSettingsFromUI(); + modal.classList.remove('active'); + // Reload to apply settings + location.reload(); + }); + + // Download link display modal + const downloadModal = document.createElement('div'); + downloadModal.id = 'dd-tools-download-link-display'; + downloadModal.innerHTML = ` +

Download Ready

+

Your download link:

+ +
+ + + +
+ `; + document.body.appendChild(downloadModal); + + // Download button + document.getElementById('dd-tools-download-now').addEventListener('click', () => { + const url = document.getElementById('dd-tools-download-url-input').value; + window.location.href = url; + downloadModal.classList.remove('active'); + }); + + // Copy link button + document.getElementById('dd-tools-copy-link').addEventListener('click', () => { + const url = document.getElementById('dd-tools-download-url-input').value; + copyLinkToClipboard(url); + }); + + // Close download modal + document.getElementById('dd-tools-download-close').addEventListener('click', () => { + downloadModal.classList.remove('active'); + }); + } + + // Save settings from UI + function saveSettingsFromUI() { + const newSettings = { + uploadExternal: document.getElementById('dd-setting-upload-external').checked, + uploadService: document.getElementById('dd-setting-upload-service').value, + hideDownload: document.getElementById('dd-setting-hide-download').checked, + spotifyFormat: document.getElementById('dd-setting-spotify-format').value, + spotifyMetadata: document.getElementById('dd-setting-spotify-metadata').checked, + hideQQDL: document.getElementById('dd-setting-hide-qqdl').checked, + hideRecent: document.getElementById('dd-setting-hide-recent').checked, + hideFooter: document.getElementById('dd-setting-hide-footer').checked, + serverChoice: document.getElementById('dd-setting-server-choice').value, + showDownloadLink: document.getElementById('dd-setting-show-download-link').checked + }; + saveSettings(newSettings); + } + + // Initialize + function init() { + try { + const settings = getSettings(); + + // Apply settings that need to run early (before DOM is ready) + handleServerRedirect(settings); + blockRecentRequests(settings); + interceptDownloads(settings); + applyCosmetics(settings); + + // Wait for DOM to be ready before creating UI + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + try { + createSettingsUI(); + applyFunctionalSettings(settings); + } catch (e) { + console.error('[DoubleDouble Tools] Error creating UI:', e); + } + }); + } else { + createSettingsUI(); + applyFunctionalSettings(settings); + } + } catch (e) { + console.error('[DoubleDouble Tools] Error during initialization:', e); + } + } + + init(); +})();