Автоматический аудиоплеер reCAPTCHA: автоплей с повтором, кнопка Play всегда видна, панель draggable
// ==UserScript==
// @name Recaptcha Audio AutoPlay + Retry Widget
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Автоматический аудиоплеер reCAPTCHA: автоплей с повтором, кнопка Play всегда видна, панель draggable
// @match https://www.google.com/recaptcha/api2/*
// @match https://www.recaptcha.net/recaptcha/api2/*
// @match *://*/*
// @run-at document-idle
// @grant none
// ==/UserScript==
(function() {
'use strict';
let autoPlayEnabled = JSON.parse(localStorage.getItem('rcpAutoPlayEnabled'));
if (autoPlayEnabled === null) autoPlayEnabled = true;
// --- Панель ---
const playerBox = document.createElement('div');
playerBox.style.cssText = `
position:fixed;
bottom:10px;
right:10px;
z-index:999999;
background:#000;
color:#0f0;
padding:8px;
font:12px monospace;
border:1px solid #0f0;
border-radius:6px;
width:300px;
cursor:move;
`;
const savedPos = JSON.parse(localStorage.getItem('rcpBoxPos') || '{}');
if (savedPos.left && savedPos.top) {
playerBox.style.left = savedPos.left + 'px';
playerBox.style.top = savedPos.top + 'px';
playerBox.style.right = 'auto';
playerBox.style.bottom = 'auto';
}
playerBox.innerHTML = `
<div style="display:flex;justify-content:space-between;align-items:center;">
<span>🔊 Audio CAPTCHA</span>
<div>
<button id="rcp-toggle" style="background:#0f0;color:#000;font-weight:bold;margin-right:4px;">–</button>
<button id="rcp-autoplay-toggle" style="background:#0f0;color:#000;font-weight:bold;">${autoPlayEnabled?'ON':'OFF'}</button>
</div>
</div>
<div id="rcp-body" style="margin-top:4px;">
<audio id="rcp-audio" controls style="width:280px;"></audio>
<button id="rcp-play" style="width:280px;margin-top:5px;background:#000;color:#0f0;font-weight:bold;">▶ Play</button>
</div>
`;
document.body.appendChild(playerBox);
const bodyDiv = document.getElementById('rcp-body');
const toggleBtn = document.getElementById('rcp-toggle');
const autoPlayBtn = document.getElementById('rcp-autoplay-toggle');
const audioTag = document.getElementById('rcp-audio');
const playBtn = document.getElementById('rcp-play');
// Сворачивание
toggleBtn.addEventListener('click', () => {
if (bodyDiv.style.display !== 'none') {
bodyDiv.style.display = 'none';
toggleBtn.textContent = '+';
} else {
bodyDiv.style.display = 'block';
toggleBtn.textContent = '–';
}
});
// Автоплей ON/OFF
autoPlayBtn.addEventListener('click', () => {
autoPlayEnabled = !autoPlayEnabled;
localStorage.setItem('rcpAutoPlayEnabled', JSON.stringify(autoPlayEnabled));
autoPlayBtn.textContent = autoPlayEnabled ? 'ON' : 'OFF';
});
// Кнопка Play
playBtn.addEventListener('click', () => {
audioTag.play().catch(()=>console.log('Автоплей разблокирован.'));
playBtn.style.background = '#000';
});
function highlightPlayButton() {
playBtn.style.background = '#0f0';
}
// MutationObserver для новых аудио
let lastSrc = null;
const observer = new MutationObserver(() => {
const aud = document.querySelector('audio[src]');
if (aud && aud.src && aud.src !== lastSrc) {
lastSrc = aud.src;
audioTag.src = lastSrc;
audioTag.load();
highlightPlayButton();
// Повторная попытка автоплей
if (autoPlayEnabled) {
const tryPlay = setInterval(() => {
audioTag.play().then(() => {
playBtn.style.background = '#000';
clearInterval(tryPlay);
}).catch(()=>{
highlightPlayButton(); // мигаем пока не удалось воспроизвести
});
}, 500); // повторяем каждые 500мс до успеха
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Перетаскивание панели
let isDragging = false, offsetX, offsetY;
playerBox.addEventListener('mousedown', (e) => {
if (e.target.tagName === 'BUTTON' || e.target.tagName === 'AUDIO') return;
isDragging = true;
offsetX = e.clientX - playerBox.getBoundingClientRect().left;
offsetY = e.clientY - playerBox.getBoundingClientRect().top;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
e.preventDefault();
});
function onMouseMove(e) {
if (!isDragging) return;
playerBox.style.left = (e.clientX - offsetX) + 'px';
playerBox.style.top = (e.clientY - offsetY) + 'px';
playerBox.style.right = 'auto';
playerBox.style.bottom = 'auto';
}
function onMouseUp() {
if (isDragging) {
isDragging = false;
localStorage.setItem('rcpBoxPos', JSON.stringify({
left: parseInt(playerBox.style.left),
top: parseInt(playerBox.style.top)
}));
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
}
})();