Explorar el Código

captcha and imgur, findthatmeme, yep imagesearch

lolcat hace 2 años
padre
commit
3aa0180774
Se han modificado 26 ficheros con 1710 adiciones y 63 borrados
  1. 19 1
      README.md
  2. 0 0
      admin.php
  3. 17 1
      api.txt
  4. 2 2
      api/v1/ac.php
  5. 6 0
      api/v1/images.php
  6. 7 0
      api/v1/music.php
  7. 6 0
      api/v1/news.php
  8. 6 0
      api/v1/videos.php
  9. 6 0
      api/v1/web.php
  10. 147 0
      captcha.php
  11. BIN
      data/captcha/font.ttf
  12. 5 5
      images.php
  13. 325 0
      lib/captcha_gen.php
  14. 5 0
      lib/curlproxy.php
  15. 25 1
      lib/frontend.php
  16. 1 1
      lib/nextpage.php
  17. 5 5
      music.php
  18. 5 5
      news.php
  19. 148 0
      scraper/ftm.php
  20. 249 0
      scraper/imgur.php
  21. 224 0
      scraper/pinterest.php
  22. 370 0
      scraper/yep.php
  23. 19 2
      settings.php
  24. 74 1
      static/style.css
  25. 5 5
      videos.php
  26. 34 34
      web.php

+ 19 - 1
README.md

@@ -23,6 +23,9 @@ https://4get.ca
 	- Yandex
 	- Yandex
 	- Google
 	- Google
 	- Brave
 	- Brave
+	- Yep
+	- Imgur
+	- FindThatMeme
 
 
 3. Videos
 3. Videos
 	- YouTube
 	- YouTube
@@ -39,6 +42,17 @@ https://4get.ca
 5. Music
 5. Music
 	- SoundCloud
 	- 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.
 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
 # 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.
 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
 ## Tor Setup
 
 

+ 0 - 0
admin.php


+ 17 - 1
api.txt

@@ -30,12 +30,25 @@
     under your own terms. Please respect the terms of use listed here so
     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.
     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 ::
     Get your instance running here ::
         https://git.lolcat.ca/lolcat/4get
         https://git.lolcat.ca/lolcat/4get
 
 
     Thanks!
     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
 + Decode the data
     All payloads returned by the API are encoded in the JSON format. If
     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
     you don't know how to tackle the problem, maybe programming is not
@@ -47,8 +60,11 @@
 + Check if an API call was successful
 + Check if an API call was successful
     All API responses come with an array index named "status". If the
     All API responses come with an array index named "status". If the
     status is something else than the string "ok", something went wrong.
     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
 + Get the next page of results

+ 2 - 2
api/v1/ac.php

@@ -17,7 +17,7 @@ class autocomplete{
 			"yep" => "https://api.yep.com/ac/?query={searchTerms}",
 			"yep" => "https://api.yep.com/ac/?query={searchTerms}",
 			"marginalia" => "https://search.marginalia.nu/suggest/?partial={searchTerms}",
 			"marginalia" => "https://search.marginalia.nu/suggest/?partial={searchTerms}",
 			"yt" => "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&q={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){
 				foreach($js[1] as $item){
 					
 					
-					$json[] = strip_tags($item[0]);
+					$json[] = htmlspecialchars_decode(strip_tags($item[0]));
 				}
 				}
 				
 				
 				echo json_encode(
 				echo json_encode(

+ 6 - 0
api/v1/images.php

@@ -7,6 +7,12 @@ chdir("../../");
 include "lib/frontend.php";
 include "lib/frontend.php";
 $frontend = new frontend();
 $frontend = new frontend();
 
 
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, false);
+
 [$scraper, $filters] = $frontend->getscraperfilters(
 [$scraper, $filters] = $frontend->getscraperfilters(
 	"images",
 	"images",
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null

+ 7 - 0
api/v1/music.php

@@ -7,6 +7,13 @@ chdir("../../");
 include "lib/frontend.php";
 include "lib/frontend.php";
 $frontend = new frontend();
 $frontend = new frontend();
 
 
+/*
+	Captcha
+*/
+$null = null;
+include "lib/captcha_gen.php";
+new captcha($null, $null, $null, $null, false);
+
 [$scraper, $filters] = $frontend->getscraperfilters(
 [$scraper, $filters] = $frontend->getscraperfilters(
 	"music",
 	"music",
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null

+ 6 - 0
api/v1/news.php

@@ -7,6 +7,12 @@ chdir("../../");
 include "lib/frontend.php";
 include "lib/frontend.php";
 $frontend = new frontend();
 $frontend = new frontend();
 
 
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, false);
+
 [$scraper, $filters] = $frontend->getscraperfilters(
 [$scraper, $filters] = $frontend->getscraperfilters(
 	"news",
 	"news",
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null

+ 6 - 0
api/v1/videos.php

@@ -7,6 +7,12 @@ chdir("../../");
 include "lib/frontend.php";
 include "lib/frontend.php";
 $frontend = new frontend();
 $frontend = new frontend();
 
 
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, false);
+
 [$scraper, $filters] = $frontend->getscraperfilters(
 [$scraper, $filters] = $frontend->getscraperfilters(
 	"videos",
 	"videos",
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null

+ 6 - 0
api/v1/web.php

@@ -7,6 +7,12 @@ chdir("../../");
 include "lib/frontend.php";
 include "lib/frontend.php";
 $frontend = new frontend();
 $frontend = new frontend();
 
 
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, false);
+
 [$scraper, $filters] = $frontend->getscraperfilters(
 [$scraper, $filters] = $frontend->getscraperfilters(
 	"web",
 	"web",
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null
 	isset($_GET["scraper"]) ? $_GET["scraper"] : null

+ 147 - 0
captcha.php

@@ -0,0 +1,147 @@
+<?php
+
+if(
+	!isset($_GET["k"]) ||
+	preg_match(
+		'/^c\.[0-9]+$/',
+		$_GET["k"]
+	)
+){
+	
+	header("Content-Type: text/plain");
+	echo "Fuck you";
+	die();
+}
+
+header("Content-Type: image/jpeg");
+
+$grid = apcu_fetch($_GET["k"]);
+
+if(
+	$grid === false ||
+	$grid[3] === true // has already been generated
+){
+	
+	http_response_code(304); // not modified
+	die();
+}
+
+header("Last-Modified: Thu, 01 Oct 1970 00:00:00 GMT");
+
+// only generate one captcha with this config
+apcu_store(
+	$_GET["k"],
+	[
+		$grid[0],
+		$grid[1],
+		$grid[2],
+		true // has captcha been generated?
+	],
+	120 // we give user another 2 minutes to solve
+);
+
+// generate image
+
+if(random_int(0,1) === 0){
+	
+	$theme = [
+		"bg" => "#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; $i<strlen($text); $i++){
+	
+	$im->annotateImage(
+		$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();

BIN
data/captcha/font.ttf


+ 5 - 5
images.php

@@ -10,11 +10,11 @@ $frontend = new frontend();
 
 
 $get = $frontend->parsegetfilters($_GET, $filters);
 $get = $frontend->parsegetfilters($_GET, $filters);
 
 
-$frontend->loadheader(
-	$get,
-	$filters,
-	"images"
-);
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, $get, $filters, "images", true);
 
 
 $payload = [
 $payload = [
 	"images" => "",
 	"images" => "",

+ 325 - 0
lib/captcha_gen.php

@@ -0,0 +1,325 @@
+<?php
+
+class captcha{
+	
+	public function __construct($frontend, $get, $filters, $page, $output){
+		
+		/*
+			Validate cookie, if it exists
+		*/
+		if(isset($_COOKIE["pass"])){
+			
+			if(
+				// check if key is not malformed
+				preg_match(
+					'/^c[0-9]+\.[A-Za-z0-9]{20}$/',
+					$_COOKIE["pass"]
+				) &&
+				// does key exist
+				apcu_exists($_COOKIE["pass"])
+			){
+					
+				// exists, increment counter
+				$inc = apcu_inc($_COOKIE["pass"]);
+				
+				// we start counting from 1
+				// when it has been incremented to 102, it has reached
+				// 100 reqs
+				if($inc >= 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<count($key[0]); $i++){
+				
+				if(!in_array($i, $answers)){
+					
+					continue;
+				}
+				
+				if($key[0][$i][0] == $key[2]){
+					
+					$check--;
+				}else{
+					
+					// got a wrong answer
+					$check = -1;
+					break;
+				}
+			}
+			
+			if($check === 0){
+				
+				// we passed the captcha
+				// set cookie
+				$inc = apcu_inc("cookie");
+				$chars =
+					array_merge(
+						range("A", "Z"),
+						range("a", "z"),
+						range(0, 9)
+					);
+				
+				$c = count($chars) - 1;
+				
+				$key = "c" . $inc . ".";
+				
+				for($i=0; $i<20; $i++){
+					
+					$key .= $chars[random_int(0, $c)];
+				}
+				
+				apcu_inc($key, 1, $stupid, 86400);
+				
+				setcookie(
+					"pass",
+					$key,
+					[
+						"expires" => time() + 86400, // expires in 24 hours
+						"samesite" => "Strict",
+						"path" => "/"
+					]
+				);
+				
+				$frontend->loadheader(
+					$get,
+					$filters,
+					$page
+				);
+				return;
+				
+			}else{
+				
+				$error = "<div class=\"quote\">You were <a href=\"https://www.youtube.com/watch?v=e1d7fkQx2rk\" target=\"_BLANK\" rel=\"noreferrer nofollow\">kicked out of Mensa.</a> Please try again.</div>";
+			}
+		}
+
+		/*
+			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<random_int(3, 6); $i++){
+			
+			$answer_pos_tmp =
+				array_splice(
+					$range,
+					random_int(
+						0,
+						14 - $i
+					),
+					1
+				);
+			
+			$answer_pos[] = $answer_pos_tmp[0];
+		}
+
+		// choose a dataset
+		$choosen = &$dataset[random_int(0, count($dataset) - 1)];
+		$choices = [];
+
+		for($i=0; $i<count($dataset); $i++){
+			
+			if($dataset[$i][0] == $choosen[0]){
+				
+				continue;
+			}
+			
+			$choices[] = $dataset[$i];
+		}
+
+		// generate grid data
+		$grid = [];
+
+		for($i=0; $i<16; $i++){
+			
+			if(in_array($i, $answer_pos)){
+				
+				$grid[] = $choosen;
+			}else{
+				
+				$grid[] = $choices[random_int(0, count($choices) - 1)];
+			}
+		}
+
+		$key = "c." . apcu_inc("captcha_gen", 1) . "." . random_int(0, 100000000);
+
+		apcu_store(
+			$key,
+			[
+				$grid,
+				count($answer_pos),
+				$choosen[0],
+				false // has captcha been generated?
+			],
+			120 // we give user 2 minutes to get captcha, in case of network error
+		);
+		
+		$payload = [
+			"class" => "",
+			"right-left" => "",
+			"right-right" => "",
+			"left" =>
+				'<div class="infobox">' .
+					'<h1>IQ test</h1>' .
+					'Due to getting hit with 20,000 bot requests per day, I had to put this up. Sorry.<br><br>' .
+					'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 .
+					'<form method="POST" enctype="text/plain" autocomplete="off">' .
+						'<div class="captcha-wrapper">' .
+							'<div class="captcha">' .
+								'<img src="captcha.php?k=' . $key . '" alt="Captcha image">' .
+								'<div class="captcha-controls">' .
+									'<input type="checkbox" name="c[0]" id="c0">' .
+									'<label for="c0"></label>' .
+									'<input type="checkbox" name="c[1]" id="c1">' .
+									'<label for="c1"></label>' .
+									'<input type="checkbox" name="c[2]" id="c2">' .
+									'<label for="c2"></label>' .
+									'<input type="checkbox" name="c[3]" id="c3">' .
+									'<label for="c3"></label>' .
+									'<input type="checkbox" name="c[4]" id="c4">' .
+									'<label for="c4"></label>' .
+									'<input type="checkbox" name="c[5]" id="c5">' .
+									'<label for="c5"></label>' .
+									'<input type="checkbox" name="c[6]" id="c6">' .
+									'<label for="c6"></label>' .
+									'<input type="checkbox" name="c[7]" id="c7">' .
+									'<label for="c7"></label>' .
+									'<input type="checkbox" name="c[8]" id="c8">' .
+									'<label for="c8"></label>' .
+									'<input type="checkbox" name="c[9]" id="c9">' .
+									'<label for="c9"></label>' .
+									'<input type="checkbox" name="c[10]" id="c10">' .
+									'<label for="c10"></label>' .
+									'<input type="checkbox" name="c[11]" id="c11">' .
+									'<label for="c11"></label>' .
+									'<input type="checkbox" name="c[12]" id="c12">' .
+									'<label for="c12"></label>' .
+									'<input type="checkbox" name="c[13]" id="c13">' .
+									'<label for="c13"></label>' .
+									'<input type="checkbox" name="c[14]" id="c14">' .
+									'<label for="c14"></label>' .
+									'<input type="checkbox" name="c[15]" id="c15">' .
+									'<label for="c15"></label>' .
+								'</div>' .
+							'</div>' .
+						'</div>' .
+						'<input type="hidden" name="k" value="' . $key . '">' .
+						'<input type="submit" value="Check IQ" class="captcha-submit">' .
+					'</form>' .
+				'</div>'
+		];
+		
+		http_response_code(429); // too many reqs
+		$frontend->loadheader(
+			$get,
+			$filters,
+			"web"
+		);
+		
+		echo $frontend->load("search.html", $payload);
+		die();
+	}
+}

+ 5 - 0
lib/curlproxy.php

@@ -127,6 +127,11 @@ class proxy{
 			throw new Exception("Too many redirects");
 			throw new Exception("Too many redirects");
 		}
 		}
 		
 		
+		if($url == "https://i.imgur.com/removed.png"){
+			
+			throw new Exception("Encountered imgur 404");
+		}
+		
 		// sanitize URL
 		// sanitize URL
 		if($this->validateurl($url) === false){
 		if($this->validateurl($url) === false){
 			
 			

+ 25 - 1
lib/frontend.php

@@ -901,7 +901,11 @@ class frontend{
 						"ddg" => "DuckDuckGo",
 						"ddg" => "DuckDuckGo",
 						"yandex" => "Yandex",
 						"yandex" => "Yandex",
 						"brave" => "Brave",
 						"brave" => "Brave",
-						"google" => "Google"
+						"google" => "Google",
+						"yep" => "Yep",
+						//"pinterest" => "Pinterest",
+						"imgur" => "Imgur",
+						"ftm" => "FindThatMeme"
 					]
 					]
 				];
 				];
 				break;
 				break;
@@ -1011,10 +1015,30 @@ class frontend{
 				$lib = new wiby();
 				$lib = new wiby();
 				break;
 				break;
 			
 			
+			case "yep":
+				include "scraper/yep.php";
+				$lib = new yep();
+				break;
+			
 			case "sc":
 			case "sc":
 				include "scraper/sc.php";
 				include "scraper/sc.php";
 				$lib = new sc();
 				$lib = new sc();
 				break;
 				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
 		// set scraper on $_GET

+ 1 - 1
lib/nextpage.php

@@ -26,7 +26,7 @@ class nextpage{
 		apcu_store(
 		apcu_store(
 			$page . "." .
 			$page . "." .
 			$this->scraper .
 			$this->scraper .
-			(string)($key),
+			(string)$key,
 			gzdeflate($salt.$iv.$out.$tag),
 			gzdeflate($salt.$iv.$out.$tag),
 			900 // cache information for 15 minutes blaze it
 			900 // cache information for 15 minutes blaze it
 		);
 		);

+ 5 - 5
music.php

@@ -10,11 +10,11 @@ $frontend = new frontend();
 
 
 $get = $frontend->parsegetfilters($_GET, $filters);
 $get = $frontend->parsegetfilters($_GET, $filters);
 
 
-$frontend->loadheader(
-	$get,
-	$filters,
-	"music"
-);
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, $get, $filters, "music", true);
 
 
 $payload = [
 $payload = [
 	"class" => "",
 	"class" => "",

+ 5 - 5
news.php

@@ -10,11 +10,11 @@ $frontend = new frontend();
 
 
 $get = $frontend->parsegetfilters($_GET, $filters);
 $get = $frontend->parsegetfilters($_GET, $filters);
 
 
-$frontend->loadheader(
-	$get,
-	$filters,
-	"news"
-);
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, $get, $filters, "news", true);
 
 
 $payload = [
 $payload = [
 	"class" => "",
 	"class" => "",

+ 148 - 0
scraper/ftm.php

@@ -0,0 +1,148 @@
+<?php
+
+class ftm{
+	
+	public function __construct(){
+		
+		include "lib/nextpage.php";
+		$this->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;
+	}
+}

+ 249 - 0
scraper/imgur.php

@@ -0,0 +1,249 @@
+<?php
+
+class imgur{
+	
+	public function __construct(){
+		
+		include "lib/nextpage.php";
+		$this->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;
+	}
+}

+ 224 - 0
scraper/pinterest.php

@@ -0,0 +1,224 @@
+<?php
+
+class pinterest{
+	
+	public function __construct(){
+		
+		include "lib/nextpage.php";
+		$this->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
+			);
+	}
+}

+ 370 - 0
scraper/yep.php

@@ -0,0 +1,370 @@
+<?php
+
+class yep{
+	
+	public function __construct(){
+		
+		include "lib/nextpage.php";
+		$this->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;
+	}
+}

+ 19 - 2
settings.php

@@ -161,6 +161,22 @@ $settings = [
 					[
 					[
 						"value" => "google",
 						"value" => "google",
 						"text" => "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
 	// refresh cookie dates
 	$loop = &$_COOKIE;
 	$loop = &$_COOKIE;
 }
 }
-	
+
 foreach($loop as $key => $value){
 foreach($loop as $key => $value){
 	
 	
 	foreach($settings as $title){
 	foreach($settings as $title){
@@ -262,7 +278,8 @@ foreach($loop as $key => $value){
 					"",
 					"",
 					[
 					[
 						"expires" => -1, // removes cookie
 						"expires" => -1, // removes cookie
-						"samesite" => "Strict"
+						"samesite" => "Strict",
+						"path" => "/"
 					]
 					]
 				);
 				);
 				
 				

+ 74 - 1
static/style.css

@@ -283,7 +283,79 @@ h3,h4,h5,h6{
 	WEB
 	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{
 .web .left{
 	width:40%;
 	width:40%;
 	float:left;
 	float:left;
@@ -294,6 +366,7 @@ h3,h4,h5,h6{
 	border:1px dashed var(--504945);
 	border:1px dashed var(--504945);
 	padding:10px;
 	padding:10px;
 	margin-bottom:17px;
 	margin-bottom:17px;
+	overflow:hidden;
 }
 }
 
 
 .infobox .code{
 .infobox .code{

+ 5 - 5
videos.php

@@ -10,11 +10,11 @@ $frontend = new frontend();
 
 
 $get = $frontend->parsegetfilters($_GET, $filters);
 $get = $frontend->parsegetfilters($_GET, $filters);
 
 
-$frontend->loadheader(
-	$get,
-	$filters,
-	"videos"
-);
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, $get, $filters, "videos", true);
 
 
 $payload = [
 $payload = [
 	"class" => "",
 	"class" => "",

+ 34 - 34
web.php

@@ -10,11 +10,11 @@ $frontend = new frontend();
 
 
 $get = $frontend->parsegetfilters($_GET, $filters);
 $get = $frontend->parsegetfilters($_GET, $filters);
 
 
-$frontend->loadheader(
-	$get,
-	$filters,
-	"web"
-);
+/*
+	Captcha
+*/
+include "lib/captcha_gen.php";
+new captcha($frontend, $get, $filters, true);
 
 
 $payload = [
 $payload = [
 	"class" => "",
 	"class" => "",
@@ -44,6 +44,35 @@ try{
 	die();
 	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 .= "<div class=\"infobox\">";
+			foreach ($resp as $title => $r) {
+				if ($title) {
+					$fortune .= "<h3>".htmlspecialchars($title)."</h3><div class=\"code\">".htmlspecialchars($r)."</div>";
+				}
+				else {
+					$fortune .= "<i>".$r."</i><br>";
+				}
+			}
+			$fortune .= "<small>Answer provided by oracle: ".$oracle->info["name"]."</small></div>";
+		}
+		break;
+	}
+}
+$payload["left"] = $fortune;
+
 $answerlen = 0;
 $answerlen = 0;
 
 
 /*
 /*
@@ -475,35 +504,6 @@ if($c !== 0){
 	$payload["left"] .= '</table>';
 	$payload["left"] .= '</table>';
 }
 }
 
 
-/*
-	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 .= "<div class=\"infobox\">";
-			foreach ($resp as $title => $r) {
-				if ($title) {
-					$fortune .= "<h3>".htmlspecialchars($title)."</h3><div class=\"code\">".htmlspecialchars($r)."</div>";
-				}
-				else {
-					$fortune .= "<i>".$r."</i><br>";
-				}
-			}
-			$fortune .= "<small>Answer provided by oracle: ".$oracle->info["name"]."</small></div>";
-		}
-		break;
-	}
-}
-$payload["left"] = $fortune . $payload["left"];
-
 /*
 /*
 	Load next page
 	Load next page
 */
 */