From 86d0d57dc823f1c3e02f206bc1a73da2a5b482ba Mon Sep 17 00:00:00 2001 From: Arnan de Gans Date: Thu, 21 Dec 2023 04:02:42 -0600 Subject: [PATCH] Version 1.1 A quality of life update - Mostly. 1.1 - December 21, 2023 - [new] API search for EZTV TV Shows. - [new] config.default.php with default settings. - [new] New option 'imdb_id_search' in 'special' settings in config.php. - [new] New option 'show_zero_seeders' in config.php. - [new] Special result and torrent redirect for IMDb IDs. - [new] Replaced image search with Yahoo! Images. - [new] Styled 'reset' button for search fields. - [tweak] Removed 'raw_output' option. - [tweak] Re-arranged results array to be more logical/easy to use. - [tweak] Re-arranged code for results to do no double checks for search results. - [tweak] Added more user-agents. - [tweak] Torrent results page. - [tweak] Sanitize scraped data earlier in the process. - [tweak] Consistent single quotes for arrays. - [tweak] Consistent spaces, tabs and newlines. - [fix] Inconsistent input height for search field vs search button. - [fix] Better check if a search is currency conversion or not. - [fix] Typos in help.php. --- assets/css/styles.css | 23 ++-- config.php => config.default.php | 58 ++++++----- engines/duckduckgo.php | 14 +-- engines/google.php | 12 ++- engines/search-image.php | 174 +++++++++++++++++++++---------- engines/search-torrent.php | 91 ++++++++++------ engines/search.php | 145 ++++++++++++++------------ engines/special/currency.php | 2 +- engines/special/wikipedia.php | 2 +- engines/torrent/1337x.php | 73 +++++++------ engines/torrent/eztv.php | 88 ++++++++++++++++ engines/torrent/nyaa.php | 81 +++++++------- engines/torrent/thepiratebay.php | 78 ++++++++------ engines/torrent/yts.php | 94 ++++++++++------- functions/search_engine.php | 91 ++++++++-------- functions/tools.php | 36 +++---- help.php | 29 ++++-- index.php | 2 +- readme.md | 52 ++++++--- results.php | 5 +- 20 files changed, 720 insertions(+), 430 deletions(-) rename config.php => config.default.php (74%) create mode 100644 engines/torrent/eztv.php diff --git a/assets/css/styles.css b/assets/css/styles.css index ec7f01e..b716bdd 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -9,7 +9,7 @@ * liability that might arise from its use. ------------------------------------------------------------------------------------ */ -body { position: relative; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; color: #222; background-color: #ffffff; line-height: 1; } +body { position: relative; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; color: #222; background-color: #ffffff; line-height: 1.2; } div { margin: 0; padding: 0; border: 0; font-size: 100%; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } h2, h3, h4, h5, h6 { font-weight: normal; } @@ -19,8 +19,11 @@ input, button { outline: none; } button { cursor: pointer; } p { font-size: 18px; color: #494949; } a { text-decoration: none; color: #4495d4; } +small, sub, sup { padding: 5px 0; color: #666; font-size: 12px; } +sub, sup { font-style: italic; } a:hover { text-decoration: underline; } input[type="text"]:invalid ~ input[type="submit"] { opacity: 0.5; pointer-events: none; } +input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; -webkit-mask-image: url("data:image/svg+xml;utf8,"); } /* Page structure */ .wrap { min-height: 100vh; overflow: hidden; } @@ -33,7 +36,8 @@ body.main { background-color: #1f242b; color: #f0f6fc; } .search-box-main, .password-generator { text-align: center; margin-top: 10%; } .search-box-main h1 { font-size: 70px; } .search-box-main .search, .password-generator .password { padding: 10px 20px; width: 600px; color: #f0f6fc; background-color: #333333; font-size: 32px; font-family: sans-serif; border: 1px solid #3C4043; border-radius: 10px; } -.search-box-buttons button { margin: 30px 20px 10px 20px; padding: 13px 10px 13px 10px; min-width: 130px; color: #f0f6fc; background-color: #333333; font-size: 14px; border: 1px solid #1c1c1c; border-radius: 6px; } +.search-box-main .search[type="search"]::-webkit-search-cancel-button { background-size: 28px 28px; height: 28px; width: 28px; background-color: #f0f6fc; } +.search-box-buttons button { margin: 30px 20px 10px 20px; padding: 13px 10px 13px 10px; min-width: 130px; color: #f0f6fc; background-color: #333333; font-size: 14px; border: 1px solid #3C4043; border-radius: 6px; } .search-box-buttons button:hover { border: 1px solid #5f6368; } .password-generator { margin: 30px auto; padding: 0; } @@ -41,8 +45,10 @@ body.main { background-color: #1f242b; color: #f0f6fc; } /* Search Results - Header */ .header-wrap { background-color: #1f242b; color: #f0f6fc; border-bottom: 2px solid #1fa4d1; } -.header-wrap .search { position: relative; margin: 28px 0 28px 158px; padding: 5px 5px 5px 15px; height: 30px; width: 580px; color: #f0f6fc; background-color: #1f242b; font-size: 100%; font-weight: 400; border: 1px solid #303842; border-radius: 10px 0 0 10px; } -.header-wrap .button { position: relative; margin: 28px 10px 28px 0; padding: 5px 20px 5px 15px; height: 40px; color: #f0f6fc; background-color: #1fa4d1; font-size: 100%; font-weight: 400; border: none; border-radius: 0 10px 10px 0; } +.header-wrap .search, .header-wrap .button { position: relative; height: 40px; color: #f0f6fc; font-size: 100%; font-weight: 400; } +.header-wrap .search { margin: 28px 0 28px 158px; padding: 5px 5px 5px 15px; width: 580px; background-color: #1f242b; border: 1px solid #303842; border-radius: 10px 0 0 10px; } +.header-wrap .search[type="search"]::-webkit-search-cancel-button { background-size: 20px 20px; height: 20px; width: 20px; background-color: #f0f6fc; } +.header-wrap .button { margin: 28px 10px 28px 0; padding: 5px 20px 5px 15px; background-color: #1fa4d1; border: none; border-radius: 0 10px 10px 0; } /* Search results - Header Navigation */ .navigation-header { margin-left: 165px; margin-bottom: 10px; } @@ -55,7 +61,7 @@ body.main { background-color: #1f242b; color: #f0f6fc; } /* Search results - Main column */ .main-column { width: 100%; } .main-column ol .result { margin: .50rem 0 .50rem 0; padding: 0; } -.main-column ol .special-result, .main-column ol .meta-time, .main-column ol .meta-error, .main-column ol .meta-did-you-mean { margin: .75rem 0 .75rem 0; padding: .5rem 10px; } +.main-column ol .special-result, .main-column ol .meta { margin: .75rem 0 .75rem 0; padding: .5rem 10px; } .main-column ol li article { padding: .5rem 10px; border: 1px solid #fefefe; border-radius: 8px; } .main-column ol li article div.url:first-child, .main-column ol li.special-result article div.source:first-child { flex-grow: 0; } @@ -82,7 +88,9 @@ body.main { background-color: #1f242b; color: #f0f6fc; } } .main-column ol.image-grid .result .image-box { position: relative; } .main-column ol.image-grid .result .image-box::after { display: block; padding-bottom: 100%; content: ""; } -.main-column ol.image-grid .result .image-box img { position: absolute; object-fit: cover; width: 100%; height: 100%; } +.main-column ol.image-grid .result .image-box img { position: absolute; object-fit: cover; width: 100%; height: 100%; border-radius: 10px; } +.main-column ol.image-grid .result .image-box img:hover { outline: none; border-color: #3C4043; box-shadow: 0 0 10px #3C4043; } +.main-column ol.image-grid .result span { padding: 5px 0 0 0; color: #666; font-size: 12px; } /* Special results - Main column */ .main-column ol .special-result { background-color: #fefefe; } @@ -102,7 +110,8 @@ body.main { background-color: #1f242b; color: #f0f6fc; } .no-decoration, .no-decoration:hover { text-decoration: none; } .hide { display: none; } .G { color: #1fa4d1; } -.meta-error { position: relative; overflow: hidden; color: #c00; background-color: #ffebe8; border: 1px solid #c00; border-radius: 8px; } +.warning { position: relative; overflow: hidden; margin: 1rem 0 1rem 0; padding: .5rem 10px; color: #db9900; background-color: #ffffe0; border: 1px solid #e6db55; border-radius: 10px; } +.error { position: relative; overflow: hidden; margin: 1rem 0 1rem 0; padding: .5rem 10px; color: #c00; background-color: #ffebe8; border: 1px solid #c00; border-radius: 10px; } .auth-error { margin-top: 15%; font-size: 32px; text-align: center; color: #eaeaea; } /* Footer bar */ diff --git a/config.php b/config.default.php similarity index 74% rename from config.php rename to config.default.php index 3c70aa8..8dd66f5 100644 --- a/config.php +++ b/config.default.php @@ -15,6 +15,13 @@ HASH: A simple lowercase passphrase (something simple like: j9fg-i2du-er6m or 1846) used for caching results. This helps to differentiate between instances on the same server. You can also add it to your url/bookmark as a simple passphrase to keep unwanted users out. +HASH AUTH: + Use the above hash as a simple passphrase. + Using it as a passphrase lets you host Goosle on a public server without providing a public service. + + Usage: https://example.com/?a=1234567890 + Disclaimer: This is not meant to 'hack proof' or truly secure the setup. Just a simple token to keep surface level prying eyes out. + CACHE: If you have ACPu it is highly recommended to enable caching as it'll speed up repeatable searched by a lot. "on" (Recommended) for active sites, requires APCu @@ -23,24 +30,13 @@ CACHE: CACHE_TIME: Minutes the result should be cached in ACPu. -HASH AUTH: - Use the above hash as a simple passphrase. - Using it as a passphrase lets you host Goosle on a public server without providing a public service. - - Usage: https://example.com/?a=1234567890 - Disclaimer: This is not meant to 'hack proof' or truly secure the setup. Just a simple token to keep surface level prying eyes out. - -RAW OUTPUT: - "off" (Recommended) for active sites - "on" Output the search results as an array instead of formatted with HTML - -ENABLE TORRENT SEARCH: - Enable or disable searching for torrent downloads. +ENABLE IMAGE SEARCH: + Enable or disable image searches - Search results are provided by Qwant. "on" (Default) "off" -ENABLE IMAGE SEARCH: - Enable or disable image searches - Search results are provided by Qwant. +ENABLE TORRENT SEARCH: + Enable or disable searching for torrent downloads. "on" (Default) "off" @@ -58,6 +54,9 @@ USER AGENTS: Chrome may attract attention because of the lack of Chrome information (tracking) aside from the user agent. Opera/Edge/Brave and many others use Chrome under the hood and are not a good pick for that reason. +SHOW ZERO SEEDERS: + Set to "on" to include results with 0 seeders (slow or stale downloads). Off to exclude these results. + BLOCK 1337x CATEGORIES: Add category IDs of 1337x categories, check /engines/torrent/1337x.php for a list of known categories. @@ -75,29 +74,35 @@ TORRENT TRACKERS: return (object) array( "hash" => "j9fg-i2du-er6m", - "cache" => "off", - "cache_time" => 30, // (Default: 30) - "hash_auth" => "off", // Default: off) - "raw_output" => "off", // (Default: off) + "hash_auth" => "off", // Default: off + "cache" => "off", // Default: off + "cache_time" => 30, // Default: 30 - "enable_torrent_search" => "on", // (Default: on) - "enable_image_search" => "on", // (Default: on) + "enable_image_search" => "on", // Default: on + "enable_torrent_search" => "on", // Default: on "special" => array( "currency" => "on", // Currency converter "definition" => "on", // Word dictionary "wikipedia" => "on", // Wikipedia highlight "phpnet" => "on", // PHP-dot-net highlight + "imdb_id_search" => "on", // Highlight IMDB IDs for tv shows, to use for torrent searches "password_generator" => "on" // Password generator on homepage ), "user_agents" => array( - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15) Gecko/20100101 Firefox/119.0", // macOS 10.15, FF 119 - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/116.0", // Windows 10, FF 116 - "Mozilla/5.0 (X11; Ubuntu; Linux x86_64) Gecko/20100101 Firefox/83.0", // Linux Ubuntu, FF 83 - "Mozilla/5.0 (X11; Linux i686) Gecko/20100101 Firefox/119.0", // Linux Generic, FF 119 + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15) Gecko/20100101 Firefox/119.0", // macOS 10.15, Firefox 119 + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/116.0", // Windows 10, Firefox 116 + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64) Gecko/20100101 Firefox/83.0", // Linux Ubuntu, Firefox 83 + "Mozilla/5.0 (X11; Linux i686) Gecko/20100101 Firefox/119.0", // Linux Generic, Firefox 119 + "Mozilla/5.0 (Linux; Android 5.0.2; AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.3 Chrome/38.0.2125.102 Safari/537.36", // Android 5, Samsungbrowser 3 + "Mozilla/5.0 (Linux; Android 7.0; AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.116 Safari/537.36", // Android 7, Chrome 60 + "Mozilla/5.0 (Linux; U; Android 4.2.2; he-il; AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", // Android 4. Some webkit browser (Chrome?) + "Mozilla/5.0 (iPhone12,1; U; CPU iPhone OS 13_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/15E148 Safari/602.1", // iOS 13, Safari + "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15", // iOS 12, Firefox 13 ), + "show_zero_seeders" => "on", // Default: on "leetx_categories_blocked" => array(3, 7, 47), // Default: 3, 7, 47 "piratebay_categories_blocked" => array(206, 210), // Default: 206, 210 "yts_categories_blocked" => array("horror"), // Default: "horror" @@ -108,6 +113,7 @@ return (object) array( "udp://exodus.desync.com:6969/announce", "udp://tracker.torrent.eu.org:451/announce", ), - "version" => "1.0.2" + + "version" => "1.1b2" // Please don't change this ); ?> diff --git a/engines/duckduckgo.php b/engines/duckduckgo.php index 8c2dc93..2a4056a 100644 --- a/engines/duckduckgo.php +++ b/engines/duckduckgo.php @@ -51,21 +51,23 @@ class DuckDuckGoRequest extends EngineRequest { } public function parse_results($response) { - $results = array(); + $results = array("search" => array()); $xpath = get_xpath($response); - if(!$xpath) return $results; + if(!$xpath) return array(); + // Scrape recommended $didyoumean = $xpath->query(".//div[@id='did_you_mean']/a[1]")[0]; if(!is_null($didyoumean)) { - array_push($results, array("did_you_mean" => $didyoumean->textContent)); + $results['did_you_mean'] = $didyoumean->textContent; } $search_specific = $xpath->query(".//div[@id='did_you_mean']/a[2]")[0]; if(!is_null($search_specific)) { - array_push($results, array("search_specific" => $search_specific->textContent)); + $results['search_specific'] = $search_specific->textContent; } - foreach($xpath->query("/html/body/div[1]/div[". count($xpath->query('/html/body/div[1]/div')) ."]/div/div/div[contains(@class, 'web-result')]/div") as $result) { + // Scrape the results + foreach($xpath->query("/html/body/div[1]/div['. count($xpath->query('/html/body/div[1]/div')) .']/div/div/div[contains(@class, 'web-result')]/div") as $result) { $url = $xpath->evaluate(".//h2[@class='result__title']//a/@href", $result)[0]; if($url == null) continue; @@ -79,7 +81,7 @@ class DuckDuckGoRequest extends EngineRequest { $description = $xpath->evaluate(".//a[@class='result__snippet']", $result)[0]; - array_push($results, array ( + array_push($results['search'], array ( "title" => htmlspecialchars($title->textContent), "url" => htmlspecialchars($url->textContent), "description" => $description == null ? 'No description was provided for this site.' : htmlspecialchars($description->textContent) diff --git a/engines/google.php b/engines/google.php index b134a14..44e8ed4 100644 --- a/engines/google.php +++ b/engines/google.php @@ -71,20 +71,22 @@ class GoogleRequest extends EngineRequest { } public function parse_results($response) { - $results = array(); + $results = array("search" => array()); $xpath = get_xpath($response); - if(!$xpath) return $results; + if(!$xpath) return array(); + // Scrape recommended $didyoumean = $xpath->query(".//a[@class='gL9Hy']")[0]; if(!is_null($didyoumean)) { - array_push($results, array("did_you_mean" => $didyoumean->textContent)); + $results['did_you_mean'] = $didyoumean->textContent; } $search_specific = $xpath->query(".//a[@class='spell_orig']")[0]; if(!is_null($search_specific)) { - array_push($results, array("search_specific" => $search_specific->textContent)); + $results['search_specific'] = $search_specific->textContent; } + // Scrape the results foreach($xpath->query("//div[@id='search']//div[contains(@class, 'g')]") as $result) { $url = $xpath->evaluate(".//div[@class='yuRUbf']//a/@href", $result)[0]; if($url == null) continue; @@ -99,7 +101,7 @@ class GoogleRequest extends EngineRequest { $description = $xpath->evaluate(".//div[contains(@class, 'VwiC3b')]", $result)[0]; - array_push($results, array ( + array_push($results['search'], array ( "title" => htmlspecialchars($title->textContent), "url" => htmlspecialchars($url->textContent), "description" => $description == null ? 'No description was provided for this site.' : htmlspecialchars($description->textContent) diff --git a/engines/search-image.php b/engines/search-image.php index c189f29..b94fd0b 100644 --- a/engines/search-image.php +++ b/engines/search-image.php @@ -1,6 +1,5 @@ = 3 && strlen($switch[1]) <= 6) && !is_numeric($switch[1])) { if($switch[1] == "med") $switch[1] = "medium"; if($switch[1] == "lrg") $switch[1] = "large"; + if($switch[1] == "xlrg") $switch[1] = "wallpaper"; - if($switch[1] == "small" || $switch[1] == "medium" || $switch[1] == "large") { + if($switch[1] == "small" || $switch[1] == "medium" || $switch[1] == "large" || $switch[1] == "wallpaper") { $size = $switch[1]; } @@ -24,12 +24,11 @@ class ImageSearch extends EngineRequest { } } - // q = query - // t = Search type (images) - // size = Preferred image size (small|medium|large) + // p = query + // imgsz = Image size (small|medium|large|wallpaper) - $args = array("q" => $this->query, "t" => "images", "size" => $size); - $url = "https://lite.qwant.com?".http_build_query($args); + $args = array("p" => $this->query, "imgsz" => $size); + $url = "https://images.search.yahoo.com/search/images?".http_build_query($args); unset($query_terms, $switch, $args, $size); @@ -40,35 +39,63 @@ class ImageSearch extends EngineRequest { $results = array("search" => array()); $xpath = get_xpath($response); - if($xpath) { - foreach($xpath->query("//a[@rel='noopener']") as $result) { - $meta = $xpath->evaluate(".//img", $result)[0]; - - if($meta) { - $encoded_url = explode("?position", explode("==/", $result->getAttribute("href"))[1])[0]; - - $url = htmlspecialchars(urldecode(base64_decode($encoded_url))); - $alt_text = get_base_url($url)." - ".htmlspecialchars($meta->getAttribute("alt")); - $image = urldecode(htmlspecialchars(urlencode($meta->getAttribute("src")))); - - // filter duplicate urls/results - if(!empty($results)) { - $result_urls = array_column($results, "url"); - if(in_array($url, $result_urls) || in_array(get_base_url($url), $result_urls)) continue; - } - - array_push($results["search"], array ( - "alt" => $alt_text, - "image" => $image, - "url" => $url, - )); - } - } + // Failed to load page + if(!$xpath) return array(); + + // Scrape recommended + $didyoumean = $xpath->query(".//section[@class='dym-c']/section/h3/a")[0]; + if(!is_null($didyoumean)) { + $results['did_you_mean'] = $didyoumean->textContent; + } + $search_specific = $xpath->query(".//section[@class='dym-c']/section/h5/a")[0]; + if(!is_null($search_specific)) { + $results['search_specific'] = $search_specific->textContent; + } + + // Scrape the results + foreach($xpath->query("//li[contains(@class, 'ld') and not(contains(@class, 'slotting'))][position() < 101]") as $result) { + $image = $xpath->evaluate(".//img/@src", $result)[0]; + if($image == null) continue; + + $url_data = $xpath->evaluate(".//a/@href", $result)[0]; + if($url_data == null) continue; + + // Get meta data + // -- Relevant $url_data (there is more, but unused by Goosle) + // w = Image width (1280) + // h = Image height (720) + // imgurl = Actual full size image (Used in Yahoo preview/popup) + // rurl = Url to page where the image is used + // size = Image size (413.1KB) + // tt = Website title (Used for image alt text) + parse_str($url_data->textContent, $url_data); + + // filter duplicate urls/results + if(!empty($results['search'])) { + $result_urls = array_column($results['search'], "direct_link"); + if(in_array($url_data['imgurl'], $result_urls)) continue; + } + + // Deal with optional or missing data + $dimensions_w = (!array_key_exists('w', $url_data) || empty($url_data['w'])) ? 0 : htmlspecialchars($url_data['w']); + $dimensions_h = (!array_key_exists('h', $url_data) || empty($url_data['h'])) ? 0 : htmlspecialchars($url_data['h']); + $filesize = (!array_key_exists('size', $url_data) || empty($url_data['size'])) ? "" : htmlspecialchars($url_data['size']); + $link = (!array_key_exists('imgurl', $url_data) || empty($url_data['imgurl'])) ? "" : "//".htmlspecialchars($url_data['imgurl']); + + array_push($results['search'], array ( + "image" => htmlspecialchars($image->textContent), + "alt" => htmlspecialchars($url_data['tt']), + "url" => htmlspecialchars($url_data['rurl']), + "height" => $dimensions_w, + "width" => $dimensions_h, + "filesize" => $filesize, + "direct_link" => $link + )); } - // Add warning if there are no results, or a text if there is no search query. + // Add error if there are no search results if(empty($results['search'])) { - $results["error"] = array( + $results['error'] = array( "message" => "No results found. Please try with less or different keywords!" ); } @@ -77,41 +104,76 @@ class ImageSearch extends EngineRequest { } public static function print_results($results, $opts) { - if($opts->raw_output == "on") { - echo '
Results: ';
-			print_r($results);
-			echo '
'; - } +/* + echo '
Results: ';
+		print_r($results);
+		echo '
'; +*/ - echo "
"; - echo "
    "; - - // Elapsed time if(array_key_exists("search", $results)) { - echo "
  1. Fetched the results in ".$results['time']." seconds.
  2. "; - } + echo "
      "; - echo "
    "; + // Elapsed time + $number_of_results = count($results['search']); + echo "
  3. Fetched ".$number_of_results." results in ".$results['time']." seconds.
  4. "; - echo "
    "; - echo "
      "; + // Did you mean/Search suggestion + if(array_key_exists("did_you_mean", $results)) { + $specific_result = ""; + + if(array_key_exists("search_specific", $results)) { + // Format query url + $search_specific = "\"".$results['search_specific']."\""; + $search_specific_url = "./results.php?q=".urlencode($search_specific)."&t=".$opts->type."&a=".$opts->hash; + + // Specific search + $specific_result = "
      Or instead search for ".$search_specific."."; + + unset($search_specific, $search_specific_url); + } + + $didyoumean_url = "./results.php?q=".urlencode($results['did_you_mean'])."&t=".$opts->type."&a=".$opts->hash; + + echo "
    1. Did you mean ".$results['did_you_mean']."?$specific_result
    2. "; + + unset($didyoumean_url, $specific_result); + } + echo "
    "; + + // Search results + echo "
    "; + echo "
      "; - // Search results - if(array_key_exists("search", $results)) { foreach($results['search'] as $result) { - if(!array_key_exists("url", $result) || !array_key_exists("alt", $result)) continue; + $meta = $links = array(); + + // Optional data + if(!empty($result['height']) && !empty($result['width'])) $meta[] = $result['width']."×".$result['height']; + if(!empty($result['filesize'])) $meta[] = $result['filesize']; + + // Links + $links[] = "Website"; + if(!empty($result['direct_link'])) $links[] = "Image"; // Put result together echo "
    1. "; - echo "\"".$result["alt"]."\""; - echo "
    2. "; + echo "\"".$result['alt']."\""; + echo "
    ".implode(" - ", $meta)."
    ".implode(" - ", $links)."
    "; + echo ""; + + unset($result, $meta, $links); } + + echo "
"; + echo ""; + echo "
Not what you're looking for? Try query)."&iax=images&ia=images\" target=\"_blank\">DuckDuckGo, query)."\" target=\"_blank\">Yahoo! Images or query)."&tbm=isch&pws=0\" target=\"_blank\">Google Images.
"; } - echo ""; - echo ""; - echo "
Not what you're looking for? Try query)."&iax=images&ia=images\" target=\"_blank\">DuckDuckGo or query)."&tbm=isch&pws=0\" target=\"_blank\">Google Images.
"; - echo "
"; + // No results found + if(array_key_exists("error", $results)) { + echo "
".$results['error']['message']."
"; + } + } } ?> \ No newline at end of file diff --git a/engines/search-torrent.php b/engines/search-torrent.php index ac929c7..255135f 100644 --- a/engines/search-torrent.php +++ b/engines/search-torrent.php @@ -20,19 +20,21 @@ class TorrentSearch extends EngineRequest { require "engines/torrent/nyaa.php"; require "engines/torrent/thepiratebay.php"; require "engines/torrent/yts.php"; + require "engines/torrent/eztv.php"; $this->requests = array( new LeetxRequest($opts, $mh), // 1337x new NyaaRequest($opts, $mh), new PirateBayRequest($opts, $mh), - new YTSRequest($opts, $mh) + new YTSRequest($opts, $mh), + new EZTVRequest($opts, $mh) ); } public function parse_results($response) { $results = $results_temp = array(); - foreach ($this->requests as $request) { + foreach($this->requests as $request) { if($request->request_successful()) { $results_temp = array_merge($results_temp, $request->get_results()); } @@ -45,13 +47,20 @@ class TorrentSearch extends EngineRequest { // Cap results $results['search'] = array_slice($results_temp, 0, 50); - unset($results_temp); + + // Count results per site + $sources = array_count_values(array_column($results['search'], 'source')); + if(count($sources) > 0) $results['sources'] = $sources; + + unset($sources); } - // Add warning if there are no results + unset($results_temp); + + // Add error if there are no search results if(empty($results)) { - $results["error"] = array( - "message" => "No results found. Please try with less or different keywords!" + $results['error'] = array( + "message" => "No results found. Please try with more specific or different keywords!" ); } @@ -59,51 +68,67 @@ class TorrentSearch extends EngineRequest { } public static function print_results($results, $opts) { - if($opts->raw_output == "on") { - echo '
Results: ';
-			print_r($results);
-			echo '
'; - } +/* + echo '
Results: ';
+		print_r($results);
+		echo '
'; +*/ - echo "
"; - echo "
    "; - - // Elapsed time - echo "
  1. Fetched the results in ".$results['time']." seconds.
  2. "; - - // No results found - if(array_key_exists("error", $results)) { - echo "
  3. ".$results['error']['message']."
  4. "; - } - - // Search results if(array_key_exists("search", $results)) { + echo "
      "; + + // Format sources + $sources = ""; + if(array_key_exists("sources", $results)) { + $sources = array(); + foreach($results['sources'] as $source => $amount) { + $plural = ($amount > 1) ? "results" : "result"; + $sources[] = $amount." ".$plural." from ".$source; + } + $sources = implode(', ', $sources); + + $last_comma = strrpos($sources, ', '); + if($last_comma !== false) { + $sources = substr_replace($sources, ' and ', $last_comma, 2); + } + + $sources = "
      Including ".$sources.". Links with the most seeders are listed first."; + } + + // Elapsed time + $number_of_results = count($results['search']); + echo "
    1. Fetched ".$number_of_results." results in ".$results['time']." seconds.".$sources."
    2. "; + + // Search results foreach($results['search'] as $result) { $meta = array(); + // Optional data if(array_key_exists('quality', $result)) $meta[] = "Quality: ".$result['quality']; if(array_key_exists('year', $result)) $meta[] = "Year: ".$result['year']; if(array_key_exists('category', $result)) $meta[] = "Category: ".$result['category']; if(array_key_exists('runtime', $result)) $meta[] = "Runtime: ".date('H:i', mktime(0, $result['runtime'])); if(array_key_exists('date_added', $result)) $meta[] = "Added: ".date('M d, Y', $result['date_added']); - if(array_key_exists('url', $result)) $meta[] = "Torrent page"; + if(array_key_exists('url', $result)) $meta[] = "Torrent page"; // Put result together echo "
    3. "; + + unset($result, $meta); } + + echo "
    "; + echo "
    Showing up to 50 results, sorted by most seeders.
    Goosle does not index, offer or distribute torrent files.
    "; } - echo "
  5. "; - echo "Goosle does not store, index, offer or distribute torrent files."; - echo "
  6. "; - - echo "
"; - echo "
Showing 50 results, sorted by most seeders.
"; - echo "
"; + // No results found + if(array_key_exists("error", $results)) { + echo "
".$results['error']['message']."
"; + } } } ?> \ No newline at end of file diff --git a/engines/search.php b/engines/search.php index 121d5ca..852904f 100644 --- a/engines/search.php +++ b/engines/search.php @@ -36,17 +36,17 @@ class TextSearch extends EngineRequest { // Abort if no results from search engine if(!isset($this->engine_request)) return $results; - // Add search results + // Add search results if there are any, otherwise add error if($this->engine_request->request_successful()) { $search_result = $this->engine_request->get_results(); if($search_result) { - $results['search'] = $search_result; + $results = $search_result; } unset($search_result); } else { - $results["error"] = array( + $results['error'] = array( "message" => "Error code ".curl_getinfo($this->engine_request->ch)['http_code']." for ".curl_getinfo($this->engine_request->ch)['url'].".
Try again in a few seconds or engine_request->ch)['url']."\" target=\"_blank\">visit the search engine in a new tab." ); } @@ -62,9 +62,9 @@ class TextSearch extends EngineRequest { unset($special_result); } - // Add warning if there are no results, or a text if there is no search query. + // Add error if there are no search results if(empty($results)) { - $results["error"] = array( + $results['error'] = array( "message" => "No results found. Please try with less or different keywords!" ); } @@ -73,93 +73,104 @@ class TextSearch extends EngineRequest { } public static function print_results($results, $opts) { - if($opts->raw_output == "on") { - echo '
Results: ';
-			print_r($results);
-			echo '
'; - } +/* + echo '
Results: ';
+		print_r($results);
+		echo '
'; +*/ - echo "
"; - echo "
    "; - - // Elapsed time if(array_key_exists("search", $results)) { + echo "
      "; + + // Elapsed time $number_of_results = count($results['search']); - echo "
    1. Fetched ".$number_of_results." results in ".$results['time']." seconds.
    2. "; - } + echo "
    3. Fetched ".$number_of_results." results in ".$results['time']." seconds.
    4. "; - // No results found - if(array_key_exists("error", $results)) { - echo "
    5. ".$results['error']['message']."
    6. "; - } + // Did you mean/Search suggestion + if(array_key_exists("did_you_mean", $results)) { + $specific_result = ""; - // Did you mean/Search suggestion - if(array_key_exists("search", $results)) { - $specific_result = ""; - - if(array_key_exists("did_you_mean", $results['search'][0])) { - if(array_key_exists("search_specific", $results['search'][1])) { + if(array_key_exists("search_specific", $results)) { // Add double quotes to Google search - $search_specific = ($opts->type == 1) ? "\"".$results['search'][1]['search_specific']."\"" : $results['search'][1]['search_specific']; + $search_specific = ($opts->type == 1) ? "\"".$results['search_specific']."\"" : $results['search_specific']; + + // Format query url $search_specific_url = "./results.php?q=" . urlencode($search_specific)."&t=".$opts->type."&a=".$opts->hash; - $specific_result = "
      Or instead search for $search_specific."; + + // Specific search + $specific_result = "
      Or instead search for ".$search_specific."."; - unset($results['search'][1], $search_specific, $search_specific_url); + unset($search_specific, $search_specific_url); } - $didyoumean = $results['search'][0]['did_you_mean']; - $didyoumean_url = "./results.php?q=" . urlencode($didyoumean)."&t=".$opts->type."&a=".$opts->hash; + $didyoumean_url = "./results.php?q=" . urlencode($results['did_you_mean'])."&t=".$opts->type."&a=".$opts->hash; - echo "
    7. Did you mean $didyoumean?$specific_result
    8. "; + echo "
    9. Did you mean ".$results['did_you_mean']."?".$specific_result."
    10. "; - unset($results['search'][0], $didyoumean, $didyoumean_url, $specific_result); - } - } - - // Special result - if(array_key_exists("special", $results)) { - echo "
    11. "; - // Maybe shorten text - if(strlen($results['special']['text']) > 1250) { - $results['special']['text'] = substr($results['special']['text'], 0, strrpos(substr($results['special']['text'], 0, 1300), ". ")); - $results['special']['text'] .= '. [...]'; + unset($didyoumean_url, $specific_result); } - // Add image to text - if(array_key_exists("image", $results['special'])) { - $image_specs = getimagesize($results['special']['image']); - $width = $image_specs[0] / 2; - $height = $image_specs[1] / 2; - - $special_image = ""; - $results['special']['text'] = $special_image.$results['special']['text']; - - unset($image_specs, $width, $height, $special_image); + // Special results + if($opts->special['imdb_id_search'] == "on") { + $found = false; + foreach($results['search'] as $search_result) { + if(!$found && preg_match_all("/(imdb.com|tt[0-9]+)/i", $search_result['url'], $imdb_result) && stristr($search_result['title'], "tv series") !== false) { + $results['special'] = array( + "title" => $search_result['title'], + "text" => "Goosle found an IMDb ID for this TV Show in your results (".$imdb_result[0][1].") - hash."&t=9\">search for magnet links?
      An IMDb ID is detected when a TV Show is present in the results. The first match is highlighted here." + ); + $found = true; + } + } } - echo "

      ".$results['special']['title']."

      "; - echo "
      ".$results['special']['text']."
      "; - if(array_key_exists("source", $results['special'])) { - echo ""; + if(array_key_exists("special", $results)) { + echo "
    12. "; + // Maybe shorten text + if(strlen($results['special']['text']) > 1250) { + $results['special']['text'] = substr($results['special']['text'], 0, strrpos(substr($results['special']['text'], 0, 1300), ". ")); + $results['special']['text'] .= '. [...]'; + } + + // Add image to text + if(array_key_exists("image", $results['special'])) { + $image_specs = getimagesize($results['special']['image']); + $width = $image_specs[0] / 2; + $height = $image_specs[1] / 2; + + $special_image = ""; + $results['special']['text'] = $special_image.$results['special']['text']; + + unset($image_specs, $width, $height, $special_image); + } + echo "

      ".$results['special']['title']."

      "; + echo "
      ".$results['special']['text']."
      "; + if(array_key_exists("source", $results['special'])) { + echo ""; + } + echo "
    13. "; } - echo "
    14. "; - } - // Search results - if(array_key_exists("search", $results)) { + // Search results foreach($results['search'] as $result) { if(array_key_exists("did_you_mean", $result)) continue; // Put result together echo "
    15. "; + + unset($result); } + + echo "
    "; } - - echo "
"; - echo "
"; + + // No results found + if(array_key_exists("error", $results)) { + echo "
".$results['error']['message']."
"; + } } } ?> diff --git a/engines/special/currency.php b/engines/special/currency.php index 59bd6f6..db19aaf 100644 --- a/engines/special/currency.php +++ b/engines/special/currency.php @@ -18,7 +18,7 @@ class CurrencyRequest extends EngineRequest { $json_response = json_decode($response, true); if(!empty($json_response)) { - $result = $json_response["rates"]; + $result = $json_response['rates']; // Process query // [0] = AMOUNT diff --git a/engines/special/wikipedia.php b/engines/special/wikipedia.php index ee0e0b5..f2896d9 100644 --- a/engines/special/wikipedia.php +++ b/engines/special/wikipedia.php @@ -51,7 +51,7 @@ class WikipediaRequest extends EngineRequest { ); if (array_key_exists("thumbnail", $result)) { - $response["image"] = strip_tags(trim($result["thumbnail"]["source"])); + $response['image'] = strip_tags(trim($result['thumbnail']['source'])); } return $response; diff --git a/engines/torrent/1337x.php b/engines/torrent/1337x.php index 8e076e0..6e51f3e 100644 --- a/engines/torrent/1337x.php +++ b/engines/torrent/1337x.php @@ -10,17 +10,17 @@ * liability that might arise from its use. ------------------------------------------------------------------------------------ */ class LeetxRequest extends EngineRequest { - public function get_request_url() { - return "https://1337x.to/search/".urlencode($this->query)."/1/"; - } - - public function parse_results($response) { - $results = array(); - $xpath = get_xpath($response); - - // Failted to load page - if(!$xpath) return $results; - + public function get_request_url() { + return "https://1337x.to/search/".urlencode($this->query)."/1/"; + } + + public function parse_results($response) { + $results = array(); + $xpath = get_xpath($response); + + // Failed to load page + if(!$xpath) return $results; + $categories = array( 1 => "DVD", 2 => "Divx/Xvid", @@ -105,38 +105,43 @@ class LeetxRequest extends EngineRequest { ); // Scrape the page - foreach($xpath->query("//table/tbody/tr") as $result) { - $category = $xpath->evaluate(".//td[@class='coll-1 name']/a/@href", $result)[0]->textContent; - $category = explode("/", trim($category)); - - // Block these categories - if(in_array($category[2], $this->opts->leetx_categories_blocked)) continue; - - $name = $xpath->evaluate(".//td[@class='coll-1 name']/a", $result)[1]->textContent; - $seeders = $xpath->evaluate(".//td[@class='coll-2 seeds']", $result)[0]->textContent; - $leechers = $xpath->evaluate(".//td[@class='coll-3 leeches']", $result)[0]->textContent; - $date_added = explode(" ", sanitize($xpath->evaluate(".//td[@class='coll-date']", $result)[0]->textContent)); - $date_added = mktime(0, 0, 0, intval(date("m", strtotime($date_added[0]))), intval(preg_replace('/[^\d.]+/', '', $date_added[1])), intval('20'.preg_replace('/[^\d.]+/', '', $date_added[2]))); - $url = "https://1337x.to".sanitize($xpath->evaluate(".//td[@class='coll-1 name']/a/@href", $result)[1]->textContent); - $size_unformatted = explode(" ", $xpath->evaluate(".//td[contains(@class, 'coll-4 size')]", $result)[0]->textContent); + foreach($xpath->query("//table/tbody/tr") as $result) { + $name = sanitize($xpath->evaluate(".//td[@class='coll-1 name']/a", $result)[1]->textContent); + $seeders = sanitize($xpath->evaluate(".//td[@class='coll-2 seeds']", $result)[0]->textContent); + $leechers = sanitize($xpath->evaluate(".//td[@class='coll-3 leeches']", $result)[0]->textContent); + $size_unformatted = explode(" ", sanitize($xpath->evaluate(".//td[contains(@class, 'coll-4 size')]", $result)[0]->textContent)); $size = $size_unformatted[0] . " " . preg_replace("/[0-9]+/", "", $size_unformatted[1]); + $category = explode("/", sanitize($xpath->evaluate(".//td[@class='coll-1 name']/a/@href", $result)[0]->textContent)); + $category = $category[2]; + $url = "https://1337x.to".sanitize($xpath->evaluate(".//td[@class='coll-1 name']/a/@href", $result)[1]->textContent); + $date_added = explode(" ", sanitize($xpath->evaluate(".//td[@class='coll-date']", $result)[0]->textContent)); + $date_added = mktime(0, 0, 0, intval(date("m", strtotime($date_added[0]))), intval(preg_replace('/[^\d.]+/', '', $date_added[1])), intval('20'.preg_replace('/[^\d.]+/', '', $date_added[2]))); + + // Block these categories + if(in_array($category, $this->opts->leetx_categories_blocked)) continue; + + // Remove results with 0 seeders? + if($this->opts->show_zero_seeders == "off" AND $seeders == 0) continue; + array_push($results, array ( - // Required + // Required "source" => "1337x.to", - "name" => sanitize($name), + "name" => $name, "magnet" => "./engines/torrent/magnetize_1337x.php?url=".$url, - "seeders" => sanitize($seeders), - "leechers" => sanitize($leechers), - "size" => sanitize($size), + "seeders" => $seeders, + "leechers" => $leechers, + "size" => $size, // Optional values - "category" => $categories[sanitize($category[2])], + "category" => $categories[$category], "url" => $url, "date_added" => $date_added )); - } - return $results; - } + unset($name, $seeders, $leechers, $size_unformatted, $size, $category, $url, $date_added); + } + + return $results; + } } ?> \ No newline at end of file diff --git a/engines/torrent/eztv.php b/engines/torrent/eztv.php new file mode 100644 index 0000000..6ffea2d --- /dev/null +++ b/engines/torrent/eztv.php @@ -0,0 +1,88 @@ +query); + if(substr(strtolower($query_terms[0]), 0, 2) !== "tt") return ""; + + // Prepare a search query by stripping out everything but numbers and abort if nothing is left + $query = preg_replace('/[^0-9]/', '', $query_terms[0]); + if(strlen($query) == 0) return ""; + + // Is eztvx.to blocked for you? Use one of these urls as an alternative + // eztv1.xyz, eztv.wf, eztv.tf, eztv.yt + return "https://eztvx.to/api/get-torrents?imdb_id=".urlencode($query); + } + + public function parse_results($response) { + $results = array(); + + $response = curl_multi_getcontent($this->ch); + $json_response = json_decode($response, true); + + // No response + if(empty($json_response)) return $results; + + // Nothing found + if($json_response['torrents_count'] == 0) return $results; + + // Use API result + foreach ($json_response['torrents'] as $episode) { + $name = sanitize($episode['title']); + $magnet = sanitize($episode['magnet_url']); + $seeders = sanitize($episode['seeds']); + $leechers = sanitize($episode['peers']); + $size = sanitize($episode['size_bytes']); + + // Find actual quality of episode + if(preg_match('/(480p|720p|1080p|2160p)/i', $name, $quality)) { + $quality = $quality[0]; + } else { + $quality = "Unknown"; + } + + $date_added = sanitize($episode['date_released_unix']); + + // Filter by Season (S01) or Season and Episode (S01E01) + $season = sanitize($episode['season']); + $episode = sanitize($episode['episode']); + + if(preg_match_all("/(S[0-9]{1,3}|E[0-9]{1,3})/i", $this->query, $filter_episode)) { + if(str_ireplace("s0", "", $filter_episode[0][0]) != $season || (array_key_exists(1, $filter_episode[1]) && str_ireplace("e0", "", $filter_episode[0][1]) != $episode)) { + continue; + } + } + + // Remove results with 0 seeders? + if($this->opts->show_zero_seeders == "off" AND $seeders == 0) continue; + + array_push($results, array ( + // Required + "source" => "EZTV", + "name" => $name, + "magnet" => $magnet, + "seeders" => $seeders, + "leechers" => $leechers, + "size" => human_filesize($size), + // Optional + "quality" => $quality, + "date_added" => $date_added + )); + + unset($name, $magnet, $seeders, $leechers, $size, $quality, $category, $date_added, $season, $episode); + } + + return $results; + } +} +?> diff --git a/engines/torrent/nyaa.php b/engines/torrent/nyaa.php index 10c0ee0..8b8147e 100644 --- a/engines/torrent/nyaa.php +++ b/engines/torrent/nyaa.php @@ -10,47 +10,54 @@ * liability that might arise from its use. ------------------------------------------------------------------------------------ */ class NyaaRequest extends EngineRequest { - public function get_request_url() { - return "https://nyaa.si/?q=".urlencode($this->query); - } - - public function parse_results($response) { - $results = array(); - $xpath = get_xpath($response); - - // Failted to load page - if(!$xpath) return $results; - + public function get_request_url() { + return "https://nyaa.si/?q=".urlencode($this->query); + } + + public function parse_results($response) { + $results = array(); + $xpath = get_xpath($response); + + // Failed to load page + if(!$xpath) return $results; + // Scrape the page - foreach($xpath->query("//tbody/tr") as $result) { - $category = $xpath->evaluate(".//td[1]//a/@title", $result)[0]->textContent; - $name = $xpath->evaluate(".//td[@colspan='2']//a[not(contains(@class, 'comments'))]/@title", $result)[0]->textContent; - $url = $xpath->evaluate(".//td[@colspan='2']//a[not(contains(@class, 'comments'))]/@href", $result)[0]->textContent; - $meta = $xpath->evaluate(".//td[@class='text-center']", $result); - $seeders = $meta[3]->textContent; - $leechers = $meta[4]->textContent; - $size = $meta[1]->textContent; - $date_added = $meta[2]->textContent; - $date_added = explode("-", substr(sanitize($date_added), 0, 10)); + foreach($xpath->query("//tbody/tr") as $result) { + $meta = $xpath->evaluate(".//td[@class='text-center']", $result); + + $name = sanitize($xpath->evaluate(".//td[@colspan='2']//a[not(contains(@class, 'comments'))]/@title", $result)[0]->textContent); + $magnet = sanitize($xpath->evaluate(".//a[2]/@href", $meta[0])[0]->textContent); + $seeders = sanitize($meta[3]->textContent); + $leechers = sanitize($meta[4]->textContent); + $size = sanitize($meta[1]->textContent); + + $category = sanitize($xpath->evaluate(".//td[1]//a/@title", $result)[0]->textContent); + $url = sanitize($xpath->evaluate(".//td[@colspan='2']//a[not(contains(@class, 'comments'))]/@href", $result)[0]->textContent); + $date_added = sanitize($meta[2]->textContent); + $date_added = explode("-", substr($date_added, 0, 10)); $date_added = mktime(0, 0, 0, intval($date_added[1]), intval($date_added[2]), intval($date_added[0])); - $magnet = $xpath->evaluate(".//a[2]/@href", $meta[0])[0]->textContent; - - array_push($results, array ( - // Required - "source" => "nyaa.si", - "name" => sanitize($name), - "magnet" => sanitize($magnet), - "seeders" => sanitize($seeders), - "leechers" => sanitize($leechers), - "size" => sanitize($size), + + // Remove results with 0 seeders? + if($this->opts->show_zero_seeders == "off" AND $seeders == 0) continue; + + array_push($results, array ( + // Required + "source" => "nyaa.si", + "name" => $name, + "magnet" => $magnet, + "seeders" => $seeders, + "leechers" => $leechers, + "size" => $size, // Optional values - "category" => str_replace(" - ", "/", sanitize($category)), - "url" => "https://nyaa.si".sanitize($url), + "category" => str_replace(" - ", "/", $category), + "url" => "https://nyaa.si".$url, "date_added" => $date_added - )); - } + )); - return $results; - } + unset($name, $magnet, $seeders, $leechers, $size, $category, $url, $date_added, $meta); + } + + return $results; + } } ?> diff --git a/engines/torrent/thepiratebay.php b/engines/torrent/thepiratebay.php index d76c965..e28ccf2 100644 --- a/engines/torrent/thepiratebay.php +++ b/engines/torrent/thepiratebay.php @@ -10,17 +10,17 @@ * liability that might arise from its use. ------------------------------------------------------------------------------------ */ class PirateBayRequest extends EngineRequest { - public function get_request_url() { - return "https://apibay.org/q.php?q=".urlencode($this->query); - } - - public function parse_results($response) { - $results = array(); - $json_response = json_decode($response, true); + public function get_request_url() { + return "https://apibay.org/q.php?q=".urlencode($this->query); + } + public function parse_results($response) { + $results = array(); + $json_response = json_decode($response, true); + // No response - if(empty($json_response)) return $results; - + if(empty($json_response)) return $results; + $categories = array( 100 => "Audio", 101 => "Music", @@ -84,32 +84,44 @@ class PirateBayRequest extends EngineRequest { ); // Use API result - foreach($json_response as $response) { + foreach($json_response as $response) { // Nothing found - if($response["name"] == "No results returned") break; - - // Block these categories - if(in_array($response["category"], $this->opts->piratebay_categories_blocked)) continue; - - $name = sanitize($response["name"]); - $magnet = "magnet:?xt=urn:btih:".sanitize($response["info_hash"])."&dn=".$name."&tr=".implode("&tr=", $this->opts->torrent_trackers); - - array_push($results, array ( - // Required - "source" => "thepiratebay.org", - "name" => $name, - "magnet" => $magnet, - "seeders" => sanitize($response["seeders"]), - "leechers" => sanitize($response["leechers"]), - "size" => human_filesize(sanitize($response["size"])), + if($response['name'] == "No results returned") break; + + $name = sanitize($response['name']); + $magnet = "magnet:?xt=urn:btih:".sanitize($response['info_hash'])."&dn=".$name."&tr=".implode("&tr=", $this->opts->torrent_trackers); + $seeders = sanitize($response['seeders']); + $leechers = sanitize($response['leechers']); + $size = sanitize($response['size']); + + $category = sanitize($response['category']); + $id = sanitize($response['id']); + $date_added = sanitize($response['added']); + + // Block these categories + if(in_array($category, $this->opts->piratebay_categories_blocked)) continue; + + // Remove results with 0 seeders? + if($this->opts->show_zero_seeders == "off" AND $seeders == 0) continue; + + array_push($results, array( + // Required + "source" => "thepiratebay.org", + "name" => $name, + "magnet" => $magnet, + "seeders" => $seeders, + "leechers" => $leechers, + "size" => human_filesize($size), // Optional - "category" => $categories[sanitize($response["category"])], - "url" => "https://thepiratebay.org/description.php?id=".sanitize($response["id"]), - "date_added" => sanitize($response["added"]), - )); - } + "category" => $categories[$category], + "url" => "https://thepiratebay.org/description.php?id=".$id, + "date_added" => $date_added, + )); - return $results; - } + unset($name, $magnet, $seeders, $leechers, $size, $category, $id, $date_added); + } + + return $results; + } } ?> diff --git a/engines/torrent/yts.php b/engines/torrent/yts.php index 0c2b60e..80589aa 100644 --- a/engines/torrent/yts.php +++ b/engines/torrent/yts.php @@ -10,55 +10,73 @@ * liability that might arise from its use. ------------------------------------------------------------------------------------ */ class YTSRequest extends EngineRequest { - public function get_request_url() { - return "https://yts.mx/api/v2/list_movies.json?query_term=".urlencode($this->query); - } - - public function parse_results($response) { - $results = array(); - $response = curl_multi_getcontent($this->ch); - $json_response = json_decode($response, true); + public function get_request_url() { + return "https://yts.mx/api/v2/list_movies.json?query_term=".urlencode($this->query); + } + public function parse_results($response) { + $results = array(); + $response = curl_multi_getcontent($this->ch); + $json_response = json_decode($response, true); + // No response - if(empty($json_response)) return $results; - + if(empty($json_response)) return $results; + // Nothing found - if($json_response["status"] != "ok" || $json_response["data"]["movie_count"] == 0) return $results; - + if($json_response['status'] != "ok" || $json_response['data']['movie_count'] == 0) return $results; + // Use API result - foreach ($json_response["data"]["movies"] as $movie) { + foreach ($json_response['data']['movies'] as $movie) { // Prevent gaps - if(!array_key_exists("year", $movie)) $movie['year'] = 0; - if(!array_key_exists("genres", $movie)) $movie['genres'] = array(); - if(!array_key_exists("runtime", $movie)) $movie['runtime'] = 0; - if(!array_key_exists("url", $movie)) $movie['url'] = ''; + if(!array_key_exists("year", $movie)) $movie['year'] = 0; + if(!array_key_exists("genres", $movie)) $movie['genres'] = array(); + if(!array_key_exists("runtime", $movie)) $movie['runtime'] = 0; + if(!array_key_exists("url", $movie)) $movie['url'] = ''; + + // Block these categories + if(array_intersect($movie['genres'], $this->opts->yts_categories_blocked)) continue; + + $name = sanitize($movie['title']); + + $year = sanitize($movie['year']); + $category = sanitize(implode(', ', $movie['genres'])); + $runtime = sanitize($movie['runtime']); + $url = sanitize($movie['url']); + $date_added = sanitize($movie['date_uploaded_unix']); - // Block these categories - if(array_intersect($movie["genres"], $this->opts->yts_categories_blocked)) continue; - - $name = sanitize($movie["title"]); - - foreach ($movie["torrents"] as $torrent) { - $magnet = "magnet:?xt=urn:btih:".sanitize($torrent["hash"])."&dn=".urlencode($name)."&tr=".implode("&tr=", $this->opts->torrent_trackers); - - array_push($results, array ( - // Required + foreach ($movie['torrents'] as $torrent) { + $magnet = "magnet:?xt=urn:btih:".sanitize($torrent['hash'])."&dn=".urlencode($name)."&tr=".implode("&tr=", $this->opts->torrent_trackers); + $seeders = sanitize($torrent['seeds']); + $leechers = sanitize($torrent['peers']); + $size = sanitize($torrent['size']); + + $quality = sanitize($torrent['quality']); + + // Remove results with 0 seeders? + if($this->opts->show_zero_seeders == "off" AND $seeders == 0) continue; + + array_push($results, array ( + // Required "source" => "yts.mx", "name" => $name, "magnet" => $magnet, - "seeders" => sanitize($torrent["seeds"]), - "leechers" => sanitize($torrent["peers"]), - "size" => sanitize($torrent["size"]), + "seeders" => $seeders, + "leechers" => $leechers, + "size" => $size, // Optional - "quality" => sanitize($torrent["quality"]), - "year" => sanitize($movie["year"]), - "category" => sanitize(implode(', ', $movie["genres"])), - "runtime" => sanitize($movie["runtime"]), - "url" => sanitize($movie["url"]), - "date_added" => sanitize($movie["date_uploaded_unix"]) + "quality" => $quality, + "year" => $year, + "category" => $category, + "runtime" => $runtime, + "url" => $url, + "date_added" => $date_added )); - } - } + + unset($magnet, $seeders, $leechers, $size, $quality); + } + + unset($name, $year, $category, $runtime, $url, $date_added); + } return $results; } diff --git a/functions/search_engine.php b/functions/search_engine.php index 2299c8b..8d73b3a 100644 --- a/functions/search_engine.php +++ b/functions/search_engine.php @@ -75,11 +75,13 @@ abstract class EngineRequest { --------------------------------------*/ public function get_results() { + // It's a torrent search? if(!isset($this->url)) return $this->parse_results(null); - // Skip if there is a cached result (from earlier search) + // If there is a cached result from an earlier search use that instead if($this->opts->cache == "on" && has_cached_results($this->url, $this->opts->hash)) return fetch_cached_results($this->url, $this->opts->hash); - + + // Curl request if(!isset($this->ch)) return $this->parse_results(null); $response = ($this->mh) ? curl_multi_getcontent($this->ch) : curl_exec($this->ch); @@ -98,17 +100,17 @@ abstract class EngineRequest { // Load and make config available, pass around variables --------------------------------------*/ function load_opts() { - $opts = require "config.php"; - - $opts->query = (isset($_REQUEST['q'])) ? sanitize($_REQUEST["q"]) : ""; - $opts->type = (isset($_REQUEST['t'])) ? sanitize($_REQUEST["t"]) : 0; - $opts->user_auth = (isset($_REQUEST['a'])) ? sanitize($_REQUEST["a"]) : ""; - + $opts = require "config.php"; + + // From the url/request + $opts->query = (isset($_REQUEST['q'])) ? sanitize($_REQUEST['q']) : ""; + $opts->type = (isset($_REQUEST['t'])) ? sanitize($_REQUEST['t']) : 0; + $opts->user_auth = (isset($_REQUEST['a'])) ? sanitize($_REQUEST['a']) : ""; + // Remove ! at the start of queries to prevent DDG Bangs (!g, !c and crap like that) - $has_exclamation_mark = substr($opts->query, 0, 1); - if($has_exclamation_mark == "!") $opts->query = ltrim($opts->query, "!"); - - return $opts; + if(substr($opts->query, 0, 1) == "!") $opts->query = substr($opts->query, 1); + + return $opts; } /*-------------------------------------- @@ -117,37 +119,44 @@ function load_opts() { function fetch_search_results($opts) { $start_time = microtime(true); - // Curl - $mh = curl_multi_init(); + echo "
"; - // Load search script - if($opts->type == 0 || $opts->type == 1) { - require "engines/search.php"; - $search = new TextSearch($opts, $mh); - } else if($opts->type == 2) { - require "engines/search-image.php"; - $search = new ImageSearch($opts, $mh); - } else if($opts->type == 9) { - require "engines/search-torrent.php"; - $search = new TorrentSearch($opts, $mh); + if(!empty($opts->query)) { + // Curl + $mh = curl_multi_init(); + + // Load search script + if($opts->type == 0 || $opts->type == 1) { + require "engines/search.php"; + $search = new TextSearch($opts, $mh); + } else if($opts->type == 2) { + require "engines/search-image.php"; + $search = new ImageSearch($opts, $mh); + } else if($opts->type == 9) { + require "engines/search-torrent.php"; + $search = new TorrentSearch($opts, $mh); + } + + $running = null; + + do { + curl_multi_exec($mh, $running); + } while ($running); + + $results = $search->get_results(); + + curl_multi_close($mh); + + // Add elapsed time to results + $results['time'] = number_format(microtime(true) - $start_time, 5, '.', ''); + + // Echoes results and special searches + $search->print_results($results, $opts); + } else { + echo "
Search query can not be empty!
Not sure what went wrong? Learn more about hash."\">how to use Goosle.
"; } - $running = null; - - do { - curl_multi_exec($mh, $running); - } while ($running); - - $results = $search->get_results(); - - curl_multi_close($mh); - - // Add elapsed time to results - $results['time'] = number_format(microtime(true) - $start_time, 5, '.', ''); - - $search->print_results($results, $opts); - - return $results; + echo "
"; } /*-------------------------------------- @@ -158,7 +167,7 @@ function special_search_request($opts) { $query_terms = explode(" ", $opts->query); // Currency converter - if($opts->special['currency'] == "on" && is_numeric($query_terms[0]) && ($query_terms[2] == 'to' || $query_terms[2] == 'in')) { + if($opts->special['currency'] == "on" && count($query_terms) == 4 && (is_numeric($query_terms[0]) && ($query_terms[2] == 'to' || $query_terms[2] == 'in'))) { require "engines/special/currency.php"; $special_request = new CurrencyRequest($opts, null); } diff --git a/functions/tools.php b/functions/tools.php index d64d4de..851abcb 100644 --- a/functions/tools.php +++ b/functions/tools.php @@ -19,24 +19,6 @@ function verify_hash($opts, $auth) { return false; } -/*-------------------------------------- -// Strip all extras from an url ---------------------------------------*/ -function get_base_url($url) { - $parsed = parse_url($url); - - return $parsed["scheme"] . "://" . $parsed["host"] . "/"; -} - -/*-------------------------------------- -// Format search result urls ---------------------------------------*/ -function get_formatted_url($url) { - $parsed = parse_url($url); - - return $parsed["scheme"] . "://" . $parsed["host"] . str_replace('/', ' › ', str_replace('%20', ' ', rtrim($parsed['path'], '/'))); -} - /*-------------------------------------- // Load pages into a DOM --------------------------------------*/ @@ -51,6 +33,24 @@ function get_xpath($response) { return $xpath; } +/*-------------------------------------- +// Strip all extras from an url +--------------------------------------*/ +function get_base_url($url) { + $url = parse_url($url); + + return $url['scheme'] . "://" . $url['host'] . "/"; +} + +/*-------------------------------------- +// Format search result urls +--------------------------------------*/ +function get_formatted_url($url) { + $url = parse_url($url); + + return $url['scheme'] . "://" . $url['host'] . str_replace('/', ' › ', str_replace('%20', ' ', rtrim($url['path'], '/'))); +} + /*-------------------------------------- // APCu Caching --------------------------------------*/ diff --git a/help.php b/help.php index ed754f0..19b1901 100644 --- a/help.php +++ b/help.php @@ -35,7 +35,7 @@ if(verify_hash($opts, $auth)) {

Goosle

- " name="q" required /> + " name="q" /> @@ -63,12 +63,12 @@ if(verify_hash($opts, $auth)) {

Google features

Searching defaults to Moderate Safe mode. You can override safe mode by adding the prefix safe:on or safe:off to your search query.
On will use 'Strict' mode, while off will disable safe searching. this may yield results that are unsuitable for work or minors.

Google results are not personalized by default, using Google's option for it.

-

Google search is language agnostic and Google will try to figure out on it's own what language to use. To search in a specific language prefix your search with lang fr for French or lang:es for Spanish. Any language that google supports will work as long as you use the ISO639-1:2002 language code. Commonly these are the 2 letter abbreviations for the language such as; en, fr, es, de, sk, and so on.

+

Google search is language agnostic and Google will try to figure out on it's own what language to use. To search in a specific language prefix your search with lang:fr for French or lang:es for Spanish. Any language that google supports will work as long as you use the ISO639-1:2002 language code. Commonly these are the 2 letter abbreviations for the language such as; en, fr, es, de, sk, and so on.

To do a category search, prefix your search with one of the following keywords; app, book, news, shop or patent. This will tell Google to look for results in that specific category.
For example: Searching for book trainspotting will (or should) show results related to the book Trainspotting.

enable_image_search == "on") { ?>

Image Search

-

Search for images through Qwant Image Search.
The number of results is limited to 50.

+

Search for images through Yahoo! Image Search.
The number of results is limited to 100. So up to 100 images may appear.

Contrary to Google Image Search which opens a popup/slider with more information. Goosle Image Search links directly to the page where the image is hosted.

You can search for images in a general size by adding size:small, size:medium or size:large to your search query.

@@ -77,7 +77,7 @@ if(verify_hash($opts, $auth)) { special['currency'] == "on") { ?>

Currency converter

Convert currency with a simple query.
- For example: Search for 20 EUR in HKD or 20 USD to MXN and DuckDuckGo or Google will search for it, but also a local conversion is done in a highlighted result.

+ For example: Search for 20 EUR in HKD or 14 USD to MXN and DuckDuckGo or Google will search for it, but also a local conversion is done in a highlighted result.

special['wikipedia'] == "on") { ?> @@ -89,24 +89,33 @@ if(verify_hash($opts, $auth)) { special['phpnet'] == "on") { ?>

PHP.net Search

Prefix your search with php to search on php.net for a PHP function.
- For example: Searching for php in_array or php trim will show you a brief description, compatible PHP versions and a usage example for that function.

+ For example: Searching for php in_array or php trim will show you a brief description, compatible PHP versions and the basic syntax for that function.

special['definition'] == "on") { ?>

Word Definition

-

You can easily look up the meaning of single words. Prefix the word you want to look up with any of the following keywords; d, define, mean, meaning.
- For example: Searching for define search will search for that on Google or DuckDuckGo, but also show the definition highlighted above the search results.

+

You can easily look up the meaning of single words. Prefix the word you want to look up with any of the following keywords; d, define, mean or meaning.
+ For example: Searching for define search will search for that on Google or DuckDuckGo, but also show the dictionary definition highlighted above the search results.

Note: Special Searches do not work for torrent searches.

enable_torrent_search == "on") { ?>

Torrent Search

-

Search for anything torrent sites have on offer the direct search result should give you the magnet link.
Results are gathered from 1337x, Nyaa, The Pirate Bay and YTS and are sorted by Seeders, highest first. The number of results is limited to 50.

+

Search for anything torrent sites have on offer the direct search result should give you the magnet link.
Results are gathered from 1337x, Nyaa, The Pirate Bay, EZTV and YTS and are sorted by Seeders, highest first. The number of results is limited to 50.

The search scripts will try to provide useful data which may include; Seeders/Leechers, A link to the torrent page, Download Category, Release year, Movie quality (720p, 4K etc.), Movie Runtime and the Download Size. Not every website makes this available and all results take a best effort approach.

-

Disclaimer: If you like, or found a use for, what you downloaded, you should probably buy a legal copy of it.

+ special['imdb_id_search'] == "on") { ?> +

Searching TV Shows

+

To do a specific search on The Pirate Bay and EZTV you can search for IMDb Title IDs. These are numeric IDs prefixed with tt. This kind of search is useful if you're looking a tv show that doesn't have a unique name.

+

If you already know the Title ID you can enter it directly in the Torrent search as your search query.
+ If you don't know the Title ID you can search DuckDuckGo or Google for imdb [tv show name], for example imdb game of thrones.
+ Goosle will detect the IMDb ID from the search results and show a special search result that offers you to search for Magnet Links through a torrent search.

+ +

To help you narrow down seasons and episodes you can search for tt0944947 S08 and get filtered results from EZTV for Game of Thrones Season 8. Searching for tt0944947 S08E05 should find Season 8 Episode 5 and so on.

+ +

Note: If you like, or found a use for, what you downloaded, you should probably buy a legal copy of it.

-

Acknowledgements:
Goosle started as a fork of LibreY, and takes some design cues from DuckDuckGo.com. Goosle is created by Arnan de Gans.

+

Acknowledgements:
Goosle started as a fork of LibreY, and takes some design cues from DuckDuckGo.com. Goosle is created by Arnan de Gans with the intent to make search fun again.

diff --git a/index.php b/index.php index 57fa1b1..9b1bb39 100644 --- a/index.php +++ b/index.php @@ -36,7 +36,7 @@ if(verify_hash($opts, $auth)) {

Goosle

- + diff --git a/readme.md b/readme.md index c43d68e..5e38c9f 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # Goosle -A fast, privacy oriented meta search engine that just works. +A fast, privacy oriented meta search engine that just works. \ Kept simple so everyone can use it and to make sure it works on most (basic) webservers. Host for yourself and friends, with a access hash key. Or set up a public search website. @@ -11,7 +11,7 @@ After-all, finding things should be easy and not turn into a chore. ## Features - Search on DuckDuckGo - Search on Google.com -- Image search through Qwant +- Image search through Yahoo! Images - Search for magnet links on popular Torrent sites - Special searches for; Currency conversion, Dictionary, Wikipedia and php.net - Instant password generator on the home page @@ -27,22 +27,25 @@ What Goosle does *not* have. And yet it just works... +If you like Goosle, or found a use for it, please support my work and [donate](https://www.arnan.me/donate.html?mtm_campaign=goosle_readme). + ## Screenshots [![Goosle Mainpage](https://ajdg.solutions/wp-content/uploads/2023/12/goosle-mainpage-150x150.png)](https://ajdg.solutions/wp-content/uploads/2023/12/goosle-mainpage.png) [![Goosle Search results](https://ajdg.solutions/wp-content/uploads/2023/12/goosle-search-150x150.png)](https://ajdg.solutions/wp-content/uploads/2023/12/goosle-search.png) [![Goosle Torrent results](https://ajdg.solutions/wp-content/uploads/2023/12/goosle-torrentsearch-150x150.png)](https://ajdg.solutions/wp-content/uploads/2023/12/goosle-torrentsearch.png) ## Requirements -Any basic webserver/webhosting package with PHP7.4 or newer. +Any basic webserver/webhosting package with PHP7.4 or newer. \ Tested to work on Apache with PHP8.2. ## Installation 1. Unzip the download. -2. Edit the config.php file with your preferences. -3. Upload all files to your webserver, for example to the root folder of a subdomain (eg. search.example.com) or a sub-folder on your main site (eg. example.com/search/) -4. Rename goosle.htaccess to .htaccess -5. Load the site in your browser. If you've enabled the access hash add ?a=YOURHASH to the url. -6. Let me know where you installed Goosle :-) +2. In the main directory. Copy config.default.php to config.php. +3. Edit config.php file and set your preferences. +4. Upload all files to your webserver, for example to the root folder of a subdomain (eg. search.example.com) or a sub-folder on your main site (eg. example.com/search/) +5. Rename goosle.htaccess to .htaccess +6. Load the site in your browser. If you've enabled the access hash add ?a=YOURHASH to the url. +7. Let me know where you installed Goosle :-) ### Notes: - The .htaccess file has a redirect to force HTTPS as well as browser caching instructions ready to go. @@ -51,25 +54,46 @@ Tested to work on Apache with PHP8.2. Have fun finding things! - ## Disclaimer -Goosle started as a fork of LibreY, and ended up as a rewrite and something different completely. While the code structure remains largely the same, most functions have been rewritten or altered to work as I need it to. -Search results take design cues from DuckDuckGo and the torrent search has been modified to show more useful information where possible. +Goosle started as a fork of LibreY, and ended up as a rewrite and something different completely. While the code structure remains largely the same, most functions have been rewritten or altered to work as I need it to. \ +Search results take design cues from DuckDuckGo and the torrent search has been modified to show more useful information where possible. \ Goosle does not index, store or distribute torrent files. If you like, or found a use for, what you downloaded, you should probably buy a legal copy of it. -THe name Goosle is my last name with an L added in. Translate it from Dutch. Not in any way a derivation of Google and DuckDuckGo combined :wink:. +The name Goosle is my last name with an L added in. Translate it from Dutch. Not in any way a derivation of Google and DuckDuckGo combined :wink: ## Support -Goosle comes with limited support. You can post your questions on Github or on my support forum on [ajdg.solutions](https://ajdg.solutions/support/). +Goosle comes with limited support. \ +You can post your questions on Github or on my support forum on [ajdg.solutions](https://ajdg.solutions/support/?mtm_campaign=goosle_readme). \ +Or say hi on [Telegram](https://t.me/arnandegans). ## Changelog +1.1 - December 21, 2023 +- [new] API search for EZTV TV Shows. +- [new] config.default.php with default settings. +- [new] New option 'imdb_id_search' in 'special' settings in config.php. +- [new] New option 'show_zero_seeders' in config.php. +- [new] Special result and torrent redirect for IMDb IDs. +- [new] Replaced image search with Yahoo! Images. +- [new] Styled 'reset' button for search fields. +- [tweak] Removed 'raw_output' option. +- [tweak] Re-arranged results array to be more logical/easy to use. +- [tweak] Re-arranged code for results to do no double checks for search results. +- [tweak] Added more user-agents. +- [tweak] Torrent results page. +- [tweak] Sanitize scraped data earlier in the process. +- [tweak] Consistent single quotes for arrays. +- [tweak] Consistent spaces, tabs and newlines. +- [fix] Inconsistent input height for search field vs search button. +- [fix] Better check if a search is currency conversion or not. +- [fix] Typos in help.php. + 1.0.2 - December 7, 2023 -- [fix] Magnet links for torrents no longer opening in new tabs. - [change] More useful error response when search doesn't work. - [change] EngineRequest::request_successful() now provides a boolean response. - [change] Removed versioning indicator from help page. - [change] Added version indicator to results.php and help.php footer. - [change] 'Nope, Go away!' for unauthorized users changed to 'Goosle'. +- [fix] Magnet links for torrents no longer opening in new tabs. 1.0.1 - December 5, 2023 - [fix] mktime() getting intermittent strings in 1337x crawler. diff --git a/results.php b/results.php index fa11812..d14b092 100644 --- a/results.php +++ b/results.php @@ -35,8 +35,9 @@ if(verify_hash($opts, $auth)) {

Goosle

- " name="q" required /> - + " name="q" /> + +