您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Preview your GeoGuessr guess before placing it!
当前为
// ==UserScript== // @name Guess Preview (GeoGuessr) // @namespace rawblocky // @version 2025.06.08 // @description Preview your GeoGuessr guess before placing it! // @author Rawblocky // @match *://*.geoguessr.com/* // @run-at document-start // @grant GM_addStyle // @grant unsafeWindow // @grant window.onurlchange // @license MIT // ==/UserScript== // Credit to Alien Perfect's original Guess Peek const SEARCH_RADIUS = 50000; const PREVIEW_SIZE_WIDTH = "30%"; GM_addStyle(` .guess-preview-button { position: absolute; bottom: 0; left: 0; width: ${PREVIEW_SIZE_WIDTH}; height: auto; z-index: 10; user-select: none; } `); let svs; function initSVS() { svs = new unsafeWindow.google.maps.StreetViewService(); } function convertDistance(distance) { if (distance >= 1000) return (distance / 1000).toFixed(1) + " km"; return distance.toFixed(1) + " m"; } function computeDistanceBetween(coords1, coords2) { return unsafeWindow.google.maps.geometry.spherical.computeDistanceBetween( coords1, coords2 ); } function getStreetViewUrl(panoId) { return `https://www.google.com/maps/@?api=1&map_action=pano&pano=${panoId}`; } async function getNearestPano(coords) { let pano = {}; let panorama, oldRadius; let radius = SEARCH_RADIUS; if (!svs) initSVS(); // eslint-disable-next-line no-constant-condition while (true) { try { panorama = await svs.getPanorama({ location: coords, radius: radius, source: "outdoor", preference: "nearest", }); let roadHeading = 0; if (panorama.data.tiles && panorama.data.tiles.centerHeading) { roadHeading = panorama.data.tiles.centerHeading; } radius = computeDistanceBetween(coords, panorama.data.location.latLng); pano.radius = radius; pano.url = getStreetViewUrl(panorama.data.location.pano) + `&heading=${roadHeading}`; pano.image = `https://streetviewpixels-pa.googleapis.com/v1/thumbnail?w=640&h=360&panoid=${panorama.data.location.pano}&yaw=${roadHeading}&cb_client=maps_sv.share`; if (oldRadius && radius >= oldRadius) break; oldRadius = radius; } catch (e) { break; } } return pano; } function removeImage() { const container = document.querySelector( '[class^="guess-map_canvasContainer__"]' ); if (container) { const button = container.querySelector(".guess-preview-button"); if (button) { container.removeChild(button); } } } function getIsClassicGame() { const currentUrl = window.location.href; return ( currentUrl.includes("geoguessr.com/game/") || currentUrl.includes("geoguessr.com/challenge/") ); } function getImage() { if (!getIsClassicGame) { return removeImage(); } const container = document.querySelector( '[class^="guess-map_canvasContainer__"]' ); if (container) { let button = container.querySelector(".guess-preview-button"); if (!button) { button = document.createElement("a"); button.className = "guess-preview-button"; button.target = "_blank"; button.style.zIndex = 10; let img = document.createElement("img"); img.className = "guess-preview"; img.style.width = "100%"; img.style.height = "100%"; img.style.zIndex = 10; button.appendChild(img); container.appendChild(button); } return [button.querySelector(".guess-preview"), button]; } else { return null; } } const originalFetch = unsafeWindow.fetch; let lastRanEpoch = 0; async function onFetch(args) { if (!getIsClassicGame) { removeImage(); return; } // Cooldown const currentEpoch = Date.now(); const previousEpoch = lastRanEpoch; lastRanEpoch = currentEpoch; if (currentEpoch - previousEpoch < 1000) { await new Promise((resolve) => setTimeout(resolve, 1000)); } if (currentEpoch !== lastRanEpoch) { return; } // Whenever the terrain api gets called, it'll send the coords with it (probably used by Geo to decide to either play the water/plonk SFX) // We'll use that to display the current location if ( args[0] === "https://www.geoguessr.com/api/v4/geo-coding/terrain" && args[1]?.method === "POST" ) { const requestBody = args[1]?.body; if (requestBody) { try { let imgInfo = getImage(); if (!imgInfo || !imgInfo[0] || !imgInfo[1]) { return; } let img = imgInfo[0]; let button = imgInfo[1]; const jsonBody = JSON.parse(requestBody); let locationInfo = await getNearestPano(jsonBody); if (!locationInfo || !locationInfo.image) { button.style.display = "none"; return; } button.style.display = "block"; img.src = locationInfo.image; button.href = locationInfo.url; } catch (e) { console.error("Failed to parse JSON body:", e); } } } } unsafeWindow.fetch = async function (...args) { Promise.resolve().then(() => onFetch(args)); const response = await originalFetch.apply(this, args); return response; };