brave.php 36 KB

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