brave.php 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703
  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(isset($result["software"]["programmingLanguage"])){
  493. $table["Programming languages"] = $result["software"]["programmingLanguage"];
  494. }
  495. }
  496. // location
  497. if(
  498. isset($result["location"]) &&
  499. is_array($result["location"])
  500. ){
  501. if(isset($result["location"]["postal_address"]["displayAddress"])){
  502. $table["Address"] = $result["location"]["postal_address"]["displayAddress"];
  503. }
  504. if(isset($result["location"]["rating"])){
  505. $table["Rating"] =
  506. $result["location"]["rating"]["ratingValue"] . "/" .
  507. $result["location"]["rating"]["bestRating"] . " (" .
  508. number_format($result["location"]["rating"]["reviewCount"]) . " votes)";
  509. }
  510. if(isset($result["location"]["contact"]["telephone"])){
  511. $table["Phone number"] =
  512. $result["location"]["contact"]["telephone"];
  513. }
  514. if(isset($result["location"]["price_range"])){
  515. $table["Price"] =
  516. $result["location"]["price_range"];
  517. }
  518. }
  519. // video
  520. if(
  521. isset($result["video"]) &&
  522. is_array($result["video"])
  523. ){
  524. foreach($result["video"] as $key => $value){
  525. if(is_string($result["video"][$key]) === false){
  526. continue;
  527. }
  528. $table[ucfirst($key)] = $value;
  529. }
  530. }
  531. // movie
  532. if(
  533. isset($result["video"]) &&
  534. is_array($result["movie"])
  535. ){
  536. if(isset($result["movie"]["release"])){
  537. $table["Release date"] = $result["movie"]["release"];
  538. }
  539. if(isset($result["movie"]["directors"])){
  540. $directors = [];
  541. foreach($result["movie"]["directors"] as $director){
  542. $directors[] = $director["name"];
  543. }
  544. if(count($directors) !== 0){
  545. $table["Directors"] = implode(", ", $directors);
  546. }
  547. }
  548. if(isset($result["movie"]["actors"])){
  549. $actors = [];
  550. foreach($result["movie"]["actors"] as $actor){
  551. $actors[] = $actor["name"];
  552. }
  553. if(count($actors) !== 0){
  554. $table["Actors"] = implode(", ", $actors);
  555. }
  556. }
  557. if(isset($result["movie"]["rating"])){
  558. $table["Rating"] =
  559. $result["movie"]["rating"]["ratingValue"] . "/" .
  560. $result["movie"]["rating"]["bestRating"] . " (" .
  561. number_format($result["movie"]["rating"]["reviewCount"]) . " votes)";
  562. }
  563. if(isset($result["movie"]["duration"])){
  564. $table["Duration"] =
  565. $result["movie"]["duration"];
  566. }
  567. if(isset($result["movie"]["genre"])){
  568. $genres = [];
  569. foreach($result["movie"]["genre"] as $genre){
  570. $genres[] = $genre;
  571. }
  572. if(count($genres) !== 0){
  573. $table["Genre"] = implode(", ", $genres);
  574. }
  575. }
  576. }
  577. if(
  578. isset($result["age"]) &&
  579. $result["age"] != "void 0" &&
  580. $result["age"] != ""
  581. ){
  582. $date = strtotime($result["age"]);
  583. }else{
  584. $date = null;
  585. }
  586. $out["web"][] = [
  587. "title" =>
  588. $this->titledots(
  589. $result["title"]
  590. ),
  591. "description" =>
  592. isset($result["review"]["description"]) ?
  593. $this->limitstrlen(
  594. $result["review"]["description"]
  595. ) :
  596. $this->titledots(
  597. $this->fuckhtml
  598. ->getTextContent(
  599. $result["description"]
  600. )
  601. ),
  602. "url" => $result["url"],
  603. "date" => $date,
  604. "type" => "web",
  605. "thumb" => $thumb,
  606. "sublink" => $sublink,
  607. "table" => $table
  608. ];
  609. }
  610. /*
  611. Get spelling autocorrect
  612. */
  613. if(
  614. isset($data["query"]["bo_altered_diff"][0][0]) &&
  615. $data["query"]["bo_altered_diff"][0][0] == "true"
  616. ){
  617. $using = [];
  618. foreach($data["query"]["bo_altered_diff"] as $diff){
  619. $using[] = $diff[1];
  620. }
  621. $out["spelling"] = [
  622. "type" => "including",
  623. "using" => implode(" ", $using),
  624. "correction" => $get["s"]
  625. ];
  626. }
  627. /*
  628. Get wikipedia heads
  629. */
  630. if(isset($data["infobox"]["results"][0])){
  631. foreach($data["infobox"]["results"] as $info){
  632. if($info["subtype"] == "code"){
  633. $description =
  634. $this->stackoverflow_parse($info["data"]["answer"]["text"]);
  635. if(isset($info["data"]["answer"]["author"])){
  636. $description[] = [
  637. "type" => "quote",
  638. "value" => "Answer from " . $info["data"]["answer"]["author"]
  639. ];
  640. }
  641. }else{
  642. $description = [];
  643. if(
  644. isset($info["description"]) &&
  645. $info["description"] != ""
  646. ){
  647. $description[] = [
  648. "type" => "quote",
  649. "value" => $info["description"]
  650. ];
  651. }
  652. if(
  653. isset($info["long_desc"]) &&
  654. $info["long_desc"] != ""
  655. ){
  656. $description[] = [
  657. "type" => "text",
  658. "value" => $this->titledots($info["long_desc"])
  659. ];
  660. }
  661. }
  662. $table = [];
  663. if(isset($info["attributes"])){
  664. foreach($info["attributes"] as $row){
  665. if(
  666. $row[1] == "null" &&
  667. count($table) !== 0
  668. ){
  669. break;
  670. }
  671. if($row[1] == "null"){
  672. continue;
  673. }
  674. $table[
  675. $this->fuckhtml->getTextContent($row[0])
  676. ] =
  677. $this->fuckhtml->getTextContent($row[1]);
  678. }
  679. }
  680. $sublink = [];
  681. if(isset($info["profiles"])){
  682. foreach($info["profiles"] as $row){
  683. $name = $this->fuckhtml->getTextContent($row["name"]);
  684. if(strtolower($name) == "steampowered"){
  685. $name = "Steam";
  686. }
  687. $sublink[
  688. $this->fuckhtml->getTextContent($name)
  689. ] =
  690. $this->fuckhtml->getTextContent($row["url"]);
  691. }
  692. }
  693. $out["answer"][] = [
  694. "title" => $this->fuckhtml->getTextContent($info["title"]),
  695. "description" => $description,
  696. "url" => $info["url"],
  697. "thumb" => isset($info["images"][0]["original"]) ? $info["images"][0]["original"] : null,
  698. "table" => $table,
  699. "sublink" => $sublink
  700. ];
  701. break; // only iterate once, we get garbage most of the time
  702. }
  703. }
  704. /*
  705. Get videos
  706. */
  707. if(isset($data["videos"]["results"])){
  708. foreach($data["videos"]["results"] as $video){
  709. $out["video"][] = [
  710. "title" => $this->titledots($video["title"]),
  711. "description" => $this->titledots($video["description"]),
  712. "date" => isset($video["age"]) ? strtotime($video["age"]) : null,
  713. "duration" => isset($video["video"]["duration"]) ? $this->hms2int($video["video"]["duration"]) : null,
  714. "views" => null,
  715. "thumb" =>
  716. isset($video["thumbnail"]["src"]) ?
  717. [
  718. "ratio" => "16:9",
  719. "url" => $this->unshiturl($video["thumbnail"]["src"])
  720. ] :
  721. [
  722. "ratio" => null,
  723. "url" => null
  724. ],
  725. "url" => $video["url"]
  726. ];
  727. }
  728. }
  729. /*
  730. Get news
  731. */
  732. if(isset($data["news"]["results"])){
  733. foreach($data["news"]["results"] as $news){
  734. $out["news"][] = [
  735. "title" => $this->titledots($news["title"]),
  736. "description" => $this->titledots($news["description"]),
  737. "date" => isset($news["age"]) ? strtotime($news["age"]) : null,
  738. "thumb" =>
  739. isset($video["thumbnail"]["src"]) ?
  740. [
  741. "ratio" => "16:9",
  742. "url" => $this->unshiturl($video["thumbnail"]["src"])
  743. ] :
  744. [
  745. "ratio" => null,
  746. "url" => null
  747. ],
  748. "url" => $news["url"]
  749. ];
  750. }
  751. }
  752. /*
  753. Get discussions
  754. */
  755. $disc_out = [];
  756. if(isset($data["discussions"]["results"])){
  757. foreach($data["discussions"]["results"] as $disc){
  758. $table = [];
  759. if(isset($disc["data"]["num_votes"])){
  760. $table["Votes"] = number_format($disc["data"]["num_votes"]);
  761. }
  762. if(isset($disc["data"]["num_answers"])){
  763. $table["Comments"] = number_format($disc["data"]["num_answers"]);
  764. }
  765. $disc_out[] = [
  766. "title" =>
  767. $this->titledots(
  768. $disc["title"]
  769. ),
  770. "description" =>
  771. $this->limitstrlen(
  772. $this->titledots(
  773. $this->fuckhtml
  774. ->getTextContent(
  775. $disc["description"]
  776. )
  777. )
  778. ),
  779. "url" => $disc["url"],
  780. "date" => isset($disc["age"]) ? strtotime($disc["age"]) : null,
  781. "type" => "web",
  782. "thumb" => [
  783. "ratio" => null,
  784. "url" => null
  785. ],
  786. "sublink" => [],
  787. "table" => $table
  788. ];
  789. }
  790. }
  791. // append discussions at position 2
  792. array_splice($out["web"], 1, 0, $disc_out);
  793. return $out;
  794. }
  795. public function news($get){
  796. $search = $get["s"];
  797. if(strlen($search) === 0){
  798. throw new Exception("Search term is empty!");
  799. }
  800. $nsfw = $get["nsfw"];
  801. $country = $get["country"];
  802. if(strlen($search) > 2048){
  803. throw new Exception("Search query is too long!");
  804. }
  805. /*
  806. $handle = fopen("scraper/brave-news.html", "r");
  807. $html = fread($handle, filesize("scraper/brave-news.html"));
  808. fclose($handle);*/
  809. try{
  810. $html =
  811. $this->get(
  812. "https://search.brave.com/news",
  813. [
  814. "q" => $search
  815. ],
  816. $nsfw,
  817. $country
  818. );
  819. }catch(Exception $error){
  820. throw new Exception("Could not fetch search page");
  821. }
  822. $out = [
  823. "status" => "ok",
  824. "npt" => null,
  825. "news" => []
  826. ];
  827. // load html
  828. $this->fuckhtml->load($html);
  829. $news =
  830. $this->fuckhtml
  831. ->getElementsByClassName(
  832. "snippet inline gap-standard",
  833. "div"
  834. );
  835. foreach($news as $article){
  836. $data = [
  837. "title" => null,
  838. "author" => null,
  839. "description" => null,
  840. "date" => null,
  841. "thumb" =>
  842. [
  843. "url" => null,
  844. "ratio" => null
  845. ],
  846. "url" => null
  847. ];
  848. $this->fuckhtml->load($article);
  849. $elems =
  850. $this->fuckhtml
  851. ->getElementsByTagName("*");
  852. // get title
  853. $data["title"] =
  854. $this->fuckhtml
  855. ->getTextContent(
  856. $this->fuckhtml
  857. ->getElementsByClassName(
  858. "snippet-title",
  859. $elems
  860. )
  861. [0]
  862. ["innerHTML"]
  863. );
  864. // get description
  865. $data["description"] =
  866. $this->titledots(
  867. $this->fuckhtml
  868. ->getTextContent(
  869. $this->fuckhtml
  870. ->getElementsByClassName(
  871. "snippet-description",
  872. $elems
  873. )
  874. [0]
  875. ["innerHTML"]
  876. )
  877. );
  878. // get date
  879. $date =
  880. explode(
  881. "•",
  882. $this->fuckhtml
  883. ->getTextContent(
  884. $this->fuckhtml
  885. ->getElementsByClassName(
  886. "snippet-url",
  887. $elems
  888. )[0]
  889. )
  890. );
  891. if(
  892. count($date) !== 1 &&
  893. trim($date[1]) != ""
  894. ){
  895. $data["date"] =
  896. strtotime(
  897. $date[1]
  898. );
  899. }
  900. // get URL
  901. $data["url"] =
  902. $this->fuckhtml->getTextContent(
  903. $this->unshiturl(
  904. $this->fuckhtml
  905. ->getElementsByClassName(
  906. "result-header",
  907. $elems
  908. )
  909. [0]
  910. ["attributes"]
  911. ["href"]
  912. )
  913. );
  914. // get thumbnail
  915. $thumb =
  916. $this->fuckhtml
  917. ->getElementsByTagName(
  918. "img"
  919. );
  920. if(
  921. count($thumb) === 2 &&
  922. trim(
  923. $thumb[1]
  924. ["attributes"]
  925. ["src"]
  926. ) != ""
  927. ){
  928. $data["thumb"] = [
  929. "url" =>
  930. $this->fuckhtml->getTextContent(
  931. $this->unshiturl(
  932. $thumb[1]
  933. ["attributes"]
  934. ["src"]
  935. )
  936. ),
  937. "ratio" => "16:9"
  938. ];
  939. }
  940. $out["news"][] = $data;
  941. }
  942. return $out;
  943. }
  944. public function image($get){
  945. $search = $get["s"];
  946. $country = $get["country"];
  947. $nsfw = $get["nsfw"];
  948. $out = [
  949. "status" => "ok",
  950. "npt" => null,
  951. "image" => []
  952. ];
  953. try{
  954. $html =
  955. $this->get(
  956. "https://search.brave.com/images",
  957. [
  958. "q" => $search
  959. ],
  960. $nsfw,
  961. $country
  962. );
  963. }catch(Exception $error){
  964. throw new Exception("Could not fetch search page");
  965. }
  966. /*
  967. $handle = fopen("scraper/brave-image.html", "r");
  968. $html = fread($handle, filesize("scraper/brave-image.html"));
  969. fclose($handle);*/
  970. preg_match(
  971. '/const data = (\[{.*}\]);/',
  972. $html,
  973. $json
  974. );
  975. if(!isset($json[1])){
  976. throw new Exception("Failed to get data object");
  977. }
  978. $json =
  979. $this->fuckhtml
  980. ->parseJsObject(
  981. $json[1]
  982. );
  983. foreach(
  984. $json[1]
  985. ["data"]
  986. ["body"]
  987. ["response"]
  988. ["results"]
  989. as $result
  990. ){
  991. $out["image"][] = [
  992. "title" => $result["title"],
  993. "source" => [
  994. [
  995. "url" => $result["properties"]["url"],
  996. "width" => null,
  997. "height" => null
  998. ],
  999. [
  1000. "url" => $result["thumbnail"]["src"],
  1001. "width" => null,
  1002. "height" => null
  1003. ]
  1004. ],
  1005. "url" => $result["url"]
  1006. ];
  1007. }
  1008. return $out;
  1009. }
  1010. public function video($get){
  1011. $search = $get["s"];
  1012. $country = $get["country"];
  1013. $nsfw = $get["nsfw"];
  1014. $out = [
  1015. "status" => "ok",
  1016. "npt" => null,
  1017. "video" => [],
  1018. "author" => [],
  1019. "livestream" => [],
  1020. "playlist" => [],
  1021. "reel" => []
  1022. ];
  1023. try{
  1024. $html =
  1025. $this->get(
  1026. "https://search.brave.com/videos",
  1027. [
  1028. "q" => $search
  1029. ],
  1030. $nsfw,
  1031. $country
  1032. );
  1033. }catch(Exception $error){
  1034. throw new Exception("Could not fetch search page");
  1035. }
  1036. /*
  1037. $handle = fopen("scraper/brave-video.html", "r");
  1038. $html = fread($handle, filesize("scraper/brave-video.html"));
  1039. fclose($handle);*/
  1040. preg_match(
  1041. '/const data = (\[{.*}\]);/',
  1042. $html,
  1043. $json
  1044. );
  1045. if(!isset($json[1])){
  1046. throw new Exception("Failed to get data object");
  1047. }
  1048. $json =
  1049. $this->fuckhtml
  1050. ->parseJsObject(
  1051. $json[1]
  1052. );
  1053. foreach(
  1054. $json
  1055. [1]
  1056. ["data"]
  1057. ["body"]
  1058. ["response"]
  1059. ["results"]
  1060. as $result
  1061. ){
  1062. if($result["video"]["author"] != "null"){
  1063. $author = [
  1064. "name" => $result["video"]["author"]["name"] == "null" ? null : $result["video"]["author"]["name"],
  1065. "url" => $result["video"]["author"]["url"] == "null" ? null : $result["video"]["author"]["url"],
  1066. "avatar" => $result["video"]["author"]["img"] == "null" ? null : $result["video"]["author"]["img"]
  1067. ];
  1068. }else{
  1069. $author = [
  1070. "name" => null,
  1071. "url" => null,
  1072. "avatar" => null
  1073. ];
  1074. }
  1075. if($result["thumbnail"] != "null"){
  1076. $thumb = [
  1077. "url" => $result["thumbnail"]["original"],
  1078. "ratio" => "16:9"
  1079. ];
  1080. }else{
  1081. $thumb = [
  1082. "url" => null,
  1083. "ratio" => null
  1084. ];
  1085. }
  1086. $out["video"][] = [
  1087. "title" => $result["title"],
  1088. "description" => $result["description"] == "null" ? null : $this->titledots($result["description"]),
  1089. "author" => $author,
  1090. "date" => ($result["age"] == "null" || $result["age"] == "void 0") ? null : strtotime($result["age"]),
  1091. "duration" => $result["video"]["duration"] == "null" ? null : $this->hms2int($result["video"]["duration"]),
  1092. "views" => $result["video"]["views"] == "null" ? null : (int)$result["video"]["views"],
  1093. "thumb" => $thumb,
  1094. "url" => $result["url"]
  1095. ];
  1096. }
  1097. return $out;
  1098. }
  1099. private function stackoverflow_parse($html){
  1100. $i = 0;
  1101. $answer = [];
  1102. $this->fuckhtml->load($html);
  1103. foreach(
  1104. $this->fuckhtml->getElementsByTagName("*")
  1105. as $snippet
  1106. ){
  1107. switch($snippet["tagName"]){
  1108. case "p":
  1109. $this->fuckhtml->load($snippet["innerHTML"]);
  1110. $codetags =
  1111. $this->fuckhtml
  1112. ->getElementsByTagName("*");
  1113. $tmphtml = $snippet["innerHTML"];
  1114. foreach($codetags as $tag){
  1115. if(!isset($tag["outerHTML"])){
  1116. continue;
  1117. }
  1118. $tmphtml =
  1119. explode(
  1120. $tag["outerHTML"],
  1121. $tmphtml,
  1122. 2
  1123. );
  1124. $value = $this->fuckhtml->getTextContent($tmphtml[0], false, false);
  1125. $this->appendtext($value, $answer, $i);
  1126. $type = null;
  1127. switch($tag["tagName"]){
  1128. case "code": $type = "inline_code"; break;
  1129. case "em": $type = "italic"; break;
  1130. case "blockquote": $type = "quote"; break;
  1131. default: $type = "text";
  1132. }
  1133. if($type !== null){
  1134. $value = $this->fuckhtml->getTextContent($tag, false, true);
  1135. if(trim($value) != ""){
  1136. if(
  1137. $i !== 0 &&
  1138. $type == "title"
  1139. ){
  1140. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  1141. }
  1142. $answer[] = [
  1143. "type" => $type,
  1144. "value" => $value
  1145. ];
  1146. $i++;
  1147. }
  1148. }
  1149. if(count($tmphtml) === 2){
  1150. $tmphtml = $tmphtml[1];
  1151. }else{
  1152. break;
  1153. }
  1154. }
  1155. if(is_array($tmphtml)){
  1156. $tmphtml = $tmphtml[0];
  1157. }
  1158. if(strlen($tmphtml) !== 0){
  1159. $value = $this->fuckhtml->getTextContent($tmphtml, false, false);
  1160. $this->appendtext($value, $answer, $i);
  1161. }
  1162. break;
  1163. case "img":
  1164. $answer[] = [
  1165. "type" => "image",
  1166. "url" =>
  1167. $this->fuckhtml
  1168. ->getTextContent(
  1169. $tag["attributes"]["src"]
  1170. )
  1171. ];
  1172. $i++;
  1173. break;
  1174. case "pre":
  1175. switch($answer[$i - 1]["type"]){
  1176. case "text":
  1177. case "italic":
  1178. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  1179. break;
  1180. }
  1181. $answer[] =
  1182. [
  1183. "type" => "code",
  1184. "value" =>
  1185. rtrim(
  1186. $this->fuckhtml
  1187. ->getTextContent(
  1188. $snippet,
  1189. true,
  1190. false
  1191. )
  1192. )
  1193. ];
  1194. $i++;
  1195. break;
  1196. case "ol":
  1197. $o = 0;
  1198. $this->fuckhtml->load($snippet);
  1199. $li =
  1200. $this->fuckhtml
  1201. ->getElementsByTagName("li");
  1202. foreach($li as $elem){
  1203. $o++;
  1204. $this->appendtext(
  1205. $o . ". " .
  1206. $this->fuckhtml
  1207. ->getTextContent(
  1208. $elem
  1209. ),
  1210. $answer,
  1211. $i
  1212. );
  1213. }
  1214. break;
  1215. }
  1216. }
  1217. if(
  1218. $i !== 0 &&
  1219. $answer[$i - 1]["type"] == "text"
  1220. ){
  1221. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  1222. }
  1223. return $answer;
  1224. }
  1225. private function hms2int($time){
  1226. $parts = explode(":", $time, 3);
  1227. $time = 0;
  1228. if(count($parts) === 3){
  1229. // hours
  1230. $time = $time + ((int)$parts[0] * 3600);
  1231. array_shift($parts);
  1232. }
  1233. if(count($parts) === 2){
  1234. // minutes
  1235. $time = $time + ((int)$parts[0] * 60);
  1236. array_shift($parts);
  1237. }
  1238. // seconds
  1239. $time = $time + (int)$parts[0];
  1240. return $time;
  1241. }
  1242. private function appendtext($payload, &$text, &$index){
  1243. if(trim($payload) == ""){
  1244. return;
  1245. }
  1246. if(
  1247. $index !== 0 &&
  1248. $text[$index - 1]["type"] == "text"
  1249. ){
  1250. $text[$index - 1]["value"] .= "\n\n" . preg_replace('/ $/', " ", $payload);
  1251. }else{
  1252. $text[] = [
  1253. "type" => "text",
  1254. "value" => preg_replace('/ $/', " ", $payload)
  1255. ];
  1256. $index++;
  1257. }
  1258. }
  1259. private function tablesublink($html_collection, &$data){
  1260. foreach($html_collection as $html){
  1261. $html["innerHTML"] = preg_replace(
  1262. '/<style>[\S\s]*<\/style>/i',
  1263. "",
  1264. $html["innerHTML"]
  1265. );
  1266. $html =
  1267. explode(
  1268. ":",
  1269. $this->fuckhtml->getTextContent($html),
  1270. 2
  1271. );
  1272. if(count($html) === 1){
  1273. $html = ["Rating", $html[0]];
  1274. }
  1275. $data["table"][trim($html[0])] = trim($html[1]);
  1276. }
  1277. }
  1278. private function getimagelinkfromstyle($thumb){
  1279. $thumb =
  1280. $this->fuckhtml
  1281. ->getElementsByClassName(
  1282. $thumb,
  1283. "div"
  1284. );
  1285. if(count($thumb) === 0){
  1286. return [
  1287. "url" => null,
  1288. "ratio" => null
  1289. ];
  1290. }
  1291. $thumb = $thumb[0]["attributes"]["style"];
  1292. preg_match(
  1293. '/background-image: ?url\((\'[^\']+\'|"[^"]+"|[^\)]+)\)/',
  1294. $thumb,
  1295. $thumb
  1296. );
  1297. $url = $this->fuckhtml->getTextContent($this->unshiturl(trim($thumb[1], '"\' ')));
  1298. if(parse_url($url, PHP_URL_HOST) == "cdn.search.brave.com"){
  1299. return [
  1300. "url" => null,
  1301. "ratio" => null
  1302. ];
  1303. }
  1304. return [
  1305. "url" => $url,
  1306. "ratio" => "16:9"
  1307. ];
  1308. }
  1309. private function limitstrlen($text){
  1310. return explode("\n", wordwrap($text, 300, "\n"))[0];
  1311. }
  1312. private function limitwhitespace($text){
  1313. return
  1314. preg_replace(
  1315. '/[\s]+/',
  1316. " ",
  1317. $text
  1318. );
  1319. }
  1320. private function titledots($title){
  1321. $substr = substr($title, -3);
  1322. if(
  1323. $substr == "..." ||
  1324. $substr == "…"
  1325. ){
  1326. return trim(substr($title, 0, -3));
  1327. }
  1328. return trim($title);
  1329. }
  1330. private function unshiturl($url){
  1331. // https://imgs.search.brave.com/XFnbR8Sl7ge82MBDEH7ju0UHImRovMVmQ2qnDvgNTuA/rs:fit:844:225:1/g:ce/aHR0cHM6Ly90c2U0/Lm1tLmJpbmcubmV0/L3RoP2lkPU9JUC54/UWotQXU5N2ozVndT/RDJnNG9BNVhnSGFF/SyZwaWQ9QXBp.jpeg
  1332. $tmp = explode("aHR0", $url);
  1333. if(count($tmp) !== 2){
  1334. // nothing to do
  1335. return $url;
  1336. }
  1337. return
  1338. base64_decode(
  1339. "aHR0" .
  1340. str_replace(["/", "_"], ["", "/"],
  1341. explode(
  1342. ".",
  1343. $tmp[1]
  1344. )[0]
  1345. )
  1346. );
  1347. }
  1348. }