' .
'
' .
' ' .
' ';
}else{
$esc =
explode(
".",
$host["host"],
2
);
if(
count($esc) === 2 &&
$esc[0] == "www"
){
$esc = $esc[1];
}else{
$esc = $esc[0];
}
$esc = substr($esc, 0, 2);
$urlencode = urlencode($link);
$payload =
'
' .
'
' .
' ' .
//' ' .
' ' .
'
';
$archives = [];
$fansites = [
[
"url" => "https://cum.st/creators?cq=",
"favicon" => "cum.st",
"favicon_alt" => "cu",
"title" => "OnlyHaven"
],
[
"url" => "https://pawchive.pw/artists?q=",
"favicon" => "pawchive.pw",
"favicon_alt" => "pa",
"title" => "Pawchive"
],
[
"url" => "https://bakemono.app/creators?q=",
"favicon" => "bakemono.app",
"favicon_alt" => "ba",
"title" => "Bakemono"
],
[
"url" => "https://kemono.cr/artists?q=",
"favicon" => "kemono.cr",
"favicon_alt" => "ke",
"title" => "Kemono"
],
[
"url" => "https://coomer.st/artists?q=",
"favicon" => "coomer.st",
"favicon_alt" => "co",
"title" => "Coomer"
]
];
// add website-specific archives
switch($host["host"]){
case "www.youtube.com":
case "music.youtube.com":
case "youtube.com":
case "m.youtube.com":
case "youtu.be":
if(
(
$host["host"] == "youtu.be" &&
isset($host["path"]) &&
preg_match(
'/^\/([A-Za-z0-9_-]+)/',
$host["path"],
$slug
)
) ||
(
isset($host["query"]) &&
preg_match(
'/v=([A-Za-z0-9_-]+)/',
$host["query"],
$slug
)
)
){
// for watch?v=slug
$archives[] = [
"url" => "https://findyoutubevideo.thetechrobo.ca/?q=" . $slug[1],
"favicon" => "findyoutubevideo.thetechrobo.ca",
"favicon_alt" => "fi",
"title" => "FindYouTubeVideo"
];
$archives[] = [
"url" => "https://filmot.com/video/" . $slug[1] . "/",
"favicon" => "filmot.com",
"favicon_alt" => "fi",
"title" => "Filmot (metadata only)"
];
}elseif(
preg_match(
'/^\/(?:channel)\/@?([A-Za-z0-9_-]+)/',
$host["path"],
$username
)
){
$archives[] = [
"url" => "https://filmot.com/channel/" . $username[1] . "/",
"favicon" => "filmot.com",
"favicon_alt" => "fi",
"title" => "Filmot (metadata only)"
];
}
break;
case "m.twitch.tv":
case "twitch.tv":
case "player.twitch.tv":
case "www.twitch.tv":
if(
isset($host["path"]) &&
preg_match(
'/^\/([A-Za-z0-9_.]+)/',
$host["path"],
$username
) &&
$username[1] != "videos"
){
$archives[] = [
"url" => "https://vodvod.top/channels/@" . $username[1],
"favicon" => "vodvod.top",
"favicon_alt" => "vo",
"title" => "vodvod"
];
$archives[] = [
"url" => "https://twitchtracker.com/" . $username[1],
"favicon" => "twitchtracker.com",
"favicon_alt" => "tw",
"title" => "TwitchTracker"
];
}
break;
case "kick.com":
case "player.kick.com":
if(
isset($host["path"]) &&
preg_match(
'/^\/([A-Za-z0-9_.]+)/',
$host["path"],
$username
)
){
$archives[] = [
"url" => "https://lick.lolcat.ca/channel?name=" . $username[1],
"favicon" => "lick.lolcat.ca",
"favicon_alt" => "li",
"title" => "lick"
];
$archives[] = [
"url" => "https://kicktracker.net/" . $username[1],
"favicon" => "kicktracker.net",
"favicon_alt" => "ki",
"title" => "Kick tracker"
];
}
break;
case "x.com":
case "twitter.com":
if(
isset($host["path"]) &&
preg_match(
'/^\/([A-Za-z0-9_.]+)/',
$host["path"],
$username
)
){
$archives[] = [
"url" => "https://web.archive.org/web/*/https://x.com/" . $username[1] . "/status*",
"favicon" => "archive.org",
"favicon_alt" => "ar",
"title" => "Archive.org: Tweets >2023"
];
$archives[] = [
"url" => "https://web.archive.org/web/*/https://twitter.com/" . $username[1] . "/status*",
"favicon" => "archive.org",
"favicon_alt" => "ar",
"title" => "Archive.org: Tweets <2023"
];
}
break;
case "www.instagram.com":
case "instagram.com":
if(
isset($host["path"]) &&
preg_match(
'/^\/([A-Za-z0-9_.]+)/',
$host["path"],
$username
) &&
$username[1] != "p"
){
$archives[] = [
"url" => "https://instarchiver.net/users?q=" . $username[1],
"favicon" => "instarchiver.net",
"favicon_alt" => "in",
"title" => "Instarchiver"
];
$archives[] = [
"url" => "https://www.storiesdb.ch/" . $username[1],
"favicon" => "www.storiesdb.ch",
"favicon_alt" => "st",
"title" => "StoriesDB"
];
}
break;
case "reddit.com":
case "old.reddit.com":
case "www.reddit.com":
if(isset($host["path"])){
// direct thread lookup
// https://ihsoyct.github.io/r/selfhosted/comments/16emfv0/4get_a_proxy_search_engine_that_doesnt_suck/?backend=pullpush
// https://ihsoyct.github.io/r/selfhosted/comments/16emfv0/4get_a_proxy_search_engine_that_doesnt_suck/?backend=artic_shift
if(
preg_match(
'/^\/r\/[^\/]+\/comments\/[^?&]+/',
$host["path"],
$slug
)
){
$archives[] = [
"url" => "https://ihsoyct.github.io{$slug[0]}?backend=artic_shift",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "Artic Shift"
];
$archives[] = [
"url" => "https://ihsoyct.github.io{$slug[0]}?backend=pullpush",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "PullPush"
];
}
// subreddit thread search
// https://ihsoyct.github.io/?subreddit=selfhosted&backend=artic_shift&mode=submissions&sort=desc
// https://ihsoyct.github.io/?subreddit=selfhosted&backend=pullpush&mode=submissions&sort=desc
// subreddit comment search
// https://ihsoyct.github.io/?subreddit=selfhosted&backend=artic_shift&mode=comments&sort=desc
// https://ihsoyct.github.io/?subreddit=selfhosted&backend=pullpush&mode=comments&sort=desc
elseif(
preg_match(
'/^\/r\/([^\/]+)\/(?:(?:search|wiki|about)\/?)?(?:$|\?|&)/',
$host["path"],
$slug
)
){
$archives[] = [
"url" => "https://ihsoyct.github.io/?subreddit={$slug[1]}&backend=artic_shift&mode=submissions&sort=desc",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "Artic Shift (Search threads)"
];
$archives[] = [
"url" => "https://ihsoyct.github.io/?subreddit={$slug[1]}&backend=pullpush&mode=submissions&sort=desc",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "PullPush (Search threads)"
];
$archives[] = [
"url" => "https://ihsoyct.github.io/?subreddit={$slug[1]}&backend=artic_shift&mode=comments&sort=desc",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "Artic Shift (Search comments)"
];
$archives[] = [
"url" => "https://ihsoyct.github.io/?subreddit={$slug[1]}&backend=pullpush&mode=comments&sort=desc",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "PullPush (Search comments)"
];
}
// user thread lookup
// https://ihsoyct.github.io/index.html?author=google&mode=submissions&backend=artic_shift
// https://ihsoyct.github.io/index.html?author=google&mode=submissions&backend=pullpush
// user comment lookup
// https://ihsoyct.github.io/index.html?author=google&mode=comments&backend=artic_shift
// https://ihsoyct.github.io/index.html?author=google&mode=comments&backend=pullpush
elseif(
preg_match(
'/^\/(?:u|user)\/([^\/]+)\/(?:(?:comments|submitted)\/?)?(?:$|\?|&)/',
$host["path"],
$slug
)
){
$archives[] = [
"url" => "https://ihsoyct.github.io/index.html?author={$slug[1]}&mode=submissions&backend=artic_shift",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "Artic Shift (Search threads)"
];
$archives[] = [
"url" => "https://ihsoyct.github.io/index.html?author={$slug[1]}&mode=submissions&backend=pullpush",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "PullPush (Search threads)"
];
$archives[] = [
"url" => "https://ihsoyct.github.io/index.html?author={$slug[1]}&mode=comments&backend=artic_shift",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "Artic Shift (Search comments)"
];
$archives[] = [
"url" => "https://ihsoyct.github.io/index.html?author={$slug[1]}&mode=comments&backend=pullpush",
"favicon" => "reddit.com",
"favicon_alt" => "re",
"title" => "PullPush (Search comments)"
];
}
}
break;
// https://cum.st/creators?cq= onlyfans fansly patreon
// https://pawchive.pw/artists?q= patreon pixiv/fanbox discord
// https://bakemono.app/creators?q= pixiv/fanbox fansly onlyfans patreon
// https://kemono.cr/artists?q= patreon pixiv/fanbox fantia afdian boosty gumroad subscribestar dlsite
// https://coomer.st/artists?q= onlyfans fansly candfans
// cant look up
/*
case "fantia.jp": // https://fantia.jp/fanclubs/
*/
// normal
case "onlyfans.com": // https://onlyfans.com/
case "fansly.com": // https://fansly.com/
case "fanbox.cc":
case "patreon.com": // https://www.patreon.com/
case "www.patreon.com":
case "boosty.to": // https://boosty.to/
case "www.subscribestar.com": // https://www.subscribestar.com/
case "subscribestar.com":
case "www.dlsite.com": // https://www.dlsite.com/ /
case "dlsite.com":
case "candfans.com": // https://candfans.com/
case "afdian.com": // https://afdian.com/a/
if(
isset($host["path"]) &&
(
(
$host["host"] == "afdian.com" &&
preg_match(
'/^\/a\/([A-Za-z0-9-_.]+)/',
$host["path"],
$username
)
) ||
preg_match(
'/^\/([A-Za-z0-9-_.]+)/',
$host["path"],
$username
)
)
){
foreach($fansites as $fansite){
$archives[] = [
"url" => $fansite["url"] . $this->sanitize_slug($username[1]),
"favicon" => $fansite["favicon"],
"favicon_alt" => $fansite["favicon_alt"],
"title" => $fansite["title"]
];
}
}
break;
}
// special fansite cases
if(
preg_match(
'/^([A-Za-z0-9_]+)\.(?:fanbox\.cc|gumroad\.com)/',
$host["host"],
$username
)
){
foreach($fansites as $fansite){
$archives[] = [
"url" => $fansite["url"] . $this->sanitize_slug($username[1]),
"favicon" => $fansite["favicon"],
"favicon_alt" => $fansite["favicon_alt"],
"title" => $fansite["title"]
];
}
}
// detect git
if(
(
stripos(
$host["host"],
"git."
) !== false ||
$host["host"] == "codeberg.org" ||
$host["host"] == "sourceforge.net" ||
$host["host"] == "github.com"
) &&
isset($host["path"]) &&
preg_match(
'/^(\/[^\/]+\/[^\/]+\/?)/',
$host["path"],
$edge
)
){
$archives[] = [
"url" => "https://archive.softwareheritage.org/browse/origin/directory/?origin_url=" . urlencode($host["scheme"] . "://" . $host["host"] . $edge[1]),
"favicon" => "www.softwareheritage.org",
"favicon_alt" => "so",
"title" => "SoftwareHeritage"
];
}
$archives =
array_merge(
$archives,
[
[
"url" => "https://web.archive.org/web/" . $urlencode,
"favicon" => "archive.org",
"favicon_alt" => "ar",
"title" => "Archive.org"
],
[
"url" => "https://archive.ph/newest/" . htmlspecialchars($link),
"favicon" => "archive.ph",
"favicon_alt" => "ar",
"title" => "Archive.ph"
],
[
"url" => "https://yandex.com/search/?text=" . urlencode("url:" . $link),
"favicon" => "yandex.com",
"favicon_alt" => "ya",
"title" => "Yandex cache"
],
[
"url" => "https://ghostarchive.org/search?term=" . $urlencode,
"favicon" => "ghostarchive.org",
"favicon_alt" => "gh",
"title" => "Ghostarchive"
],
[
"url" => "https://arquivo.pt/wayback/" . htmlspecialchars($link),
"favicon" => "arquivo.pt",
"favicon_alt" => "ar",
"title" => "Arquivo.pt"
],
[
"url" => "https://megalodon.jp/?url=" . $urlencode,
"favicon" => "megalodon.jp",
"favicon_alt" => "me",
"title" => "Megalodon"
],
[
"url" => "https://www.webcitation.org/query?url=" . $urlencode,
"favicon" => "webcitation.org",
"favicon_alt" => "we",
"title" => "Webcitation"
]
]
);
foreach($archives as $archive){
$payload .= ' ' . $archive["title"] . ' ';
}
$payload .= ' ';
}
/*
Draw link
*/
$parts = explode("/", $link);
$clickurl = "";
// remove trailing /
$c = count($parts) - 1;
if($parts[$c] == ""){
$parts[$c - 1] = $parts[$c - 1] . "/";
unset($parts[$c]);
}
// merge https://site together
if(isset($host["host"])){
$parts = [
$parts[0] . $parts[1] . '//' . $parts[2],
...array_slice($parts, 3, count($parts) - 1)
];
}
$c = count($parts);
for($i=0; $i<$c; $i++){
if($i !== 0){ $clickurl .= "/"; }
$clickurl .= $parts[$i];
if($i === $c - 1){
$parts[$i] = rtrim($parts[$i], "/");
}
$payload .=
'
' .
htmlspecialchars(urldecode($parts[$i])) .
' ';
if($i !== $c - 1){
$payload .= '
';
}
}
return $payload . '
';
}
public function getscraperfilters($page){
// hack: enable error reporting if configured
if(config::DISPLAY_ERRORS === true){
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
}
$get_scraper = isset($_COOKIE["scraper_$page"]) ? $_COOKIE["scraper_$page"] : null;
if(
isset($_GET["scraper"]) &&
is_string($_GET["scraper"])
){
$get_scraper = $_GET["scraper"];
}else{
if(
isset($_GET["npt"]) &&
is_string($_GET["npt"])
){
$get_scraper = explode(".", $_GET["npt"], 2)[0];
$get_scraper =
preg_replace(
'/[0-9]+$/',
"",
$get_scraper
);
}
}
// add search field
$filters =
[
"s" => [
"option" => "_SEARCH"
]
];
// define default scrapers
switch($page){
case "web":
$filters["scraper"] = [
"display" => "Scraper",
"option" => [
//"fget" => "fget",
"ddg" => "DuckDuckGo",
//"yahoo" => "Yahoo!",
"brave" => "Brave",
"yandex" => "Yandex",
"google" => "Google",
"google_api" => "Google API",
"google_cse" => "Google CSE",
"yahoo_japan" => "Yahoo! JAPAN",
"startpage" => "Startpage",
"yep" => "Yep",
"mwmbl" => "Mwmbl",
"mojeek" => "Mojeek",
"naver" => "Naver",
"baidu" => "Baidu",
"coccoc" => "Cốc Cốc",
"solofield" => "Solofield",
"marginalia" => "Marginalia",
"purili" => "Purili",
"wiby" => "wiby"
]
];
break;
case "images":
$filters["scraper"] = [
"display" => "Scraper",
"option" => [
"ddg" => "DuckDuckGo",
"yandex" => "Yandex",
"brave" => "Brave",
"google" => "Google",
"google_api" => "Google API",
"google_cse" => "Google CSE",
"yahoo_japan" => "Yahoo! JAPAN",
"startpage" => "Startpage",
"naver" => "Naver",
"baidu" => "Baidu",
"solofield" => "Solofield",
"pinterest" => "Pinterest",
"flickr" => "Flickr",
"pexels" => "Pexels",
"pixabay" => "Pixabay",
"unsplash" => "Unsplash",
"fivehpx" => "500px",
"vsco" => "VSCO",
"imgur" => "Imgur",
"ftm" => "FindThatMeme"
]
];
break;
case "videos":
$filters["scraper"] = [
"display" => "Scraper",
"option" => [
"yt" => "YouTube",
//"archiveorg" => "Archive.org",
//"dailymotion" => "Dailymotion",
"vimeo" => "Vimeo",
//"odysee" => "Odysee",
"sepiasearch" => "Sepia Search",
//"fb" => "Facebook videos",
"ddg" => "DuckDuckGo",
"brave" => "Brave",
"yandex" => "Yandex",
"google" => "Google",
"yahoo_japan" => "Yahoo! JAPAN",
"startpage" => "Startpage",
"naver" => "Naver",
"baidu" => "Baidu",
"coccoc" => "Cốc Cốc",
"purili" => "Purili",
"solofield" => "Solofield"
]
];
break;
case "news":
$filters["scraper"] = [
"display" => "Scraper",
"option" => [
"ddg" => "DuckDuckGo",
"brave" => "Brave",
"google" => "Google",
"yahoo_japan" => "Yahoo! JAPAN",
"startpage" => "Startpage",
//"mojeek" => "Mojeek",
"baidu" => "Baidu"
]
];
break;
case "music":
$filters["scraper"] = [
"display" => "Scraper",
"option" => [
"sc" => "SoundCloud",
"swisscows" => "Swisscows (SoundCloud)"
//"spotify" => "Spotify"
]
];
break;
case "booru":
$filters["scraper"] = [
"display" => "Scraper",
"option" => [
"safebooru" => "Safebooru",
"konachan" => "Konachan",
"tbib" => "The Big Imageboard",
"gelbooru" => "Gelbooru",
"yandere" => "Yande.re",
"tbib" => "The Big Imageboard",
"sankakucomplex" => "SankakuComplex",
"soybooru" => "SoyBooru"
]
];
break;
}
// get scraper name from user input, or default out to preferred scraper
$scraper_out = null;
$first = true;
foreach($filters["scraper"]["option"] as $scraper_name => $scraper_pretty){
if($first === true){
$first = $scraper_name;
}
if($scraper_name == $get_scraper){
$scraper_out = $scraper_name;
}
}
if($scraper_out === null){
$scraper_out = $first;
}
include "scraper/$scraper_out.php";
$lib = new $scraper_out();
// set scraper on $_GET
$_GET["scraper"] = $scraper_out;
// set nsfw on $_GET
if(
isset($_COOKIE["nsfw"]) &&
!isset($_GET["nsfw"])
){
$_GET["nsfw"] = $_COOKIE["nsfw"];
}
return
[
$lib,
array_merge_recursive(
$filters,
$lib->getfilters($page)
)
];
}
public function parsegetfilters($parameters, $whitelist){
$sanitized = [];
// add npt token
if(
isset($parameters["npt"]) &&
is_string($parameters["npt"])
){
$sanitized["npt"] = $parameters["npt"];
}else{
$sanitized["npt"] = false;
}
// we're iterating over $whitelist, so
// you can't polluate $sanitized with useless
// parameters
foreach($whitelist as $parameter => $value){
if(isset($parameters[$parameter])){
if(!is_string($parameters[$parameter])){
$sanitized[$parameter] = null;
continue;
}
// parameter is already set, use that value
$sanitized[$parameter] = $parameters[$parameter];
}else{
// parameter is not set, add it
if(is_string($value["option"])){
// special field: set default value manually
switch($value["option"]){
case "_DATE":
// no date set
$sanitized[$parameter] = false;
break;
case "_SEARCH":
// no search set
$sanitized[$parameter] = "";
break;
}
}else{
// set a default value
$sanitized[$parameter] = array_keys($value["option"])[0];
}
}
// sanitize input
if(is_array($value["option"])){
if(
!in_array(
$sanitized[$parameter],
$keys = array_keys($value["option"])
)
){
$sanitized[$parameter] = $keys[0];
}
}else{
// sanitize search & string
switch($value["option"]){
case "_DATE":
if($sanitized[$parameter] !== false){
$sanitized[$parameter] = strtotime($sanitized[$parameter]);
if($sanitized[$parameter] <= 0){
$sanitized[$parameter] = false;
}
}
break;
case "_SEARCH":
// get search string
$sanitized["s"] = trim($sanitized[$parameter]);
}
}
}
// invert dates if needed
if(
isset($sanitized["older"]) &&
isset($sanitized["newer"]) &&
$sanitized["newer"] !== false &&
$sanitized["older"] !== false &&
$sanitized["newer"] > $sanitized["older"]
){
// invert
[
$sanitized["older"],
$sanitized["newer"]
] = [
$sanitized["newer"],
$sanitized["older"]
];
}
return $sanitized;
}
public function s_to_timestamp($seconds){
if(is_string($seconds)){
return "LIVE";
}
return ($seconds >= 60) ? ltrim(gmdate("H:i:s", $seconds), ":0") : gmdate("0:s", $seconds);
}
public function generatehtmltabs($page, $query){
$html = null;
//foreach(["web", "images", "videos", "news", "music", "booru"] as $type){
foreach(["web", "images", "videos", "news", "music"] as $type){
$html .= '
' . ucfirst($type) . ' ';
}
return $html;
}
public function generatehtmlfilters($filters, $params){
$html = null;
foreach($filters as $filter_name => $filter_values){
if(!isset($filter_values["display"])){
continue;
}
$output = true;
$tmp =
'
' .
'
' . htmlspecialchars($filter_values["display"]) . '
';
if(is_array($filter_values["option"])){
$tmp .= '
';
foreach($filter_values["option"] as $option_name => $option_title){
$tmp .= '';
}
$tmp .= ' ';
}else{
switch($filter_values["option"]){
case "_DATE":
$tmp .= '
$value){
if(
$value == null ||
$value == false ||
$key == "npt" ||
$key == "extendedsearch" ||
$value == "any" ||
$value == "all" ||
$key == "spellcheck" ||
(
$ommit === true &&
$key == "s"
)
){
continue;
}
if(
$key == "older" ||
$key == "newer"
){
$value = date("Y-m-d", (int)$value);
}
$out[$key] = $value;
}
return http_build_query($out);
}
private function sanitize_slug($username){
return urlencode(preg_replace('/[-_]/', " ", $username));
}
public function increment_real_reqs($scraper){
apcu_inc(intdiv(time(), 3600) . ".real_requests", 1, $s, 262800);
apcu_inc(intdiv(time(), 3600) . ".$scraper.requests", 1, $s, 262800);
}
public function htmlimage($image, $format){
if(
preg_match(
'/^data:/',
$image
)
){
return htmlspecialchars($image);
}
//return "https://4get.ca/proxy?i=" . urlencode($image) . "&s=" . $format;
return "/proxy?i=" . urlencode($image) . "&s=" . $format;
}
public function htmlnextpage($gets, $npt, $page){
$query = $this->buildquery($gets);
return $page . "?" . $query . "&npt=" . $npt;
}
}