diff --git a/README.md b/README.md index 651944a..70c475e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ https://4get.ca - Yandex - Google - Brave + - Yep + - Imgur + - FindThatMeme 3. Videos - YouTube @@ -39,6 +42,17 @@ https://4get.ca 5. Music - SoundCloud +6. Autocompleter + - Brave + - DuckDuckGo + - Yandex + - Google + - Qwant + - Yep + - Marginalia + - YouTube + - SoundCloud + More scrapers are coming soon. I currently want to add Google web/video/news search, HackerNews (durr orange site!!) and Qwant. A shopping and files tab is also in my todo list. # Setup @@ -166,7 +180,11 @@ certbot --nginx --key-type ecdsa -d www.yourdomain.com -d yourdomain.com After doing that certbot should deploy the certificate automatically into your 4get nginx config file. It should be ready to use at that point. -Ok bye!!! +## Captcha + +Right now the setup for this shit is absolutely awful. + +Edit line 190 in `lib/captcha_gen.php` and specify your image sets. You can't disable the captcha right now lol. Just use a previous commit if you want to do that. Call me a shitcoder all you want I've had no energy lately. Images must be stored in `data/captcha`. Create a folder for each category. All files in there should be named from `1.png` to `321839.png`, for example. ## Tor Setup diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..e69de29 diff --git a/api.txt b/api.txt index 40b0ed3..f3c8b17 100644 --- a/api.txt +++ b/api.txt @@ -30,12 +30,25 @@ under your own terms. Please respect the terms of use listed here so that this website may be available to all in the far future. + P.s fuck whoever botted my site for months on end, choke on my dick + lol!!!! + Get your instance running here :: https://git.lolcat.ca/lolcat/4get Thanks! ++ Passes + Depending of the instance, you may need to provide a "pass" token + in the cookies of your request. These can be obtained from solving + a captcha which will allow you to make 100 requests in the next 24 + hours. In the future, you will be able to ask the serber maintainer + for a "pass" which will allow you to bypass the captcha requirement. + + The captcha doesn't need javascript to work. + + + Decode the data All payloads returned by the API are encoded in the JSON format. If you don't know how to tackle the problem, maybe programming is not @@ -47,8 +60,11 @@ + Check if an API call was successful All API responses come with an array index named "status". If the status is something else than the string "ok", something went wrong. + You can supply the content of the "status" string back to your + application to inform the user of what went wrong. - The HTTP code will always be 200 as to not cause issues with CORS. + The HTTP code will be 429 if your pass is invalid. It is set to 200 + otherwise. + Get the next page of results diff --git a/api/v1/ac.php b/api/v1/ac.php index 0964fd9..3ee1481 100644 --- a/api/v1/ac.php +++ b/api/v1/ac.php @@ -17,7 +17,7 @@ class autocomplete{ "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" + "sc" => "https://api-v2.soundcloud.com/search/queries?q={searchTerms}&client_id=ArYppSEotE3YiXCO4Nsgid2LLqJutiww&limit=10&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en" ]; /* @@ -100,7 +100,7 @@ class autocomplete{ foreach($js[1] as $item){ - $json[] = strip_tags($item[0]); + $json[] = htmlspecialchars_decode(strip_tags($item[0])); } echo json_encode( diff --git a/api/v1/images.php b/api/v1/images.php index 694658e..474e2dd 100644 --- a/api/v1/images.php +++ b/api/v1/images.php @@ -7,6 +7,12 @@ chdir("../../"); include "lib/frontend.php"; $frontend = new frontend(); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, false); + [$scraper, $filters] = $frontend->getscraperfilters( "images", isset($_GET["scraper"]) ? $_GET["scraper"] : null diff --git a/api/v1/music.php b/api/v1/music.php index faf2d96..7512ac0 100644 --- a/api/v1/music.php +++ b/api/v1/music.php @@ -7,6 +7,13 @@ chdir("../../"); include "lib/frontend.php"; $frontend = new frontend(); +/* + Captcha +*/ +$null = null; +include "lib/captcha_gen.php"; +new captcha($null, $null, $null, $null, false); + [$scraper, $filters] = $frontend->getscraperfilters( "music", isset($_GET["scraper"]) ? $_GET["scraper"] : null diff --git a/api/v1/news.php b/api/v1/news.php index 775ef94..97738fc 100644 --- a/api/v1/news.php +++ b/api/v1/news.php @@ -7,6 +7,12 @@ chdir("../../"); include "lib/frontend.php"; $frontend = new frontend(); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, false); + [$scraper, $filters] = $frontend->getscraperfilters( "news", isset($_GET["scraper"]) ? $_GET["scraper"] : null diff --git a/api/v1/videos.php b/api/v1/videos.php index 225611a..9277792 100644 --- a/api/v1/videos.php +++ b/api/v1/videos.php @@ -7,6 +7,12 @@ chdir("../../"); include "lib/frontend.php"; $frontend = new frontend(); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, false); + [$scraper, $filters] = $frontend->getscraperfilters( "videos", isset($_GET["scraper"]) ? $_GET["scraper"] : null diff --git a/api/v1/web.php b/api/v1/web.php index 7f6d769..c479149 100644 --- a/api/v1/web.php +++ b/api/v1/web.php @@ -7,6 +7,12 @@ chdir("../../"); include "lib/frontend.php"; $frontend = new frontend(); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, false); + [$scraper, $filters] = $frontend->getscraperfilters( "web", isset($_GET["scraper"]) ? $_GET["scraper"] : null diff --git a/captcha.php b/captcha.php new file mode 100755 index 0000000..21da034 --- /dev/null +++ b/captcha.php @@ -0,0 +1,147 @@ + "#ebdbb2", + "fg" => "#1d2021" + ]; +}else{ + + $theme = [ + "bg" => "#1d2021", + "fg" => "#ebdbb2" + ]; +} + +$im = new Imagick(); +$im->newImage(400, 400, $theme["bg"]); +$im->setImageBackgroundColor($theme["bg"]); +$im->setImageFormat("jpg"); + +$noise = [ + imagick::NOISE_GAUSSIAN, + imagick::NOISE_LAPLACIAN +]; + +$distort = [ + imagick::DISTORTION_AFFINE, + imagick::DISTORTION_SHEPARDS +]; + +$i = 0; +for($y=0; $y<4; $y++){ + + for($x=0; $x<4; $x++){ + + $tmp = new Imagick("./data/captcha/" . $grid[0][$i][0] . "/" . random_int(1, $grid[0][$i][1]) . ".png"); + + // convert transparency correctly + $tmp->setImageBackgroundColor("black"); + $tmp->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE); + + // distort $tmp + $tmp->distortImage( + $distort[random_int(0,1)], + [ + 0, 0, + random_int(-15, 15), random_int(-15, 15), + + 100, 0, + random_int(80, 120), random_int(-15, 15), + + 100, 100, + random_int(80, 120), random_int(80, 120), + + 0, 100, + random_int(-15, 15), random_int(80, 120) + ], + false + ); + + // append image + $im->compositeImage($tmp->getImage(), Imagick::COMPOSITE_DEFAULT, $x * 100, $y * 100); + + $i++; + } +} + +// add noise +$im->addNoiseImage($noise[random_int(0, 1)]); + +// expand top of image +$im->setImageGravity(Imagick::GRAVITY_SOUTH); +$im->chopImage(0, -27, 400, 400); +$im->extentImage(0, 0, 0, -27); + +// add text +$draw = new ImagickDraw(); +$draw->setFontSize(20); +$draw->setFillColor($theme["fg"]); +//$draw->setTextAntialias(false); +$draw->setFont("./data/captcha/font.ttf"); + +$text = "Pick " . $grid[1] . " images of " . str_replace("_", " ", $grid[2]); + +$pos = 200 - ($im->queryFontMetrics($draw, $text)["textWidth"] / 2); + +for($i=0; $iannotateImage( + $draw, + $pos, + 20, + random_int(-15, 15), + $text[$i] + ); + + $pos += $im->queryFontMetrics($draw, $text[$i])["textWidth"]; + +} + +$im->setFormat("jpeg"); +$im->setImageCompressionQuality(90); +$im->setImageCompression(Imagick::COMPRESSION_JPEG2000); +echo $im->getImageBlob(); diff --git a/data/captcha/font.ttf b/data/captcha/font.ttf new file mode 100644 index 0000000..13f5dc7 Binary files /dev/null and b/data/captcha/font.ttf differ diff --git a/images.php b/images.php index ff420c9..5be8de4 100644 --- a/images.php +++ b/images.php @@ -10,11 +10,11 @@ $frontend = new frontend(); $get = $frontend->parsegetfilters($_GET, $filters); -$frontend->loadheader( - $get, - $filters, - "images" -); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, $get, $filters, "images", true); $payload = [ "images" => "", diff --git a/lib/captcha_gen.php b/lib/captcha_gen.php new file mode 100644 index 0000000..2ae824b --- /dev/null +++ b/lib/captcha_gen.php @@ -0,0 +1,325 @@ += 102){ + + // reached limit, delete and give captcha + apcu_delete($_COOKIE["pass"]); + }else{ + + // the cookie is OK! dont die() and give results + if($output === true){ + $frontend->loadheader( + $get, + $filters, + $page + ); + } + return; + } + } + } + + if($output === false){ + + echo json_encode([ + "status" => "The \"pass\" token in your cookies is missing or has expired!!" + ]); + die(); + } + + /* + Validate form data + */ + $lines = + explode( + "\r\n", + file_get_contents("php://input") + ); + + $invalid = false; + $answers = []; + $key = false; + $error = ""; + + foreach($lines as $line){ + + $line = explode("=", $line, 2); + + if(count($line) !== 2){ + + $invalid = true; + break; + } + + preg_match( + '/^c\[([0-9]+)\]$/', + $line[0], + $regex + ); + + if( + $line[1] != "on" || + !isset($regex[0][1]) + ){ + + // check if its k + if( + $line[0] == "k" && + strpos($line[1], "c.") === 0 + ){ + + $key = apcu_fetch($line[1]); + apcu_delete($line[1]); + } + break; + } + + $regex = (int)$regex[1]; + + if( + $regex >= 16 || + $regex <= -1 + ){ + + $invalid = true; + break; + } + + $answers[] = $regex; + } + + if( + !$invalid && + $key !== false + ){ + $check = $key[1]; + + // validate answer + for($i=0; $i time() + 86400, // expires in 24 hours + "samesite" => "Strict", + "path" => "/" + ] + ); + + $frontend->loadheader( + $get, + $filters, + $page + ); + return; + + }else{ + + $error = "
You were kicked out of Mensa. Please try again.
"; + } + } + + /* + Generate random grid data to pass to captcha.php + */ + $dataset = [ + ["birds", 2263], + ["fumo_plushies", 1006], + ["minecraft", 848] + ]; + + // get the positions for the answers + // will return between 3 and 6 answer positions + $range = range(0, 15); + $answer_pos = []; + + array_splice($range, 0, 1); + + for($i=0; $i "", + "right-left" => "", + "right-right" => "", + "left" => + '
' . + '

IQ test

' . + 'Due to getting hit with 20,000 bot requests per day, I had to put this up. Sorry.

' . + 'Solving this captcha will allow you to make 100 searches today. I will add a way for legit users to bypass the captcha later. Sorry /g/tards!!' . + $error . + '
' . + '
' . + '
' . + 'Captcha image' . + '
' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '
' . + '
' . + '
' . + '' . + '' . + '
' . + '
' + ]; + + http_response_code(429); // too many reqs + $frontend->loadheader( + $get, + $filters, + "web" + ); + + echo $frontend->load("search.html", $payload); + die(); + } +} diff --git a/lib/curlproxy.php b/lib/curlproxy.php index 93cdbdc..ef9085b 100644 --- a/lib/curlproxy.php +++ b/lib/curlproxy.php @@ -127,6 +127,11 @@ class proxy{ throw new Exception("Too many redirects"); } + if($url == "https://i.imgur.com/removed.png"){ + + throw new Exception("Encountered imgur 404"); + } + // sanitize URL if($this->validateurl($url) === false){ diff --git a/lib/frontend.php b/lib/frontend.php index 4c5e232..97c8c5b 100644 --- a/lib/frontend.php +++ b/lib/frontend.php @@ -901,7 +901,11 @@ class frontend{ "ddg" => "DuckDuckGo", "yandex" => "Yandex", "brave" => "Brave", - "google" => "Google" + "google" => "Google", + "yep" => "Yep", + //"pinterest" => "Pinterest", + "imgur" => "Imgur", + "ftm" => "FindThatMeme" ] ]; break; @@ -1011,10 +1015,30 @@ class frontend{ $lib = new wiby(); break; + case "yep": + include "scraper/yep.php"; + $lib = new yep(); + break; + case "sc": include "scraper/sc.php"; $lib = new sc(); break; + + case "pinterest": + include "scraper/pinterest.php"; + $lib = new pinterest(); + break; + + case "imgur": + include "scraper/imgur.php"; + $lib = new imgur(); + break; + + case "ftm": + include "scraper/ftm.php"; + $lib = new ftm(); + break; } // set scraper on $_GET diff --git a/lib/nextpage.php b/lib/nextpage.php index 3fab855..7516667 100644 --- a/lib/nextpage.php +++ b/lib/nextpage.php @@ -26,7 +26,7 @@ class nextpage{ apcu_store( $page . "." . $this->scraper . - (string)($key), + (string)$key, gzdeflate($salt.$iv.$out.$tag), 900 // cache information for 15 minutes blaze it ); diff --git a/music.php b/music.php index 61078b9..c95fb4c 100644 --- a/music.php +++ b/music.php @@ -10,11 +10,11 @@ $frontend = new frontend(); $get = $frontend->parsegetfilters($_GET, $filters); -$frontend->loadheader( - $get, - $filters, - "music" -); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, $get, $filters, "music", true); $payload = [ "class" => "", diff --git a/news.php b/news.php index eb5817f..ff37489 100644 --- a/news.php +++ b/news.php @@ -10,11 +10,11 @@ $frontend = new frontend(); $get = $frontend->parsegetfilters($_GET, $filters); -$frontend->loadheader( - $get, - $filters, - "news" -); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, $get, $filters, "news", true); $payload = [ "class" => "", diff --git a/scraper/ftm.php b/scraper/ftm.php new file mode 100644 index 0000000..af39c12 --- /dev/null +++ b/scraper/ftm.php @@ -0,0 +1,148 @@ +nextpage = new nextpage("ftm"); + } + + public function getfilters($page){ + + return []; + } + + private function get($url, $search, $offset){ + + $curlproc = curl_init(); + + curl_setopt($curlproc, CURLOPT_URL, $url); + + $payload = + json_encode( + [ + "search" => $search, + "offset" => $offset + ] + ); + + curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding + curl_setopt($curlproc, CURLOPT_HTTPHEADER, + ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/110.0", + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language: en-US,en;q=0.5", + "Accept-Encoding: gzip", + "Content-Length: " . strlen($payload), + "Content-Type: application/json", + "DNT: 1", + "Connection: keep-alive", + "Origin: https://findthatmeme.com", + "Referer: https://findthatmeme.com/?search=" . urlencode($search), + "Upgrade-Insecure-Requests: 1", + "Sec-Fetch-Dest: document", + "Sec-Fetch-Mode: navigate", + "Sec-Fetch-Site: none", + "Sec-Fetch-User: ?1", + "X-Auth-Key: undefined", + "X-CSRF-Validation-Header: true"] + ); + + curl_setopt($curlproc, CURLOPT_POST, true); + curl_setopt($curlproc, CURLOPT_POSTFIELDS, $payload); + + 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; + } + + public function image($get){ + + $search = $get["s"]; + + $out = [ + "status" => "ok", + "npt" => null, + "image" => [] + ]; + + if($get["npt"]){ + + $count = (int)$this->nextpage->get($get["npt"], "images"); + }else{ + + $count = 0; + } + + try{ + $json = + json_decode( + $this->get( + "https://findthatmeme.com/api/v1/search", + $search, + $count + ), + true + ); + + }catch(Exception $error){ + + throw new Exception("Failed to fetch JSON"); + } + + if($json === null){ + + throw new Exception("Failed to decode JSON"); + } + + foreach($json as $item){ + + $count++; + + if($item["type"] == "VIDEO"){ + + $thumb = "thumb/" . $item["thumbnail"]; + }else{ + + $thumb = $item["image_path"]; + } + + $out["image"][] = [ + "title" => date("jS \of F Y @ g:ia", strtotime($item["created_at"])), + "source" => [ + [ + "url" => + "https://findthatmeme.us-southeast-1.linodeobjects.com/" . + $thumb, + "width" => null, + "height" => null + ] + ], + "url" => $item["source_page_url"] + ]; + } + + if($count === 50){ + + $out["npt"] = + $this->nextpage->store( + $count, + "images" + ); + } + + return $out; + } +} diff --git a/scraper/imgur.php b/scraper/imgur.php new file mode 100644 index 0000000..4a16de7 --- /dev/null +++ b/scraper/imgur.php @@ -0,0 +1,249 @@ +nextpage = new nextpage("imgur"); + + include "lib/fuckhtml.php"; + $this->fuckhtml = new fuckhtml(); + } + + public function getfilters($page){ + + return [ + "sort" => [ // /score/ + "display" => "Sort by", + "option" => [ + "score" => "Highest scoring", + "relevance" => "Most relevant", + "time" => "Newest first" + ] + ], + "time" => [ // /score/day/ + "display" => "Time posted", + "option" => [ + "all" => "All time", + "day" => "Today", + "week" => "This week", + "month" => "This month", + "year" => "This year" + ] + ], + "format" => [ // q_type + "display" => "Format", + "option" => [ + "any" => "Any format", + "jpg" => "JPG", + "png" => "PNG", + "gif" => "GIF", + "anigif" => "Animated GIF", + "album" => "Albums" + ] + ], + "size" => [ // q_size_px + "display" => "Size", + "option" => [ + "any" => "Any size", + "small" => "Small (500px or less)", + "med" => "Medium (500px to 2000px)", + "big" => "Big (2000px to 5000px)", + "lrg" => "Large (5000px to 10000px)", + "huge" => "Huge (10000px and above)" + ] + ] + ]; + } + + private function get($url, $get = []){ + + $curlproc = curl_init(); + + if($get !== []){ + $get = http_build_query($get); + $url .= "?scrolled&" . $get; + } + + 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:107.0) Gecko/20100101 Firefox/110.0", + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language: en-US,en;q=0.5", + "Accept-Encoding: gzip", + "DNT: 1", + "Referer: https://imgur.com/search/", + "Connection: keep-alive", + "Sec-Fetch-Dest: empty", + "Sec-Fetch-Mode: cors", + "Sec-Fetch-Site: same-origin", + "TE: trailers", + "X-Requested-With: XMLHttpRequest"] + ); + + 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; + } + + public function image($get){ + + if($get["npt"]){ + + $filter = + json_decode( + $this->nextpage->get( + $get["npt"], + "images" + ), + true + ); + + $search = $filter["s"]; + unset($filter["s"]); + + $sort = $filter["sort"]; + unset($filter["sort"]); + + $time = $filter["time"]; + unset($filter["time"]); + + $format = $filter["format"]; + unset($filter["format"]); + + $size = $filter["size"]; + unset($filter["size"]); + + $page = $filter["page"]; + unset($filter["page"]); + }else{ + + $search = $get["s"]; + $sort = $get["sort"]; + $time = $get["time"]; + $format = $get["format"]; + $size = $get["size"]; + $page = 0; + + $filter = [ + "q" => $search + ]; + + if($format != "any"){ + + $filter["q_type"] = $format; + } + + if($size != "any"){ + + $filter["q_size_px"] = $size; + $filter["q_size_is_mpx"] = "off"; + } + } + + $out = [ + "status" => "ok", + "npt" => null, + "image" => [] + ]; + + try{ + $html = + $this->get( + "https://imgur.com/search/$sort/$time/page/$page", + $filter + ); + + }catch(Exception $error){ + + throw new Exception("Failed to fetch HTML"); + } + + $this->fuckhtml->load($html); + + $posts = + $this->fuckhtml + ->getElementsByClassName( + "post", + "div" + ); + + foreach($posts as $post){ + + $this->fuckhtml->load($post); + + $image = + $this->fuckhtml + ->getElementsByTagName("img")[0]; + + $image_url = "https:" . substr($this->fuckhtml->getTextContent($image["attributes"]["src"]), 0, -5); + + $out["image"][] = [ + "title" => + $this->fuckhtml + ->getTextContent( + $image["attributes"]["alt"] + ), + "source" => [ + [ + "url" => $image_url . ".jpg", + "width" => null, + "height" => null + ], + [ + "url" => $image_url . "m.jpg", + "width" => null, + "height" => null + ] + ], + "url" => + "https://imgur.com" . + $this->fuckhtml + ->getTextContent( + $this->fuckhtml + ->getElementsByClassName( + "image-list-link", + "a" + ) + [0] + ["attributes"] + ["href"] + ) + ]; + } + + if(isset($out["image"][0])){ + + // store nextpage + $filter["s"] = $search; + $filter["sort"] = $sort; + $filter["time"] = $time; + $filter["format"] = $format; + $filter["size"] = $size; + $filter["page"] = $page + 1; + + $out["npt"] = + $this->nextpage->store( + json_encode($filter), + "images" + ); + } + + return $out; + } +} diff --git a/scraper/pinterest.php b/scraper/pinterest.php new file mode 100644 index 0000000..2bb5b71 --- /dev/null +++ b/scraper/pinterest.php @@ -0,0 +1,224 @@ +nextpage = new nextpage("pinterest"); + } + + public function getfilters($page){ + + return []; + } + + private function get($url, $get = []){ + + $curlproc = curl_init(); + + if($get !== []){ + $get = http_build_query($get); + $url .= "?" . $get; + } + + 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:107.0) Gecko/20100101 Firefox/110.0", + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language: en-US,en;q=0.5", + "Accept-Encoding: gzip", + "DNT: 1", + "Connection: keep-alive", + "Upgrade-Insecure-Requests: 1", + "Sec-Fetch-Dest: document", + "Sec-Fetch-Mode: navigate", + "Sec-Fetch-Site: none", + "Sec-Fetch-User: ?1"] + ); + + 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; + } + + public function image($get){ + + $search = $get["s"]; + + $out = [ + "status" => "ok", + "npt" => null, + "image" => [] + ]; + + $filter = [ + "source_url" => "/search/pins/?q=" . urlencode($search), + "rs" => "typed", + "data" => + json_encode( + [ + "options" => [ + "article" => null, + "applied_filters" => null, + "appliedProductFilters" => "---", + "auto_correction_disabled" => false, + "corpus" => null, + "customized_rerank_type" => null, + "filters" => null, + "query" => $search, + "query_pin_sigs" => null, + "redux_normalize_feed" => true, + "rs" => "typed", + "scope" => "pins", // pins, boards, videos, + "source_id" => null + ], + "context" => [] + ] + ), + "_" => substr(str_replace(".", "", (string)microtime(true)), 0, -1) + ]; + + try{ + $json = + json_decode( + $this->get( + "https://www.pinterest.ca/resource/BaseSearchResource/get/", + $filter + ), + true + ); + + }catch(Exception $error){ + + throw new Exception("Failed to fetch JSON"); + } + + if($json === null){ + + throw new Exception("Failed to decode JSON"); + } + + //print_r($json); + + foreach( + $json + ["resource_response"] + ["data"] + ["results"] + as $item + ){ + + switch($item["type"]){ + + case "pin": + + /* + Handle image object + */ + $images = array_values($item["images"]); + $image = &$images[count($images) - 1]; // original + $thumb = &$images[1]; // 236x + + $title = []; + + if( + isset($item["grid_title"]) && + trim($item["grid_title"]) != "" + ){ + + $title[] = $item["grid_title"]; + } + + if( + isset($item["description"]) && + trim($item["description"]) != "" + ){ + + $title[] = $item["description"]; + } + + $title = implode(": ", $title); + + if( + $title == "" && + isset($item["board"]["name"]) && + trim($item["board"]["name"]) != "" + ){ + + $title = $item["board"]["name"]; + } + + if($title == ""){ + + $title = null; + } + + $out["image"][] = [ + "title" => $title, + "source" => [ + [ + "url" => $image["url"], + "width" => (int)$image["width"], + "height" => (int)$image["height"] + ], + [ + "url" => $thumb["url"], + "width" => (int)$thumb["width"], + "height" => (int)$thumb["height"] + ] + ], + "url" => "https://www.pinterest.com/pin/" . $item["id"] + ]; + break; + + case "board": + + if(isset($item["cover_pin"]["image_url"])){ + + $image = [ + "url" => $item["cover_pin"]["image_url"], + "width" => (int)$item["cover_pin"]["size"][0], + "height" => (int)$item["cover_pin"]["size"][1] + ]; + }elseif(isset($item["image_cover_url_hd"])){ + /* + $image = [ + "url" => + "width" => null, + "height" => null + ];*/ + } + break; + } + } + + return $out; + } + + private function getfullresimage($image, $has_og){ + + $has_og = $has_og ? "1200x" : "originals"; + + return + preg_replace( + '/https:\/\/i\.pinimg\.com\/[^\/]+\//', + "https://i.pinimg.com/" . $has_og . "/", + $image + ); + } +} diff --git a/scraper/yep.php b/scraper/yep.php new file mode 100644 index 0000000..8ff4a57 --- /dev/null +++ b/scraper/yep.php @@ -0,0 +1,370 @@ +nextpage = new nextpage("yep"); + } + + public function getfilters($page){ + + return [ + "country" => [ + "display" => "Country", + "option" => [ + "all" => "All regions", + "af" => "Afghanistan", + "al" => "Albania", + "dz" => "Algeria", + "as" => "American Samoa", + "ad" => "Andorra", + "ao" => "Angola", + "ai" => "Anguilla", + "ag" => "Antigua and Barbuda", + "ar" => "Argentina", + "am" => "Armenia", + "aw" => "Aruba", + "au" => "Australia", + "at" => "Austria", + "az" => "Azerbaijan", + "bs" => "Bahamas", + "bh" => "Bahrain", + "bd" => "Bangladesh", + "bb" => "Barbados", + "by" => "Belarus", + "be" => "Belgium", + "bz" => "Belize", + "bj" => "Benin", + "bt" => "Bhutan", + "bo" => "Bolivia", + "ba" => "Bosnia and Herzegovina", + "bw" => "Botswana", + "br" => "Brazil", + "bn" => "Brunei Darussalam", + "bg" => "Bulgaria", + "bf" => "Burkina Faso", + "bi" => "Burundi", + "cv" => "Cabo Verde", + "kh" => "Cambodia", + "cm" => "Cameroon", + "ca" => "Canada", + "ky" => "Cayman Islands", + "cf" => "Central African Republic", + "td" => "Chad", + "cl" => "Chile", + "cn" => "China", + "co" => "Colombia", + "cg" => "Congo", + "cd" => "Congo, Democratic Republic", + "ck" => "Cook Islands", + "cr" => "Costa Rica", + "hr" => "Croatia", + "cu" => "Cuba", + "cy" => "Cyprus", + "cz" => "Czechia", + "ci" => "Côte d'Ivoire", + "dk" => "Denmark", + "dj" => "Djibouti", + "dm" => "Dominica", + "do" => "Dominican Republic", + "ec" => "Ecuador", + "eg" => "Egypt", + "sv" => "El Salvador", + "gq" => "Equatorial Guinea", + "ee" => "Estonia", + "et" => "Ethiopia", + "fo" => "Faroe Islands", + "fj" => "Fiji", + "fi" => "Finland", + "fr" => "France", + "gf" => "French Guiana", + "pf" => "French Polynesia", + "ga" => "Gabon", + "gm" => "Gambia", + "ge" => "Georgia", + "de" => "Germany", + "gh" => "Ghana", + "gi" => "Gibraltar", + "gr" => "Greece", + "gl" => "Greenland", + "gd" => "Grenada", + "gp" => "Guadeloupe", + "gu" => "Guam", + "gt" => "Guatemala", + "gg" => "Guernsey", + "gn" => "Guinea", + "gy" => "Guyana", + "ht" => "Haiti", + "hn" => "Honduras", + "hk" => "Hong Kong", + "hu" => "Hungary", + "is" => "Iceland", + "in" => "India", + "id" => "Indonesia", + "iq" => "Iraq", + "ie" => "Ireland", + "im" => "Isle of Man", + "il" => "Israel", + "it" => "Italy", + "jm" => "Jamaica", + "jp" => "Japan", + "je" => "Jersey", + "jo" => "Jordan", + "kz" => "Kazakhstan", + "ke" => "Kenya", + "ki" => "Kiribati", + "kw" => "Kuwait", + "kg" => "Kyrgyzstan", + "la" => "Lao People's Democratic Republic", + "lv" => "Latvia", + "lb" => "Lebanon", + "ls" => "Lesotho", + "ly" => "Libya", + "li" => "Liechtenstein", + "lt" => "Lithuania", + "lu" => "Luxembourg", + "mk" => "Macedonia", + "mg" => "Madagascar", + "mw" => "Malawi", + "my" => "Malaysia", + "mv" => "Maldives", + "ml" => "Mali", + "mt" => "Malta", + "mq" => "Martinique", + "mr" => "Mauritania", + "mu" => "Mauritius", + "yt" => "Mayotte", + "mx" => "Mexico", + "fm" => "Micronesia, Federated States of", + "md" => "Moldova", + "mc" => "Monaco", + "mn" => "Mongolia", + "me" => "Montenegro", + "ms" => "Montserrat", + "ma" => "Morocco", + "mz" => "Mozambique", + "mm" => "Myanmar", + "na" => "Namibia", + "nr" => "Nauru", + "np" => "Nepal", + "nl" => "Netherlands", + "nc" => "New Caledonia", + "nz" => "New Zealand", + "ni" => "Nicaragua", + "ne" => "Niger", + "ng" => "Nigeria", + "nu" => "Niue", + "no" => "Norway", + "om" => "Oman", + "pk" => "Pakistan", + "ps" => "Palestine, State of", + "pa" => "Panama", + "pg" => "Papua New Guinea", + "py" => "Paraguay", + "pe" => "Peru", + "ph" => "Philippines", + "pn" => "Pitcairn", + "pl" => "Poland", + "pt" => "Portugal", + "pr" => "Puerto Rico", + "qa" => "Qatar", + "ro" => "Romania", + "ru" => "Russian Federation", + "rw" => "Rwanda", + "re" => "Réunion", + "sh" => "Saint Helena", + "kn" => "Saint Kitts and Nevis", + "lc" => "Saint Lucia", + "vc" => "Saint Vincent and the Grenadines", + "ws" => "Samoa", + "sm" => "San Marino", + "st" => "Sao Tome and Principe", + "sa" => "Saudi Arabia", + "sn" => "Senegal", + "rs" => "Serbia", + "sc" => "Seychelles", + "sl" => "Sierra Leone", + "sg" => "Singapore", + "sk" => "Slovakia", + "si" => "Slovenia", + "sb" => "Solomon Islands", + "so" => "Somalia", + "kr" => "Sourth Korea", + "za" => "South Africa", + "es" => "Spain", + "lk" => "Sri Lanka", + "sr" => "Suriname", + "se" => "Sweden", + "ch" => "Switzerland", + "tw" => "Taiwan", + "tj" => "Tajikistan", + "tz" => "Tanzania", + "th" => "Thailand", + "tl" => "Timor-Leste", + "tg" => "Togo", + "tk" => "Tokelau", + "to" => "Tonga", + "tt" => "Trinidad and Tobago", + "tn" => "Tunisia", + "tr" => "Turkey", + "tm" => "Turkmenistan", + "ug" => "Uganda", + "ua" => "Ukraine", + "ae" => "United Arab Emirates", + "gb" => "United Kingdom", + "us" => "United States", + "uy" => "Uruguay", + "uz" => "Uzbekistan", + "vu" => "Vanuatu", + "ve" => "Venezuela", + "vn" => "Vietnam", + "vg" => "Virgin Islands, British", + "vi" => "Virgin Islands, U.S.", + "ye" => "Yemen", + "zm" => "Zambia", + "zw" => "Zimbabwe" + ] + ], + "nsfw" => [ + "display" => "NSFW", + "option" => [ + "yes" => "Yes", + "maybe" => "Maybe", + "no" => "No" + ] + ] + ]; + } + + private function get($url, $get = []){ + + $curlproc = curl_init(); + + if($get !== []){ + $get = http_build_query($get); + $url .= "?" . $get; + } + + 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:107.0) Gecko/20100101 Firefox/110.0", + "Accept: */*", + "Accept-Language: en-US,en;q=0.5", + "Accept-Encoding: gzip", + "DNT: 1", + "Origin: https://yep.com", + "Referer: https://yep.com/", + "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; + } + + public function image($get){ + + $search = $get["s"]; + $country = $get["country"]; + $nsfw = $get["nsfw"]; + + switch($nsfw){ + + case "yes": $nsfw = "off"; break; + case "maybe": $nsfw = "moderate"; break; + case "no": $nsfw = "strict"; break; + } + + $out = [ + "status" => "ok", + "npt" => null, + "image" => [] + ]; + + try{ + + $json = + json_decode( + $this->get( + "https://api.yep.com/fs/2/search", + [ + "client" => "web", + "gl" => $country == "all" ? $country : strtoupper($country), + "no_correct" => "false", + "q" => $search, + "safeSearch" => $nsfw, + "type" => "images" + ] + ), + true + ); + }catch(Exception $error){ + + throw new Exception("Failed to fetch JSON"); + } + + if($json === null){ + + throw new Exception("Failed to decode JSON"); + } + + foreach($json[1]["results"] as $item){ + + if( + $item["width"] !== 0 && + $item["height"] !== 0 + ){ + + $thumb_width = $item["width"] >= 260 ? 260 : $item["width"]; + $thumb_height = ceil($item["height"] * ($thumb_width / $item["width"])); + + $width = $item["width"]; + $height = $item["height"]; + }else{ + + $thumb_width = null; + $thumb_height = null; + $width = null; + $height = null; + } + + $out["image"][] = [ + "title" => $item["title"], + "source" => [ + [ + "url" => $item["image_id"], + "width" => $width, + "height" => $height + ], + [ + "url" => $item["src"], + "width" => $thumb_width, + "height" => $thumb_height + ] + ], + "url" => $item["host_page"] + ]; + } + + return $out; + } +} diff --git a/settings.php b/settings.php index bce3af0..98e6b49 100644 --- a/settings.php +++ b/settings.php @@ -161,6 +161,22 @@ $settings = [ [ "value" => "google", "text" => "Google" + ], + [ + "value" => "yep", + "text" => "Yep" + ], + /*[ + "value" => "pinterest", + "text" => "Pinterest" + ],*/ + [ + "value" => "imgur", + "text" => "Imgur" + ], + [ + "value" => "ftm", + "text" => "FindThatMeme" ] ] ], @@ -243,7 +259,7 @@ if($_POST){ // refresh cookie dates $loop = &$_COOKIE; } - + foreach($loop as $key => $value){ foreach($settings as $title){ @@ -262,7 +278,8 @@ foreach($loop as $key => $value){ "", [ "expires" => -1, // removes cookie - "samesite" => "Strict" + "samesite" => "Strict", + "path" => "/" ] ); diff --git a/static/style.css b/static/style.css index ec55624..fdb4951 100644 --- a/static/style.css +++ b/static/style.css @@ -283,7 +283,79 @@ h3,h4,h5,h6{ WEB */ -/* Left wrapper */ +/* Captcha */ +.captcha-wrapper{ + position:relative; + max-width:400px; + margin:17px auto 0; + border:1px solid var(--928374); +} + +.captcha{ + padding-bottom:100%; + padding-top:6.2%; +} + +.captcha-wrapper img{ + position:absolute; + top:0; + left:0; + width:100%; + height:100%; +} + +.captcha-controls{ + position:absolute; + top:0; + left:0; + bottom:0; + right:0; + top:6.3%; +} + +.captcha-wrapper img{ + display:block; + background:#282828; +} + +.captcha-wrapper input{ + display:none; +} + +.captcha-wrapper label{ + float:left; + width:25%; + height:25%; + position:relative; + cursor:pointer; +} + +.captcha-wrapper label:hover{ + background:rgba(255,255,255,0.2); +} + +.captcha-wrapper input:checked + label{ + background:rgba(0,0,0,0.5); +} + +.captcha-wrapper input:checked + label:after{ + content:""; + position:absolute; + left:39%; + top:29%; + width:20%; + height:30%; + transform:rotate(45deg); + border-right:7px solid var(--ebdbb2); + border-bottom:7px solid var(--ebdbb2); + box-sizing:border-box; +} + +.captcha-submit{ + float:right; + margin:12px 0 4px; +} + .web .left{ width:40%; float:left; @@ -294,6 +366,7 @@ h3,h4,h5,h6{ border:1px dashed var(--504945); padding:10px; margin-bottom:17px; + overflow:hidden; } .infobox .code{ diff --git a/videos.php b/videos.php index 2842703..ddc281c 100644 --- a/videos.php +++ b/videos.php @@ -10,11 +10,11 @@ $frontend = new frontend(); $get = $frontend->parsegetfilters($_GET, $filters); -$frontend->loadheader( - $get, - $filters, - "videos" -); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, $get, $filters, "videos", true); $payload = [ "class" => "", diff --git a/web.php b/web.php index e34672d..4df2609 100644 --- a/web.php +++ b/web.php @@ -10,11 +10,11 @@ $frontend = new frontend(); $get = $frontend->parsegetfilters($_GET, $filters); -$frontend->loadheader( - $get, - $filters, - "web" -); +/* + Captcha +*/ +include "lib/captcha_gen.php"; +new captcha($frontend, $get, $filters, true); $payload = [ "class" => "", @@ -44,6 +44,35 @@ try{ die(); } +/* + Prepend Oracle output, if applicable +*/ +include("oracles/encoder.php"); +include("oracles/calc.php"); +include("oracles/time.php"); +include("oracles/numerics.php"); +$oracles = [new calculator(), new encoder(), new time(), new numerics()]; +$fortune = ""; +foreach ($oracles as $oracle) { + if ($oracle->check_query($_GET["s"])) { + $resp = $oracle->generate_response($_GET["s"]); + if ($resp != "") { + $fortune .= "
"; + foreach ($resp as $title => $r) { + if ($title) { + $fortune .= "

".htmlspecialchars($title)."

".htmlspecialchars($r)."
"; + } + else { + $fortune .= "".$r."
"; + } + } + $fortune .= "Answer provided by oracle: ".$oracle->info["name"]."
"; + } + break; + } +} +$payload["left"] = $fortune; + $answerlen = 0; /* @@ -475,35 +504,6 @@ if($c !== 0){ $payload["left"] .= ''; } -/* - Prepend Oracle output, if applicable -*/ -include_once("oracles/encoder.php"); -include_once("oracles/calc.php"); -include_once("oracles/time.php"); -include_once("oracles/numerics.php"); -$oracles = [new calculator(), new encoder(), new time(), new numerics()]; -$fortune = ""; -foreach ($oracles as $oracle) { - if ($oracle->check_query($_GET["s"])) { - $resp = $oracle->generate_response($_GET["s"]); - if ($resp != "") { - $fortune .= "
"; - foreach ($resp as $title => $r) { - if ($title) { - $fortune .= "

".htmlspecialchars($title)."

".htmlspecialchars($r)."
"; - } - else { - $fortune .= "".$r."
"; - } - } - $fortune .= "Answer provided by oracle: ".$oracle->info["name"]."
"; - } - break; - } -} -$payload["left"] = $fortune . $payload["left"]; - /* Load next page */