// ==UserScript== // @name 图寻音效 // @namespace https://greasyfork.org/users/1179204 // @version 1.0.6 // @description 增加地图点击音效以及倒计时提醒音效,支持表情快捷键 // @author KaKa // @icon https://www.svgrepo.com/show/521999/bell.svg // @match *://tuxun.fun/* // @exclude *://tuxun.fun/replay-pano?* // @grant GM_addStyle // @license BSD // @downloadURL none // ==/UserScript== (function() { let google,map,streetViewPanorama,gameData,gameMode,gameId,roundState,currentRound,previousSize,isRecording,recordMode,events=[] let replay_data=JSON.parse(localStorage.getItem('replay_data')) if(!replay_data) replay_data={} let videoIcon=` ` let iconUrl=svgToUrl(videoIcon) const button = document.createElement('button'); button.id = 'replay'; let intervalId=setInterval(function(){ const streetViewContainer= document.getElementById('viewer') const wrapper=document.querySelector('.wrapper___NMMQn') if(streetViewContainer){ wrapper.appendChild(button) clearInterval(intervalId)} },500); GM_addStyle(` #replay { position:absolute; left:24px; top:18%; width: 40px; height: 40px; border: none; border-radius:20px; background-image: url('${iconUrl}'); background-size: auto; background-position: center; background-color:#000000; background-repeat: no-repeat; cursor: pointer; color: white; font-size: 16px; display: inline-flex; align-items: center; justify-content: center; z-Index:9999 } `); button.onclick = () => { if(!isRecording){ console.log('开始录制') initObserver() mapListener() isRecording=true const svgContent = videoIcon.replace(/fill="#fff"/g, 'fill="#D87A16"') button.style.backgroundImage=`url(${svgToUrl(svgContent)})` } else{ isRecording=false console.log('停止录制') if(!replay_data[gameId])replay_data[gameId]={} replay_data[gameId][currentRound]=events localStorage.setItem('replay_data',JSON.stringify(replay_data)) events=[] button.style.backgroundImage=`url('${iconUrl}')` } } const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { node.querySelectorAll && node.querySelectorAll('.countdownTimer___pqf8b').forEach(childNode => { let intervalId = setInterval(function() { const timeLeft = childNode.innerHTML.toString(); if (timeLeft.includes(':')) { recordEvent('CountDown', timeLeft); clearInterval(intervalId); } }, 100); }); }); } } }); const config = { childList: true, subtree: true }; observer.observe(document.body, config); function getSvContainer(){ const streetViewContainer= document.getElementById('viewer') const keys = Object.keys(streetViewContainer) const key = keys.find(key => key.startsWith("__reactFiber")) const props = streetViewContainer[key] streetViewPanorama=props.return.child.memoizedProps.children[1].props.googleMapInstance gameData=props.return.return.return.return.return.memoizedState.next.next.memoizedState.current.gameData gameMode=gameData.type if(gameMode==='infinity'||gameMode==='challenge'){ recordMode=true } } function svgToUrl(svgText) { const svgBlob = new Blob([svgText], {type: 'image/svg+xml'}); const svgUrl = URL.createObjectURL(svgBlob); return svgUrl; } function getMap(){ var mapContainer = document.getElementById('map') const keys = Object.keys(mapContainer) const key = keys.find(key => key.startsWith("__reactFiber$")) const props = mapContainer[key] const x = props.child.memoizedProps.value.map map=x.getMap() google=unsafeWindow.google } function mapListener() { const mapContainer = document.querySelector('.maplibregl-canvas'); if (!mapContainer) { console.error('Map container not found.'); return; } const observer = new MutationObserver((mutationsList) => { for (let mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName === 'style'&&isRecording) { handleMapChange(mapContainer) } } }); observer.observe(mapContainer, { attributes: true, attributeFilter: ['style'] }); } function handleMapChange(mapContainer) { const style = window.getComputedStyle(mapContainer); const { width, height } = mapContainer.getBoundingClientRect(); const currentScreenWidth = window.innerWidth; const widthRatio =Math.round((width / currentScreenWidth) * 100); if (widthRatio===50&&roundState&&!recordMode){ recordEvent('RoundEnd') roundState=false if(!replay_data[gameId])replay_data[gameId]={} replay_data[gameId][currentRound]=events localStorage.setItem('replay_data',JSON.stringify(replay_data)) currentRound+=1 events=[] } else if (widthRatio!=50&&!roundState&&!recordMode){ roundState=true } if (widthRatio>=90&&previousSize<90&&recordMode){ recordEvent('RoundEnd') roundState=false if(!replay_data[gameId])replay_data[gameId]={} replay_data[gameId][currentRound]=events localStorage.setItem('replay_data',JSON.stringify(replay_data)) currentRound+=1 events=[] } else if (widthRatio<90&&previousSize>=90&&recordMode){ roundState=true } if (previousSize!=widthRatio&&roundState&&widthRatio!=50&&previousSize!=50){ recordEvent('MapSize',[Math.round(width),Math.round(height)]); } previousSize=widthRatio } function recordEvent(type,data) { if(roundState&&isRecording){ events.push({time:new Date().getTime(), type:type, data:data}); } } function setObserver(panorama) { let zoomLevel,bounds,mapCenter if (gameData.status==='finish') return panorama.addListener('position_changed', () => { recordEvent('PanoPosition', panorama.getPano()); }); panorama.addListener('pov_changed', () => { const pov = panorama.getPov() const formattedPov = { heading: pov.heading.toFixed(2), pitch: pov.pitch.toFixed(2) }; recordEvent('PanoPov',[formattedPov.heading,formattedPov.pitch]); }); panorama.addListener('zoom_changed', () => { recordEvent('PanoZoom', panorama.getZoom().toFixed(2)); }); map.on('moveend', () => { bounds = map.getBounds(); mapCenter=bounds.getCenter(); recordEvent('MapView', [mapCenter.lat.toFixed(5),mapCenter.lng.toFixed(5)]); }); map.on('zoom', (event) => { zoomLevel = map.getZoom(); bounds = map.getBounds(); mapCenter = bounds.getCenter(); recordEvent('MapZoom', [mapCenter.lat.toFixed(5),mapCenter.lng.toFixed(5),zoomLevel]); }); map.on('click', (event) => { const { lngLat } = event; recordEvent('Pin', [lngLat.lat.toFixed(5), lngLat.lng.toFixed(5)]); }); } function initObserver(){ try { getSvContainer() getMap()} catch(error){ console.log('Failed to start listening')} if(streetViewPanorama){ gameId=gameData.id||gameData.challengeId currentRound=gameData.currentRound var round if (gameData.rounds.length===1&¤tRound!=1){ roundState=true } else{round=gameData.rounds[currentRound-1].endTime if (round) roundState=false else roundState=true} setObserver(streetViewPanorama)} } let onKeyDown = (e) => { if (e.key === 'v' || e.key === 'V') { e.stopImmediatePropagation(); if(!isRecording){ console.log('开始录制') initObserver() mapListener() isRecording=true const svgContent = videoIcon.replace(/fill="#fff"/g, 'fill="#D87A16"') button.style.backgroundImage=`url(${svgToUrl(svgContent)})` } else{ isRecording=false console.log('停止录制') if(!replay_data[gameId])replay_data[gameId]={} replay_data[gameId][currentRound]=events localStorage.setItem('replay_data',JSON.stringify(replay_data)) events=[] button.style.backgroundImage=`url('${iconUrl}')` } } } document.addEventListener("keydown", onKeyDown); })();