summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/v1/ac.php225
-rw-r--r--scraper/brave.php2
-rw-r--r--scraper/google.php32
-rw-r--r--scraper/sc.php9
-rw-r--r--settings.php91
-rw-r--r--static/client.js266
-rw-r--r--static/style.css16
-rw-r--r--template/header.html2
-rw-r--r--template/home.html4
9 files changed, 594 insertions, 53 deletions
diff --git a/api/v1/ac.php b/api/v1/ac.php
new file mode 100644
index 0000000..0964fd9
--- /dev/null
+++ b/api/v1/ac.php
@@ -0,0 +1,225 @@
+<?php
+
+new autocomplete();
+
+class autocomplete{
+
+ public function __construct(){
+
+ header("Content-Type: application/json");
+
+ $this->scrapers = [
+ "brave" => "https://search.brave.com/api/suggest?q={searchTerms}",
+ "ddg" => "https://duckduckgo.com/ac/?q={searchTerms}&type=list",
+ "yandex" => "https://suggest.yandex.com/suggest-ff.cgi?part={searchTerms}&uil=en&v=3&sn=5&lr=21276&yu=4861394161661655015",
+ "google" => "https://www.google.com/complete/search?client=mobile-gws-lite&q={searchTerms}",
+ "qwant" => "https://api.qwant.com/v3/suggest/?q={searchTerms}&client=opensearch",
+ "yep" => "https://api.yep.com/ac/?query={searchTerms}",
+ "marginalia" => "https://search.marginalia.nu/suggest/?partial={searchTerms}",
+ "yt" => "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&q={searchTerms}",
+ "sc" => "https://api-v2.soundcloud.com/search/queries?q={searchTerms}&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=10&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en"
+ ];
+
+ /*
+ Sanitize input
+ */
+ if(!isset($_GET["s"])){
+
+ $this->do404("Missing search(s) parameter");
+ }
+
+ if(is_string($_GET["s"]) === false){
+
+ $this->do404("Invalid search(s) parameter");
+ }
+
+ if(strlen($_GET["s"]) > 500){
+
+ $this->do404("Search(s) exceeds the 500 char length");
+ }
+
+ if(
+ isset($_GET["scraper"]) &&
+ is_string($_GET["scraper"]) === false
+ ){
+
+ $_GET["scraper"] = "brave"; // default option
+ }
+
+ /*
+ Get $scraper
+ */
+ if(!isset($_GET["scraper"])){
+
+ if(isset($_COOKIE["scraper_ac"])){
+
+ $scraper = $_COOKIE["scraper_ac"];
+ }else{
+
+ $scraper = "brave"; // default option
+ }
+ }else{
+
+ $scraper = $_GET["scraper"];
+ }
+
+ if($scraper == "disabled"){
+
+ // this shouldnt happen, but let's handle it anyways
+ $this->doempty();
+ }
+
+ // make sure it exists
+ if(!isset($this->scrapers[$scraper])){
+
+ $scraper = "brave"; // default option
+ }
+
+ // return results
+
+ switch($scraper){
+
+ case "google":
+ case "yt":
+ // handle google cause they want to be a special snowflake :(
+ $js = $this->get($this->scrapers[$scraper], $_GET["s"]);
+
+ preg_match(
+ '/\((\[.*\])\)/',
+ $js,
+ $js
+ );
+
+ if(!isset($js[1])){
+
+ $this->doempty();
+ }
+
+ $js = json_decode($js[1]);
+ $json = [];
+
+ foreach($js[1] as $item){
+
+ $json[] = strip_tags($item[0]);
+ }
+
+ echo json_encode(
+ [
+ $_GET["s"],
+ $json
+ ]
+ );
+ break;
+
+ case "sc":
+ // soundcloud
+ $js = $this->get($this->scrapers[$scraper], $_GET["s"]);
+
+ $js = json_decode($js, true);
+
+ if(!isset($js["collection"])){
+
+ $this->doempty();
+ }
+
+ $json = [];
+ foreach($js["collection"] as $item){
+
+ $json[] = $item["query"];
+ }
+
+ echo json_encode(
+ [
+ $_GET["s"],
+ $json
+ ]
+ );
+ break;
+
+ case "marginalia":
+ $json = $this->get($this->scrapers[$scraper], $_GET["s"]);
+
+ $json = json_decode($json, true);
+ if($json === null){
+
+
+ $this->doempty();
+ }
+
+ echo json_encode(
+ [
+ $_GET["s"],
+ $json
+ ]
+ );
+ break;
+
+ default:
+ // if it respects the openSearch protocol
+ $json = json_decode($this->get($this->scrapers[$scraper], $_GET["s"]), true);
+
+ echo json_encode(
+ [
+ $_GET["s"],
+ $json[1] // ensure it contains valid key 0
+ ]
+ );
+ break;
+ }
+ }
+
+ private function get($url, $query){
+
+ $curlproc = curl_init();
+
+ $url = str_replace("{searchTerms}", urlencode($query), $url);
+
+ curl_setopt($curlproc, CURLOPT_URL, $url);
+
+ curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
+ curl_setopt($curlproc, CURLOPT_HTTPHEADER,
+ ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0",
+ "Accept: application/json, text/javascript, */*; q=0.01",
+ "Accept-Language: en-US,en;q=0.5",
+ "Accept-Encoding: gzip",
+ "DNT: 1",
+ "Connection: keep-alive",
+ "Sec-Fetch-Dest: empty",
+ "Sec-Fetch-Mode: cors",
+ "Sec-Fetch-Site: same-site"]
+ );
+
+ curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
+ curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
+ curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
+
+ $data = curl_exec($curlproc);
+
+ if(curl_errno($curlproc)){
+
+ throw new Exception(curl_error($curlproc));
+ }
+
+ curl_close($curlproc);
+ return $data;
+ }
+
+ private function do404($error){
+
+ echo json_encode(["error" => $error]);
+ die();
+ }
+
+ private function doempty(){
+
+ echo json_encode(
+ [
+ $_GET["s"],
+ []
+ ]
+ );
+ die();
+ }
+}
diff --git a/scraper/brave.php b/scraper/brave.php
index bf11865..93256a8 100644
--- a/scraper/brave.php
+++ b/scraper/brave.php
@@ -574,8 +574,6 @@ class brave{
}
}
- echo "test";
-
if($rating !== null){
$table["Rating"] = $rating;
diff --git a/scraper/google.php b/scraper/google.php
index d0e90ca..ca77231 100644
--- a/scraper/google.php
+++ b/scraper/google.php
@@ -1616,21 +1616,23 @@ class google{
$imgvl
);
- $imgvl = $imgvl[1];
-
- $params["async"] = "_id:islrg_c,_fmt:html";
- $params["asearch"] = "ichunklite";
- $params["ved"] = $ved;
- $params["vet"] = "1" . $ved . "..i";
- $params["start"] = 100;
- $params["ijn"] = 1;
- $params["imgvl"] = $imgvl;
-
- $out["npt"] =
- $this->nextpage->store(
- json_encode($params),
- "images"
- );
+ if(isset($imgvl[1])){
+ $imgvl = $imgvl[1];
+
+ $params["async"] = "_id:islrg_c,_fmt:html";
+ $params["asearch"] = "ichunklite";
+ $params["ved"] = $ved;
+ $params["vet"] = "1" . $ved . "..i";
+ $params["start"] = 100;
+ $params["ijn"] = 1;
+ $params["imgvl"] = $imgvl;
+
+ $out["npt"] =
+ $this->nextpage->store(
+ json_encode($params),
+ "images"
+ );
+ }
}
}
diff --git a/scraper/sc.php b/scraper/sc.php
index f297723..1774c20 100644
--- a/scraper/sc.php
+++ b/scraper/sc.php
@@ -288,7 +288,7 @@ class sc{
if(count($description) != 0){
- $description = $count . " songs. " . implode(", ", $description);
+ $description = trim($count . " songs. " . implode(", ", $description));
}
if(
@@ -320,7 +320,7 @@ class sc{
$out["playlist"][] = [
"title" => $item["title"],
- "description" => $description,
+ "description" => $this->limitstrlen($description),
"author" => [
"name" => $item["user"]["username"],
"url" => $item["user"]["permalink_url"],
@@ -385,13 +385,14 @@ class sc{
"\n",
wordwrap(
str_replace(
- "\n",
+ ["\n\r", "\r\n", "\n", "\r"],
" ",
$text
),
300,
"\n"
- )
+ ),
+ 2
)[0];
}
}
diff --git a/settings.php b/settings.php
index f6abb12..bce3af0 100644
--- a/settings.php
+++ b/settings.php
@@ -59,6 +59,56 @@ $settings = [
"name" => "Scrapers to use",
"settings" => [
[
+ "description" => "Autocomplete<br><i>Picking <div class=\"code-inline\">Auto</div> changes the source dynamically depending of the page's scraper<br>Picking <div class=\"code-inline\">Disabled</div> disables this feature</i>",
+ "parameter" => "scraper_ac",
+ "options" => [
+ [
+ "value" => "disabled",
+ "text" => "Disabled"
+ ],
+ [
+ "value" => "auto",
+ "text" => "Auto"
+ ],
+ [
+ "value" => "brave",
+ "text" => "Brave"
+ ],
+ [
+ "value" => "ddg",
+ "text" => "DuckDuckGo"
+ ],
+ [
+ "value" => "yandex",
+ "text" => "Yandex"
+ ],
+ [
+ "value" => "google",
+ "text" => "Google"
+ ],
+ [
+ "value" => "qwant",
+ "text" => "Qwant"
+ ],
+ [
+ "value" => "yep",
+ "text" => "Yep"
+ ],
+ [
+ "value" => "marginalia",
+ "text" => "Marginalia"
+ ],
+ [
+ "value" => "yt",
+ "text" => "YouTube"
+ ],
+ [
+ "value" => "sc",
+ "text" => "SoundCloud"
+ ]
+ ]
+ ],
+ [
"description" => "Web",
"parameter" => "scraper_web",
"options" => [
@@ -183,8 +233,13 @@ $settings = [
if($_POST){
$loop = &$_POST;
-}else{
+}elseif(count($_GET) !== 0){
+
+ // redirect user to front page
+ $loop = &$_GET;
+ header("Location: /");
+}else{
// refresh cookie dates
$loop = &$_COOKIE;
}
@@ -245,7 +300,7 @@ echo
'<head>' .
'<meta http-equiv="Content-Type" content="text/html;charset=utf-8">' .
'<title>Settings</title>' .
- '<link rel="stylesheet" href="/static/style.css?v3">' .
+ '<link rel="stylesheet" href="/static/style.css?v4">' .
'<meta name="viewport" content="width=device-width,initial-scale=1">' .
'<meta name="robots" content="index,follow">' .
'<link rel="icon" type="image/x-icon" href="/favicon.ico">' .
@@ -260,14 +315,14 @@ $left =
'By clicking <div class="code-inline">Update settings!</div>, a plaintext <div class="code-inline">key=value</div> cookie will be stored on your browser. When selecting a default setting, the parameter is removed from your cookies.';
$c = count($_COOKIE);
+$code = "";
+
if($c !== 0){
$left .=
'<br><br>Your current cookie looks like this:' .
'<div class="code">';
- $code = "";
-
$ca = 0;
foreach($_COOKIE as $key => $value){
@@ -326,17 +381,23 @@ $left .=
'</div>' .
'<div class="settings-submit">' .
'<input type="submit" value="Update settings!">' .
- '<a href="../">&lt; Return to main page</a>' .
+ '<a href="../">&lt; Return to front page</a>' .
'</div>' .
'</form>';
-echo
- $frontend->load(
- "search.html",
- [
- "class" => "",
- "right-left" => "",
- "right-right" => "",
- "left" => $left
- ]
- );
+if(count($_GET) === 0){
+
+ echo
+ $frontend->load(
+ "search.html",
+ [
+ "class" => "",
+ "right-left" =>
+ '<div class="infobox"><h2>Preference link</h2>Follow this link to auto-apply all cookies. Useful if your browser clears out cookies after a browsing session. Following this link will redirect you to the front page, unless no settings are set.<br><br>' .
+ '<a href="settings' . rtrim("?" . str_replace("; ", "&", $code), "?") . '">Bookmark me!</a>' .
+ '</div>',
+ "right-right" => "",
+ "left" => $left
+ ]
+ );
+}
diff --git a/static/client.js b/static/client.js
index 89e9a5e..a53cdb6 100644
--- a/static/client.js
+++ b/static/client.js
@@ -660,15 +660,16 @@ function changeimage(event){
centerpopup();
}
-/*
- Shortcuts
-*/
var searchbox_wrapper = document.getElementsByClassName("searchbox");
if(searchbox_wrapper.length !== 0){
+
searchbox_wrapper = searchbox_wrapper[0];
var searchbox = searchbox_wrapper.getElementsByTagName("input")[1];
-
+
+ /*
+ Textarea shortcuts
+ */
document.addEventListener("keydown", function(key){
switch(key.keyCode){
@@ -695,4 +696,261 @@ if(searchbox_wrapper.length !== 0){
break;
}
});
+
+ /*
+ Autocompleter
+ */
+ if( // make sure the user wants it
+ document.cookie.includes("scraper_ac=") &&
+ document.cookie.includes("scraper_ac=disabled") === false
+ ){
+
+ var autocomplete_cache = [];
+ var focuspos = -1;
+ var list = [];
+ var autocomplete_div = document.getElementsByClassName("autocomplete")[0];
+
+ if(
+ document.cookie.includes("scraper_ac=auto") &&
+ typeof scraper_dropdown != "undefined"
+ ){
+
+ var ac_req_appendix = "&scraper=" + scraper_dropdown.value;
+ }else{
+
+ var ac_req_appendix = "";
+ }
+
+ function getsearchboxtext(){
+
+ var value =
+ searchbox.value
+ .trim()
+ .replace(
+ / +/g,
+ " "
+ )
+ .toLowerCase();
+
+ return value;
+ }
+
+ searchbox.addEventListener("input", async function(){
+
+ // ratelimit on input only
+ // dont ratelimit if we already have res
+ if(typeof autocomplete_cache[getsearchboxtext()] != "undefined"){
+
+ await getac();
+ }else{
+
+ await getac_ratelimit();
+ }
+ });
+
+ async function getac(){
+
+ var curvalue = getsearchboxtext();
+
+ if(curvalue == ""){
+
+ // hide autocompleter
+ autocomplete_div.style.display = "none";
+ return;
+ }
+
+ if(typeof autocomplete_cache[curvalue] == "undefined"){
+
+ /*
+ Fetch autocomplete
+ */
+ // make sure we dont fetch same thing twice
+ autocomplete_cache[curvalue] = [];
+
+ var res = await fetch("/api/v1/ac?s=" + encodeURIComponent(curvalue) + ac_req_appendix);
+ var json = await res.json();
+
+ autocomplete_cache[curvalue] = json[1];
+
+ if(curvalue == getsearchboxtext()){
+
+ render_ac(curvalue, autocomplete_cache[curvalue]);
+ }
+ return;
+ }
+
+ render_ac(curvalue, autocomplete_cache[curvalue]);
+ }
+
+ var ac_func = null;
+ function getac_ratelimit(){
+
+ return new Promise(async function(resolve, reject){
+
+ if(ac_func !== null){
+
+ clearTimeout(ac_func);
+ }//else{
+
+ // no ratelimits
+ //getac();
+ //}
+
+ ac_func =
+ setTimeout(function(){
+
+ ac_func = null;
+ getac(); // get results after 100ms of no keystroke
+ resolve();
+ }, 300);
+ });
+ }
+
+ function render_ac(query, list){
+
+ if(list.length === 0){
+
+ autocomplete_div.style.display = "none";
+ return;
+ }
+
+ html = "";
+
+ // prepare regex
+ var highlight = query.split(" ");
+ var regex = [];
+
+ for(var k=0; k<highlight.length; k++){
+
+ // espace regex
+ regex.push(
+ highlight[k].replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
+ );
+ }
+
+ regex = new RegExp(highlight.join("|"), "gi");
+
+ for(var i=0; i<list.length; i++){
+
+ html +=
+ '<div tabindex="0" class="entry" onclick="handle_entry_click(this);">' +
+ htmlspecialchars(
+ list[i]
+ ).replace(
+ regex,
+ '<u>$&</u>'
+ ) +
+ '</div>';
+ }
+
+ autocomplete_div.innerHTML = html;
+ autocomplete_div.style.display = "block";
+ }
+
+ var should_focus = false;
+ document.addEventListener("keydown", function(event){
+
+ if(event.key == "Escape"){
+
+ document.activeElement.blur();
+ focuspos = -1;
+ autocomplete_div.style.display = "none";
+ return;
+ }
+
+ if(
+ is_click_within(event.target, "searchbox") === false ||
+ typeof autocomplete_cache[getsearchboxtext()] == "undefined"
+ ){
+
+ return;
+ }
+
+ switch(event.key){
+
+ case "ArrowUp":
+ event.preventDefault();
+ focuspos--;
+ if(focuspos === -2){
+
+ focuspos = autocomplete_cache[getsearchboxtext()].length - 1;
+ }
+ break;
+
+ case "ArrowDown":
+ case "Tab":
+ event.preventDefault();
+
+ focuspos++;
+ if(focuspos >= autocomplete_cache[getsearchboxtext()].length){
+
+ focuspos = -1;
+ }
+ break;
+
+ case "Enter":
+ should_focus = true;
+
+ if(focuspos !== -1){
+
+ // replace input content
+ event.preventDefault();
+ searchbox.value =
+ autocomplete_div.getElementsByClassName("entry")[focuspos].innerText;
+ break;
+ }
+ break;
+
+ default:
+ focuspos = -1;
+ break;
+ }
+
+ if(focuspos === -1){
+
+ searchbox.focus();
+ return;
+ }
+
+ autocomplete_div.getElementsByClassName("entry")[focuspos].focus();
+ });
+
+ window.addEventListener("blur", function(){
+
+ autocomplete_div.style.display = "none";
+ });
+
+ document.addEventListener("keyup", function(event){
+
+ // handle ENTER key on entry
+ if(should_focus){
+
+ should_focus = false;
+ searchbox.focus();
+ }
+ });
+
+ document.addEventListener("mousedown", function(event){
+
+ // hide input if click is outside
+ if(is_click_within(event.target, "searchbox") === false){
+
+ autocomplete_div.style.display = "none";
+ return;
+ }
+ });
+
+ function handle_entry_click(event){
+
+ searchbox.value = event.innerText;
+ focuspos = -1;
+ searchbox.focus();
+ }
+
+ searchbox.addEventListener("focus", function(){
+
+ focuspos = -1;
+ getac();
+ });
+ }
}
diff --git a/static/style.css b/static/style.css
index ee320a7..ec55624 100644
--- a/static/style.css
+++ b/static/style.css
@@ -149,31 +149,27 @@ h3,h4,h5,h6{
left:-1px;
right:-1px;
background:var(--282828);
- border:1px solid var(--504945);
+ border:1px solid var(--928374);
border-top:none;
border-radius:0 0 2px 2px;
z-index:10;
+ overflow:hidden;
}
.autocomplete .entry{
overflow:hidden;
padding:4px 10px;
cursor:pointer;
+ outline:none;
+ user-select:none;
}
.autocomplete .entry:hover{
background:var(--3c3836);
}
-.autocomplete .title{
- float:left;
-}
-
-.autocomplete .subtext{
- float:right;
- font-size:14px;
- color:var(--928374);
- margin-left:7px;
+.autocomplete .entry:focus{
+ background:var(--3c3836);
}
/* Tabs */
diff --git a/template/header.html b/template/header.html
index b687d27..9e519fc 100644
--- a/template/header.html
+++ b/template/header.html
@@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>{%title%}</title>
- <link rel="stylesheet" href="/static/style.css?v3">
+ <link rel="stylesheet" href="/static/style.css?v4">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="{%index%}index,{%index%}follow">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
diff --git a/template/home.html b/template/home.html
index 25267c8..9818677 100644
--- a/template/home.html
+++ b/template/home.html
@@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>4get</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
- <link rel="stylesheet" href="/static/style.css?v3">
+ <link rel="stylesheet" href="/static/style.css?v4">
<meta name="robots" content="index,follow">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta name="description" content="4get.ca: They live in our walls!">
@@ -33,6 +33,6 @@
Report a problem: <a href="https://lolcat.ca">lolcat.ca</a>
</div>
</div>
- <script src="/static/client.js?v3"></script>
+ <script src="/static/client.js?v4"></script>
</body>
</html>