Geoguessr Location Retriever

Get the actual location Geoguessr gave you from the result screens. Works for games and streaks, solo and challenge.

当前为 2022-11-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Geoguessr Location Retriever
  3. // @match https://www.geoguessr.com/*
  4. // @description Get the actual location Geoguessr gave you from the result screens. Works for games and streaks, solo and challenge.
  5. // @version 1.1.3
  6. // @author victheturtle#5159
  7. // @grant none
  8. // @license MIT
  9. // @icon https://www.svgrepo.com/show/12218/find.svg
  10. // @namespace https://greasyfork.org/users/967692-victheturtle
  11. // ==/UserScript==
  12.  
  13. let lastChecked = 0;
  14. let checkedResults = false;
  15.  
  16. function getPins() {
  17. return document.querySelectorAll("[class*='map-pin_clickable']");
  18. };
  19.  
  20. function panoIdDecoder(geoguessrPanoId) {
  21. let gsvPanoId = "";
  22. for (let i = 0; i < geoguessrPanoId.length; i+=2) {
  23. let seq = geoguessrPanoId.substring(i, i+2);
  24. gsvPanoId += String.fromCharCode(parseInt(seq, 16));
  25. }
  26. return gsvPanoId;
  27. }
  28.  
  29. function linkOfLocation(round, copyToClipboard) {
  30. if (round.panoId == null) return null;
  31. let lat = round.lat;
  32. let lng = round.lng;
  33. let pid = panoIdDecoder(round.panoId);
  34. let rh = round.heading;
  35. let rp = round.pitch;
  36. let rz = round.zoom;
  37. let h = Math.round(round.heading * 100) / 100;
  38. let p = Math.round((90 + round.pitch) * 100) / 100;
  39. let z = Math.round((90 - round.zoom/2.75*90) * 10) / 10;
  40. const extra = `"countryCode":null,"stateCode":null,"extra":{"tags":[]}`;
  41. let link = `https://www.google.com/maps/@${lat},${lng},3a,${z}y,${h}h${(p==90)?"":","+p+"t"}/data=!3m6!1e1!3m4!1s${pid}!2e0!7i13312!8i6656`;
  42. let loc_json = `{"lat":${lat},"lng":${lng},"heading":${rh},"pitch":${rp},"panoId":"${pid}","zoom":${rz},${extra}}`;
  43. //console.log(link);
  44. //console.log(loc_json+",");
  45. if (copyToClipboard) {
  46. try {
  47. navigator.clipboard.writeText(loc_json+",");
  48. } catch (e) {console.log(e)};
  49. }
  50. return link;
  51. }
  52.  
  53. function addFlagOnclicks(rounds) {
  54. let pin = getPins();
  55. for (let i = 0; i < pin.length; i++) {
  56. let link = linkOfLocation(rounds[(pin.length>1) ? pin[i].innerText-1 : rounds.length-1-i], pin.length==1);
  57. if (link != null) pin[i].onclick = function () {window.open(link, '_blank');};
  58. }
  59. };
  60.  
  61. function addStreakChallengeOnclicks(rounds) {
  62. setTimeout(() => {
  63. let playersTable = document.querySelectorAll(".results_highscoreHeader__oQH32")[0].parentElement.children[1];
  64. let roundsTable = document.querySelectorAll(".results_highscoreHeader__oQH32")[1].parentElement;
  65. for (let i = 0; i < playersTable.children.length; i += 2) {
  66. playersTable.children[i].onclick = function () {addStreakChallengeOnclicks(rounds);};
  67. }
  68. for (let i = 1; i < roundsTable.children.length; i++) {
  69. let link = linkOfLocation(rounds[i-1], false);
  70. if (link != null) roundsTable.children[i].onclick = function () {window.open(link, '_blank');};
  71. roundsTable.children[i].style="cursor: pointer;";
  72. }
  73. }, 200);
  74. }
  75.  
  76. function check() {
  77. const game_tag = location.pathname.split("/")[2];
  78. let api_url = "https://www.geoguessr.com/api/v3/games/"+game_tag;
  79. if (location.pathname.startsWith("/challenge") || !!document.querySelector(".switch_switch__LMkRx")) {
  80. api_url = "https://www.geoguessr.com/api/v3/challenges/"+game_tag+"/game";
  81. };
  82. fetch(api_url)
  83. .then(res => res.json())
  84. .then(out => {
  85. addFlagOnclicks(out.rounds.slice(0, out.player.guesses.length));
  86. if (out.type == "challenge" && out.mode == "streak") {
  87. let api_url2 = "https://www.geoguessr.com/api/v3/results/scores/"+game_tag+"/0/1";
  88. fetch(api_url2)
  89. .then(res => res.json())
  90. .then(out => addStreakChallengeOnclicks(out[0].game.rounds))
  91. .catch(err => { throw err });
  92. };
  93. }).catch(err => { throw err });
  94.  
  95. };
  96.  
  97. function doCheck() {
  98. if (!(location.pathname.startsWith("/results") || location.pathname.startsWith("/game") || location.pathname.startsWith("/challenge"))) return;
  99. let pinCount = getPins().length;
  100. if (pinCount == 0) {
  101. lastChecked = 0;
  102. checkedResults = false;
  103. } else if (pinCount != lastChecked || location.pathname.startsWith("/results") && !checkedResults && document.readyState == "complete") {
  104. lastChecked = pinCount;
  105. checkedResults = location.pathname.startsWith("/results");
  106. check();
  107. }
  108. };
  109.  
  110. function checkSoon() {
  111. for (let timeout of [250,500,1200,2000,5000]) {
  112. setTimeout(doCheck, timeout);
  113. }
  114. };
  115.  
  116. document.addEventListener('click', checkSoon, false);
  117. document.addEventListener('keypress', (e) => { if (e.key === " ") checkSoon(); });
  118. document.addEventListener('load', checkSoon(), false);