1
0

ac.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. new autocomplete();
  3. class autocomplete{
  4. public function __construct(){
  5. header("Content-Type: application/json");
  6. $this->scrapers = [
  7. "brave" => "https://search.brave.com/api/suggest?q={searchTerms}",
  8. "ddg" => "https://duckduckgo.com/ac/?q={searchTerms}&type=list",
  9. "yandex" => "https://suggest.yandex.com/suggest-ff.cgi?part={searchTerms}&uil=en&v=3&sn=5&lr=21276&yu=4861394161661655015",
  10. "google" => "https://www.google.com/complete/search?client=mobile-gws-lite&q={searchTerms}",
  11. "qwant" => "https://api.qwant.com/v3/suggest/?q={searchTerms}&client=opensearch",
  12. "yep" => "https://api.yep.com/ac/?query={searchTerms}",
  13. "marginalia" => "https://search.marginalia.nu/suggest/?partial={searchTerms}",
  14. "yt" => "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&q={searchTerms}",
  15. "sc" => "https://api-v2.soundcloud.com/search/queries?q={searchTerms}&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=10&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en"
  16. ];
  17. /*
  18. Sanitize input
  19. */
  20. if(!isset($_GET["s"])){
  21. $this->do404("Missing search(s) parameter");
  22. }
  23. if(is_string($_GET["s"]) === false){
  24. $this->do404("Invalid search(s) parameter");
  25. }
  26. if(strlen($_GET["s"]) > 500){
  27. $this->do404("Search(s) exceeds the 500 char length");
  28. }
  29. if(
  30. isset($_GET["scraper"]) &&
  31. is_string($_GET["scraper"]) === false
  32. ){
  33. $_GET["scraper"] = "brave"; // default option
  34. }
  35. /*
  36. Get $scraper
  37. */
  38. if(!isset($_GET["scraper"])){
  39. if(isset($_COOKIE["scraper_ac"])){
  40. $scraper = $_COOKIE["scraper_ac"];
  41. }else{
  42. $scraper = "brave"; // default option
  43. }
  44. }else{
  45. $scraper = $_GET["scraper"];
  46. }
  47. if($scraper == "disabled"){
  48. // this shouldnt happen, but let's handle it anyways
  49. $this->doempty();
  50. }
  51. // make sure it exists
  52. if(!isset($this->scrapers[$scraper])){
  53. $scraper = "brave"; // default option
  54. }
  55. // return results
  56. switch($scraper){
  57. case "google":
  58. case "yt":
  59. // handle google cause they want to be a special snowflake :(
  60. $js = $this->get($this->scrapers[$scraper], $_GET["s"]);
  61. preg_match(
  62. '/\((\[.*\])\)/',
  63. $js,
  64. $js
  65. );
  66. if(!isset($js[1])){
  67. $this->doempty();
  68. }
  69. $js = json_decode($js[1]);
  70. $json = [];
  71. foreach($js[1] as $item){
  72. $json[] = strip_tags($item[0]);
  73. }
  74. echo json_encode(
  75. [
  76. $_GET["s"],
  77. $json
  78. ]
  79. );
  80. break;
  81. case "sc":
  82. // soundcloud
  83. $js = $this->get($this->scrapers[$scraper], $_GET["s"]);
  84. $js = json_decode($js, true);
  85. if(!isset($js["collection"])){
  86. $this->doempty();
  87. }
  88. $json = [];
  89. foreach($js["collection"] as $item){
  90. $json[] = $item["query"];
  91. }
  92. echo json_encode(
  93. [
  94. $_GET["s"],
  95. $json
  96. ]
  97. );
  98. break;
  99. case "marginalia":
  100. $json = $this->get($this->scrapers[$scraper], $_GET["s"]);
  101. $json = json_decode($json, true);
  102. if($json === null){
  103. $this->doempty();
  104. }
  105. echo json_encode(
  106. [
  107. $_GET["s"],
  108. $json
  109. ]
  110. );
  111. break;
  112. default:
  113. // if it respects the openSearch protocol
  114. $json = json_decode($this->get($this->scrapers[$scraper], $_GET["s"]), true);
  115. echo json_encode(
  116. [
  117. $_GET["s"],
  118. $json[1] // ensure it contains valid key 0
  119. ]
  120. );
  121. break;
  122. }
  123. }
  124. private function get($url, $query){
  125. $curlproc = curl_init();
  126. $url = str_replace("{searchTerms}", urlencode($query), $url);
  127. curl_setopt($curlproc, CURLOPT_URL, $url);
  128. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  129. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  130. ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0",
  131. "Accept: application/json, text/javascript, */*; q=0.01",
  132. "Accept-Language: en-US,en;q=0.5",
  133. "Accept-Encoding: gzip",
  134. "DNT: 1",
  135. "Connection: keep-alive",
  136. "Sec-Fetch-Dest: empty",
  137. "Sec-Fetch-Mode: cors",
  138. "Sec-Fetch-Site: same-site"]
  139. );
  140. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  141. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  142. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  143. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  144. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  145. $data = curl_exec($curlproc);
  146. if(curl_errno($curlproc)){
  147. throw new Exception(curl_error($curlproc));
  148. }
  149. curl_close($curlproc);
  150. return $data;
  151. }
  152. private function do404($error){
  153. echo json_encode(["error" => $error]);
  154. die();
  155. }
  156. private function doempty(){
  157. echo json_encode(
  158. [
  159. $_GET["s"],
  160. []
  161. ]
  162. );
  163. die();
  164. }
  165. }