qwant.php 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. <?php
  2. //curl 'https://dd.qwant.com/js/' \
  3. //-X POST \
  4. //-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:152.0) Gecko/20100101 Firefox/152.0' \
  5. //-H 'Accept: */*' \
  6. //-H 'Accept-Language: en-US,en;q=0.9' \
  7. //-H 'Accept-Encoding: gzip, deflate, br, zstd' \
  8. //-H 'Referer: https://www.qwant.com/' \
  9. //-H 'Content-type: application/x-www-form-urlencoded' \
  10. //-H 'Origin: https://www.qwant.com' \
  11. //-H 'DNT: 1' \
  12. //-H 'Sec-GPC: 1' \
  13. //-H 'Connection: keep-alive' \
  14. //-H 'Sec-Fetch-Dest: empty' \
  15. //-H 'Sec-Fetch-Mode: cors' \
  16. //-H 'Sec-Fetch-Site: same-site' \
  17. //-H 'Pragma: no-cache' \
  18. //-H 'Cache-Control: no-cache' \
  19. //--data-raw 'jspl=vNWp303yWe339-y3I7k8lLVcD5DIYf3OAS4FgU0t2LT7G6yCiA_7iFV-cc-YZRpRjb8tRIlUJUgAP-LMmZ2TaQbCXRyJ8dMJFzNiB7A5bgV6ExgkaH3KUwHpeAs6-m21SNns4ObqUc2yYDAn8insJyPENvxduC2_xbUzKUzoNATNIhlYHJk9NiGbV6R8H57zCr1LQYMwt1OnC_kLUDvqMOMZGGcg89Rh_HK9LCKKRqqkbqogH9A7mAN-9ZLVqxZSAgUTFLnM67ZzlMIzxzSzwzLHHTGupumwsMuu_kg63_CduamVJ61s0yuE4gmP5kCB1iYCZoRKbMSN3yWIrTT-7WH2AEPh_X2yNBw3IFiNSAefVEAcaI4a19d6_ioTsXwNe48-12MeGd28pEmdxmyyp57eE3YNB10-4nFsyiWuPjcV8ifhajFIohUmdld7vCrqo3Am_tH8QHp9RWELH1cyBupp06xSHQG177okwaAn973kQNvm-Ohtqxy3yS4Z0uTl4NhjarcOq12UY58YaA8OooyET43psctg4C2ZnaHy2wF8HNVOVQdN1ul1asfbyvA4bkWBwgniAw6_Np8Qe-c06D0nVb-7SARNxt74gcRwsnfhocY8QGp6rLr1pJtQRWgYbdHyRgVMe4180flKfFVD5mGglJRcRcCHQ9XZmUCXXGdYNB7Ol4gwOcIlFKQHF9WcaPbrdTibpmkpd4dzEk5PWM1uze4HjVpKf5H4NkwSlOjBlP5PQwsUWBOsSmvmLWq8oX1eu8P9USehEGLfzHN903goldtZwPbbAhINlCkjgP7YciGd2ZA24Z0I74pubIeXiL3430WlZQVLeCQOJ_TtQqNNzCad9JjuLK8wVKVB0MpJl5fCYeVp-SN-qYSzrKYBWCkvGnN_DkoZDVLeV33h8plgQSR21lJOTqarGVZLS0fI9LxIi5szg-xV2sWt4IN8RzPpMBE2DsFqwFL6sIyHYCdfYoN35EhfoI0CrtuGI3pgKSYsQs3ZpzCgEQGWVbJV13iS2xdRIiXEEyzFD063CExNzZWKofE3n2vyiO17KFs8R1X_WFck9ZamKHAq5lR3E_kv_9MQUcHNTPvSWn3N9QL_CAOoUlKnohT4hNlzA2LbBO136H7pFLdjz_9zJV44fIh8vXmsbI6BbIhD7fvEEYyeejMDo_zKkhizvdpGtxsPNdEfw7CwmVtfR30eVfcGBwM81GVFsK0LsmTqS60ovDsa-Cmqwl4BcIPj8uUtpb6pciLRBGHka9OU-RfQb4KqJgDcJ_lQTh2zdPGGxEaeMuirhC0VLNoiGRSnZk8UtQOYB_ts7lFXGEMkWbtX1Qoccl0XsiNcFlyxOR4E7h5LDV8Pafr5J0et6nzxMJYkofNWvw7qUdl31O63kBOIJauKmDP9ko6k9rwmhgzqAfTqYtUGkUQwDw2DJI3RPIP8lPBzqVIbHnbQGfEBrStjhsh0UeS5J4z5o8Xpc1JtbZ0t_3kMnhErjq9AZ0gfRq6Mm7736wFUxGpPTh0EN85Zc7tR3YlnaXFrf9usyoSUchTd9ecBiLTJY9lXT2c-aiuLGm9vjX-oKZLhK5vkrzL-vBr5jO0u_LmykqAz280cFIrjfdC0Ruov34dRf1yfA01XGZTw4Tmq-ZoUuuUbfHiwhTliQmVh0uQ4b4XOPh_gImqPM5Vgzb8phCuKXNDO9UVCnnbf3UZUei7Tfa7dBdZuhMhg1Wc0umkX9tDpWgYG_A250X5KfR9MFb8gfPHhEdvnDLFuVxhZrU3-Kb8RJIeSje5xLFmV-obHGjAS1jUnILXCWaYfFpRSdIjbIWFudjrqreJPxZF1swdunfLKsIHiR5IdMkPyCr9gq5IANlythNyVKAmqJ552pVpUzXaGkhkwFfjHVDxG3aOryDoOD1v-sjKgKraI9gkZIjX9qwxPIGIZ3Xa7KyO9aa8PwKQdvOg2-jKhMRTjn5pzgEtMtQJYwdUiMNP-8P25-px8uUNwgB-pzpnk1ExosEmZuUAiv7U11dtix0wJEzfCQyCIozgaOggloCPv7YvVGmsbTtH0z9Nybh8jHPJzQ-mo9_b0hAFUwX2ix4bxeH3WTQFKkhJSZ6O1keDu_9Jsp1RS_wktsqTyyVpt_nwLxyKLF0OFeMkNQxcY-aIAPWznW0tpp5WTkZT7ZNepueFnd6ZcgT1wfpDkhsWzvLy4KOOxXsFPpJjSAEesakQiN3yMRZvr7JecXMrqqPCCNwbsEiG9BLmzjuHP6Oq8FXondLt7_KIWGmm-udcK07g-2Z_icfDYzAcsdQBKergGwMBsgPymtQGizzx51tceDLnmIuII3JIeRPokMaid0sS-y8Myf4ShCqTPJXuxwTiOW4UWufJHg9oC_eGXYG8oVkrB8yFX5Wnh8fjNgNm3b7xzMup4SmenP_xcbk5dsCOTxxzO-Gq5dFBD1uqLFhZPUtD2KPqPR5NyCTCVViY23-qP2_-mHnJzjwycHS6oU6IbEYYegvWgDLUTUbM8Vy6dxkH6OCYRsOxcPRCNdgHm7lxT4sSQQWZU5js1-V077ZGCIPC4CU6TBG5RjG4tUyQVEdsba4sk1pgp8RrVwm4vdMEIy6SaUlY5SgmD73DRdxVgHbMyuIMolTkk08DswjuxuHzgmCPvDMO3aYT5fNVwbIBBhN4bF8DeCZVrdfJeohGKuroNW8QvoTvAtwi6GbG0rY1V8n1zwCEY_le6vcB6qK7pJe69QSeBu_vUXt-9BJqlYdE8YGC3TB7TA6x7GJj6-uFWCNbuqbm7KVATx_ow3Iaqn_WvXsXS6V8pTnKPS9LMWwTUz2DJtYJHKtl70w6ntCc9K6bt5VaNYtRFJXxBNDNN-mZljN0oYhx2WKuMZWzxNug646Dx1RMTHsUTkaph4v9xu001f9GObGuIDeEviETaBmDGOhdZhZnFLoowP8ubbCybA_PqsDC04pf_QkKndahtD-llhNvhFrGxWXj1mBcCAt1__ha1xurg7fpWUYtsoeqhrwlPJfQ-tvNHeba21FfzV7y5sJpuwXCZqKZ_xwMZ_THkmAinfFgYYG6uBnLaeBGDNaaA35jjWGgtFe4ciOrGGZzA1opxps3ryGbwHCEgM_9UNsTmtysv5ssMNRoLske28q99oeBk-pkIJHtSBRt-uLucb6MO5lfk5JGtZK9PBNf3AU-tBAqZo5CtNrL4VeZgd1PgWusXaH_id0CT4D6WNA3ENXdm-efBxJA7nIQixo7Q02HrBv67my0HyCsNRoJhJjSmNHDr4bZPg5SWv2sedd13Vqldl1Xis1FAToSK74Dwvm1RGC-kJth6YNwJFUMRLIzQQPymbm8ccXu1bar33i4nr6BYbVsB5eynMFCXJpSjqIU6GwdO4OFHqHRJ30lRYyYcVLjCZnr80mOVqWg8yaf3DF6CijnNGrbC3pRqaB8MuYuoobnB_-35mI-5foeftnQp_xCuLw2AL-rrEiwhE6iN-kp3xdVTxP6YbRlPUCsJ7KZmSiPPekR2QFs-ijYGivIWA4CD4ftdX8MmbBxqtIWhUYBHICMVn-Frnf8IQRMwk1VlLu42w_9RbLtBslBE4UQUpg_3ExNJZeEFb1OgWK3z7nOHP4BcDvvEwjl_ie-meCdL2GBgC5vWIULcJi0GQzw-d0SFCEmn4dwXNolxvsHk4EizZV3V9X5cn_1njviM8FzDQV2I2AG1dQEKYV8PAK7GAQetiyO-f3Nfud0-uR5TocEntpBg12NDDLmZlchixVFdFAgaYimW_jo15P5qx7BvPJbWzd1hfRynDV67kcD3eFcbNVBJb6MqVZ0iPkpUXiIQnaYjdTVmJb2U3B8EYYmGX6jGdE1iXvRKsYiStOYsEr5vZFp1Dto9wLFx8-fbf7ys-2NqdnO6k8r6H40yVSKFEJvafPv7coXKd-APYgK8fmeM9VqN0p224OvbiEPzAMYfc3Ibc4zXp7BdrvxDj3naBao3njM67tbhBRt4yw_OnrfbAJPg4pnwdUq6BP_MMR-oUwpGeQs5ojbHRmmw4TgX9bFKxDbhRM_w0sqynJeKfXgjS3Xn5x95jCt_dw_Gfh9GAJELxKSv_b-VMMGXNIFzQyXeEIY-ieE0-Q46twzQTz8u70GHSsBGq20jinc6sa1m16q3uVs3wyVfnSEW5mS0tLgSqBdOzVoMdmjUzZY7s21UnCdupUxZHF9K355UgfByad37kMorobHm52BRPEHTEdvxigkd-jcwDIl40uTpY5M36iwRgUob8VaMIRW9Suxg39W0hXxIjbd9fgg4tddWdrBBZTGexyBtUbCCgBdd5TDjMFXNIUXwyvvjwFErFrvaiO80jBWHSkgm4D4abj7968zCrWxIcYt2YDUZXioB3EhMp3kVygpRzti9vZcUdouEwyquGK0zLAKGI_hX0b-hGQnUiH96WZUS3Prf5ylg0P25O0FJ5QnTEmkqfCPKq7LfQpK0VAkduLK9BXNV3znx5T7avofWZI6S8hU0Rve904kYqKpU3s7S3Jp6IML6d-cHhtvZQTBdRsCxNFRMU_L8vspgPaEJmuCalgaHwTEJPWcIwA_WBahht8FsfdikkdY1rsexXHLhXa3767CVs8dhR3DB5wPTaUV2HlD6PF&eventCounters=%5B%5D&jsType=ch&cid=kYTjYX20GtjREVyt6XE1~8FuJuJf8EThptWfE9ZIm04Emkmj0ks4X9P7NnB1A0Am3AXBNNR3gpxi7TkOEc9J5AgToM9~uiwAiolqJKHU2Tk09K~NW7~0B6oW8n3GnVdc&ddk=78B13B7513D180B7AB6D6FF9EB0A51&Referer=https%253A%252F%252Fwww.qwant.com%252F%253Fq%253Dasmr%2526t%253Dweb&request=%252F%253Fq%253Dasmr%2526t%253Dweb&responsePage=origin&ddv=5.8.0'
  20. class qwant{
  21. public function __construct(){
  22. include "lib/backend.php";
  23. $this->backend = new backend("qwant");
  24. }
  25. public function getfilters($page){
  26. $base = [
  27. "nsfw" => [
  28. "display" => "NSFW",
  29. "option" => [
  30. "yes" => "Yes",
  31. "maybe" => "Maybe",
  32. "no" => "No"
  33. ]
  34. ],
  35. "country" => [
  36. "display" => "Country",
  37. "option" => [
  38. "en_US" => "United States",
  39. "fr_FR" => "France",
  40. "en_GB" => "Great Britain",
  41. "de_DE" => "Germany",
  42. "it_IT" => "Italy",
  43. "es_AR" => "Argentina",
  44. "en_AU" => "Australia",
  45. "es_ES" => "Spain (es)",
  46. "ca_ES" => "Spain (ca)",
  47. "cs_CZ" => "Czech Republic",
  48. "ro_RO" => "Romania",
  49. "el_GR" => "Greece",
  50. "zh_CN" => "China",
  51. "zh_HK" => "Hong Kong",
  52. "en_NZ" => "New Zealand",
  53. "fr_FR" => "France",
  54. "th_TH" => "Thailand",
  55. "ko_KR" => "South Korea",
  56. "sv_SE" => "Sweden",
  57. "nb_NO" => "Norway",
  58. "da_DK" => "Denmark",
  59. "hu_HU" => "Hungary",
  60. "et_EE" => "Estonia",
  61. "es_MX" => "Mexico",
  62. "es_CL" => "Chile",
  63. "en_CA" => "Canada (en)",
  64. "fr_CA" => "Canada (fr)",
  65. "en_MY" => "Malaysia",
  66. "bg_BG" => "Bulgaria",
  67. "fi_FI" => "Finland",
  68. "pl_PL" => "Poland",
  69. "nl_NL" => "Netherlands",
  70. "pt_PT" => "Portugal",
  71. "de_CH" => "Switzerland (de)",
  72. "fr_CH" => "Switzerland (fr)",
  73. "it_CH" => "Switzerland (it)",
  74. "de_AT" => "Austria",
  75. "fr_BE" => "Belgium (fr)",
  76. "nl_BE" => "Belgium (nl)",
  77. "en_IE" => "Ireland",
  78. "he_IL" => "Israel"
  79. ]
  80. ]
  81. ];
  82. switch($page){
  83. case "web":
  84. $base = array_merge(
  85. $base,
  86. [
  87. "time" => [
  88. "display" => "Time posted",
  89. "option" => [
  90. "any" => "Any time",
  91. "day" => "Past 24 hours",
  92. "week" => "Past week",
  93. "month" => "Past month"
  94. ]
  95. ],
  96. "extendedsearch" => [
  97. // no display, wont show in interface
  98. "option" => [
  99. "yes" => "Yes",
  100. "no" => "No"
  101. ]
  102. ]
  103. ]
  104. );
  105. break;
  106. case "images":
  107. $base = array_merge(
  108. $base,
  109. [
  110. "time" => [
  111. "display" => "Time posted",
  112. "option" => [
  113. "any" => "Any time",
  114. "day" => "Past 24 hours",
  115. "week" => "Past week",
  116. "month" => "Past month"
  117. ]
  118. ],
  119. "size" => [
  120. "display" => "Size",
  121. "option" => [
  122. "any" => "Any size",
  123. "large" => "Large",
  124. "medium" => "Medium",
  125. "small" => "Small"
  126. ]
  127. ],
  128. "color" => [
  129. "display" => "Color",
  130. "option" => [
  131. "any" => "Any color",
  132. "coloronly" => "Color only",
  133. "monochrome" => "Monochrome",
  134. "black" => "Black",
  135. "brown" => "Brown",
  136. "gray" => "Gray",
  137. "white" => "White",
  138. "yellow" => "Yellow",
  139. "orange" => "Orange",
  140. "red" => "Red",
  141. "pink" => "Pink",
  142. "purple" => "Purple",
  143. "blue" => "Blue",
  144. "teal" => "Teal",
  145. "green" => "Green"
  146. ]
  147. ],
  148. "imagetype" => [
  149. "display" => "Type",
  150. "option" => [
  151. "any" => "Any type",
  152. "animatedgif" => "Animated GIF",
  153. "photo" => "Photograph",
  154. "transparent" => "Transparent"
  155. ]
  156. ],
  157. "license" => [
  158. "display" => "License",
  159. "option" => [
  160. "any" => "Any license",
  161. "share" => "Non-commercial reproduction and sharing",
  162. "sharecommercially" => "Reproduction and sharing",
  163. "modify" => "Non-commercial reproduction, sharing and modification",
  164. "modifycommercially" => "Reproduction, sharing and modification",
  165. "public" => "Public domain"
  166. ]
  167. ]
  168. ]
  169. );
  170. break;
  171. case "videos":
  172. $base = array_merge(
  173. $base,
  174. [
  175. "order" => [
  176. "display" => "Order by",
  177. "option" => [
  178. "relevance" => "Relevance",
  179. "views" => "Views",
  180. "date" => "Most recent",
  181. ]
  182. ],
  183. "source" => [
  184. "display" => "Source",
  185. "option" => [
  186. "any" => "Any source",
  187. "youtube" => "YouTube",
  188. "dailymotion" => "Dailymotion",
  189. ]
  190. ]
  191. ]
  192. );
  193. break;
  194. case "news":
  195. $base = array_merge(
  196. $base,
  197. [
  198. "time" => [
  199. "display" => "Time posted",
  200. "option" => [
  201. "any" => "Any time",
  202. "hour" => "Less than 1 hour ago",
  203. "day" => "Past 24 hours",
  204. "week" => "Past week",
  205. "month" => "Past month"
  206. ]
  207. ],
  208. "order" => [
  209. "display" => "Order by",
  210. "option" => [
  211. "relevance" => "Relevance",
  212. "date" => "Most recent"
  213. ]
  214. ]
  215. ]
  216. );
  217. break;
  218. }
  219. return $base;
  220. }
  221. private function get($proxy, $url, $get = [], $is_post = false, $cookie = null){
  222. $curlproc = curl_init();
  223. if($is_post){
  224. $headers = [
  225. "User-Agent: " . config::USER_AGENT,
  226. "Accept: */*",
  227. "Accept-Language: en-US,en;q=0.9",
  228. "Accept-Encoding: gzip, deflate, br, zstd",
  229. "Referer: https://www.qwant.com/",
  230. "Content-type: application/x-www-form-urlencoded",
  231. "Origin: https://www.qwant.com",
  232. "DNT: 1",
  233. "Sec-GPC: 1",
  234. "Connection: keep-alive",
  235. "Sec-Fetch-Dest: empty",
  236. "Sec-Fetch-Mode: cors",
  237. "Sec-Fetch-Site: same-site",
  238. "Pragma: no-cache",
  239. "Cache-Control: no-cache"
  240. ];
  241. curl_setopt($curlproc, CURLOPT_POST, true);
  242. curl_setopt($curlproc, CURLOPT_POSTFIELDS, http_build_query($get));
  243. }else{
  244. $headers = [
  245. "User-Agent: " . config::USER_AGENT,
  246. "Accept: application/json, text/plain, */*",
  247. "Accept-Language: en-US,en;q=0.9",
  248. "Accept-Encoding: gzip, deflate, br, zstd",
  249. "Referer: https://www.qwant.com/",
  250. "Origin: https://www.qwant.com",
  251. "DNT: 1",
  252. ($cookie !== null ? "Cookie: {$cookie}" : ""),
  253. "Sec-GPC: 1",
  254. "Connection: keep-alive",
  255. "Sec-Fetch-Dest: empty",
  256. "Sec-Fetch-Mode: cors",
  257. "Sec-Fetch-Site: same-site",
  258. "Priority: u=4",
  259. "Pragma: no-cache",
  260. "Cache-Control: no-cache",
  261. "TE: trailers"
  262. ];
  263. if($get !== []){
  264. $get = http_build_query($get);
  265. $url .= "?" . $get;
  266. }
  267. }
  268. curl_setopt($curlproc, CURLOPT_URL, $url);
  269. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  270. curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
  271. // Bypass HTTP/2 check
  272. curl_setopt($curlproc, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
  273. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  274. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  275. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  276. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  277. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  278. $this->backend->assign_proxy($curlproc, $proxy);
  279. $data = curl_exec($curlproc);
  280. if(curl_errno($curlproc)){
  281. throw new Exception(curl_error($curlproc));
  282. }
  283. curl_close($curlproc);
  284. return $data;
  285. }
  286. public function web($get){
  287. if($get["npt"]){
  288. // get next page data
  289. [$params, $proxy] = $this->backend->get($get["npt"], "web");
  290. $params = json_decode($params, true);
  291. $cookie = $params["cookie"];
  292. unset($params["cookie"]);
  293. }else{
  294. // get _GET data instead
  295. $search = $get["s"];
  296. if(strlen($search) === 0){
  297. throw new Exception("Search term is empty!");
  298. }
  299. if(strlen($search) > 2048){
  300. throw new Exception("Search term is too long!");
  301. }
  302. $proxy = $this->backend->get_ip();
  303. $params = [
  304. "q" => $search,
  305. "freshness" => $get["time"],
  306. "count" => 10,
  307. "locale" => $get["country"],
  308. "offset" => 0,
  309. "device" => "desktop",
  310. "tgp" => 3,
  311. "safesearch" => 0,
  312. "displayed" => "true",
  313. "llm" => "false"
  314. ];
  315. switch($get["nsfw"]){
  316. case "yes": $params["safesearch"] = 0; break;
  317. case "maybe": $params["safesearch"] = 1; break;
  318. case "no": $params["safesearch"] = 2; break;
  319. }
  320. $cookie = null;
  321. }
  322. /*
  323. $handle = fopen("scraper/qwant_web.json", "r");
  324. $json = fread($handle, filesize("scraper/qwant_web.json"));
  325. fclose($handle);*/
  326. if($cookie === null){
  327. // get datadome bullshit
  328. // yeah go ahead and patch this you code monkey
  329. // ... you know, instead of fixing this shit, can you make the second page of results return more than 1 result?
  330. // i search shit like "higurashi" and i only get 1 result and then just a bunch of related searches ad finitum
  331. // also, stop pretending you guys have your own index, this is clearly all bing results
  332. try{
  333. $datadome =
  334. $this->get(
  335. $proxy,
  336. "https://dd.qwant.com/js/",
  337. [
  338. "jspl" => "-oirHDeP7G3G9LXY3rhxGDYf6t_ZlYPzaiL2dRhQ34uNUc4dO2Td0SmmKu_VJp7-jrAIoOMrTqKgCtV4cPzntMyNuOYN7QQ1q0Fi2c-679vYpJr9ub5dRFXMhCMQmjZsOWXUa8yafIXf85UA5Goct-n4hVT5dFfBGM-dE6gwZEFkHQ1j1195sLybZFTNA1IfX26MBMxlVHoyT-aHtkd6YjGXbvOPMMsObVHDa_Cf4BmApRGo-eehsAttsCqDRwkl0M2E1b0OXCIwMVnCK-qWZ81MGaJGjqxdj_LaxhAjE9JI_4oNedAg4Yj4m_vkQBKdmWJT_bTUsllAZXqX5IxYQHJ8WvlknOTr6sR3wgoSOa_JNVHkUQxqXBbQcoDdy6VciL5rIqwWl4Y2caVYzeXa8YNXxwsfrPDegH3dQ6vOiIiCDT3bexkcS_y7GAioPPv9qDu7M9y4sI8t0VkPgMbWY6MIu5u9WAv6lEiKIYrtlyU-iiXnNdkd9EnpC1TD8_s12PtCuLg5bGgLVmI1dw8VXVZxmVyFvfBtuqXkzM-kZOyv-YIRcf-wPY8DUtnHox1d6t64oYhGv-pYV5dmZ0rGs99LC6ThiifeF_oHz5ZK46Ms68WsiAjc1HLAbFPFxWw8X6lyLlpT5aHgFdDBeCTExKXyItlc9PBxkgK8UnXHQFiWX3HTen8rziwkI31AbkMU7xGP6cvuwuLG7MA7sH3yLF1uimQlhr6EuTrImWQNV7X81DqyZ0OE7I-uT9bp_xISa5it-6AJpFtGcwz04_OuO2zvZazBeiKJI5Fw0X2r3RHTHdA29SXqTIe4y2LRAAp0MSAZUIJAMIbbrz9BG-3D5BHfQUA1Bbuklxv-tNWiuanWbbTmAiuxTAo9P-D2ZMrWHwdN8sCb3nINLur38z1xviSs2qEEjKo9kzQIWoHgwm_amV-jjDtNBXwpCQnoaqt3FfL8e59ecYWVg61Gnl3YCwq6rubFdkIv6UStcWSlj4pRFlFZSjQxzznvGTAc5Nz1JJWVeBHJItei4wqDUYEw2BNTS_fU9keV3LFyKDH_x248AxHLiXt583DhZZwPAKlpmMo0E7odzfOgVQGM5b9l64NL3RQRq8cDxI8KgmOFnPHdI7Myq1A3nW1uoBB8m9_GR_9orboKl5tAQrT0VTMRd6JyNwOOQ71ZoQpMwsD5Xd9YDZd618TNE9dA38h9OyBco_-hwKCYayyEqib1atLLBO_gnO4InR471AklOokDOKx1oY_xgNhlFrZskE4nQQOqeCQ_47oysob9s3kblA8UqDrL2vM3i2KPLWwlQzMAPrQAIv7Amp6rO-wYwBQdbP7XghYympxx9-hNxc1Duq-nS4R3pjPtZg3LoCvG1N_0JLf9jfPBqw_rdPM6DnjEOvpCdvrfv4wBlEpgqbaRQCZBPEz-O5ZlY24OyPvaNX0krPeXkbi-KYuwZSAbfW0zsksXb8cbRmz6tvh25IRYP1XNDPVd8NFrYOdDH9GzhV5MYZziYMurQlVwgumGLz85Y6qTHZU2D5qkMH7o5C5bzptReO96889oxkReS_LDhzdxH0tAQ5a9GgETJTHAXJ9Lt7X037i0qQlatbPLizOl9fo4vUBGJ7w-uSUmIzg1yIwOo8xhLu39WghXfoJ_EX5ptNi7arpAo11UoJIGEgWgq7bS40q2dtnG5HYpp9ef37iFgztM7PV5dlJf_W-n9uXJyCk2nhxZeuGiIGhMTzjP_8oeyiMFelmoyLlqK8wOs6hvWqgDD4NwtZc-XBkEAKsxuPrirb_sszp0z6BWsh3D6fBxAW_lTrNVkxZ1EBtoEN72HvO1aeX8PVuJP3CKwkkUcAhJ5Zvajo8t4Ai8ave2MTZul4QhDIe7ZALJMo71u-hNSj8YG9JLPqF1jcnbkl8ykt_XMMxX8wSZ_AfTsfmAuOWBfEzxv8Fl2Ne-oxcEtfuYnYNJIjQDHXXdtYbWKBezE41wonQu7FWh3wSSF3DDm9EocWMsUZ610nrIviOFdtTqjrY7KnOZp0hS9Hm2V04z-06Db-To71L5O5poYKuOlDAP_ULfW0hgMgYaPnt4Nive9gtue6ZQ5yNFylKM11zCWcF8vmuEPGx4NIZjvNFB-QSkdxZtBTQOgO6r4BqHJNZIDqiD9A6PyMemVtQEo-JhooX6bwHFHx6C84r-kKq1lg0RaDelbu1h6347kocxOczdgHWlagKPFrUvHpPHvCZeFmzLLmPWbDEpUhhfkbEZlNe47svO5WN1TYZMCAlE0g01pe6b_NB9-twQ4FJB1SkUQRA4o5DxiIHSgXBBfw1X98XPEKcJgCU2zRbYuU69mvyPcsxoPsLo8Cl2OAXj2U1tcLUxVhR_vu3I0SG_Ee8hxBp5b6QbpIu2sE0bgpqfi88XqUXf3qvxGbkoZDZ8dPvEvCVmpDZOOGJY5Db4MnpKBByW79lUnpbLCbsVOoxYu8SVmVQ63eGY9yBlACuWxlXA_PhCpnKYwMppjxKvKqalNJGMMg5WQ8cvhsIftE5wgaj21n0A15GwRO1WgUcsO_dLe1MqO88hTHWHV0Q3gNut3sdsmVr_n-0SniLtVAaw4IEtDZWNUfhpaoC7iyKxoJ86t8r697EYPlWUu4nTQhb3TOXAjHF22rFsJJMldKHVxjxjd086ky3OY3L_7cbjVof60-y4gTJdumw5hch-mhaT93nVnaTuPLGCAC48dZSgOitW8Dw62mPS05fTPAfKUY1HpyIWr9ACk7U2KVAijpzFB-bXvqJeCyBeUHr4cB61K2XxTmkNA8Oo9RC32TeYDrThFzoUyYZ_D6zPe1lLtQaFsPRvDBSsYCcr5fcABlST4DyUgnkgqtx-0-D1QEwtWiH3xiHLQGa82ldjqNIXL9LqwxYoCRBKuljHyqEylZD26A9Jp6SL9rk_Ol6TJlYa3zx44PHyMoA3Coy-HIZW5NMwVKdV7egUDDOchvuFnc1Zv2QpSFKc0JY8lYFtSm1HZFwjq7hf2hBqkU0Xneh8AT4Ty9QVb4mJ8rpzc8m_NBZNrpZVE-pIzmv2yGjnRwKSrwsjat7b2iY34UPFqGBlDk5FX6EfRlN9bq-nztWdextLc47ndr1gxsjC-vr6tbdXl5FxMVhbsnLH80wOC6hHubxCysr5C7Ft59s3MLhVQCc3IIOPMBhLOjjjVsu6K8kAJvvamIpj-iYUQj9T8JztAqfobytzbiwPsQ3mywUGkJpZxTjXjqkKdT6yfFdW5ksR8p3VlKGAAKft9TKKcc5IgErmIRtiZF4hGhB45nwIO5XHA59V94QuV3b0oJrleuDCBj0iDFqWYpsUVHyKl3RjrO86Yq1IbNRwXIIMrlsVYos9CS1I75k9kW4yQC6ni3a2ZitDkiEB-SoQuTlBsvfTs892Wt5q3CWH6_7srZO6l2QEAlWqyQp_P4i8j8uiEbZksGsgghVIgOkriblDP9ml7PdkpRX7S3OpY7jjQgTNR8XHaZ-SEw0-zPiqIIt5VioFK8JljPn-nMIDx-ew_vTDKqmKEOF39ezQcc5oQNTQNUvTAe8qCbK4ghS3fagItezoHtB5zm3qwhx9Iu6JMJy0UhywUZBaInY6NokiVZGDgtoqlVjOk7K4299MVZgsOr2P9QHRDo41lYnCeWWFUf50d-ouEOUO_ngteDpgG1DfV64eycrUOWAnpfI8XOUZ7mxS89SqJriw3T3tcljowHiOFw9QjYsjJi0vuHL5jfoBZVdYAUH6KiLfIScO-KgqitdUTpFwks8Qk7xfd2HcEfArlzSiAt3Hmb2Ty-5mhvtQj2RbPehgoI7gqa60b6Qffth-5tbIjXoKlZCQsaKVMAggF9C3w0Rv5uJ3ExCTGsfKO9IVOf--AfxaA8cNoO0W3NTaMH2iWftY0O1-1x3VJEcerp2v9BcZ0iIL3VFJ9eyOPYNHNwTYrI3S6OrVZxuQXFbu2E_Us32wxYqXvnMx-DEOk7bUUFIanL69Cbn_mR_9Wm1sFixBQ15diJoe1Oyl3XWHWL6_YF4XnHzZlMWySt3Awdeynwu99Ter8WMGXl-6K30novKRWC9jDVOfAtxPvXBkkRtpm8nnJT8fwkmIQoWOEPT2MCg3VYzzHzVEH-AueuRiCZj_o7L-fRqbtFrWPyVnGuetUYS5u0D3n7Rr6kCVqWhLfwjqAwV_calzIQs8-J0-AJe6GGaaONPYfXwXjYan0Sl11CS64lXvx7i3MgeOZuH210T6g6hj54dSLw0upO1L8k0FGli3I5OhLMekeGMLT_N2akyHktjbz8Odpk2ms0pJP_PdqvUegjXgH81F5lr40HAXZs7jfC9ytgcWaEjwlmnoFHSCTdlCIgJqEBzItXiz6ilk5zP35VwxdQTNoms7Meqg-ZrgRuLRaqXNtpQT9qWBmiFP6zoYLwymqytZ_P1SV9DCM-KIZQ2Cq-ew8PsYSrwl8GmfuegoKEMBiON1I4mC9ChDgzJmbo5P9te60T0URS5g9ExiQuSo-JXs0MKC57LLiNo3MPZvqR1wRJOTg0_FqS4JFxzPwN5OqhBzfs25f6kK-axf0wWp-LetUx3U1CTNaxfbCTK5tb8KnLOIs3lDwJw5H5wIIRgXnfNLBJT8A2srfXkdpA",
  339. "eventCounters" => [],
  340. "jsType" => "ch",
  341. "cid" => "3i4vPGHCl74uIBumWaxF2a0PjFkyCJxcSvFnn~heX7keA6sGHgRbnzSYcMr_RzJFlfeeBdg9HeufzFPxnvSHbyWowSvjm6mNCSPhkx6MwDqptANoj93ZgGVbrzxGtcYe",
  342. "ddk" => "78B13B7513D180B7AB6D6FF9EB0A51",
  343. "Referer" => "https://www.qwant.com/?q={$search}&t=web",
  344. "request" => "/?q={$search}&t=web",
  345. "responsePage" => "origin",
  346. "ddv" => "5.8.0"
  347. ],
  348. true
  349. );
  350. }catch(Exception $error){
  351. throw new Exception("Failed to fetch JS challenge token");
  352. }
  353. $datadome = json_decode($datadome, true);
  354. if($datadome === null){
  355. throw new Exception("Failed to decode JS challenge JSON");
  356. }
  357. if(!isset($datadome["cookie"])){
  358. throw new Exception("Failed to get cookie from JS challenge endpoint");
  359. }
  360. $cookie = explode(";", $datadome["cookie"])[0];
  361. }
  362. try{
  363. $json =
  364. $this->get(
  365. $proxy,
  366. "https://api.qwant.com/v3/search/web",
  367. $params,
  368. false,
  369. $cookie
  370. );
  371. }catch(Exception $error){
  372. throw new Exception("Could not fetch JSON");
  373. }
  374. $json = json_decode($json, true);
  375. if($json === NULL){
  376. throw new Exception("Failed to decode JSON");
  377. }
  378. if(isset($json["data"]["message"][0])){
  379. throw new Exception("Server returned an error:\n" . $json["data"]["message"][0]);
  380. }
  381. $out = [
  382. "status" => "ok",
  383. "spelling" => [
  384. "type" => "no_correction",
  385. "using" => null,
  386. "correction" => null
  387. ],
  388. "npt" => null,
  389. "answer" => [],
  390. "web" => [],
  391. "image" => [],
  392. "video" => [],
  393. "news" => [],
  394. "related" => []
  395. ];
  396. $this->detect_errors($json);
  397. if(
  398. $json["status"] != "success" &&
  399. $json["data"]["error_code"] === 5
  400. ){
  401. // no results
  402. return $out;
  403. }
  404. if(!isset($json["data"]["result"]["items"]["mainline"])){
  405. throw new Exception("Server did not return a result object");
  406. }
  407. // data is OK, parse
  408. // get instant answer
  409. if(
  410. $get["extendedsearch"] == "yes" &&
  411. isset($json["data"]["result"]["items"]["sidebar"][0]["endpoint"])
  412. ){
  413. try{
  414. $answer =
  415. $this->get(
  416. $proxy,
  417. "https://api.qwant.com/v3" .
  418. $json["data"]["result"]["items"]["sidebar"][0]["endpoint"],
  419. []
  420. );
  421. $answer = json_decode($answer, true);
  422. if(
  423. $answer === null ||
  424. $answer["status"] != "success" ||
  425. $answer["data"]["result"] === null
  426. ){
  427. throw new Exception();
  428. }
  429. // parse answer
  430. $out["answer"][] = [
  431. "title" => $answer["data"]["result"]["title"],
  432. "description" => [
  433. [
  434. "type" => "text",
  435. "value" => $this->trimdots($answer["data"]["result"]["description"])
  436. ]
  437. ],
  438. "url" => $answer["data"]["result"]["url"],
  439. "thumb" =>
  440. $answer["data"]["result"]["thumbnail"]["landscape"] == null ?
  441. null :
  442. $this->unshitimage($answer["data"]["result"]["thumbnail"]["landscape"]),
  443. "table" => [],
  444. "sublink" => []
  445. ];
  446. }catch(Exception $error){
  447. // do nothing in case of failure
  448. }
  449. }
  450. // get word correction
  451. if(isset($json["data"]["query"]["queryContext"]["alteredQuery"])){
  452. $out["spelling"] = [
  453. "type" => "including",
  454. "using" => $json["data"]["query"]["queryContext"]["alteredQuery"],
  455. "correction" => $json["data"]["query"]["queryContext"]["alterationOverrideQuery"]
  456. ];
  457. }
  458. // check for next page
  459. if($json["data"]["result"]["lastPage"] === false){
  460. $params["offset"] = $params["offset"] + 10;
  461. $params["cookie"] = $cookie;
  462. $out["npt"] =
  463. $this->backend->store(
  464. json_encode($params),
  465. "web",
  466. $proxy
  467. );
  468. }
  469. // parse results
  470. foreach($json["data"]["result"]["items"]["mainline"] as $item){
  471. switch($item["type"]){ // ignores ads
  472. case "web":
  473. $first_iteration = true;
  474. foreach($item["items"] as $result){
  475. if(isset($result["thumbnailUrl"])){
  476. $thumb = [
  477. "url" => $this->unshitimage($result["thumbnailUrl"]),
  478. "ratio" => "16:9"
  479. ];
  480. }else{
  481. $thumb = [
  482. "url" => null,
  483. "ratio" => null
  484. ];
  485. }
  486. $sublinks = [];
  487. if(isset($result["links"])){
  488. foreach($result["links"] as $link){
  489. $sublinks[] = [
  490. "title" => $this->trimdots($link["title"]),
  491. "date" => null,
  492. "description" => isset($link["desc"]) ? $this->trimdots($link["desc"]) : null,
  493. "url" => $link["url"]
  494. ];
  495. }
  496. }
  497. // detect gibberish results
  498. if(
  499. $first_iteration &&
  500. !isset($result["urlPingSuffix"])
  501. ){
  502. throw new Exception("Qwant returned gibberish results");
  503. }
  504. $out["web"][] = [
  505. "title" => $this->trimdots($result["title"]),
  506. "description" => $this->trimdots($result["desc"]),
  507. "url" => $result["url"],
  508. "date" => null,
  509. "type" => "web",
  510. "thumb" => $thumb,
  511. "sublink" => $sublinks,
  512. "table" => []
  513. ];
  514. $first_iteration = false;
  515. }
  516. break;
  517. case "images":
  518. foreach($item["items"] as $image){
  519. $out["image"][] = [
  520. "title" => $image["title"],
  521. "source" => [
  522. [
  523. "url" => $image["media"],
  524. "width" => (int)$image["width"],
  525. "height" => (int)$image["height"]
  526. ],
  527. [
  528. "url" => $this->unshitimage($image["thumbnail"]),
  529. "width" => $image["thumb_width"],
  530. "height" => $image["thumb_height"]
  531. ]
  532. ],
  533. "url" => $image["url"]
  534. ];
  535. }
  536. break;
  537. case "videos":
  538. foreach($item["items"] as $video){
  539. $out["video"][] = [
  540. "title" => $video["title"],
  541. "description" => null,
  542. "date" => (int)$video["date"],
  543. "duration" => $video["duration"] === null ? null : $video["duration"] / 1000,
  544. "views" => null,
  545. "thumb" =>
  546. $video["thumbnail"] === null ?
  547. [
  548. "url" => null,
  549. "ratio" => null,
  550. ] :
  551. [
  552. "url" => $this->unshitimage($video["thumbnail"]),
  553. "ratio" => "16:9",
  554. ],
  555. "url" => $video["url"]
  556. ];
  557. }
  558. break;
  559. case "related_searches":
  560. foreach($item["items"] as $related){
  561. $out["related"][] = $related["text"];
  562. }
  563. break;
  564. }
  565. }
  566. return $out;
  567. }
  568. public function image($get){
  569. if($get["npt"]){
  570. [$params, $proxy] =
  571. $this->backend->get(
  572. $get["npt"],
  573. "images"
  574. );
  575. $params = json_decode($params, true);
  576. }else{
  577. $search = $get["s"];
  578. if(strlen($search) === 0){
  579. throw new Exception("Search term is empty!");
  580. }
  581. $proxy = $this->backend->get_ip();
  582. $params = [
  583. "t" => "images",
  584. "q" => $search,
  585. "count" => 125,
  586. "locale" => $get["country"],
  587. "offset" => 0, // increment by 125
  588. "device" => "desktop",
  589. "tgp" => 3
  590. ];
  591. if($get["time"] != "any"){
  592. $params["freshness"] = $get["time"];
  593. }
  594. foreach(["size", "color", "imagetype", "license"] as $p){
  595. if($get[$p] != "any"){
  596. $params[$p] = $get[$p];
  597. }
  598. }
  599. switch($get["nsfw"]){
  600. case "yes": $params["safesearch"] = 0; break;
  601. case "maybe": $params["safesearch"] = 1; break;
  602. case "no": $params["safesearch"] = 2; break;
  603. }
  604. }
  605. try{
  606. $json = $this->get(
  607. $proxy,
  608. "https://api.qwant.com/v3/search/images",
  609. $params,
  610. );
  611. }catch(Exception $err){
  612. throw new Exception("Failed to get JSON");
  613. }
  614. /*
  615. $handle = fopen("scraper/yandex.json", "r");
  616. $json = fread($handle, filesize("scraper/yandex.json"));
  617. fclose($handle);*/
  618. $json = json_decode($json, true);
  619. if($json === null){
  620. throw new Exception("Failed to decode JSON");
  621. }
  622. $this->detect_errors($json);
  623. if(isset($json["data"]["result"]["items"]["mainline"])){
  624. throw new Exception("Qwant returned gibberish results");
  625. }
  626. $out = [
  627. "status" => "ok",
  628. "npt" => null,
  629. "image" => []
  630. ];
  631. if($json["data"]["result"]["lastPage"] === false){
  632. $params["offset"] = $params["offset"] + 125;
  633. $out["npt"] = $this->backend->store(
  634. json_encode($params),
  635. "images",
  636. $proxy
  637. );
  638. }
  639. foreach($json["data"]["result"]["items"] as $image){
  640. $out["image"][] = [
  641. "title" => $this->trimdots($image["title"]),
  642. "source" => [
  643. [
  644. "url" => $image["media"],
  645. "width" => $image["width"],
  646. "height" => $image["height"]
  647. ],
  648. [
  649. "url" => $this->unshitimage($image["thumbnail"]),
  650. "width" => $image["thumb_width"],
  651. "height" => $image["thumb_height"]
  652. ]
  653. ],
  654. "url" => $image["url"]
  655. ];
  656. }
  657. return $out;
  658. }
  659. public function video($get){
  660. $search = $get["s"];
  661. if(strlen($search) === 0){
  662. throw new Exception("Search term is empty!");
  663. }
  664. $params = [
  665. "t" => "videos",
  666. "q" => $search,
  667. "count" => 50,
  668. "locale" => $get["country"],
  669. "offset" => 0, // dont implement pagination
  670. "device" => "desktop",
  671. "tgp" => 3
  672. ];
  673. switch($get["nsfw"]){
  674. case "yes": $params["safesearch"] = 0; break;
  675. case "maybe": $params["safesearch"] = 1; break;
  676. case "no": $params["safesearch"] = 2; break;
  677. }
  678. try{
  679. $json =
  680. $this->get(
  681. $this->backend->get_ip(),
  682. "https://api.qwant.com/v3/search/videos",
  683. $params
  684. );
  685. }catch(Exception $error){
  686. throw new Exception("Could not fetch JSON");
  687. }
  688. /*
  689. $handle = fopen("scraper/yandex-video.json", "r");
  690. $json = fread($handle, filesize("scraper/yandex-video.json"));
  691. fclose($handle);
  692. */
  693. $json = json_decode($json, true);
  694. if($json === null){
  695. throw new Exception("Could not parse JSON");
  696. }
  697. $this->detect_errors($json);
  698. if(isset($json["data"]["result"]["items"]["mainline"])){
  699. throw new Exception("Qwant returned gibberish results");
  700. }
  701. $out = [
  702. "status" => "ok",
  703. "npt" => null,
  704. "video" => [],
  705. "author" => [],
  706. "livestream" => [],
  707. "playlist" => [],
  708. "reel" => []
  709. ];
  710. foreach($json["data"]["result"]["items"] as $video){
  711. if(empty($video["thumbnail"])){
  712. $thumb = [
  713. "url" => null,
  714. "ratio" => null
  715. ];
  716. }else{
  717. $thumb = [
  718. "url" => $this->unshitimage($video["thumbnail"]),
  719. "ratio" => "16:9"
  720. ];
  721. }
  722. $duration = (int)$video["duration"];
  723. $out["video"][] = [
  724. "title" => $video["title"],
  725. "description" => $this->limitstrlen($video["desc"]),
  726. "author" => [
  727. "name" => $video["channel"],
  728. "url" => null,
  729. "avatar" => null
  730. ],
  731. "date" => (int)$video["date"],
  732. "duration" => $duration === 0 ? null : $duration,
  733. "views" => null,
  734. "thumb" => $thumb,
  735. "url" => preg_replace("/\?syndication=.+/", "", $video["url"])
  736. ];
  737. }
  738. return $out;
  739. }
  740. public function news($get){
  741. $search = $get["s"];
  742. if(strlen($search) === 0){
  743. throw new Exception("Search term is empty!");
  744. }
  745. $params = [
  746. "t" => "news",
  747. "q" => $search,
  748. "count" => 50,
  749. "locale" => $get["country"],
  750. "offset" => 0, // dont implement pagination
  751. "device" => "desktop",
  752. "tgp" => 3
  753. ];
  754. switch($get["nsfw"]){
  755. case "yes": $params["safesearch"] = 0; break;
  756. case "maybe": $params["safesearch"] = 1; break;
  757. case "no": $params["safesearch"] = 2; break;
  758. }
  759. try{
  760. $json =
  761. $this->get(
  762. $this->backend->get_ip(),
  763. "https://api.qwant.com/v3/search/news",
  764. $params
  765. );
  766. }catch(Exception $error){
  767. throw new Exception("Could not fetch JSON");
  768. }
  769. /*
  770. $handle = fopen("scraper/yandex-video.json", "r");
  771. $json = fread($handle, filesize("scraper/yandex-video.json"));
  772. fclose($handle);
  773. */
  774. $json = json_decode($json, true);
  775. if($json === null){
  776. throw new Exception("Could not parse JSON");
  777. }
  778. $this->detect_errors($json);
  779. if(isset($json["data"]["result"]["items"]["mainline"])){
  780. throw new Exception("Qwant returned gibberish results");
  781. }
  782. $out = [
  783. "status" => "ok",
  784. "npt" => null,
  785. "news" => []
  786. ];
  787. foreach($json["data"]["result"]["items"] as $news){
  788. if(empty($news["media"][0]["pict_big"]["url"])){
  789. $thumb = [
  790. "url" => null,
  791. "ratio" => null
  792. ];
  793. }else{
  794. $thumb = [
  795. "url" => $this->unshitimage($news["media"][0]["pict_big"]["url"]),
  796. "ratio" => "16:9"
  797. ];
  798. }
  799. $out["news"][] = [
  800. "title" => $news["title"],
  801. "author" => $news["press_name"],
  802. "description" => $this->trimdots($news["desc"]),
  803. "date" => (int)$news["date"],
  804. "thumb" => $thumb,
  805. "url" => $news["url"]
  806. ];
  807. }
  808. return $out;
  809. }
  810. private function detect_errors($json){
  811. if(
  812. isset($json["status"]) &&
  813. $json["status"] == "error"
  814. ){
  815. if(isset($json["data"]["error_data"]["captchaUrl"])){
  816. throw new Exception("Qwant returned a captcha");
  817. }elseif(isset($json["data"]["error_data"]["error_code"])){
  818. throw new Exception(
  819. "Qwant returned an API error: " .
  820. $json["data"]["error_data"]["error_code"]
  821. );
  822. }
  823. throw new Exception("Qwant returned an API error");
  824. }
  825. if(
  826. isset($json["url"]) &&
  827. preg_match(
  828. '/captcha/i',
  829. $json["url"]
  830. )
  831. ){
  832. throw new Exception("Qwant returned a captcha redirect");
  833. }
  834. }
  835. private function limitstrlen($text){
  836. return explode("\n", wordwrap($text, 300, "\n"))[0];
  837. }
  838. private function trimdots($text){
  839. return trim($text, ". ");
  840. }
  841. private function unshitimage($url){
  842. // https://s1.qwant.com/thumbr/0x0/8/d/f6de4deb2c2b12f55d8bdcaae576f9f62fd58a05ec0feeac117b354d1bf5c2/th.jpg?u=https%3A%2F%2Fwww.bing.com%2Fth%3Fid%3DOIP.vvDWsagzxjoKKP_rOqhwrQAAAA%26w%3D160%26h%3D160%26c%3D7%26pid%3D5.1&q=0&b=1&p=0&a=0
  843. // https://s2.qwant.com/thumbr/474x289/7/f/412d13b3fe3a03eb2b89633c8e88b609b7d0b93cdd9a5e52db3c663e41e65e/th.jpg?u=https%3A%2F%2Ftse.mm.bing.net%2Fth%3Fid%3DOIP.9Tm_Eo6m7V7ltN19mxduDgHaEh%26pid%3DApi&q=0&b=1&p=0&a=0
  844. $image = parse_url($url);
  845. if(
  846. !isset($image["host"]) ||
  847. !isset($image["query"])
  848. ){
  849. // cant do anything
  850. return $url;
  851. }
  852. $id = null;
  853. if(
  854. preg_match(
  855. '/s[0-9]+\.qwant\.com$/',
  856. $image["host"]
  857. )
  858. ){
  859. parse_str($image["query"], $str);
  860. // we're being served a proxy URL
  861. if(isset($str["u"])){
  862. $bing_url = $str["u"];
  863. }else{
  864. // give up
  865. return $url;
  866. }
  867. }
  868. // parse bing URL
  869. $id = null;
  870. $image = parse_url($bing_url);
  871. if(isset($image["query"])){
  872. parse_str($image["query"], $str);
  873. if(isset($str["id"])){
  874. $id = $str["id"];
  875. }
  876. }
  877. if($id === null){
  878. $id = explode("/th/id/", $image["path"], 2);
  879. if(count($id) !== 2){
  880. // malformed
  881. return $url;
  882. }
  883. $id = $id[1];
  884. }
  885. if(is_array($id)){
  886. // fuck off, let proxy.php deal with it
  887. return $url;
  888. }
  889. return "https://" . $image["host"] . "/th?id=" . rawurlencode($id);
  890. }
  891. }