1
0

ddg.php 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722
  1. <?php
  2. class ddg{
  3. public function __construct(){
  4. include "lib/nextpage.php";
  5. $this->nextpage = new nextpage("ddg");
  6. }
  7. /*
  8. curl functions
  9. */
  10. private const req_web = 0;
  11. private const req_xhr = 1;
  12. private function get($url, $get = [], $reqtype = self::req_web){
  13. $curlproc = curl_init();
  14. if($get !== []){
  15. $get = http_build_query($get);
  16. $url .= "?" . $get;
  17. }
  18. curl_setopt($curlproc, CURLOPT_URL, $url);
  19. switch($reqtype){
  20. case self::req_web:
  21. $headers =
  22. ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0",
  23. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
  24. "Accept-Encoding: gzip",
  25. "Accept-Language: en-US,en;q=0.5",
  26. "DNT: 1",
  27. "Connection: keep-alive",
  28. "Upgrade-Insecure-Requests: 1",
  29. "Sec-Fetch-Dest: document",
  30. "Sec-Fetch-Mode: navigate",
  31. "Sec-Fetch-Site: cross-site",
  32. "Upgrade-Insecure-Requests: 1"];
  33. break;
  34. case self::req_xhr:
  35. $headers =
  36. ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0",
  37. "Accept: */*",
  38. "Accept-Encoding: gzip",
  39. "Accept-Language: en-US,en;q=0.5",
  40. "Connection: keep-alive",
  41. "Referer: https://duckduckgo.com/",
  42. "X-Requested-With: XMLHttpRequest",
  43. "DNT: 1",
  44. "Sec-Fetch-Dest: script",
  45. "Sec-Fetch-Mode: no-cors",
  46. "Sec-Fetch-Site: same-site"];
  47. break;
  48. }
  49. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  50. curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
  51. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  52. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  53. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  54. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  55. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  56. $data = curl_exec($curlproc);
  57. if(curl_errno($curlproc)){
  58. throw new Exception(curl_error($curlproc));
  59. }
  60. curl_close($curlproc);
  61. return $data;
  62. }
  63. public function getfilters($pagetype){
  64. switch($pagetype){
  65. case "web":
  66. return
  67. [
  68. "country" => [
  69. "display" => "Country",
  70. "option" => [
  71. "any" => "All Regions",
  72. "ar-es" => "Argentina",
  73. "au-en" => "Australia",
  74. "at-de" => "Austria",
  75. "be-fr" => "Belgium (fr)",
  76. "be-nl" => "Belgium (nl)",
  77. "br-pt" => "Brazil",
  78. "bg-bg" => "Bulgaria",
  79. "ca-en" => "Canada (en)",
  80. "ca-fr" => "Canada (fr)",
  81. "ct-ca" => "Catalonia",
  82. "cl-es" => "Chile",
  83. "cn-zh" => "China",
  84. "co-es" => "Colombia",
  85. "hr-hr" => "Croatia",
  86. "cz-cs" => "Czech Republic",
  87. "dk-da" => "Denmark",
  88. "ee-et" => "Estonia",
  89. "fi-fi" => "Finland",
  90. "fr-fr" => "France",
  91. "de-de" => "Germany",
  92. "gr-el" => "Greece",
  93. "hk-tzh" => "Hong Kong",
  94. "hu-hu" => "Hungary",
  95. "in-en" => "India (en)",
  96. "id-en" => "Indonesia (en)",
  97. "ie-en" => "Ireland",
  98. "il-en" => "Israel (en)",
  99. "it-it" => "Italy",
  100. "jp-jp" => "Japan",
  101. "kr-kr" => "Korea",
  102. "lv-lv" => "Latvia",
  103. "lt-lt" => "Lithuania",
  104. "my-en" => "Malaysia (en)",
  105. "mx-es" => "Mexico",
  106. "nl-nl" => "Netherlands",
  107. "nz-en" => "New Zealand",
  108. "no-no" => "Norway",
  109. "pk-en" => "Pakistan (en)",
  110. "pe-es" => "Peru",
  111. "ph-en" => "Philippines (en)",
  112. "pl-pl" => "Poland",
  113. "pt-pt" => "Portugal",
  114. "ro-ro" => "Romania",
  115. "ru-ru" => "Russia",
  116. "xa-ar" => "Saudi Arabia",
  117. "sg-en" => "Singapore",
  118. "sk-sk" => "Slovakia",
  119. "sl-sl" => "Slovenia",
  120. "za-en" => "South Africa",
  121. "es-ca" => "Spain (ca)",
  122. "es-es" => "Spain (es)",
  123. "se-sv" => "Sweden",
  124. "ch-de" => "Switzerland (de)",
  125. "ch-fr" => "Switzerland (fr)",
  126. "tw-tzh" => "Taiwan",
  127. "th-en" => "Thailand (en)",
  128. "tr-tr" => "Turkey",
  129. "us-en" => "US (English)",
  130. "us-es" => "US (Spanish)",
  131. "ua-uk" => "Ukraine",
  132. "uk-en" => "United Kingdom",
  133. "vn-en" => "Vietnam (en)"
  134. ]
  135. ],
  136. "nsfw" => [
  137. "display" => "NSFW",
  138. "option" => [
  139. "yes" => "Yes",
  140. "maybe" => "Maybe",
  141. "no" => "No"
  142. ]
  143. ],
  144. "newer" => [
  145. "display" => "Newer than",
  146. "option" => "_DATE"
  147. ],
  148. "older" => [
  149. "display" => "Older than",
  150. "option" => "_DATE"
  151. ],
  152. "extendedsearch" => [
  153. // undefined display, so it wont show in frontend
  154. "option" => [
  155. "yes" => "Yes",
  156. "no" => "No"
  157. ]
  158. ]
  159. ];
  160. break;
  161. case "images":
  162. return
  163. [
  164. "country" => [
  165. "display" => "Country",
  166. "option" => [
  167. "us-en" => "US (English)",
  168. "ar-es" => "Argentina",
  169. "au-en" => "Australia",
  170. "at-de" => "Austria",
  171. "be-fr" => "Belgium (fr)",
  172. "be-nl" => "Belgium (nl)",
  173. "br-pt" => "Brazil",
  174. "bg-bg" => "Bulgaria",
  175. "ca-en" => "Canada (en)",
  176. "ca-fr" => "Canada (fr)",
  177. "ct-ca" => "Catalonia",
  178. "cl-es" => "Chile",
  179. "cn-zh" => "China",
  180. "co-es" => "Colombia",
  181. "hr-hr" => "Croatia",
  182. "cz-cs" => "Czech Republic",
  183. "dk-da" => "Denmark",
  184. "ee-et" => "Estonia",
  185. "fi-fi" => "Finland",
  186. "fr-fr" => "France",
  187. "de-de" => "Germany",
  188. "gr-el" => "Greece",
  189. "hk-tzh" => "Hong Kong",
  190. "hu-hu" => "Hungary",
  191. "in-en" => "India (en)",
  192. "id-en" => "Indonesia (en)",
  193. "ie-en" => "Ireland",
  194. "il-en" => "Israel (en)",
  195. "it-it" => "Italy",
  196. "jp-jp" => "Japan",
  197. "kr-kr" => "Korea",
  198. "lv-lv" => "Latvia",
  199. "lt-lt" => "Lithuania",
  200. "my-en" => "Malaysia (en)",
  201. "mx-es" => "Mexico",
  202. "nl-nl" => "Netherlands",
  203. "nz-en" => "New Zealand",
  204. "no-no" => "Norway",
  205. "pk-en" => "Pakistan (en)",
  206. "pe-es" => "Peru",
  207. "ph-en" => "Philippines (en)",
  208. "pl-pl" => "Poland",
  209. "pt-pt" => "Portugal",
  210. "ro-ro" => "Romania",
  211. "ru-ru" => "Russia",
  212. "xa-ar" => "Saudi Arabia",
  213. "sg-en" => "Singapore",
  214. "sk-sk" => "Slovakia",
  215. "sl-sl" => "Slovenia",
  216. "za-en" => "South Africa",
  217. "es-ca" => "Spain (ca)",
  218. "es-es" => "Spain (es)",
  219. "se-sv" => "Sweden",
  220. "ch-de" => "Switzerland (de)",
  221. "ch-fr" => "Switzerland (fr)",
  222. "tw-tzh" => "Taiwan",
  223. "th-en" => "Thailand (en)",
  224. "tr-tr" => "Turkey",
  225. "us-es" => "US (Spanish)",
  226. "ua-uk" => "Ukraine",
  227. "uk-en" => "United Kingdom",
  228. "vn-en" => "Vietnam (en)"
  229. ]
  230. ],
  231. "nsfw" => [
  232. "display" => "NSFW",
  233. "option" => [
  234. "yes" => "Yes",
  235. "no" => "No"
  236. ]
  237. ],
  238. "date" => [
  239. "display" => "Time posted",
  240. "option" => [
  241. "any" => "Any time",
  242. "Day" => "Past day",
  243. "Week" => "Past week",
  244. "Month" => "Past month"
  245. ]
  246. ],
  247. "size" => [
  248. "display" => "Size",
  249. "option" => [
  250. "any" => "Any size",
  251. "Small" => "Small",
  252. "Medium" => "Medium",
  253. "Large" => "Large",
  254. "Wallpaper" => "Wallpaper"
  255. ]
  256. ],
  257. "color" => [
  258. "display" => "Colors",
  259. "option" => [
  260. "any" => "All colors",
  261. "Monochrome" => "Black and white",
  262. "Red" => "Red",
  263. "Orange" => "Orange",
  264. "Yellow" => "Yellow",
  265. "Green" => "Green",
  266. "Blue" => "Blue",
  267. "Purple" => "Purple",
  268. "Pink" => "Pink",
  269. "Brown" => "Brown",
  270. "Black" => "Black",
  271. "Gray" => "Gray",
  272. "Teal" => "Teal",
  273. "White" => "White"
  274. ]
  275. ],
  276. "type" => [
  277. "display" => "Type",
  278. "option" => [
  279. "any" => "All types",
  280. "photo" => "Photograph",
  281. "clipart" => "Clipart",
  282. "gif" => "Animated GIF",
  283. "transparent" => "Transparent"
  284. ]
  285. ],
  286. "layout" => [
  287. "display" => "Layout",
  288. "option" => [
  289. "any" => "All layouts",
  290. "Square" => "Square",
  291. "Tall" => "Tall",
  292. "Wide" => "Wide"
  293. ]
  294. ],
  295. "license" => [
  296. "display" => "License",
  297. "option" => [
  298. "any" => "All licenses", // blame ddg for this
  299. "Any" => "All Creative Commons",
  300. "Public" => "Public domain",
  301. "Share" => "Free to Share and Use",
  302. "ShareCommercially" => "Free to Share and Use Commercially",
  303. "Modify" => "Free to Modify, Share, and Use",
  304. "ModifyCommercially" => "Free to Modify, Share, and Use Commercially"
  305. ]
  306. ]
  307. ];
  308. break;
  309. case "videos":
  310. return
  311. [
  312. "country" => [
  313. "display" => "Country",
  314. "option" => [
  315. "us-en" => "US (English)",
  316. "ar-es" => "Argentina",
  317. "au-en" => "Australia",
  318. "at-de" => "Austria",
  319. "be-fr" => "Belgium (fr)",
  320. "be-nl" => "Belgium (nl)",
  321. "br-pt" => "Brazil",
  322. "bg-bg" => "Bulgaria",
  323. "ca-en" => "Canada (en)",
  324. "ca-fr" => "Canada (fr)",
  325. "ct-ca" => "Catalonia",
  326. "cl-es" => "Chile",
  327. "cn-zh" => "China",
  328. "co-es" => "Colombia",
  329. "hr-hr" => "Croatia",
  330. "cz-cs" => "Czech Republic",
  331. "dk-da" => "Denmark",
  332. "ee-et" => "Estonia",
  333. "fi-fi" => "Finland",
  334. "fr-fr" => "France",
  335. "de-de" => "Germany",
  336. "gr-el" => "Greece",
  337. "hk-tzh" => "Hong Kong",
  338. "hu-hu" => "Hungary",
  339. "in-en" => "India (en)",
  340. "id-en" => "Indonesia (en)",
  341. "ie-en" => "Ireland",
  342. "il-en" => "Israel (en)",
  343. "it-it" => "Italy",
  344. "jp-jp" => "Japan",
  345. "kr-kr" => "Korea",
  346. "lv-lv" => "Latvia",
  347. "lt-lt" => "Lithuania",
  348. "my-en" => "Malaysia (en)",
  349. "mx-es" => "Mexico",
  350. "nl-nl" => "Netherlands",
  351. "nz-en" => "New Zealand",
  352. "no-no" => "Norway",
  353. "pk-en" => "Pakistan (en)",
  354. "pe-es" => "Peru",
  355. "ph-en" => "Philippines (en)",
  356. "pl-pl" => "Poland",
  357. "pt-pt" => "Portugal",
  358. "ro-ro" => "Romania",
  359. "ru-ru" => "Russia",
  360. "xa-ar" => "Saudi Arabia",
  361. "sg-en" => "Singapore",
  362. "sk-sk" => "Slovakia",
  363. "sl-sl" => "Slovenia",
  364. "za-en" => "South Africa",
  365. "es-ca" => "Spain (ca)",
  366. "es-es" => "Spain (es)",
  367. "se-sv" => "Sweden",
  368. "ch-de" => "Switzerland (de)",
  369. "ch-fr" => "Switzerland (fr)",
  370. "tw-tzh" => "Taiwan",
  371. "th-en" => "Thailand (en)",
  372. "tr-tr" => "Turkey",
  373. "us-en" => "US (English)",
  374. "us-es" => "US (Spanish)",
  375. "ua-uk" => "Ukraine",
  376. "uk-en" => "United Kingdom",
  377. "vn-en" => "Vietnam (en)"
  378. ]
  379. ],
  380. "nsfw" => [
  381. "display" => "NSFW",
  382. "option" => [
  383. "yes" => "Yes",
  384. "no" => "No"
  385. ]
  386. ],
  387. "date" => [
  388. "display" => "Time fetched",
  389. "option" => [
  390. "any" => "Any time",
  391. "d" => "Past day",
  392. "w" => "Past week",
  393. "m" => "Past month"
  394. ]
  395. ],
  396. "resolution" => [ //videoDefinition
  397. "display" => "Resolution",
  398. "option" => [
  399. "any" => "Any resolution",
  400. "high" => "High definition",
  401. "standard" => "Standard definition"
  402. ]
  403. ],
  404. "duration" => [ // videoDuration
  405. "display" => "Duration",
  406. "option" => [
  407. "any" => "Any duration",
  408. "short" => "Short (>5min)",
  409. "medium" => "Medium (5-20min)",
  410. "long" => "Long (<20min)"
  411. ]
  412. ],
  413. "license" => [
  414. "display" => "License",
  415. "option" => [
  416. "any" => "Any license",
  417. "creativeCommon" => "Creative Commons",
  418. "youtube" => "YouTube Standard"
  419. ]
  420. ]
  421. ];
  422. break;
  423. case "news":
  424. return
  425. [
  426. "country" => [
  427. "display" => "Country",
  428. "option" => [
  429. "us-en" => "US (English)",
  430. "ar-es" => "Argentina",
  431. "au-en" => "Australia",
  432. "at-de" => "Austria",
  433. "be-fr" => "Belgium (fr)",
  434. "be-nl" => "Belgium (nl)",
  435. "br-pt" => "Brazil",
  436. "bg-bg" => "Bulgaria",
  437. "ca-en" => "Canada (en)",
  438. "ca-fr" => "Canada (fr)",
  439. "ct-ca" => "Catalonia",
  440. "cl-es" => "Chile",
  441. "cn-zh" => "China",
  442. "co-es" => "Colombia",
  443. "hr-hr" => "Croatia",
  444. "cz-cs" => "Czech Republic",
  445. "dk-da" => "Denmark",
  446. "ee-et" => "Estonia",
  447. "fi-fi" => "Finland",
  448. "fr-fr" => "France",
  449. "de-de" => "Germany",
  450. "gr-el" => "Greece",
  451. "hk-tzh" => "Hong Kong",
  452. "hu-hu" => "Hungary",
  453. "in-en" => "India (en)",
  454. "id-en" => "Indonesia (en)",
  455. "ie-en" => "Ireland",
  456. "il-en" => "Israel (en)",
  457. "it-it" => "Italy",
  458. "jp-jp" => "Japan",
  459. "kr-kr" => "Korea",
  460. "lv-lv" => "Latvia",
  461. "lt-lt" => "Lithuania",
  462. "my-en" => "Malaysia (en)",
  463. "mx-es" => "Mexico",
  464. "nl-nl" => "Netherlands",
  465. "nz-en" => "New Zealand",
  466. "no-no" => "Norway",
  467. "pk-en" => "Pakistan (en)",
  468. "pe-es" => "Peru",
  469. "ph-en" => "Philippines (en)",
  470. "pl-pl" => "Poland",
  471. "pt-pt" => "Portugal",
  472. "ro-ro" => "Romania",
  473. "ru-ru" => "Russia",
  474. "xa-ar" => "Saudi Arabia",
  475. "sg-en" => "Singapore",
  476. "sk-sk" => "Slovakia",
  477. "sl-sl" => "Slovenia",
  478. "za-en" => "South Africa",
  479. "es-ca" => "Spain (ca)",
  480. "es-es" => "Spain (es)",
  481. "se-sv" => "Sweden",
  482. "ch-de" => "Switzerland (de)",
  483. "ch-fr" => "Switzerland (fr)",
  484. "tw-tzh" => "Taiwan",
  485. "th-en" => "Thailand (en)",
  486. "tr-tr" => "Turkey",
  487. "us-en" => "US (English)",
  488. "us-es" => "US (Spanish)",
  489. "ua-uk" => "Ukraine",
  490. "uk-en" => "United Kingdom",
  491. "vn-en" => "Vietnam (en)"
  492. ]
  493. ],
  494. "nsfw" => [
  495. "display" => "NSFW",
  496. "option" => [
  497. "yes" => "Yes",
  498. "maybe" => "Maybe",
  499. "no" => "No"
  500. ]
  501. ],
  502. "date" => [
  503. "display" => "Time posted",
  504. "option" => [
  505. "any" => "Any time",
  506. "d" => "Past day",
  507. "w" => "Past week",
  508. "m" => "Past month"
  509. ]
  510. ]
  511. ];
  512. break;
  513. default:
  514. return [];
  515. break;
  516. }
  517. }
  518. public function web($get){
  519. if($get["npt"]){
  520. $jsgrep = $this->nextpage->get($get["npt"], "web");
  521. $extendedsearch = false;
  522. $inithtml = "";
  523. }else{
  524. $search = $get["s"];
  525. if(strlen($search) === 0){
  526. throw new Exception("Search term is empty!");
  527. }
  528. $country = $get["country"];
  529. $nsfw = $get["nsfw"];
  530. $older = $get["older"];
  531. $newer = $get["newer"];
  532. $extendedsearch = $get["extendedsearch"] == "yes" ? true : false;
  533. // generate filters
  534. $get_filters = [
  535. "q" => $search,
  536. "kz" => "1" // force instant answers
  537. ];
  538. if($country == "any"){
  539. $get_filters["kl"] = "wt-wt";
  540. }else{
  541. $get_filters["kl"] = $country;
  542. }
  543. switch($nsfw){
  544. case "yes": $get_filters["kp"] = "-2"; break;
  545. case "maybe": $get_filters["kp"] = "-1"; break;
  546. case "no": $get_filters["kp"] = "1"; break;
  547. }
  548. $df = true;
  549. if($newer === false){
  550. if($older !== false){
  551. $start = 36000;
  552. $end = $older;
  553. }else{
  554. $df = false;
  555. }
  556. }else{
  557. $start = $newer;
  558. if($older !== false){
  559. $end = $older;
  560. }else{
  561. $end = time();
  562. }
  563. }
  564. if($df === true){
  565. $get_filters["df"] = date("Y-m-d", $start) . ".." . date("Y-m-d", $end);
  566. }
  567. /*
  568. Get html
  569. */
  570. // https://duckduckgo.com/?q=minecraft&kz=1&k1=-1&kp=-2
  571. try{
  572. $inithtml = $this->get(
  573. "https://duckduckgo.com/",
  574. $get_filters
  575. );
  576. }catch(Exception $e){
  577. throw new Exception("Failed to get html");
  578. }
  579. preg_match(
  580. '/DDG\.deep\.initialize\(\'(.*)\',/U',
  581. $inithtml,
  582. $jsgrep
  583. );
  584. if(!isset($jsgrep[1])){
  585. throw new Exception("Failed to get d.js URL");
  586. }
  587. $jsgrep = $jsgrep[1];
  588. }
  589. // get javascript
  590. try{
  591. $js = $this->get(
  592. "https://links.duckduckgo.com" . $jsgrep,
  593. [],
  594. ddg::req_xhr
  595. );
  596. }catch(Exception $e){
  597. throw new Exception("Failed to fetch d.js");
  598. }
  599. // initialize api response array
  600. $out = [
  601. "status" => "ok",
  602. "spelling" => [
  603. "type" => "no_correction",
  604. "using" => null,
  605. "correction" => null
  606. ],
  607. "npt" => null,
  608. "answer" => [],
  609. "web" => [],
  610. "image" => [],
  611. "video" => [],
  612. "news" => [],
  613. "related" => []
  614. ];
  615. /*
  616. Additional requests
  617. */
  618. if($extendedsearch){
  619. /*
  620. Check for worknik results
  621. */
  622. preg_match(
  623. '/nrj\(\'\/js\/spice\/dictionary\/definition\/([^\']+)\'\)/',
  624. $js,
  625. $wordnik
  626. );
  627. if(isset($wordnik[1])){
  628. try{
  629. $wordnik = $wordnik[1];
  630. // get definition
  631. $wordnikjs = $this->get(
  632. "https://duckduckgo.com/js/spice/dictionary/definition/" . $wordnik,
  633. [],
  634. ddg::req_xhr
  635. );
  636. preg_match(
  637. '/ddg_spice_dictionary_definition\(\n?(\[{[\S\s]*}])/',
  638. $wordnikjs,
  639. $wordnikjson
  640. );
  641. if(isset($wordnikjson[1])){
  642. $wordnikjson = json_decode($wordnikjson[1], true);
  643. $out["answer"][0] = [
  644. "title" => urldecode($wordnik),
  645. "description" => [],
  646. "url" => "https://www.wordnik.com/words/" . $wordnik,
  647. "thumb" => null,
  648. "table" => [],
  649. "sublink" => []
  650. ];
  651. $partofspeech = false;
  652. $wastext = false;
  653. $textindent = 1;
  654. // get audio
  655. $wordnikaudio_json =
  656. json_decode(
  657. $this->get(
  658. "https://duckduckgo.com/js/spice/dictionary/audio/" . $wordnik,
  659. [],
  660. ddg::req_xhr
  661. ),
  662. true
  663. );
  664. if(isset($wordnikaudio_json[0]["id"])){
  665. usort($wordnikaudio_json, function($a, $b){
  666. return $a["id"] < $b["id"];
  667. });
  668. $out["answer"][0]["description"][] = [
  669. "type" => "audio",
  670. "url" => $wordnikaudio_json[0]["fileUrl"]
  671. ];
  672. }
  673. $collection = [];
  674. $e[] = [];
  675. foreach($wordnikjson as $data){
  676. if(!isset($data["partOfSpeech"])){
  677. continue;
  678. }
  679. if(isset($data["text"])){
  680. if(!isset($collection[$data["partOfSpeech"]])){
  681. $collection[$data["partOfSpeech"]] = [];
  682. $c = 0;
  683. }else{
  684. $c = count($collection[$data["partOfSpeech"]]);
  685. }
  686. if(!isset($e[$data["partOfSpeech"]])){
  687. $e[$data["partOfSpeech"]] = 0;
  688. }
  689. $e[$data["partOfSpeech"]]++;
  690. $text = $e[$data["partOfSpeech"]] . ". " . $this->unescapehtml(strip_tags($data["text"]));
  691. $syn = false;
  692. if(
  693. isset($data["relatedWords"]) &&
  694. count($data["relatedWords"]) !== 0
  695. ){
  696. $syn = " (";
  697. $u = 0;
  698. foreach($data["relatedWords"] as $related){
  699. $syn .= ucfirst($related["relationshipType"]) . ": ";
  700. $c = count($related["words"]);
  701. $b = 0;
  702. foreach($related["words"] as $word){
  703. $syn .= trim($this->unescapehtml(strip_tags($word)));
  704. $b++;
  705. if($b !== $c){
  706. $syn .= ", ";
  707. }
  708. }
  709. $u++;
  710. if($u !== count($data["relatedWords"])){
  711. $syn .= ". ";
  712. }
  713. }
  714. $syn .= ")";
  715. }
  716. if(
  717. $c !== 0 &&
  718. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text"
  719. ){
  720. $collection[$data["partOfSpeech"]][$c - 1]["value"] .=
  721. "\n" . $text;
  722. }else{
  723. if(
  724. $c !== 0 &&
  725. (
  726. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text" ||
  727. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "italic"
  728. )
  729. ){
  730. $text = "\n" . $text;
  731. }
  732. $collection[$data["partOfSpeech"]][] =
  733. [
  734. "type" => "text",
  735. "value" => $text
  736. ];
  737. }
  738. if($syn){
  739. $collection[$data["partOfSpeech"]][] = [
  740. "type" => "italic",
  741. "value" => $syn
  742. ];
  743. }
  744. if(isset($data["exampleUses"])){
  745. foreach($data["exampleUses"] as $use){
  746. $collection[$data["partOfSpeech"]][] = [
  747. "type" => "quote",
  748. "value" => $this->unescapehtml(strip_tags($use["text"]))
  749. ];
  750. }
  751. }
  752. if(isset($data["citations"])){
  753. foreach($data["citations"] as $citation){
  754. if(!isset($citation["cite"])){
  755. continue;
  756. }
  757. $value = $this->unescapehtml(strip_tags($citation["cite"]));
  758. if(
  759. isset($citation["source"]) &&
  760. trim($citation["source"]) != ""
  761. ){
  762. $value .= " - " . $this->unescapehtml(strip_tags($citation["source"]));
  763. }
  764. $collection[$data["partOfSpeech"]][] = [
  765. "type" => "quote",
  766. "value" => $value
  767. ];
  768. }
  769. }
  770. }
  771. }
  772. foreach($collection as $key => $items){
  773. $out["answer"][0]["description"][] =
  774. [
  775. "type" => "title",
  776. "value" => $key
  777. ];
  778. $out["answer"][0]["description"] =
  779. array_merge($out["answer"][0]["description"], $items);
  780. }
  781. }
  782. }catch(Exception $e){
  783. // do nothing
  784. }
  785. }
  786. unset($wordnik);
  787. /*
  788. Check for stackoverflow answers
  789. */
  790. // /a.js?p=1&src_id=stack_overflow&from=nlp_qa&id=3390396,2559318&q=how%20can%20i%20check%20for%20undefined%20in%20javascript&s=stackoverflow.com&tl=How%20can%20I%20check%20for%20%22undefined%22%20in%20JavaScript%3F%20%2D%20Stack%20Overflow
  791. // /a.js?p=1&src_id=arqade&from=nlp_qa&id=370293,375682&q=what%20is%20the%20difference%20between%20at%20and%20positioned%20in%20execute&s=gaming.stackexchange.com&tl=minecraft%20java%20edition%20minecraft%20commands%20%2D%20What%20is%20the%20difference
  792. // /a.js?p=1&src_id=unix&from=nlp_qa&id=312754&q=how%20to%20strip%20metadata%20from%20image%20files&s=unix.stackexchange.com&tl=How%20to%20strip%20metadata%20from%20image%20files%20%2D%20Unix%20%26%20Linux%20Stack%20Exchange
  793. preg_match(
  794. '/nrj\(\'(\/a\.js\?.*from=nlp_qa.*)\'\)/U',
  795. $js,
  796. $stack
  797. );
  798. if(isset($stack[1])){
  799. $stack = $stack[1];
  800. try{
  801. $stackjs = $this->get(
  802. "https://duckduckgo.com" . $stack,
  803. [],
  804. ddg::req_xhr
  805. );
  806. if(
  807. !preg_match(
  808. '/^DDG\.duckbar\.failed/',
  809. $stackjs
  810. )
  811. ){
  812. preg_match(
  813. '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U',
  814. $stackjs,
  815. $stackjson
  816. );
  817. $stackjson = json_decode($stackjson[1], true)[0]["data"][0];
  818. $out["answer"][] = [
  819. "title" => $stackjson["Heading"],
  820. "description" => $this->htmltoarray($stackjson["Abstract"]),
  821. "url" => str_replace(["http://", "ddg"], ["https://", ""], $stackjson["AbstractURL"]),
  822. "thumb" => null,
  823. "table" => [],
  824. "sublink" => []
  825. ];
  826. }
  827. }catch(Exception $e){
  828. // do nothing
  829. }
  830. }
  831. /*
  832. Check for musicmatch (lyrics)
  833. */
  834. preg_match(
  835. '/nrj\(\'(\/a\.js\?.*&s=lyrics.*)\'\)/U',
  836. $js,
  837. $lyrics
  838. );
  839. if(isset($lyrics[1])){
  840. $lyrics = $lyrics[1];
  841. try{
  842. $lyricsjs = $this->get(
  843. "https://duckduckgo.com" . $lyrics,
  844. [],
  845. ddg::req_xhr
  846. );
  847. if(
  848. !preg_match(
  849. '/^DDG\.duckbar\.failed/',
  850. $lyricsjs
  851. )
  852. ){
  853. preg_match(
  854. '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U',
  855. $lyricsjs,
  856. $lyricsjson
  857. );
  858. $lyricsjson = json_decode($lyricsjson[1], true)[0]["data"][0];
  859. $title = null;
  860. if(isset($lyricsjson["Heading"])){
  861. $title = $lyricsjson["Heading"];
  862. }elseif(isset($lyricsjson["data"][1]["urlTitle"])){
  863. $title = $lyricsjson["data"][1]["urlTitle"];
  864. }else{
  865. $title = $lyricsjson["data"][0]["song_title"];
  866. }
  867. $description = [
  868. [
  869. "type" => "text",
  870. "value" => null
  871. ]
  872. ];
  873. $parts =
  874. explode(
  875. "<br>",
  876. str_ireplace(
  877. ["<br>", "</br>", "<br/>"],
  878. "<br>",
  879. $lyricsjson["Abstract"]
  880. ),
  881. );
  882. for($i=0; $i<count($parts); $i++){
  883. $description[0]["value"] .= trim($parts[$i]) . "\n";
  884. }
  885. $description[0]["value"] = trim($description[0]["value"]);
  886. $description[] =
  887. [
  888. "type" => "quote",
  889. "value" =>
  890. "Written by " . implode(", ", $lyricsjson["data"][0]["writers"]) .
  891. "\nFrom the album " . $lyricsjson["data"][0]["albums"][0]["title"] .
  892. "\nReleased on the " . date("jS \of F Y", strtotime($lyricsjson["data"][0]["albums"][0]["release_date"]))
  893. ];
  894. $out["answer"][] = [
  895. "title" => $title,
  896. "description" => $description,
  897. "url" => $lyricsjson["AbstractURL"],
  898. "thumb" => null,
  899. "table" => [],
  900. "sublink" => []
  901. ];
  902. }
  903. }catch(Exception $e){
  904. // do nothing
  905. }
  906. }
  907. }
  908. /*
  909. Get related searches
  910. */
  911. preg_match(
  912. '/DDG\.duckbar\.loadModule\(\'related_searches\', ?{[\s\S]*"results":(\[{[\s\S]*}]),"vqd"/U',
  913. $js,
  914. $related
  915. );
  916. if(isset($related[1])){
  917. try{
  918. $related = json_decode($related[1], true);
  919. for($i=0; $i<count($related); $i++){
  920. if(isset($related[$i]["text"])){
  921. array_push($out["related"], $related[$i]["text"]);
  922. }
  923. }
  924. }catch(Exception $e){
  925. // do nothing
  926. }
  927. }
  928. unset($related);
  929. /*
  930. Get answers
  931. */
  932. $answer_count = preg_match_all(
  933. '/DDG\.duckbar\.add\(({.*[\S\s]*})(?:\);|,null,"index"\))/U',
  934. $js . $inithtml,
  935. $answers
  936. );
  937. try{
  938. if(isset($answers[1])){
  939. $answers = $answers[1];
  940. for($i=0; $i<$answer_count; $i++){
  941. $answers[$i] = json_decode($answers[$i], true);
  942. // remove dupes
  943. for($k=0; $k<count($out["answer"]); $k++){
  944. if(
  945. !isset($answers[$i]["data"]["AbstractURL"]) ||
  946. str_replace("_", "%20", $out["answer"][$k]["url"]) == str_replace("_", "%20", $this->sanitizeurl($answers[$i]["data"]["AbstractURL"]))
  947. ){
  948. continue 2;
  949. }
  950. }
  951. // get more related queries
  952. if(
  953. isset($answers[$i]["data"]["RelatedTopics"]) &&
  954. $answers[$i]["data"]["RelatedTopics"] != 0
  955. ){
  956. for($k=0; $k<count($answers[$i]["data"]["RelatedTopics"]); $k++){
  957. if(isset($answers[$i]["data"]["RelatedTopics"][$k]["Result"])){
  958. preg_match(
  959. '/">(.*)<\//',
  960. $answers[$i]["data"]["RelatedTopics"][$k]["Result"],
  961. $label
  962. );
  963. array_push($out["related"], htmlspecialchars_decode(strip_tags($label[1])));
  964. }
  965. }
  966. }
  967. $image = null;
  968. // get image
  969. if(
  970. isset($answers[$i]["data"]["Image"]) &&
  971. !empty($answers[$i]["data"]["Image"]) &&
  972. $answers[$i]["data"]["Image"] != "https://duckduckgo.com/i/"
  973. ){
  974. if(strpos($answers[$i]["data"]["Image"], "https://duckduckgo.com/i/") === true){
  975. $image = $answers[$i]["data"]["Image"];
  976. }else{
  977. if(
  978. strlen($answers[$i]["data"]["Image"]) > 0 &&
  979. $answers[$i]["data"]["Image"][0] == "/"
  980. ){
  981. $answers[$i]["data"]["Image"] = substr($answers[$i]["data"]["Image"], 1);
  982. }
  983. $image = "https://duckduckgo.com/" . $answers[$i]["data"]["Image"];
  984. }
  985. }
  986. $count = count($out["answer"]);
  987. if(isset($answers[$i]["data"]["AbstractText"]) && !empty($answers[$i]["data"]["AbstractText"])){
  988. $description = $this->htmltoarray($answers[$i]["data"]["AbstractText"]);
  989. }elseif(isset($answers[$i]["data"]["Abstract"]) && !empty($answers[$i]["data"]["Abstract"])){
  990. $description = $this->htmltoarray($answers[$i]["data"]["Abstract"]);
  991. }elseif(isset($answers[$i]["data"]["Answer"]) && !empty($answers[$i]["data"]["Answer"])){
  992. $description = $this->htmltoarray($answers[$i]["data"]["Answer"]);
  993. }else{
  994. $description = [];
  995. }
  996. if(isset($answers[$i]["data"]["Heading"]) && !empty($answers[$i]["data"]["Heading"])){
  997. $title = $this->unescapehtml($answers[$i]["data"]["Heading"]);
  998. }else{
  999. // no title, ignore bs
  1000. continue;
  1001. //$title = null;
  1002. }
  1003. if(isset($answers[$i]["data"]["AbstractURL"]) && !empty($answers[$i]["data"]["AbstractURL"])){
  1004. $url = $answers[$i]["data"]["AbstractURL"];
  1005. }else{
  1006. $url = null;
  1007. }
  1008. $out["answer"][$count] = [
  1009. "title" => $title,
  1010. "description" => $description,
  1011. "url" => $this->sanitizeurl($url),
  1012. "thumb" => $image,
  1013. "table" => [],
  1014. "sublink" => []
  1015. ];
  1016. if(isset($answers[$i]["data"]["Infobox"]["content"])){
  1017. for($k=0; $k<count($answers[$i]["data"]["Infobox"]["content"]); $k++){
  1018. // populate table
  1019. if($answers[$i]["data"]["Infobox"]["content"][$k]["data_type"] == "string"){
  1020. $out["answer"][$count]["table"][$answers[$i]["data"]["Infobox"]["content"][$k]["label"]] =
  1021. $answers[$i]["data"]["Infobox"]["content"][$k]["value"];
  1022. continue;
  1023. }
  1024. $url = "";
  1025. $type = "Website";
  1026. switch($answers[$i]["data"]["Infobox"]["content"][$k]["data_type"]){
  1027. case "official_site":
  1028. case "official_website":
  1029. $type = "Website";
  1030. break;
  1031. case "wikipedia": $type = "Wikipedia"; break;
  1032. case "itunes": $type = "iTunes"; break;
  1033. case "amazon": $type = "Amazon"; break;
  1034. case "imdb_title_id":
  1035. case "imdb_id":
  1036. case "imdb_name_id":
  1037. $type = "IMDb";
  1038. $delim = substr($answers[$i]["data"]["Infobox"]["content"][$k]["value"], 0, 2);
  1039. if($delim == "nm"){
  1040. $url = "https://www.imdb.com/name/";
  1041. }elseif($delim == "tt"){
  1042. $url = "https://www.imdb.com/title/";
  1043. }elseif($delim == "co"){
  1044. $url = "https://www.imdb.com/search/title/?companies=";
  1045. }else{
  1046. $url = "https://www.imdb.com/title/";
  1047. }
  1048. break;
  1049. case "imdb_name_id": $url = "https://www.imdb.com/name/"; $type = "IMDb"; break;
  1050. case "twitter_profile": $url = "https://twitter.com/"; $type = "Twitter"; break;
  1051. case "instagram_profile": $url = "https://instagram.com/"; $type = "Instagram"; break;
  1052. case "facebook_profile": $url = "https://facebook.com/"; $type = "Facebook"; break;
  1053. case "spotify_artist_id": $url = "https://open.spotify.com/artist/"; $type = "Spotify"; break;
  1054. case "rotten_tomatoes": $url = "https://rottentomatoes.com/"; $type = "Rotten Tomatoes"; break;
  1055. case "youtube_channel": $url = "https://youtube.com/channel/"; $type = "YouTube"; break;
  1056. case "soundcloud_id": $url = "https://soundcloud.com/"; $type = "SoundCloud"; break;
  1057. default:
  1058. continue 2;
  1059. }
  1060. // populate sublinks
  1061. $out["answer"][$count]["sublink"][$type] =
  1062. $url . $answers[$i]["data"]["Infobox"]["content"][$k]["value"];
  1063. }
  1064. }
  1065. }
  1066. }
  1067. }catch(Exception $e){
  1068. // do nothing
  1069. }
  1070. /*
  1071. Get shitcoin conversions
  1072. */
  1073. if($extendedsearch){
  1074. if(
  1075. preg_match(
  1076. '/"https?:\/\/(?:www\.coinbase\.com\/converter\/([a-z0-9]+)\/([a-z0-9]+)|changelly\.com\/exchange\/([a-z0-9]+)\/([a-z0-9]+)|coinmarketcap\.com\/currencies\/[a-z0-9]+\/([a-z0-9]+)\/([a-z0-9]+))\/?"/',
  1077. $js,
  1078. $shitcoins
  1079. )
  1080. ){
  1081. $shitcoins = array_values(array_filter($shitcoins));
  1082. preg_match(
  1083. '/(?:[\s,.]*[0-9]+)+/',
  1084. $search,
  1085. $amount
  1086. );
  1087. if(count($amount) === 1){
  1088. $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]);
  1089. }else{
  1090. $amount = 1;
  1091. }
  1092. try{
  1093. $description = [];
  1094. $shitcoinjs = $this->get(
  1095. "https://duckduckgo.com/js/spice/cryptocurrency/{$shitcoins[1]}/{$shitcoins[2]}/1",
  1096. [],
  1097. ddg::req_xhr
  1098. );
  1099. preg_match(
  1100. '/ddg_spice_cryptocurrency\(\s*({[\S\s]*})\s*\);/',
  1101. $shitcoinjs,
  1102. $shitcoinjson
  1103. );
  1104. $shitcoinjson = json_decode($shitcoinjson[1], true);
  1105. if(
  1106. !isset($shitcoinjson["error"]) &&
  1107. $shitcoinjson["status"]["error_code"] == 0
  1108. ){
  1109. $shitcoinjson = $shitcoinjson["data"];
  1110. $array_values = array_values($shitcoinjson["quote"])[0];
  1111. if($amount != 1){
  1112. // show conversion
  1113. $description[] = [
  1114. "type" => "title",
  1115. "value" => "Conversion"
  1116. ];
  1117. $description[] = [
  1118. "type" => "text",
  1119. "value" =>
  1120. "{$amount} {$shitcoinjson["name"]} ({$shitcoinjson["symbol"]}) = " . $this->number_format($array_values["price"] * $amount) . " " . strtoupper($shitcoins[2]) . "\n" .
  1121. "{$amount} " . strtoupper($shitcoins[2]) . " = " . $this->number_format((1 / $array_values["price"]) * $amount) . " {$shitcoinjson["symbol"]}"
  1122. ];
  1123. }
  1124. $description[] = [
  1125. "type" => "title",
  1126. "value" => "Current rates"
  1127. ];
  1128. // rates
  1129. $description[] = [
  1130. "type" => "text",
  1131. "value" =>
  1132. "1 {$shitcoinjson["name"]} ({$shitcoinjson["symbol"]}) = " . $this->number_format($array_values["price"]) . " " . strtoupper($shitcoins[2]) . "\n" .
  1133. "1 " . strtoupper($shitcoins[2]) . " = " . $this->number_format(1 / $array_values["price"]) . " {$shitcoinjson["symbol"]}"
  1134. ];
  1135. $description[] = [
  1136. "type" => "quote",
  1137. "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($shitcoinjson["last_updated"]))
  1138. ];
  1139. $out["answer"][] = [
  1140. "title" => $shitcoinjson["name"] . " (" . strtoupper($shitcoins[1]) . ") & " . strtoupper($shitcoins[2]) . " market",
  1141. "description" => $description,
  1142. "url" => "https://coinmarketcap.com/converter/" . strtoupper($shitcoins[1]) . "/" . strtoupper($shitcoins[2]) . "/?amt={$amount}",
  1143. "thumb" => null,
  1144. "table" => [],
  1145. "sublink" => []
  1146. ];
  1147. }
  1148. }catch(Exception $e){
  1149. // do nothing
  1150. }
  1151. }else{
  1152. /*
  1153. Get currency conversion
  1154. */
  1155. if(
  1156. preg_match(
  1157. '/"https:\/\/www\.xe\.com\/currencyconverter\/convert\/\?From=([A-Z0-9]+)&To=([A-Z0-9]+)"/',
  1158. $js,
  1159. $currencies
  1160. )
  1161. ){
  1162. preg_match(
  1163. '/(?:[\s,.]*[0-9]+)+/',
  1164. $search,
  1165. $amount
  1166. );
  1167. if(count($amount) === 1){
  1168. $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]);
  1169. }else{
  1170. $amount = 1;
  1171. }
  1172. try{
  1173. $currencyjs = $this->get(
  1174. "https://duckduckgo.com/js/spice/currency/{$amount}/" . strtolower($currencies[1]) . "/" . strtolower($currencies[2]),
  1175. [],
  1176. ddg::req_xhr
  1177. );
  1178. preg_match(
  1179. '/ddg_spice_currency\(\s*({[\S\s]*})\s*\);/',
  1180. $currencyjs,
  1181. $currencyjson
  1182. );
  1183. $currencyjson = json_decode($currencyjson[1], true);
  1184. if(empty($currencyjson["headers"]["description"])){
  1185. $currencyjson = $currencyjson["conversion"];
  1186. $description = [];
  1187. if($amount != 1){
  1188. $description[] =
  1189. [
  1190. "type" => "title",
  1191. "value" => "Conversion"
  1192. ];
  1193. $description[] =
  1194. [
  1195. "type" => "text",
  1196. "value" =>
  1197. $this->number_format($currencyjson["from-amount"]) . " {$currencyjson["from-currency-symbol"]} = " .
  1198. $this->number_format($currencyjson["converted-amount"]) . " {$currencyjson["to-currency-symbol"]}"
  1199. ];
  1200. }
  1201. $description[] =
  1202. [
  1203. "type" => "title",
  1204. "value" => "Current rates"
  1205. ];
  1206. $description[] =
  1207. [
  1208. "type" => "text",
  1209. "value" =>
  1210. "{$currencyjson["conversion-rate"]}\n" .
  1211. "{$currencyjson["conversion-inverse"]}"
  1212. ];
  1213. $description[] =
  1214. [
  1215. "type" => "quote",
  1216. "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($currencyjson["rate-utc-timestamp"]))
  1217. ];
  1218. $out["answer"][] = [
  1219. "title" =>
  1220. "{$currencyjson["from-currency-name"]} ({$currencyjson["from-currency-symbol"]}) to " .
  1221. "{$currencyjson["to-currency-name"]} ({$currencyjson["to-currency-symbol"]})",
  1222. "description" => $description,
  1223. "url" => "https://www.xe.com/currencyconverter/convert/?Amount={$amount}&From={$currencies[1]}&To={$currencies[2]}",
  1224. "thumb" => null,
  1225. "table" => [],
  1226. "sublink" => []
  1227. ];
  1228. }
  1229. }catch(Exception $e){
  1230. // do nothing
  1231. }
  1232. }
  1233. }
  1234. }
  1235. /*
  1236. Get small answer
  1237. */
  1238. preg_match(
  1239. '/DDG\.ready\(function ?\(\) ?{DDH\.add\(({[\S\s]+}),"index"\)}\)/U',
  1240. $inithtml,
  1241. $smallanswer
  1242. );
  1243. if(isset($smallanswer[1])){
  1244. $smallanswer = json_decode($smallanswer[1], true);
  1245. if(
  1246. !isset($smallanswer["require"]) &&
  1247. isset($smallanswer["data"]["title"])
  1248. ){
  1249. if(isset($smallanswer["data"]["url"])){
  1250. $url = $this->unescapehtml($smallanswer["data"]["url"]);
  1251. }elseif(isset($smallanswer["meta"]["sourceUrl"])){
  1252. $url = $this->unescapehtml($smallanswer["meta"]["sourceUrl"]);
  1253. }else{
  1254. $url = null;
  1255. }
  1256. $out["answer"] = [
  1257. [
  1258. "title" => $this->unescapehtml($smallanswer["data"]["title"]),
  1259. "description" => [],
  1260. "url" => $this->sanitizeurl($url),
  1261. "thumb" => null,
  1262. "table" => [],
  1263. "sublink" => []
  1264. ],
  1265. ...$out["answer"]
  1266. ];
  1267. if(isset($smallanswer["data"]["subtitle"])){
  1268. $out["answer"][0]["description"][] =
  1269. [
  1270. "type" => "text",
  1271. "value" => isset($smallanswer["data"]["subtitle"]) ? $this->unescapehtml($smallanswer["data"]["subtitle"]) : null
  1272. ];
  1273. }
  1274. }
  1275. }
  1276. unset($inithtml);
  1277. unset($answers);
  1278. unset($answer_count);
  1279. /*
  1280. Get spelling autocorrect
  1281. */
  1282. preg_match(
  1283. '/DDG\.page\.showMessage\(\'spelling\',({[\S\s]+})\)/U',
  1284. $js,
  1285. $spelling
  1286. );
  1287. if(isset($spelling[1])){
  1288. $spelling = json_decode($spelling[1], true);
  1289. switch((int)$spelling["qc"]){
  1290. case 1:
  1291. case 3:
  1292. case 5:
  1293. $type = "including";
  1294. break;
  1295. default:
  1296. $type = "not_many";
  1297. break;
  1298. }
  1299. $out["spelling"] = [
  1300. "type" => $type,
  1301. "using" => $this->unescapehtml(strip_tags($spelling["suggestion"])),
  1302. "correction" => $this->unescapehtml(strip_tags($spelling["recourseText"]))
  1303. ];
  1304. }
  1305. unset($spelling);
  1306. /*
  1307. Get web results
  1308. */
  1309. preg_match(
  1310. '/DDG\.pageLayout\.load\(\'d\', ?(\[{"[\S\s]*"}])\)/U',
  1311. $js,
  1312. $web
  1313. );
  1314. if(isset($web[1])){
  1315. try{
  1316. $web = json_decode($web[1], true);
  1317. for($i=0; $i<count($web); $i++){
  1318. // ignore google placeholder + fake next page
  1319. if(
  1320. isset($web[$i]["t"]) &&
  1321. (
  1322. $web[$i]["t"] == "EOP" ||
  1323. $web[$i]["t"] == "EOF"
  1324. ) &&
  1325. strpos($web[$i]["c"], "://www.google.") !== false
  1326. ){
  1327. break;
  1328. }
  1329. // store next page token
  1330. if(isset($web[$i]["n"])){
  1331. $out["npt"] = $this->nextpage->store($web[$i]["n"] . "&biaexp=b&eslexp=a&litexp=c&msvrtexp=b&wrap=1", "web");
  1332. continue;
  1333. }
  1334. // ignore malformed data
  1335. if(!isset($web[$i]["t"])){
  1336. continue;
  1337. }
  1338. $sublinks = [];
  1339. if(isset($web[$i]["l"])){
  1340. for($k=0; $k<count($web[$i]["l"]); $k++){
  1341. if(
  1342. !isset($web[$i]["l"][$k]["targetUrl"]) ||
  1343. !isset($web[$i]["l"][$k]["text"])
  1344. ){
  1345. continue;
  1346. }
  1347. array_push(
  1348. $sublinks,
  1349. [
  1350. "title" => $this->titledots($this->unescapehtml($web[$i]["l"][$k]["text"])),
  1351. "date" => null,
  1352. "description" => isset($web[$i]["l"][$k]["snippet"]) ? $this->titledots($this->unescapehtml($web[$i]["l"][$k]["snippet"])) : null,
  1353. "url" => $this->sanitizeurl($web[$i]["l"][$k]["targetUrl"])
  1354. ]
  1355. );
  1356. }
  1357. }
  1358. if(
  1359. preg_match(
  1360. '/^<span class="result__type">PDF<\/span>/',
  1361. $web[$i]["t"]
  1362. )
  1363. ){
  1364. $type = "pdf";
  1365. $web[$i]["t"] =
  1366. str_replace(
  1367. '<span class="result__type">PDF</span>',
  1368. "",
  1369. $web[$i]["t"]
  1370. );
  1371. }else{
  1372. $type = "web";
  1373. }
  1374. if(isset($web[$i]["e"])){
  1375. $date = strtotime($web[$i]["e"]);
  1376. }else{
  1377. $date = null;
  1378. }
  1379. array_push(
  1380. $out["web"],
  1381. [
  1382. "title" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["t"]))),
  1383. "description" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["a"]))),
  1384. "url" => isset($web[$i]["u"]) ? $this->sanitizeurl($web[$i]["u"]) : $this->sanitizeurl($web[$i]["c"]),
  1385. "date" => $date,
  1386. "type" => $type,
  1387. "thumb" =>
  1388. [
  1389. "url" => null,
  1390. "ratio" => null
  1391. ],
  1392. "sublink" => $sublinks,
  1393. "table" => []
  1394. ]
  1395. );
  1396. }
  1397. }catch(Exception $e){
  1398. // do nothing
  1399. }
  1400. }
  1401. unset($web);
  1402. /*
  1403. Get images
  1404. */
  1405. preg_match(
  1406. '/DDG\.duckbar\.load\(\'images\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1407. $js,
  1408. $images
  1409. );
  1410. if(isset($images[1])){
  1411. try{
  1412. $images = json_decode($images[1], true);
  1413. for($i=0; $i<count($images); $i++){
  1414. if(
  1415. !isset($images[$i]["title"]) ||
  1416. !isset($images[$i]["image"]) ||
  1417. !isset($images[$i]["thumbnail"]) ||
  1418. !isset($images[$i]["width"]) ||
  1419. !isset($images[$i]["height"])
  1420. ){
  1421. continue;
  1422. }
  1423. $ratio =
  1424. $this->bingratio(
  1425. (int)$images[$i]["width"],
  1426. (int)$images[$i]["height"]
  1427. );
  1428. array_push(
  1429. $out["image"],
  1430. [
  1431. "title" => $this->titledots($this->unescapehtml($images[$i]["title"])),
  1432. "source" => [
  1433. [
  1434. "url" => $images[$i]["image"],
  1435. "width" => (int)$images[$i]["width"],
  1436. "height" => (int)$images[$i]["height"]
  1437. ],
  1438. [
  1439. "url" => $this->bingimg($images[$i]["thumbnail"]),
  1440. "width" => $ratio[0],
  1441. "height" => $ratio[1]
  1442. ]
  1443. ],
  1444. "url" => $this->sanitizeurl($images[$i]["url"])
  1445. ]
  1446. );
  1447. }
  1448. }catch(Exception $e){
  1449. // do nothing
  1450. }
  1451. }
  1452. unset($images);
  1453. /*
  1454. Get videos
  1455. */
  1456. preg_match(
  1457. '/DDG\.duckbar\.load\(\'videos\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1458. $js,
  1459. $videos
  1460. );
  1461. if(isset($videos[1])){
  1462. try{
  1463. $videos = json_decode($videos[1], true);
  1464. for($i=0; $i<count($videos); $i++){
  1465. $cachekey = false;
  1466. foreach(["large", "medium", "small"] as &$key){
  1467. if(isset($videos[$i]["images"][$key])){
  1468. $cachekey = $key;
  1469. break;
  1470. }
  1471. }
  1472. if(
  1473. !isset($videos[$i]["title"]) ||
  1474. !isset($videos[$i]["description"]) ||
  1475. $cachekey === false ||
  1476. !isset($videos[$i]["content"])
  1477. ){
  1478. continue;
  1479. }
  1480. array_push(
  1481. $out["video"],
  1482. [
  1483. "title" => $this->titledots($this->unescapehtml($videos[$i]["title"])),
  1484. "description" => $videos[$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($videos[$i]["description"])),
  1485. "date" => $videos[$i]["published"] == "" ? null : strtotime($videos[$i]["published"]),
  1486. "duration" => $videos[$i]["duration"] == 0 ? null : $this->hmstoseconds($videos[$i]["duration"]),
  1487. "views" => $videos[$i]["statistics"]["viewCount"] == 0 ? null : $videos[$i]["statistics"]["viewCount"],
  1488. "thumb" =>
  1489. [
  1490. "url" => $this->bingimg($videos[$i]["images"][$cachekey]),
  1491. "ratio" => "16:9"
  1492. ],
  1493. "url" => $this->sanitizeurl($videos[$i]["content"])
  1494. ]
  1495. );
  1496. }
  1497. }catch(Exception $e){
  1498. // do nothing
  1499. }
  1500. }
  1501. unset($videos);
  1502. /*
  1503. Get news
  1504. */
  1505. preg_match(
  1506. '/DDG\.duckbar\.load\(\'news\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1507. $js,
  1508. $news
  1509. );
  1510. if(isset($news[1])){
  1511. try{
  1512. $news = json_decode($news[1], true);
  1513. for($i=0; $i<count($news); $i++){
  1514. if(
  1515. !isset($news[$i]["title"]) ||
  1516. !isset($news[$i]["excerpt"]) ||
  1517. !isset($news[$i]["url"])
  1518. ){
  1519. continue;
  1520. }
  1521. array_push(
  1522. $out["news"],
  1523. [
  1524. "title" => $this->titledots($this->unescapehtml($news[$i]["title"])),
  1525. "description" => $this->titledots($this->unescapehtml(strip_tags($news[$i]["excerpt"]))),
  1526. "date" => isset($news[$i]["date"]) ? (int)$news[$i]["date"] : null,
  1527. "thumb" =>
  1528. [
  1529. "url" => isset($news[$i]["image"]) ? $news[$i]["image"] : null,
  1530. "ratio" => "16:9"
  1531. ],
  1532. "url" => $this->sanitizeurl($news[$i]["url"])
  1533. ]
  1534. );
  1535. }
  1536. }catch(Exception $e){
  1537. // do nothing
  1538. }
  1539. }
  1540. return $out;
  1541. }
  1542. public function image($get){
  1543. if($get["npt"]){
  1544. $npt = $this->nextpage->get($get["npt"], "images");
  1545. try{
  1546. $json = json_decode($this->get(
  1547. "https://duckduckgo.com/i.js?" . $npt,
  1548. [],
  1549. ddg::req_xhr
  1550. ), true);
  1551. }catch(Exception $err){
  1552. throw new Exception("Failed to get i.js");
  1553. }
  1554. }else{
  1555. $search = $get["s"];
  1556. if(strlen($search) === 0){
  1557. throw new Exception("Search term is empty!");
  1558. }
  1559. $country = $get["country"];
  1560. $nsfw = $get["nsfw"];
  1561. $date = $get["date"];
  1562. $size = $get["size"];
  1563. $color = $get["color"];
  1564. $type = $get["type"];
  1565. $layout = $get["layout"];
  1566. $license = $get["license"];
  1567. $filter = [];
  1568. $get_filters = [
  1569. "q" => $search,
  1570. "iax" => "images",
  1571. "ia" => "images"
  1572. ];
  1573. if($date != "any"){ $filter[] = "time:$date"; }
  1574. if($size != "any"){ $filter[] = "size:$size"; }
  1575. if($color != "any"){ $filter[] = "color:$color"; }
  1576. if($type != "any"){ $filter[] = "type:$type"; }
  1577. if($layout != "any"){ $filter[] = "layout:$layout"; }
  1578. if($license != "any"){ $filter[] = "license:$license"; }
  1579. $filter = implode(",", $filter);
  1580. if($filter != ""){
  1581. $get_filters["iaf"] = $filter;
  1582. }
  1583. switch($nsfw){
  1584. case "yes": $get_filters["kp"] = "-2"; break;
  1585. case "no": $get_filters["kp"] = "-1"; break;
  1586. }
  1587. try{
  1588. $html = $this->get(
  1589. "https://duckduckgo.com",
  1590. $get_filters,
  1591. ddg::req_web
  1592. );
  1593. }catch(Exception $err){
  1594. throw new Exception("Failed to get html");
  1595. }
  1596. preg_match(
  1597. '/vqd=([0-9-]+)/',
  1598. $html,
  1599. $vqd
  1600. );
  1601. if(!isset($vqd[1])){
  1602. throw new Exception("Failed to get vqd token");
  1603. }
  1604. $vqd = $vqd[1];
  1605. // @TODO: s param = image offset
  1606. $js_params = [
  1607. "l" => $country,
  1608. "o" => "json",
  1609. "q" => $search,
  1610. "vqd" => $vqd
  1611. ];
  1612. switch($nsfw){
  1613. case "yes": $js_params["p"] = "-1"; break;
  1614. case "no": $js_params["p"] = "1"; break;
  1615. }
  1616. if(empty($filter)){
  1617. $js_params["f"] = "1";
  1618. }else{
  1619. $js_params["f"] = $filter;
  1620. }
  1621. try{
  1622. $json = json_decode($this->get(
  1623. "https://duckduckgo.com/i.js",
  1624. $js_params,
  1625. ddg::req_xhr
  1626. ), true);
  1627. }catch(Exception $err){
  1628. throw new Exception("Failed to get i.js");
  1629. }
  1630. }
  1631. $out = [
  1632. "status" => "ok",
  1633. "npt" => null,
  1634. "image" => []
  1635. ];
  1636. if(isset($json["next"])){
  1637. if(!isset($vqd)){
  1638. $vqd = array_values($json["vqd"])[0];
  1639. }
  1640. $out["npt"] =
  1641. $this->nextpage->store(
  1642. explode("?", $json["next"])[1] . "&vqd=" .
  1643. $vqd,
  1644. "images"
  1645. );
  1646. }
  1647. for($i=0; $i<count($json["results"]); $i++){
  1648. $bingimg = $this->bingimg($json["results"][$i]["thumbnail"]);
  1649. $ratio =
  1650. $this->bingratio(
  1651. (int)$json["results"][$i]["width"],
  1652. (int)$json["results"][$i]["height"]
  1653. );
  1654. $out["image"][] = [
  1655. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1656. "source" => [
  1657. [
  1658. "url" => $json["results"][$i]["image"],
  1659. "width" => (int)$json["results"][$i]["width"],
  1660. "height" => (int)$json["results"][$i]["height"]
  1661. ],
  1662. [
  1663. "url" => $bingimg,
  1664. "width" => $ratio[0],
  1665. "height" => $ratio[1],
  1666. ]
  1667. ],
  1668. "url" => $this->sanitizeurl($json["results"][$i]["url"])
  1669. ];
  1670. }
  1671. return $out;
  1672. }
  1673. public function video($get){
  1674. if($get["npt"]){
  1675. $npt = $this->nextpage->get($get["npt"], "videos");
  1676. try{
  1677. $json = json_decode($this->get(
  1678. "https://duckduckgo.com/v.js?" .
  1679. $npt,
  1680. [],
  1681. ddg::req_xhr
  1682. ), true);
  1683. }catch(Exception $err){
  1684. throw new Exception("Failed to get v.js");
  1685. }
  1686. }else{
  1687. $search = $get["s"];
  1688. if(strlen($search) === 0){
  1689. throw new Exception("Search term is empty!");
  1690. }
  1691. $country = $get["country"];
  1692. $nsfw = $get["nsfw"];
  1693. $date = $get["date"];
  1694. $resolution = $get["resolution"];
  1695. $duration = $get["duration"];
  1696. $license = $get["license"];
  1697. $filter = [];
  1698. $get_filters = [
  1699. "q" => $search,
  1700. "iax" => "videos",
  1701. "ia" => "videos"
  1702. ];
  1703. switch($nsfw){
  1704. case "yes": $get_filters["kp"] = "-2"; break;
  1705. case "no": $get_filters["kp"] = "-1"; break;
  1706. }
  1707. if($date != "any"){ $filter[] = "publishedAfter:{$date}"; }
  1708. if($resolution != "any"){ $filter[] = "videoDefinition:{$resolution}"; }
  1709. if($duration != "any"){ $filter[] = "videoDuration:{$duration}"; }
  1710. if($license != "any"){ $filter[] = "videoLicense:{$license}"; }
  1711. $filter = implode(",", $filter);
  1712. try{
  1713. $html = $this->get(
  1714. "https://duckduckgo.com",
  1715. $get_filters,
  1716. ddg::req_web
  1717. );
  1718. }catch(Exception $err){
  1719. throw new Exception("Failed to get html");
  1720. }
  1721. preg_match(
  1722. '/vqd=([0-9-]+)/',
  1723. $html,
  1724. $vqd
  1725. );
  1726. if(!isset($vqd[1])){
  1727. throw new Exception("Failed to get vqd token");
  1728. }
  1729. $vqd = $vqd[1];
  1730. try{
  1731. $json = json_decode($this->get(
  1732. "https://duckduckgo.com/v.js",
  1733. [
  1734. "l" => "us-en",
  1735. "o" => "json",
  1736. "sr" => 1,
  1737. "q" => $search,
  1738. "vqd" => $vqd,
  1739. "f" => $filter,
  1740. "p" => $get_filters["kp"]
  1741. ],
  1742. ddg::req_xhr
  1743. ), true);
  1744. }catch(Exception $err){
  1745. throw new Exception("Failed to get v.js");
  1746. }
  1747. }
  1748. $out = [
  1749. "status" => "ok",
  1750. "npt" => null,
  1751. "video" => [],
  1752. "author" => [],
  1753. "livestream" => [],
  1754. "playlist" => [],
  1755. "reel" => []
  1756. ];
  1757. if(isset($json["next"])){
  1758. $out["npt"] =
  1759. $this->nextpage->store(
  1760. explode("?", $json["next"])[1],
  1761. "videos"
  1762. );
  1763. }
  1764. for($i=0; $i<count($json["results"]); $i++){
  1765. $cachekey = false;
  1766. foreach(["large", "medium", "small"] as &$key){
  1767. if(isset($json["results"][$i]["images"][$key])){
  1768. $cachekey = $key;
  1769. break;
  1770. }
  1771. }
  1772. if(
  1773. !isset($json["results"][$i]["title"]) ||
  1774. !isset($json["results"][$i]["description"]) ||
  1775. $cachekey === false ||
  1776. !isset($json["results"][$i]["content"])
  1777. ){
  1778. continue;
  1779. }
  1780. array_push(
  1781. $out["video"],
  1782. [
  1783. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1784. "description" => $json["results"][$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($json["results"][$i]["description"])),
  1785. "author" => [
  1786. "name" => empty($json["results"][$i]["uploader"]) ? null : $this->unescapehtml($json["results"][$i]["uploader"]),
  1787. "url" => null,
  1788. "avatar" => null
  1789. ],
  1790. "date" => $json["results"][$i]["published"] == "" ? null : strtotime($json["results"][$i]["published"]),
  1791. "duration" => $json["results"][$i]["duration"] == 0 ? null : $this->hmstoseconds($json["results"][$i]["duration"]),
  1792. "views" => $json["results"][$i]["statistics"]["viewCount"] == 0 ? null : $json["results"][$i]["statistics"]["viewCount"],
  1793. "thumb" => [
  1794. "url" => $this->bingimg($json["results"][$i]["images"][$cachekey]),
  1795. "ratio" => "16:9"
  1796. ],
  1797. "url" => $this->sanitizeurl($json["results"][$i]["content"])
  1798. ]
  1799. );
  1800. }
  1801. return $out;
  1802. }
  1803. public function news($get){
  1804. if($get["npt"]){
  1805. $req = $this->nextpage->get($get["npt"], "news");
  1806. try{
  1807. $json = json_decode($this->get(
  1808. "https://duckduckgo.com/news.js?" .
  1809. $req,
  1810. [],
  1811. ddg::req_xhr
  1812. ), true);
  1813. }catch(Exception $err){
  1814. throw new Exception("Failed to get news.js");
  1815. }
  1816. }else{
  1817. $search = $get["s"];
  1818. if(strlen($search) === 0){
  1819. throw new Exception("Search term is empty!");
  1820. }
  1821. $country = $get["country"];
  1822. $nsfw = $get["nsfw"];
  1823. $date = $get["date"];
  1824. $get_params = [
  1825. "q" => $search,
  1826. "iar" => "news",
  1827. "ia" => "news"
  1828. ];
  1829. switch($nsfw){
  1830. case "yes": $get_filters["kp"] = "-2"; break;
  1831. case "maybe": $get_filters["kp"] = "-1"; break;
  1832. case "no": $get_filters["kp"] = "1"; break;
  1833. }
  1834. if($date != "any"){
  1835. $get_params["df"] = $date;
  1836. }
  1837. try{
  1838. $html = $this->get(
  1839. "https://duckduckgo.com",
  1840. $get_params,
  1841. ddg::req_web
  1842. );
  1843. }catch(Exception $err){
  1844. throw new Exception("Failed to get html");
  1845. }
  1846. preg_match(
  1847. '/vqd=([0-9-]+)/',
  1848. $html,
  1849. $vqd
  1850. );
  1851. if(!isset($vqd[1])){
  1852. throw new Exception("Failed to get vqd token");
  1853. }
  1854. $vqd = $vqd[1];
  1855. try{
  1856. $js_params = [
  1857. "l" => $country,
  1858. "o" => "json",
  1859. "noamp" => "1",
  1860. "q" => $search,
  1861. "vqd" => $vqd,
  1862. "p" => $get_filters["kp"]
  1863. ];
  1864. if($date != "any"){
  1865. $js_params["df"] = $date;
  1866. }else{
  1867. $js_params["df"] = "";
  1868. }
  1869. $json = json_decode($this->get(
  1870. "https://duckduckgo.com/news.js",
  1871. $js_params,
  1872. ddg::req_xhr
  1873. ), true);
  1874. }catch(Exception $err){
  1875. throw new Exception("Failed to get news.js");
  1876. }
  1877. }
  1878. $out = [
  1879. "status" => "ok",
  1880. "npt" => null,
  1881. "news" => []
  1882. ];
  1883. if(isset($json["next"])){
  1884. $out["npt"] =
  1885. $this->nextpage->store(
  1886. explode("?", $json["next"])[1],
  1887. "news"
  1888. );
  1889. }
  1890. for($i=0; $i<count($json["results"]); $i++){
  1891. $out["news"][] = [
  1892. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1893. "author" => $this->unescapehtml($json["results"][$i]["source"]),
  1894. "description" => $this->titledots($this->unescapehtml(strip_tags($json["results"][$i]["excerpt"]))),
  1895. "date" => $json["results"][$i]["date"],
  1896. "thumb" =>
  1897. [
  1898. "url" => isset($json["results"][$i]["image"]) ? $json["results"][$i]["image"] : null,
  1899. "ratio" => "16:9"
  1900. ],
  1901. "url" => $this->sanitizeurl($json["results"][$i]["url"])
  1902. ];
  1903. }
  1904. return $out;
  1905. }
  1906. private function hmstoseconds($time){
  1907. $parts = explode(":", $time, 3);
  1908. $time = 0;
  1909. if(count($parts) === 3){
  1910. // hours
  1911. $time = $time + ((int)$parts[0] * 3600);
  1912. array_shift($parts);
  1913. }
  1914. if(count($parts) === 2){
  1915. // minutes
  1916. $time = $time + ((int)$parts[0] * 60);
  1917. array_shift($parts);
  1918. }
  1919. // seconds
  1920. $time = $time + (int)$parts[0];
  1921. return $time;
  1922. }
  1923. private function titledots($title){
  1924. $substr = substr($title, -3);
  1925. if(
  1926. $substr == "..." ||
  1927. $substr == "…"
  1928. ){
  1929. return trim(substr($title, 0, -3));
  1930. }
  1931. return trim($title);
  1932. }
  1933. private function unescapehtml($str){
  1934. return html_entity_decode(
  1935. str_replace(
  1936. [
  1937. "<br>",
  1938. "<br/>",
  1939. "</br>",
  1940. "<BR>",
  1941. "<BR/>",
  1942. "</BR>",
  1943. ],
  1944. "\n",
  1945. $str
  1946. ),
  1947. ENT_QUOTES | ENT_XML1, 'UTF-8'
  1948. );
  1949. }
  1950. private function bingimg($url){
  1951. $parse = parse_url($url);
  1952. parse_str($parse["query"], $parts);
  1953. return "https://" . $parse["host"] . "/th?id=" . urlencode($parts["id"]);
  1954. }
  1955. private function htmltoarray($html){
  1956. $html = strip_tags($html, ["img", "pre", "code", "br", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "a"]);
  1957. libxml_use_internal_errors(true);
  1958. $dom = new DOMDocument("1.0", "utf-8");
  1959. $dom->loadHTML('<div>' . $html . '</div>');
  1960. $xpath = new DOMXPath($dom);
  1961. $descendants = $xpath->query('//div/node()');
  1962. $images = $xpath->query('//div/node()/img');
  1963. $imageiterator = 0;
  1964. if(count($descendants) === 0){
  1965. return [
  1966. "type" => "text",
  1967. "value" => $this->unescapehtml($html)
  1968. ];
  1969. }
  1970. $array = [];
  1971. $previoustype = null;
  1972. foreach($descendants as $node){
  1973. // $node->nodeValue = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $node->nodeValue);
  1974. // get node type
  1975. switch($node->nodeName){
  1976. case "#text":
  1977. $type = "text";
  1978. break;
  1979. case "pre":
  1980. $type = "code";
  1981. break;
  1982. case "code":
  1983. $type = "inline_code";
  1984. break;
  1985. case "h1":
  1986. case "h2":
  1987. case "h3":
  1988. case "h4":
  1989. case "h5":
  1990. case "h6":
  1991. $type = "title";
  1992. break;
  1993. case "blockquote":
  1994. $type = "quote";
  1995. break;
  1996. case "a":
  1997. $type = "link";
  1998. break;
  1999. case "img":
  2000. $type = "image";
  2001. break;
  2002. }
  2003. // add node to array
  2004. switch($type){
  2005. case "text":
  2006. $value = preg_replace(
  2007. '/ {2,}/',
  2008. " ",
  2009. $this->limitnewlines($this->unescapehtml($node->textContent))
  2010. );
  2011. if(
  2012. $previoustype == "quote" ||
  2013. $previoustype === null ||
  2014. $previoustype == "image" ||
  2015. $previoustype == "title" ||
  2016. $previoustype == "code"
  2017. ){
  2018. $value = ltrim($value);
  2019. }
  2020. if($value == ""){
  2021. $previoustype = $type;
  2022. continue 2;
  2023. }
  2024. // merge with previous text node
  2025. if($previoustype == "text"){
  2026. $array[count($array) - 1]["value"] = trim($array[count($array) - 1]["value"]) . "\n" . $this->bstoutf8($value);
  2027. }else{
  2028. $array[] = [
  2029. "type" => "text",
  2030. "value" => $this->bstoutf8($value)
  2031. ];
  2032. }
  2033. break;
  2034. case "inline_code":
  2035. case "bold":
  2036. $array[] = [
  2037. "type" => "inline_code",
  2038. "value" => $this->bstoutf8(trim($this->limitnewlines($this->unescapehtml($node->textContent))))
  2039. ];
  2040. break;
  2041. case "link":
  2042. // check for link nested inside of image
  2043. if(strlen($node->childNodes->item(0)->textContent) !== 0){
  2044. $array[] = [
  2045. "type" => "link",
  2046. "value" => $this->bstoutf8(trim($this->unescapehtml($node->textContent))),
  2047. "url" => $this->bstoutf8(preg_replace('/\/ddg$/', "", preg_replace('/^http:\/\//', "https://", $this->sanitizeurl($node->getAttribute("href")))))
  2048. ];
  2049. break;
  2050. }
  2051. $type = "image";
  2052. if($previoustype == "text"){
  2053. $array[count($array) - 1]["value"] = rtrim($array[count($array) - 1]["value"]);
  2054. }
  2055. $array[] = [
  2056. "type" => "image",
  2057. "url" => $this->bstoutf8(preg_replace('/^http:\/\//', "https://", preg_replace('/^\/\/images\.duckduckgo\.com\/iu\/\?u=/', "", $images->item($imageiterator)->getAttribute("src"))))
  2058. ];
  2059. $imageiterator++;
  2060. break;
  2061. case "image":
  2062. if($previoustype == "text"){
  2063. $array[count($array) - 1]["value"] = rtrim($array[count($array) - 1]["value"]);
  2064. }
  2065. $array[] = [
  2066. "type" => "image",
  2067. "url" => $this->bstoutf8(preg_replace('/^http:\/\//', "https://", preg_replace('/^\/\/images\.duckduckgo\.com\/iu\/\?u=/', "", $node->getAttribute("src"))))
  2068. ];
  2069. break;
  2070. case "quote":
  2071. case "title":
  2072. case "code":
  2073. if($previoustype == "text"){
  2074. $array[count($array) - 1]["value"] = rtrim($array[count($array) - 1]["value"]);
  2075. }
  2076. // no break
  2077. default:
  2078. $value = trim($this->limitnewlines($this->unescapehtml($node->textContent)));
  2079. if($type != "code"){
  2080. $value = preg_replace(
  2081. '/ {2,}/',
  2082. " ",
  2083. $value
  2084. );
  2085. }
  2086. $array[] = [
  2087. "type" => $type,
  2088. "value" => $this->bstoutf8($value)
  2089. ];
  2090. break;
  2091. }
  2092. $previoustype = $type;
  2093. }
  2094. return $array;
  2095. }
  2096. private function bstoutf8($bs){
  2097. return iconv("UTF-8", "ISO-8859-1//TRANSLIT", $bs);
  2098. }
  2099. private function limitnewlines($text){
  2100. preg_replace(
  2101. '/(?:[\n\r] *){2,}/m',
  2102. "\n\n",
  2103. $text
  2104. );
  2105. return $text;
  2106. }
  2107. private function sanitizeurl($url){
  2108. // check for domains w/out first short subdomain (ex: www.)
  2109. $domain = parse_url($url, PHP_URL_HOST);
  2110. $subdomain = preg_replace(
  2111. '/^[A-z0-9]{1,3}\./',
  2112. "",
  2113. $domain
  2114. );
  2115. switch($subdomain){
  2116. case "ebay.com.au":
  2117. case "ebay.at":
  2118. case "ebay.ca":
  2119. case "ebay.fr":
  2120. case "ebay.de":
  2121. case "ebay.com.hk":
  2122. case "ebay.ie":
  2123. case "ebay.it":
  2124. case "ebay.com.my":
  2125. case "ebay.nl":
  2126. case "ebay.ph":
  2127. case "ebay.pl":
  2128. case "ebay.com.sg":
  2129. case "ebay.es":
  2130. case "ebay.ch":
  2131. case "ebay.co.uk":
  2132. case "cafr.ebay.ca":
  2133. case "ebay.com":
  2134. case "community.ebay.com":
  2135. case "pages.ebay.com":
  2136. // remove ebay tracking elements
  2137. $old_params = parse_url($url, PHP_URL_QUERY);
  2138. parse_str($old_params, $params);
  2139. if(isset($params["mkevt"])){ unset($params["mkevt"]); }
  2140. if(isset($params["mkcid"])){ unset($params["mkcid"]); }
  2141. if(isset($params["mkrid"])){ unset($params["mkrid"]); }
  2142. if(isset($params["campid"])){ unset($params["campid"]); }
  2143. if(isset($params["customid"])){ unset($params["customid"]); }
  2144. if(isset($params["toolid"])){ unset($params["toolid"]); }
  2145. if(isset($params["_sop"])){ unset($params["_sop"]); }
  2146. if(isset($params["_dcat"])){ unset($params["_dcat"]); }
  2147. if(isset($params["epid"])){ unset($params["epid"]); }
  2148. if(isset($params["epid"])){ unset($params["oid"]); }
  2149. $params = http_build_query($params);
  2150. if(strlen($params) === 0){
  2151. $replace = "\?";
  2152. }else{
  2153. $replace = "";
  2154. }
  2155. $url = preg_replace(
  2156. "/" . $replace . preg_quote($old_params, "/") . "$/",
  2157. $params,
  2158. $url
  2159. );
  2160. break;
  2161. }
  2162. return $url;
  2163. }
  2164. private function number_format($number){
  2165. $number = explode(".", sprintf('%f', $number));
  2166. if(count($number) === 1){
  2167. return number_format((float)$number[0], 0, ",", ".");
  2168. }
  2169. return number_format((float)$number[0], 0, ",", "") . "." . (string)$number[1];
  2170. }
  2171. private function bingratio($width, $height){
  2172. $ratio = [
  2173. 474 / $width,
  2174. 474 / $height
  2175. ];
  2176. if($ratio[0] < $ratio[1]){
  2177. $ratio = $ratio[0];
  2178. }else{
  2179. $ratio = $ratio[1];
  2180. }
  2181. return [
  2182. floor($width * $ratio),
  2183. floor($height * $ratio)
  2184. ];
  2185. }
  2186. }