brave.php 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706
  1. <?php
  2. class brave{
  3. public function __construct(){
  4. include "lib/fuckhtml.php";
  5. $this->fuckhtml = new fuckhtml();
  6. include "lib/nextpage.php";
  7. $this->nextpage = new nextpage("brave");
  8. }
  9. public function getfilters($page){
  10. switch($page){
  11. case "web":
  12. return [
  13. "country" => [
  14. "display" => "Country",
  15. "option" => [
  16. "all" => "All Regions",
  17. "ar" => "Argentina",
  18. "au" => "Australia",
  19. "at" => "Austria",
  20. "be" => "Belgium",
  21. "br" => "Brazil",
  22. "ca" => "Canada",
  23. "cl" => "Chile",
  24. "cn" => "China",
  25. "dk" => "Denmark",
  26. "fi" => "Finland",
  27. "fr" => "France",
  28. "de" => "Germany",
  29. "hk" => "Hong Kong",
  30. "in" => "India",
  31. "id" => "Indonesia",
  32. "it" => "Italy",
  33. "jp" => "Japan",
  34. "kr" => "Korea",
  35. "my" => "Malaysia",
  36. "mx" => "Mexico",
  37. "nl" => "Netherlands",
  38. "nz" => "New Zealand",
  39. "no" => "Norway",
  40. "pl" => "Poland",
  41. "pt" => "Portugal",
  42. "ph" => "Philippines",
  43. "ru" => "Russia",
  44. "sa" => "Saudi Arabia",
  45. "za" => "South Africa",
  46. "es" => "Spain",
  47. "se" => "Sweden",
  48. "ch" => "Switzerland",
  49. "tw" => "Taiwan",
  50. "tr" => "Turkey",
  51. "gb" => "United Kingdom",
  52. "us" => "United States"
  53. ]
  54. ],
  55. "nsfw" => [
  56. "display" => "NSFW",
  57. "option" => [
  58. "yes" => "Yes",
  59. "maybe" => "Maybe",
  60. "no" => "No"
  61. ]
  62. ],
  63. "newer" => [
  64. "display" => "Newer than",
  65. "option" => "_DATE"
  66. ],
  67. "older" => [
  68. "display" => "Older than",
  69. "option" => "_DATE"
  70. ],
  71. "spellcheck" => [
  72. "display" => "Spellcheck",
  73. "option" => [
  74. "yes" => "Yes",
  75. "no" => "No"
  76. ]
  77. ]
  78. ];
  79. break;
  80. case "images":
  81. case "videos":
  82. case "news":
  83. return [
  84. "country" => [
  85. "display" => "Country",
  86. "option" => [
  87. "all" => "All regions",
  88. "ar" => "Argentina",
  89. "au" => "Australia",
  90. "at" => "Austria",
  91. "be" => "Belgium",
  92. "br" => "Brazil",
  93. "ca" => "Canada",
  94. "cl" => "Chile",
  95. "cn" => "China",
  96. "dk" => "Denmark",
  97. "fi" => "Finland",
  98. "fr" => "France",
  99. "de" => "Germany",
  100. "hk" => "Hong Kong",
  101. "in" => "India",
  102. "id" => "Indonesia",
  103. "it" => "Italy",
  104. "jp" => "Japan",
  105. "kr" => "Korea",
  106. "my" => "Malaysia",
  107. "mx" => "Mexico",
  108. "nl" => "Netherlands",
  109. "nz" => "New Zealand",
  110. "no" => "Norway",
  111. "pl" => "Poland",
  112. "pt" => "Portugal",
  113. "ph" => "Philippines",
  114. "ru" => "Russia",
  115. "sa" => "Saudi Arabia",
  116. "za" => "South Africa",
  117. "es" => "Spain",
  118. "se" => "Sweden",
  119. "ch" => "Switzerland",
  120. "tw" => "Taiwan",
  121. "tr" => "Turkey",
  122. "gb" => "United Kingdom",
  123. "us" => "United States"
  124. ]
  125. ],
  126. "nsfw" => [
  127. "display" => "NSFW",
  128. "option" => [
  129. "yes" => "Yes",
  130. "maybe" => "Maybe",
  131. "no" => "No"
  132. ]
  133. ]
  134. ];
  135. break;
  136. }
  137. }
  138. private function get($url, $get = [], $nsfw, $country){
  139. switch($nsfw){
  140. case "yes": $nsfw = "off"; break;
  141. case "maybe": $nsfw = "moderate"; break;
  142. case "no": $nsfw = "strict"; break;
  143. }
  144. if($country == "any"){
  145. $country = "all";
  146. }
  147. $headers = [
  148. "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/110.0",
  149. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
  150. "Accept-Language: en-US,en;q=0.5",
  151. "Accept-Encoding: gzip",
  152. "Cookie: safesearch={$nsfw}; country={$country}; useLocation=0; summarizer=0",
  153. "DNT: 1",
  154. "Connection: keep-alive",
  155. "Upgrade-Insecure-Requests: 1",
  156. "Sec-Fetch-Dest: document",
  157. "Sec-Fetch-Mode: navigate",
  158. "Sec-Fetch-Site: none",
  159. "Sec-Fetch-User: ?1"
  160. ];
  161. $curlproc = curl_init();
  162. if($get !== []){
  163. $get = http_build_query($get);
  164. $url .= "?" . $get;
  165. }
  166. curl_setopt($curlproc, CURLOPT_URL, $url);
  167. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  168. curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
  169. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  170. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  171. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  172. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  173. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  174. $data = curl_exec($curlproc);
  175. if(curl_errno($curlproc)){
  176. throw new Exception(curl_error($curlproc));
  177. }
  178. curl_close($curlproc);
  179. return $data;
  180. }
  181. public function web($get){
  182. if($get["npt"]){
  183. // get next page data
  184. $q = json_decode($this->nextpage->get($get["npt"], "web"), true);
  185. $search = $q["q"];
  186. $q["spellcheck"] = "0";
  187. $nsfw = $q["nsfw"];
  188. unset($q["nsfw"]);
  189. $country = $q["country"];
  190. unset($q["country"]);
  191. }else{
  192. // get _GET data instead
  193. $search = $get["s"];
  194. if(strlen($search) === 0){
  195. throw new Exception("Search term is empty!");
  196. }
  197. if(strlen($search) > 2048){
  198. throw new Exception("Search query is too long!");
  199. }
  200. $nsfw = $get["nsfw"];
  201. $country = $get["country"];
  202. $older = $get["older"];
  203. $newer = $get["newer"];
  204. $spellcheck = $get["spellcheck"];
  205. $q = [
  206. "q" => $search
  207. ];
  208. /*
  209. Pass older/newer filters to brave
  210. */
  211. if($newer !== false){
  212. $newer = date("Y-m-d", $newer);
  213. if($older === false){
  214. $older = date("Y-m-d", time());
  215. }
  216. }
  217. if(
  218. is_string($older) === false &&
  219. $older !== false
  220. ){
  221. $older = date("Y-m-d", $older);
  222. if($newer === false){
  223. $newer = "1970-01-02";
  224. }
  225. }
  226. if($older !== false){
  227. $q["tf"] = "{$newer}to{$older}";
  228. }
  229. // spellcheck
  230. if($spellcheck == "no"){
  231. $q["spellcheck"] = "0";
  232. }
  233. }
  234. /*
  235. $handle = fopen("scraper/brave.html", "r");
  236. $html = fread($handle, filesize("scraper/brave.html"));
  237. fclose($handle);
  238. */
  239. try{
  240. $html =
  241. $this->get(
  242. "https://search.brave.com/search",
  243. $q,
  244. $nsfw,
  245. $country
  246. );
  247. }catch(Exception $error){
  248. throw new Exception("Could not fetch search page");
  249. }
  250. $out = [
  251. "status" => "ok",
  252. "spelling" => [
  253. "type" => "no_correction",
  254. "using" => null,
  255. "correction" => null
  256. ],
  257. "npt" => null,
  258. "answer" => [],
  259. "web" => [],
  260. "image" => [],
  261. "video" => [],
  262. "news" => [],
  263. "related" => []
  264. ];
  265. // load html
  266. $this->fuckhtml->load($html);
  267. /*
  268. Get next page "token"
  269. */
  270. $nextpage =
  271. $this->fuckhtml
  272. ->getElementById(
  273. "pagination",
  274. "div"
  275. );
  276. if($nextpage){
  277. $this->fuckhtml->load($nextpage);
  278. $nextpage =
  279. $this->fuckhtml
  280. ->getElementsByClassName("btn", "a");
  281. if(count($nextpage) !== 0){
  282. $nextpage =
  283. $nextpage[count($nextpage) - 1];
  284. if(
  285. strtolower(
  286. $this->fuckhtml
  287. ->getTextContent(
  288. $nextpage
  289. )
  290. ) == "next"
  291. ){
  292. preg_match(
  293. '/offset=([0-9]+)/',
  294. $this->fuckhtml->getTextContent($nextpage["attributes"]["href"]),
  295. $nextpage
  296. );
  297. $q["offset"] = (int)$nextpage[1];
  298. $q["nsfw"] = $nsfw;
  299. $q["country"] = $country;
  300. $out["npt"] =
  301. $this->nextpage->store(
  302. json_encode($q),
  303. "web"
  304. );
  305. }
  306. }
  307. }
  308. $this->fuckhtml->load($html);
  309. $script_disc =
  310. $this->fuckhtml
  311. ->getElementsByTagName(
  312. "script"
  313. );
  314. $grep = [];
  315. foreach($script_disc as $discs){
  316. preg_match(
  317. '/const data ?= ?(\[{.*}]);/',
  318. $discs["innerHTML"],
  319. $grep
  320. );
  321. if(isset($grep[1])){
  322. break;
  323. }
  324. }
  325. if(!isset($grep[1])){
  326. throw new Exception("Could not get data JS");
  327. }
  328. $data =
  329. $this->fuckhtml
  330. ->parseJsObject(
  331. $grep[1]
  332. );
  333. unset($grep);
  334. $data = $data[1]["data"]["body"]["response"];
  335. /*
  336. Get web results
  337. */
  338. if(!isset($data["web"]["results"])){
  339. return $out;
  340. }
  341. foreach($data["web"]["results"] as $result){
  342. if(
  343. isset($result["thumbnail"]) &&
  344. is_array($result["thumbnail"])
  345. ){
  346. $thumb = [
  347. "ratio" => $result["thumbnail"]["logo"] == "false" ? "16:9" : "1:1",
  348. "url" => $result["thumbnail"]["original"]
  349. ];
  350. }else{
  351. $thumb = [
  352. "ratio" => null,
  353. "url" => null
  354. ];
  355. }
  356. // get sublinks
  357. $sublink = [];
  358. if(
  359. isset($result["cluster"]) &&
  360. is_array($result["cluster"])
  361. ){
  362. foreach($result["cluster"] as $cluster){
  363. $sublink[] = [
  364. "title" => $this->titledots($cluster["title"]),
  365. "description" =>
  366. $this->titledots(
  367. $this->fuckhtml
  368. ->getTextContent(
  369. $cluster["description"]
  370. )
  371. ),
  372. "url" => $cluster["url"],
  373. "date" => null
  374. ];
  375. }
  376. }
  377. // more sublinks
  378. if(
  379. isset($result["deep_results"]) &&
  380. is_array($result["deep_results"])
  381. ){
  382. foreach($result["deep_results"]["buttons"] as $r){
  383. $sublink[] = [
  384. "title" => $this->titledots($r["title"]),
  385. "description" => null,
  386. "url" => $r["url"],
  387. "date" => null
  388. ];
  389. }
  390. }
  391. // parse table elements
  392. $table = [];
  393. /*
  394. [locations] => void 0 Done
  395. [video] => void 0 Done
  396. [movie] => void 0 Done
  397. [faq] => void 0
  398. [recipe] => void 0
  399. [qa] => void 0 Not needed
  400. [book] => void 0
  401. [rating] => void 0
  402. [article] => void 0
  403. [product] => void 0 Done
  404. [product_cluster] => void 0
  405. [cluster_type] => void 0
  406. [cluster] => void 0 Done
  407. [creative_work] => void 0 Done
  408. [music_recording] => void 0
  409. [review] => void 0 Done
  410. [software] => void 0 Done
  411. [content_type] => void 0
  412. [descriptionLength] => 271
  413. */
  414. // product
  415. // creative_work
  416. $ref = null;
  417. if(isset($result["product"])){
  418. $ref = &$result["product"];
  419. }elseif(isset($result["creative_work"])){
  420. $ref = &$result["creative_work"];
  421. }
  422. if($ref !== null){
  423. if(isset($ref["offers"])){
  424. foreach($ref["offers"] as $offer){
  425. $price = null;
  426. if(isset($offer["price"])){
  427. if((float)$offer["price"] == 0){
  428. $price = "Free";
  429. }else{
  430. $price = $offer["price"];
  431. }
  432. }
  433. if($price !== "Free"){
  434. if(isset($offer["priceCurrency"])){
  435. $price .= " " . $offer["priceCurrency"];
  436. }
  437. }
  438. if($price !== null){
  439. $table["Price"] = trim($price);
  440. }
  441. }
  442. }
  443. if(isset($ref["rating"])){
  444. $rating = null;
  445. if(isset($ref["rating"]["ratingValue"])){
  446. $rating = $ref["rating"]["ratingValue"];
  447. if(isset($ref["rating"]["bestRating"])){
  448. $rating .= "/" . $ref["rating"]["bestRating"];
  449. }
  450. }
  451. if(isset($ref["rating"]["reviewCount"])){
  452. $isnull = $rating === null ? false : true;
  453. if($isnull){
  454. $rating .= " (";
  455. }
  456. $rating .= number_format($ref["rating"]["reviewCount"]) . " hits";
  457. if($isnull){
  458. $rating .= ")";
  459. }
  460. }
  461. echo "test";
  462. if($rating !== null){
  463. $table["Rating"] = $rating;
  464. }
  465. }
  466. }
  467. // review
  468. if(
  469. isset($result["review"]) &&
  470. is_array($result["review"])
  471. ){
  472. if(isset($result["review"]["rating"]["ratingValue"])){
  473. $table["Rating"] =
  474. $result["review"]["rating"]["ratingValue"] . "/" .
  475. $result["review"]["rating"]["bestRating"];
  476. }
  477. }
  478. // software
  479. if(
  480. isset($result["software"]) &&
  481. is_array($result["software"])
  482. ){
  483. if(isset($result["software"]["author"])){
  484. $table["Author"] = $result["software"]["author"];
  485. }
  486. if(isset($result["software"]["stars"])){
  487. $table["Stars"] = number_format($result["software"]["stars"]);
  488. }
  489. if(isset($result["software"]["forks"])){
  490. $table["Forks"] = number_format($result["software"]["forks"]);
  491. }
  492. if(
  493. isset($result["software"]["programmingLanguage"]) &&
  494. $result["software"]["programmingLanguage"] != ""
  495. ){
  496. $table["Programming languages"] = $result["software"]["programmingLanguage"];
  497. }
  498. }
  499. // location
  500. if(
  501. isset($result["location"]) &&
  502. is_array($result["location"])
  503. ){
  504. if(isset($result["location"]["postal_address"]["displayAddress"])){
  505. $table["Address"] = $result["location"]["postal_address"]["displayAddress"];
  506. }
  507. if(isset($result["location"]["rating"])){
  508. $table["Rating"] =
  509. $result["location"]["rating"]["ratingValue"] . "/" .
  510. $result["location"]["rating"]["bestRating"] . " (" .
  511. number_format($result["location"]["rating"]["reviewCount"]) . " votes)";
  512. }
  513. if(isset($result["location"]["contact"]["telephone"])){
  514. $table["Phone number"] =
  515. $result["location"]["contact"]["telephone"];
  516. }
  517. if(isset($result["location"]["price_range"])){
  518. $table["Price"] =
  519. $result["location"]["price_range"];
  520. }
  521. }
  522. // video
  523. if(
  524. isset($result["video"]) &&
  525. is_array($result["video"])
  526. ){
  527. foreach($result["video"] as $key => $value){
  528. if(is_string($result["video"][$key]) === false){
  529. continue;
  530. }
  531. $table[ucfirst($key)] = $value;
  532. }
  533. }
  534. // movie
  535. if(
  536. isset($result["video"]) &&
  537. is_array($result["movie"])
  538. ){
  539. if(isset($result["movie"]["release"])){
  540. $table["Release date"] = $result["movie"]["release"];
  541. }
  542. if(isset($result["movie"]["directors"])){
  543. $directors = [];
  544. foreach($result["movie"]["directors"] as $director){
  545. $directors[] = $director["name"];
  546. }
  547. if(count($directors) !== 0){
  548. $table["Directors"] = implode(", ", $directors);
  549. }
  550. }
  551. if(isset($result["movie"]["actors"])){
  552. $actors = [];
  553. foreach($result["movie"]["actors"] as $actor){
  554. $actors[] = $actor["name"];
  555. }
  556. if(count($actors) !== 0){
  557. $table["Actors"] = implode(", ", $actors);
  558. }
  559. }
  560. if(isset($result["movie"]["rating"])){
  561. $table["Rating"] =
  562. $result["movie"]["rating"]["ratingValue"] . "/" .
  563. $result["movie"]["rating"]["bestRating"] . " (" .
  564. number_format($result["movie"]["rating"]["reviewCount"]) . " votes)";
  565. }
  566. if(isset($result["movie"]["duration"])){
  567. $table["Duration"] =
  568. $result["movie"]["duration"];
  569. }
  570. if(isset($result["movie"]["genre"])){
  571. $genres = [];
  572. foreach($result["movie"]["genre"] as $genre){
  573. $genres[] = $genre;
  574. }
  575. if(count($genres) !== 0){
  576. $table["Genre"] = implode(", ", $genres);
  577. }
  578. }
  579. }
  580. if(
  581. isset($result["age"]) &&
  582. $result["age"] != "void 0" &&
  583. $result["age"] != ""
  584. ){
  585. $date = strtotime($result["age"]);
  586. }else{
  587. $date = null;
  588. }
  589. $out["web"][] = [
  590. "title" =>
  591. $this->titledots(
  592. $result["title"]
  593. ),
  594. "description" =>
  595. isset($result["review"]["description"]) ?
  596. $this->limitstrlen(
  597. $result["review"]["description"]
  598. ) :
  599. $this->titledots(
  600. $this->fuckhtml
  601. ->getTextContent(
  602. $result["description"]
  603. )
  604. ),
  605. "url" => $result["url"],
  606. "date" => $date,
  607. "type" => "web",
  608. "thumb" => $thumb,
  609. "sublink" => $sublink,
  610. "table" => $table
  611. ];
  612. }
  613. /*
  614. Get spelling autocorrect
  615. */
  616. if(
  617. isset($data["query"]["bo_altered_diff"][0][0]) &&
  618. $data["query"]["bo_altered_diff"][0][0] == "true"
  619. ){
  620. $using = [];
  621. foreach($data["query"]["bo_altered_diff"] as $diff){
  622. $using[] = $diff[1];
  623. }
  624. $out["spelling"] = [
  625. "type" => "including",
  626. "using" => implode(" ", $using),
  627. "correction" => $get["s"]
  628. ];
  629. }
  630. /*
  631. Get wikipedia heads
  632. */
  633. if(isset($data["infobox"]["results"][0])){
  634. foreach($data["infobox"]["results"] as $info){
  635. if($info["subtype"] == "code"){
  636. $description =
  637. $this->stackoverflow_parse($info["data"]["answer"]["text"]);
  638. if(isset($info["data"]["answer"]["author"])){
  639. $description[] = [
  640. "type" => "quote",
  641. "value" => "Answer from " . $info["data"]["answer"]["author"]
  642. ];
  643. }
  644. }else{
  645. $description = [];
  646. if(
  647. isset($info["description"]) &&
  648. $info["description"] != ""
  649. ){
  650. $description[] = [
  651. "type" => "quote",
  652. "value" => $info["description"]
  653. ];
  654. }
  655. if(
  656. isset($info["long_desc"]) &&
  657. $info["long_desc"] != ""
  658. ){
  659. $description[] = [
  660. "type" => "text",
  661. "value" => $this->titledots($info["long_desc"])
  662. ];
  663. }
  664. }
  665. $table = [];
  666. if(isset($info["attributes"])){
  667. foreach($info["attributes"] as $row){
  668. if(
  669. $row[1] == "null" &&
  670. count($table) !== 0
  671. ){
  672. break;
  673. }
  674. if($row[1] == "null"){
  675. continue;
  676. }
  677. $table[
  678. $this->fuckhtml->getTextContent($row[0])
  679. ] =
  680. $this->fuckhtml->getTextContent($row[1]);
  681. }
  682. }
  683. $sublink = [];
  684. if(isset($info["profiles"])){
  685. foreach($info["profiles"] as $row){
  686. $name = $this->fuckhtml->getTextContent($row["name"]);
  687. if(strtolower($name) == "steampowered"){
  688. $name = "Steam";
  689. }
  690. $sublink[
  691. $this->fuckhtml->getTextContent($name)
  692. ] =
  693. $this->fuckhtml->getTextContent($row["url"]);
  694. }
  695. }
  696. $out["answer"][] = [
  697. "title" => $this->fuckhtml->getTextContent($info["title"]),
  698. "description" => $description,
  699. "url" => $info["url"],
  700. "thumb" => isset($info["images"][0]["original"]) ? $info["images"][0]["original"] : null,
  701. "table" => $table,
  702. "sublink" => $sublink
  703. ];
  704. break; // only iterate once, we get garbage most of the time
  705. }
  706. }
  707. /*
  708. Get videos
  709. */
  710. if(isset($data["videos"]["results"])){
  711. foreach($data["videos"]["results"] as $video){
  712. $out["video"][] = [
  713. "title" => $this->titledots($video["title"]),
  714. "description" => $this->titledots($video["description"]),
  715. "date" => isset($video["age"]) ? strtotime($video["age"]) : null,
  716. "duration" => isset($video["video"]["duration"]) ? $this->hms2int($video["video"]["duration"]) : null,
  717. "views" => null,
  718. "thumb" =>
  719. isset($video["thumbnail"]["src"]) ?
  720. [
  721. "ratio" => "16:9",
  722. "url" => $this->unshiturl($video["thumbnail"]["src"])
  723. ] :
  724. [
  725. "ratio" => null,
  726. "url" => null
  727. ],
  728. "url" => $video["url"]
  729. ];
  730. }
  731. }
  732. /*
  733. Get news
  734. */
  735. if(isset($data["news"]["results"])){
  736. foreach($data["news"]["results"] as $news){
  737. $out["news"][] = [
  738. "title" => $this->titledots($news["title"]),
  739. "description" => $this->titledots($news["description"]),
  740. "date" => isset($news["age"]) ? strtotime($news["age"]) : null,
  741. "thumb" =>
  742. isset($video["thumbnail"]["src"]) ?
  743. [
  744. "ratio" => "16:9",
  745. "url" => $this->unshiturl($video["thumbnail"]["src"])
  746. ] :
  747. [
  748. "ratio" => null,
  749. "url" => null
  750. ],
  751. "url" => $news["url"]
  752. ];
  753. }
  754. }
  755. /*
  756. Get discussions
  757. */
  758. $disc_out = [];
  759. if(isset($data["discussions"]["results"])){
  760. foreach($data["discussions"]["results"] as $disc){
  761. $table = [];
  762. if(isset($disc["data"]["num_votes"])){
  763. $table["Votes"] = number_format($disc["data"]["num_votes"]);
  764. }
  765. if(isset($disc["data"]["num_answers"])){
  766. $table["Comments"] = number_format($disc["data"]["num_answers"]);
  767. }
  768. $disc_out[] = [
  769. "title" =>
  770. $this->titledots(
  771. $disc["title"]
  772. ),
  773. "description" =>
  774. $this->limitstrlen(
  775. $this->titledots(
  776. $this->fuckhtml
  777. ->getTextContent(
  778. $disc["description"]
  779. )
  780. )
  781. ),
  782. "url" => $disc["url"],
  783. "date" => isset($disc["age"]) ? strtotime($disc["age"]) : null,
  784. "type" => "web",
  785. "thumb" => [
  786. "ratio" => null,
  787. "url" => null
  788. ],
  789. "sublink" => [],
  790. "table" => $table
  791. ];
  792. }
  793. }
  794. // append discussions at position 2
  795. array_splice($out["web"], 1, 0, $disc_out);
  796. return $out;
  797. }
  798. public function news($get){
  799. $search = $get["s"];
  800. if(strlen($search) === 0){
  801. throw new Exception("Search term is empty!");
  802. }
  803. $nsfw = $get["nsfw"];
  804. $country = $get["country"];
  805. if(strlen($search) > 2048){
  806. throw new Exception("Search query is too long!");
  807. }
  808. /*
  809. $handle = fopen("scraper/brave-news.html", "r");
  810. $html = fread($handle, filesize("scraper/brave-news.html"));
  811. fclose($handle);*/
  812. try{
  813. $html =
  814. $this->get(
  815. "https://search.brave.com/news",
  816. [
  817. "q" => $search
  818. ],
  819. $nsfw,
  820. $country
  821. );
  822. }catch(Exception $error){
  823. throw new Exception("Could not fetch search page");
  824. }
  825. $out = [
  826. "status" => "ok",
  827. "npt" => null,
  828. "news" => []
  829. ];
  830. // load html
  831. $this->fuckhtml->load($html);
  832. $news =
  833. $this->fuckhtml
  834. ->getElementsByClassName(
  835. "snippet inline gap-standard",
  836. "div"
  837. );
  838. foreach($news as $article){
  839. $data = [
  840. "title" => null,
  841. "author" => null,
  842. "description" => null,
  843. "date" => null,
  844. "thumb" =>
  845. [
  846. "url" => null,
  847. "ratio" => null
  848. ],
  849. "url" => null
  850. ];
  851. $this->fuckhtml->load($article);
  852. $elems =
  853. $this->fuckhtml
  854. ->getElementsByTagName("*");
  855. // get title
  856. $data["title"] =
  857. $this->fuckhtml
  858. ->getTextContent(
  859. $this->fuckhtml
  860. ->getElementsByClassName(
  861. "snippet-title",
  862. $elems
  863. )
  864. [0]
  865. ["innerHTML"]
  866. );
  867. // get description
  868. $data["description"] =
  869. $this->titledots(
  870. $this->fuckhtml
  871. ->getTextContent(
  872. $this->fuckhtml
  873. ->getElementsByClassName(
  874. "snippet-description",
  875. $elems
  876. )
  877. [0]
  878. ["innerHTML"]
  879. )
  880. );
  881. // get date
  882. $date =
  883. explode(
  884. "•",
  885. $this->fuckhtml
  886. ->getTextContent(
  887. $this->fuckhtml
  888. ->getElementsByClassName(
  889. "snippet-url",
  890. $elems
  891. )[0]
  892. )
  893. );
  894. if(
  895. count($date) !== 1 &&
  896. trim($date[1]) != ""
  897. ){
  898. $data["date"] =
  899. strtotime(
  900. $date[1]
  901. );
  902. }
  903. // get URL
  904. $data["url"] =
  905. $this->fuckhtml->getTextContent(
  906. $this->unshiturl(
  907. $this->fuckhtml
  908. ->getElementsByClassName(
  909. "result-header",
  910. $elems
  911. )
  912. [0]
  913. ["attributes"]
  914. ["href"]
  915. )
  916. );
  917. // get thumbnail
  918. $thumb =
  919. $this->fuckhtml
  920. ->getElementsByTagName(
  921. "img"
  922. );
  923. if(
  924. count($thumb) === 2 &&
  925. trim(
  926. $thumb[1]
  927. ["attributes"]
  928. ["src"]
  929. ) != ""
  930. ){
  931. $data["thumb"] = [
  932. "url" =>
  933. $this->fuckhtml->getTextContent(
  934. $this->unshiturl(
  935. $thumb[1]
  936. ["attributes"]
  937. ["src"]
  938. )
  939. ),
  940. "ratio" => "16:9"
  941. ];
  942. }
  943. $out["news"][] = $data;
  944. }
  945. return $out;
  946. }
  947. public function image($get){
  948. $search = $get["s"];
  949. $country = $get["country"];
  950. $nsfw = $get["nsfw"];
  951. $out = [
  952. "status" => "ok",
  953. "npt" => null,
  954. "image" => []
  955. ];
  956. try{
  957. $html =
  958. $this->get(
  959. "https://search.brave.com/images",
  960. [
  961. "q" => $search
  962. ],
  963. $nsfw,
  964. $country
  965. );
  966. }catch(Exception $error){
  967. throw new Exception("Could not fetch search page");
  968. }
  969. /*
  970. $handle = fopen("scraper/brave-image.html", "r");
  971. $html = fread($handle, filesize("scraper/brave-image.html"));
  972. fclose($handle);*/
  973. preg_match(
  974. '/const data = (\[{.*}\]);/',
  975. $html,
  976. $json
  977. );
  978. if(!isset($json[1])){
  979. throw new Exception("Failed to get data object");
  980. }
  981. $json =
  982. $this->fuckhtml
  983. ->parseJsObject(
  984. $json[1]
  985. );
  986. foreach(
  987. $json[1]
  988. ["data"]
  989. ["body"]
  990. ["response"]
  991. ["results"]
  992. as $result
  993. ){
  994. $out["image"][] = [
  995. "title" => $result["title"],
  996. "source" => [
  997. [
  998. "url" => $result["properties"]["url"],
  999. "width" => null,
  1000. "height" => null
  1001. ],
  1002. [
  1003. "url" => $result["thumbnail"]["src"],
  1004. "width" => null,
  1005. "height" => null
  1006. ]
  1007. ],
  1008. "url" => $result["url"]
  1009. ];
  1010. }
  1011. return $out;
  1012. }
  1013. public function video($get){
  1014. $search = $get["s"];
  1015. $country = $get["country"];
  1016. $nsfw = $get["nsfw"];
  1017. $out = [
  1018. "status" => "ok",
  1019. "npt" => null,
  1020. "video" => [],
  1021. "author" => [],
  1022. "livestream" => [],
  1023. "playlist" => [],
  1024. "reel" => []
  1025. ];
  1026. try{
  1027. $html =
  1028. $this->get(
  1029. "https://search.brave.com/videos",
  1030. [
  1031. "q" => $search
  1032. ],
  1033. $nsfw,
  1034. $country
  1035. );
  1036. }catch(Exception $error){
  1037. throw new Exception("Could not fetch search page");
  1038. }
  1039. /*
  1040. $handle = fopen("scraper/brave-video.html", "r");
  1041. $html = fread($handle, filesize("scraper/brave-video.html"));
  1042. fclose($handle);*/
  1043. preg_match(
  1044. '/const data = (\[{.*}\]);/',
  1045. $html,
  1046. $json
  1047. );
  1048. if(!isset($json[1])){
  1049. throw new Exception("Failed to get data object");
  1050. }
  1051. $json =
  1052. $this->fuckhtml
  1053. ->parseJsObject(
  1054. $json[1]
  1055. );
  1056. foreach(
  1057. $json
  1058. [1]
  1059. ["data"]
  1060. ["body"]
  1061. ["response"]
  1062. ["results"]
  1063. as $result
  1064. ){
  1065. if($result["video"]["author"] != "null"){
  1066. $author = [
  1067. "name" => $result["video"]["author"]["name"] == "null" ? null : $result["video"]["author"]["name"],
  1068. "url" => $result["video"]["author"]["url"] == "null" ? null : $result["video"]["author"]["url"],
  1069. "avatar" => $result["video"]["author"]["img"] == "null" ? null : $result["video"]["author"]["img"]
  1070. ];
  1071. }else{
  1072. $author = [
  1073. "name" => null,
  1074. "url" => null,
  1075. "avatar" => null
  1076. ];
  1077. }
  1078. if($result["thumbnail"] != "null"){
  1079. $thumb = [
  1080. "url" => $result["thumbnail"]["original"],
  1081. "ratio" => "16:9"
  1082. ];
  1083. }else{
  1084. $thumb = [
  1085. "url" => null,
  1086. "ratio" => null
  1087. ];
  1088. }
  1089. $out["video"][] = [
  1090. "title" => $result["title"],
  1091. "description" => $result["description"] == "null" ? null : $this->titledots($result["description"]),
  1092. "author" => $author,
  1093. "date" => ($result["age"] == "null" || $result["age"] == "void 0") ? null : strtotime($result["age"]),
  1094. "duration" => $result["video"]["duration"] == "null" ? null : $this->hms2int($result["video"]["duration"]),
  1095. "views" => $result["video"]["views"] == "null" ? null : (int)$result["video"]["views"],
  1096. "thumb" => $thumb,
  1097. "url" => $result["url"]
  1098. ];
  1099. }
  1100. return $out;
  1101. }
  1102. private function stackoverflow_parse($html){
  1103. $i = 0;
  1104. $answer = [];
  1105. $this->fuckhtml->load($html);
  1106. foreach(
  1107. $this->fuckhtml->getElementsByTagName("*")
  1108. as $snippet
  1109. ){
  1110. switch($snippet["tagName"]){
  1111. case "p":
  1112. $this->fuckhtml->load($snippet["innerHTML"]);
  1113. $codetags =
  1114. $this->fuckhtml
  1115. ->getElementsByTagName("*");
  1116. $tmphtml = $snippet["innerHTML"];
  1117. foreach($codetags as $tag){
  1118. if(!isset($tag["outerHTML"])){
  1119. continue;
  1120. }
  1121. $tmphtml =
  1122. explode(
  1123. $tag["outerHTML"],
  1124. $tmphtml,
  1125. 2
  1126. );
  1127. $value = $this->fuckhtml->getTextContent($tmphtml[0], false, false);
  1128. $this->appendtext($value, $answer, $i);
  1129. $type = null;
  1130. switch($tag["tagName"]){
  1131. case "code": $type = "inline_code"; break;
  1132. case "em": $type = "italic"; break;
  1133. case "blockquote": $type = "quote"; break;
  1134. default: $type = "text";
  1135. }
  1136. if($type !== null){
  1137. $value = $this->fuckhtml->getTextContent($tag, false, true);
  1138. if(trim($value) != ""){
  1139. if(
  1140. $i !== 0 &&
  1141. $type == "title"
  1142. ){
  1143. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  1144. }
  1145. $answer[] = [
  1146. "type" => $type,
  1147. "value" => $value
  1148. ];
  1149. $i++;
  1150. }
  1151. }
  1152. if(count($tmphtml) === 2){
  1153. $tmphtml = $tmphtml[1];
  1154. }else{
  1155. break;
  1156. }
  1157. }
  1158. if(is_array($tmphtml)){
  1159. $tmphtml = $tmphtml[0];
  1160. }
  1161. if(strlen($tmphtml) !== 0){
  1162. $value = $this->fuckhtml->getTextContent($tmphtml, false, false);
  1163. $this->appendtext($value, $answer, $i);
  1164. }
  1165. break;
  1166. case "img":
  1167. $answer[] = [
  1168. "type" => "image",
  1169. "url" =>
  1170. $this->fuckhtml
  1171. ->getTextContent(
  1172. $tag["attributes"]["src"]
  1173. )
  1174. ];
  1175. $i++;
  1176. break;
  1177. case "pre":
  1178. switch($answer[$i - 1]["type"]){
  1179. case "text":
  1180. case "italic":
  1181. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  1182. break;
  1183. }
  1184. $answer[] =
  1185. [
  1186. "type" => "code",
  1187. "value" =>
  1188. rtrim(
  1189. $this->fuckhtml
  1190. ->getTextContent(
  1191. $snippet,
  1192. true,
  1193. false
  1194. )
  1195. )
  1196. ];
  1197. $i++;
  1198. break;
  1199. case "ol":
  1200. $o = 0;
  1201. $this->fuckhtml->load($snippet);
  1202. $li =
  1203. $this->fuckhtml
  1204. ->getElementsByTagName("li");
  1205. foreach($li as $elem){
  1206. $o++;
  1207. $this->appendtext(
  1208. $o . ". " .
  1209. $this->fuckhtml
  1210. ->getTextContent(
  1211. $elem
  1212. ),
  1213. $answer,
  1214. $i
  1215. );
  1216. }
  1217. break;
  1218. }
  1219. }
  1220. if(
  1221. $i !== 0 &&
  1222. $answer[$i - 1]["type"] == "text"
  1223. ){
  1224. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  1225. }
  1226. return $answer;
  1227. }
  1228. private function hms2int($time){
  1229. $parts = explode(":", $time, 3);
  1230. $time = 0;
  1231. if(count($parts) === 3){
  1232. // hours
  1233. $time = $time + ((int)$parts[0] * 3600);
  1234. array_shift($parts);
  1235. }
  1236. if(count($parts) === 2){
  1237. // minutes
  1238. $time = $time + ((int)$parts[0] * 60);
  1239. array_shift($parts);
  1240. }
  1241. // seconds
  1242. $time = $time + (int)$parts[0];
  1243. return $time;
  1244. }
  1245. private function appendtext($payload, &$text, &$index){
  1246. if(trim($payload) == ""){
  1247. return;
  1248. }
  1249. if(
  1250. $index !== 0 &&
  1251. $text[$index - 1]["type"] == "text"
  1252. ){
  1253. $text[$index - 1]["value"] .= "\n\n" . preg_replace('/ $/', " ", $payload);
  1254. }else{
  1255. $text[] = [
  1256. "type" => "text",
  1257. "value" => preg_replace('/ $/', " ", $payload)
  1258. ];
  1259. $index++;
  1260. }
  1261. }
  1262. private function tablesublink($html_collection, &$data){
  1263. foreach($html_collection as $html){
  1264. $html["innerHTML"] = preg_replace(
  1265. '/<style>[\S\s]*<\/style>/i',
  1266. "",
  1267. $html["innerHTML"]
  1268. );
  1269. $html =
  1270. explode(
  1271. ":",
  1272. $this->fuckhtml->getTextContent($html),
  1273. 2
  1274. );
  1275. if(count($html) === 1){
  1276. $html = ["Rating", $html[0]];
  1277. }
  1278. $data["table"][trim($html[0])] = trim($html[1]);
  1279. }
  1280. }
  1281. private function getimagelinkfromstyle($thumb){
  1282. $thumb =
  1283. $this->fuckhtml
  1284. ->getElementsByClassName(
  1285. $thumb,
  1286. "div"
  1287. );
  1288. if(count($thumb) === 0){
  1289. return [
  1290. "url" => null,
  1291. "ratio" => null
  1292. ];
  1293. }
  1294. $thumb = $thumb[0]["attributes"]["style"];
  1295. preg_match(
  1296. '/background-image: ?url\((\'[^\']+\'|"[^"]+"|[^\)]+)\)/',
  1297. $thumb,
  1298. $thumb
  1299. );
  1300. $url = $this->fuckhtml->getTextContent($this->unshiturl(trim($thumb[1], '"\' ')));
  1301. if(parse_url($url, PHP_URL_HOST) == "cdn.search.brave.com"){
  1302. return [
  1303. "url" => null,
  1304. "ratio" => null
  1305. ];
  1306. }
  1307. return [
  1308. "url" => $url,
  1309. "ratio" => "16:9"
  1310. ];
  1311. }
  1312. private function limitstrlen($text){
  1313. return explode("\n", wordwrap($text, 300, "\n"))[0];
  1314. }
  1315. private function limitwhitespace($text){
  1316. return
  1317. preg_replace(
  1318. '/[\s]+/',
  1319. " ",
  1320. $text
  1321. );
  1322. }
  1323. private function titledots($title){
  1324. $substr = substr($title, -3);
  1325. if(
  1326. $substr == "..." ||
  1327. $substr == "…"
  1328. ){
  1329. return trim(substr($title, 0, -3));
  1330. }
  1331. return trim($title);
  1332. }
  1333. private function unshiturl($url){
  1334. // https://imgs.search.brave.com/XFnbR8Sl7ge82MBDEH7ju0UHImRovMVmQ2qnDvgNTuA/rs:fit:844:225:1/g:ce/aHR0cHM6Ly90c2U0/Lm1tLmJpbmcubmV0/L3RoP2lkPU9JUC54/UWotQXU5N2ozVndT/RDJnNG9BNVhnSGFF/SyZwaWQ9QXBp.jpeg
  1335. $tmp = explode("aHR0", $url);
  1336. if(count($tmp) !== 2){
  1337. // nothing to do
  1338. return $url;
  1339. }
  1340. return
  1341. base64_decode(
  1342. "aHR0" .
  1343. str_replace(["/", "_"], ["", "/"],
  1344. explode(
  1345. ".",
  1346. $tmp[1]
  1347. )[0]
  1348. )
  1349. );
  1350. }
  1351. }