Просмотр исходного кода

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.
Arnan de Gans 1 год назад
Родитель
Сommit
86d0d57dc8

+ 16 - 7
assets/css/styles.css

@@ -9,7 +9,7 @@
 *  liability that might arise from its use.
 *  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; }
 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; }
 article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; }
 h2, h3, h4, h5, h6 { font-weight: normal; }
 h2, h3, h4, h5, h6 { font-weight: normal; }
@@ -19,8 +19,11 @@ input, button { outline: none; }
 button { cursor: pointer; }
 button { cursor: pointer; }
 p { font-size: 18px; color: #494949; }
 p { font-size: 18px; color: #494949; }
 a { text-decoration: none; color: #4495d4; }
 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; }
 a:hover { text-decoration: underline; }
 input[type="text"]:invalid ~ input[type="submit"] { opacity: 0.5; pointer-events: none; }
 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,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23777'><path d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>"); }
 
 
 /* Page structure */
 /* Page structure */
 .wrap { min-height: 100vh; overflow: hidden; }
 .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, .password-generator { text-align: center; margin-top: 10%; }
 .search-box-main h1 { font-size: 70px; }
 .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-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; }
 .search-box-buttons button:hover { border: 1px solid #5f6368; }
 
 
 .password-generator { margin: 30px auto; padding: 0; }
 .password-generator { margin: 30px auto; padding: 0; }
@@ -41,8 +45,10 @@ body.main { background-color: #1f242b; color: #f0f6fc; }
 
 
 /* Search Results - Header */
 /* Search Results - Header */
 .header-wrap { background-color: #1f242b; color: #f0f6fc; border-bottom: 2px solid #1fa4d1; }
 .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 */
 /* Search results - Header Navigation */
 .navigation-header { margin-left: 165px; margin-bottom: 10px; }
 .navigation-header { margin-left: 165px; margin-bottom: 10px; }
@@ -55,7 +61,7 @@ body.main { background-color: #1f242b; color: #f0f6fc; }
 /* Search results - Main column */
 /* Search results - Main column */
 .main-column { width: 100%; }
 .main-column { width: 100%; }
 .main-column ol .result { margin: .50rem 0 .50rem 0; padding: 0; }
 .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 { 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; }
 .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 { 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::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 */
 /* Special results - Main column */
 .main-column ol .special-result { background-color: #fefefe; }
 .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; }
 .no-decoration, .no-decoration:hover { text-decoration: none; }
 .hide { display: none; }
 .hide { display: none; }
 .G { color: #1fa4d1; }
 .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; }
 .auth-error { margin-top: 15%; font-size: 32px; text-align: center; color: #eaeaea; }
 
 
 /* Footer bar */
 /* Footer bar */

+ 32 - 26
config.php → 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.
 	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.
 	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:
 CACHE:
 	If you have ACPu it is highly recommended to enable caching as it'll speed up repeatable searched by a lot.
 	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
 	"on" (Recommended) for active sites, requires APCu
@@ -23,24 +30,13 @@ CACHE:
 CACHE_TIME:
 CACHE_TIME:
 	Minutes the result should be cached in ACPu.
 	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)
 	"on" (Default)
 	"off"
 	"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)
 	"on" (Default)
 	"off"
 	"off"
 
 
@@ -58,6 +54,9 @@ USER AGENTS:
 	Chrome may attract attention because of the lack of Chrome information (tracking) aside from the user agent.
 	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.
 	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:
 BLOCK 1337x CATEGORIES:
 	Add category IDs of 1337x categories, check /engines/torrent/1337x.php for a list of known 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(
 return (object) array(
 	"hash" => "j9fg-i2du-er6m",
 	"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(
 	"special" => array(
 		"currency" => "on", // Currency converter
 		"currency" => "on", // Currency converter
 		"definition" => "on", // Word dictionary
 		"definition" => "on", // Word dictionary
 		"wikipedia" => "on", // Wikipedia highlight
 		"wikipedia" => "on", // Wikipedia highlight
 		"phpnet" => "on", // PHP-dot-net 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
 		"password_generator" => "on" // Password generator on homepage
 	),
 	),
 
 
 	"user_agents" => array(
 	"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
     "leetx_categories_blocked" => array(3, 7, 47), // Default: 3, 7, 47
     "piratebay_categories_blocked" => array(206, 210), // Default: 206, 210
     "piratebay_categories_blocked" => array(206, 210), // Default: 206, 210
     "yts_categories_blocked" => array("horror"), // Default: "horror"
     "yts_categories_blocked" => array("horror"), // Default: "horror"
@@ -108,6 +113,7 @@ return (object) array(
     	"udp://exodus.desync.com:6969/announce", 
     	"udp://exodus.desync.com:6969/announce", 
     	"udp://tracker.torrent.eu.org:451/announce",
     	"udp://tracker.torrent.eu.org:451/announce",
     ),
     ),
-    "version" => "1.0.2"
+
+    "version" => "1.1b2" // Please don't change this
 );
 );
 ?>
 ?>

+ 8 - 6
engines/duckduckgo.php

@@ -51,21 +51,23 @@ class DuckDuckGoRequest extends EngineRequest {
     }
     }
 
 
     public function parse_results($response) {
     public function parse_results($response) {
-		$results = array();
+		$results = array("search" => array());
 		$xpath = get_xpath($response);
 		$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];
 		$didyoumean = $xpath->query(".//div[@id='did_you_mean']/a[1]")[0];
 		if(!is_null($didyoumean)) {
 		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];
         $search_specific = $xpath->query(".//div[@id='did_you_mean']/a[2]")[0];
         if(!is_null($search_specific)) {
         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];
             $url = $xpath->evaluate(".//h2[@class='result__title']//a/@href", $result)[0];
             if($url == null) continue;
             if($url == null) continue;
 
 
@@ -79,7 +81,7 @@ class DuckDuckGoRequest extends EngineRequest {
 
 
 			$description = $xpath->evaluate(".//a[@class='result__snippet']", $result)[0];
 			$description = $xpath->evaluate(".//a[@class='result__snippet']", $result)[0];
 
 
-            array_push($results, array (
+            array_push($results['search'], array (
                 "title" => htmlspecialchars($title->textContent),
                 "title" => htmlspecialchars($title->textContent),
                 "url" =>  htmlspecialchars($url->textContent),
                 "url" =>  htmlspecialchars($url->textContent),
                 "description" =>  $description == null ? 'No description was provided for this site.' : htmlspecialchars($description->textContent)
                 "description" =>  $description == null ? 'No description was provided for this site.' : htmlspecialchars($description->textContent)

+ 7 - 5
engines/google.php

@@ -71,20 +71,22 @@ class GoogleRequest extends EngineRequest {
     }
     }
 
 
     public function parse_results($response) {
     public function parse_results($response) {
-        $results = array();
+		$results = array("search" => array());
         $xpath = get_xpath($response);
         $xpath = get_xpath($response);
 
 
-        if(!$xpath) return $results;
+        if(!$xpath) return array();
 
 
+		// Scrape recommended
         $didyoumean = $xpath->query(".//a[@class='gL9Hy']")[0];
         $didyoumean = $xpath->query(".//a[@class='gL9Hy']")[0];
         if(!is_null($didyoumean)) {
         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];
         $search_specific = $xpath->query(".//a[@class='spell_orig']")[0];
         if(!is_null($search_specific)) {
         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) {
         foreach($xpath->query("//div[@id='search']//div[contains(@class, 'g')]") as $result) {
             $url = $xpath->evaluate(".//div[@class='yuRUbf']//a/@href", $result)[0];
             $url = $xpath->evaluate(".//div[@class='yuRUbf']//a/@href", $result)[0];
 			if($url == null) continue;
 			if($url == null) continue;
@@ -99,7 +101,7 @@ class GoogleRequest extends EngineRequest {
 
 
 			$description = $xpath->evaluate(".//div[contains(@class, 'VwiC3b')]", $result)[0];
 			$description = $xpath->evaluate(".//div[contains(@class, 'VwiC3b')]", $result)[0];
 
 
-            array_push($results, array (
+            array_push($results['search'], array (
                 "title" => htmlspecialchars($title->textContent),
                 "title" => htmlspecialchars($title->textContent),
                 "url" =>  htmlspecialchars($url->textContent),
                 "url" =>  htmlspecialchars($url->textContent),
                 "description" =>  $description == null ? 'No description was provided for this site.' : htmlspecialchars($description->textContent)
                 "description" =>  $description == null ? 'No description was provided for this site.' : htmlspecialchars($description->textContent)

+ 118 - 56
engines/search-image.php

@@ -1,6 +1,5 @@
 <?php
 <?php
 class ImageSearch extends EngineRequest {
 class ImageSearch extends EngineRequest {
-
 	public function get_request_url() {
 	public function get_request_url() {
         $results = array();
         $results = array();
 
 
@@ -15,8 +14,9 @@ class ImageSearch extends EngineRequest {
 			if((strlen($switch[1]) >= 3 && strlen($switch[1]) <= 6) && !is_numeric($switch[1])) {
 			if((strlen($switch[1]) >= 3 && strlen($switch[1]) <= 6) && !is_numeric($switch[1])) {
 				if($switch[1] == "med") $switch[1] = "medium";
 				if($switch[1] == "med") $switch[1] = "medium";
 				if($switch[1] == "lrg") $switch[1] = "large";
 				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];
 					$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);
         unset($query_terms, $switch, $args, $size);
 
 
@@ -40,35 +39,63 @@ class ImageSearch extends EngineRequest {
 		$results = array("search" => array());
 		$results = array("search" => array());
 		$xpath = get_xpath($response);
 		$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;
 
 
-		// Add warning if there are no results, or a text if there is no search query.
+ 			$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 error if there are no search results
 		if(empty($results['search'])) {
 		if(empty($results['search'])) {
-			$results["error"] = array(
+			$results['error'] = array(
 				"message" => "No results found. Please try with less or different keywords!"
 				"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) {
 	public static function print_results($results, $opts) {
-		if($opts->raw_output == "on") {
-			echo '<pre>Results: ';
-			print_r($results);
-			echo '</pre>';
-		}
-
-		echo "<section class=\"main-column\">";
-		echo "<ol>";
+/*
+		echo '<pre>Results: ';
+		print_r($results);
+		echo '</pre>';
+*/
 
 
-		// Elapsed time
 		if(array_key_exists("search", $results)) {
 		if(array_key_exists("search", $results)) {
-			echo "<li class=\"meta-time\">Fetched the results in ".$results['time']." seconds.</li>";
-		}
+			echo "<ol>";
+
+			// Elapsed time
+			$number_of_results = count($results['search']);
+			echo "<li class=\"meta\">Fetched ".$number_of_results." results in ".$results['time']." seconds.</li>";
 
 
-        echo "</ol>";
+			// 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 = "<br /><small>Or instead search for <a href=\"".$search_specific_url."\">".$search_specific."</a>.</small>";
+		
+					unset($search_specific, $search_specific_url);
+				}
 
 
-		echo "<div class=\"image-wrapper\">";
-		echo "<ol class=\"image-grid\">";
+				$didyoumean_url = "./results.php?q=".urlencode($results['did_you_mean'])."&t=".$opts->type."&a=".$opts->hash;
+	
+				echo "<li class=\"meta\">Did you mean <a href=\"".$didyoumean_url."\">".$results['did_you_mean']."</a>?$specific_result</li>";
+	
+				unset($didyoumean_url, $specific_result);
+			}
+			echo "</ol>";
+
+			// Search results
+			echo "<div class=\"image-wrapper\">";
+			echo "<ol class=\"image-grid\">";
 	
 	
-		// Search results
-		if(array_key_exists("search", $results)) {
 	        foreach($results['search'] as $result) {
 	        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']."&times;".$result['height'];
+				if(!empty($result['filesize'])) $meta[] = $result['filesize'];
+
+				// Links
+				$links[] = "<a href=\"".$result['url']."\" target=\"_blank\">Website</a>";
+				if(!empty($result['direct_link'])) $links[] = "<a href=\"".$result['direct_link']."\" target=\"_blank\">Image</a>";
 
 
 				// Put result together
 				// Put result together
 				echo "<li class=\"result\"><div class=\"image-box\">";
 				echo "<li class=\"result\"><div class=\"image-box\">";
-				echo "<a href=\"".$result["url"]."\" target=\"_blank\" title=\"".$result["alt"]."\"><img src=\"".$result["image"]."\" alt=\"".$result["alt"]."\" /></a>";
-				echo "</div></li>";
+				echo "<a href=\"".$result['url']."\" target=\"_blank\" title=\"".$result['alt']."\"><img src=\"".$result['image']."\" alt=\"".$result['alt']."\" /></a>";
+				echo "</div><span>".implode(" - ", $meta)."<br />".implode(" - ", $links)."</span>";
+				echo "</li>";
+				
+				unset($result, $meta, $links);
 	        }
 	        }
+
+	        echo "</ol>";
+	        echo "</div>";
+	 		echo "<center><small>Not what you're looking for? Try <a href=\"https://duckduckgo.com/?q=".urlencode($opts->query)."&iax=images&ia=images\" target=\"_blank\">DuckDuckGo</a>, <a href=\"https://images.search.yahoo.com/search/images?p=".urlencode($opts->query)."\" target=\"_blank\">Yahoo! Images</a> or <a href=\"https://www.google.com/search?q=".urlencode($opts->query)."&tbm=isch&pws=0\" target=\"_blank\">Google Images</a>.</small></center>";
 		}
 		}
 
 
-        echo "</ol>";
-        echo "</div>";
- 		echo "<center><small>Not what you're looking for? Try <a href=\"https://duckduckgo.com/?q=".urlencode($opts->query)."&iax=images&ia=images\" target=\"_blank\">DuckDuckGo</a> or <a href=\"https://www.google.com/search?q=".urlencode($opts->query)."&tbm=isch&pws=0\" target=\"_blank\">Google Images</a>.</small></center>";
- 		echo "</section>";
+		// No results found
+        if(array_key_exists("error", $results)) {
+            echo "<div class=\"error\">".$results['error']['message']."</div>";
+        }
+
 	}
 	}
 }
 }
 ?>
 ?>

+ 56 - 31
engines/search-torrent.php

@@ -20,19 +20,21 @@ class TorrentSearch extends EngineRequest {
 		require "engines/torrent/nyaa.php";
 		require "engines/torrent/nyaa.php";
 		require "engines/torrent/thepiratebay.php";
 		require "engines/torrent/thepiratebay.php";
 		require "engines/torrent/yts.php";
 		require "engines/torrent/yts.php";
+		require "engines/torrent/eztv.php";
 		
 		
 		$this->requests = array(
 		$this->requests = array(
 			new LeetxRequest($opts, $mh), // 1337x
 			new LeetxRequest($opts, $mh), // 1337x
 			new NyaaRequest($opts, $mh),
 			new NyaaRequest($opts, $mh),
 			new PirateBayRequest($opts, $mh),
 			new PirateBayRequest($opts, $mh),
-			new YTSRequest($opts, $mh)
+			new YTSRequest($opts, $mh),
+			new EZTVRequest($opts, $mh)
 		);
 		);
 	}
 	}
 
 
     public function parse_results($response) {
     public function parse_results($response) {
         $results = $results_temp = array();
         $results = $results_temp = array();
 
 
-        foreach ($this->requests as $request) {
+        foreach($this->requests as $request) {
             if($request->request_successful()) {
             if($request->request_successful()) {
                 $results_temp = array_merge($results_temp, $request->get_results());
                 $results_temp = array_merge($results_temp, $request->get_results());
             }
             }
@@ -45,13 +47,20 @@ class TorrentSearch extends EngineRequest {
 	
 	
 			// Cap results
 			// Cap results
 			$results['search'] = array_slice($results_temp, 0, 50);
 			$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)) {
         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) {
     public static function print_results($results, $opts) {
-		if($opts->raw_output == "on") {
-			echo '<pre>Results: ';
-			print_r($results);
-			echo '</pre>';
-		}
+/*
+		echo '<pre>Results: ';
+		print_r($results);
+		echo '</pre>';
+*/
 
 
-		echo "<section class=\"main-column\">";
-		echo "<ol>";
+		if(array_key_exists("search", $results)) {
+			echo "<ol>";
 
 
-		// Elapsed time
-		echo "<li class=\"meta-time\">Fetched the results in ".$results['time']." seconds.</li>";
+			// 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);
+			    }
 
 
-		// No results found
-        if(array_key_exists("error", $results)) {
-            echo "<li class=\"meta-error\">".$results['error']['message']."</li>";
-        }
+				$sources = "<br /><small>Including ".$sources.". Links with the most seeders are listed first.</small>";
+			}
 
 
-		// Search results
-		if(array_key_exists("search", $results)) {
+			// Elapsed time
+			$number_of_results = count($results['search']);
+			echo "<li class=\"meta\">Fetched ".$number_of_results." results in ".$results['time']." seconds.".$sources."</li>";
+
+			// Search results
 			foreach($results['search'] as $result) {
 			foreach($results['search'] as $result) {
 				$meta = array();
 				$meta = array();
+
 				// Optional data
 				// Optional data
 				if(array_key_exists('quality', $result)) $meta[] = "<strong>Quality:</strong> ".$result['quality'];
 				if(array_key_exists('quality', $result)) $meta[] = "<strong>Quality:</strong> ".$result['quality'];
 				if(array_key_exists('year', $result)) $meta[] = "<strong>Year:</strong> ".$result['year'];
 				if(array_key_exists('year', $result)) $meta[] = "<strong>Year:</strong> ".$result['year'];
 				if(array_key_exists('category', $result)) $meta[] = "<strong>Category:</strong> ".$result['category'];
 				if(array_key_exists('category', $result)) $meta[] = "<strong>Category:</strong> ".$result['category'];
 				if(array_key_exists('runtime', $result)) $meta[] = "<strong>Runtime:</strong> ".date('H:i', mktime(0, $result['runtime']));
 				if(array_key_exists('runtime', $result)) $meta[] = "<strong>Runtime:</strong> ".date('H:i', mktime(0, $result['runtime']));
 				if(array_key_exists('date_added', $result)) $meta[] = "<strong>Added:</strong> ".date('M d, Y', $result['date_added']);
 				if(array_key_exists('date_added', $result)) $meta[] = "<strong>Added:</strong> ".date('M d, Y', $result['date_added']);
-				if(array_key_exists('url', $result)) $meta[] = "<a href=\"".$result["url"]."\" target=\"_blank\" title=\"Careful - Site may contain intrusive popup ads and malware!\">Torrent page</a>";
+				if(array_key_exists('url', $result)) $meta[] = "<a href=\"".$result['url']."\" target=\"_blank\" title=\"Careful - Site may contain intrusive popup ads and malware!\">Torrent page</a>";
 	
 	
 				// Put result together
 				// Put result together
 				echo "<li class=\"result\"><article>";
 				echo "<li class=\"result\"><article>";
-				echo "<div class=\"url\"><a href=\"".$result["magnet"]."\">".$result["source"]."</a></div>";
-				echo "<div class=\"title\"><a href=\"".$result["magnet"]."\"><h2>".stripslashes($result["name"])."</h2></a></div>";
+				echo "<div class=\"url\"><a href=\"".$result['magnet']."\">".$result['source']."</a></div>";
+				echo "<div class=\"title\"><a href=\"".$result['magnet']."\"><h2>".stripslashes($result['name'])."</h2></a></div>";
 				echo "<div class=\"description\"><strong>Seeds:</strong> <span class=\"seeders\">".$result['seeders']."</span> - <strong>Peers:</strong> <span class=\"leechers\">".$result['leechers']."</span> - <strong>Size:</strong> ".$result['size']."<br />".implode(" - ", $meta)."</div>";
 				echo "<div class=\"description\"><strong>Seeds:</strong> <span class=\"seeders\">".$result['seeders']."</span> - <strong>Peers:</strong> <span class=\"leechers\">".$result['leechers']."</span> - <strong>Size:</strong> ".$result['size']."<br />".implode(" - ", $meta)."</div>";
 				echo "</article></li>";
 				echo "</article></li>";
+
+				unset($result, $meta);
 			}
 			}
-		}
 
 
-		echo "<li class=\"result\"><article>";
-		echo "<small>Goosle does not store, index, offer or distribute torrent files.</small>";
-		echo "</article></li>";
+			echo "</ol>";
+			echo "<center><small>Showing up to 50 results, sorted by most seeders.<br />Goosle does not index, offer or distribute torrent files.</small></center>";
+		}
 
 
-		echo "</ol>";
-		echo "<center><small>Showing 50 results, sorted by most seeders.</small></center>";
-		echo "</section>";
+		// No results found
+        if(array_key_exists("error", $results)) {
+            echo "<div class=\"error\">".$results['error']['message']."</div>";
+        }
 	}
 	}
 }
 }
 ?>
 ?>

+ 78 - 67
engines/search.php

@@ -36,17 +36,17 @@ class TextSearch extends EngineRequest {
         // Abort if no results from search engine
         // Abort if no results from search engine
         if(!isset($this->engine_request)) return $results;
         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()) {
 		if($this->engine_request->request_successful()) {
 			$search_result = $this->engine_request->get_results();
 			$search_result = $this->engine_request->get_results();
 
 
 			if($search_result) {
 			if($search_result) {
-				$results['search'] = $search_result;
+				$results = $search_result;
 			}
 			}
 
 
 			unset($search_result);
 			unset($search_result);
 		} else {
 		} 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'].".<br />Try again in a few seconds or <a href=\"".curl_getinfo($this->engine_request->ch)['url']."\" target=\"_blank\">visit the search engine</a> in a new tab."
                 "message" => "Error code ".curl_getinfo($this->engine_request->ch)['http_code']." for ".curl_getinfo($this->engine_request->ch)['url'].".<br />Try again in a few seconds or <a href=\"".curl_getinfo($this->engine_request->ch)['url']."\" target=\"_blank\">visit the search engine</a> in a new tab."
             );
             );
 		}			
 		}			
@@ -62,9 +62,9 @@ class TextSearch extends EngineRequest {
 			unset($special_result);
 			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)) {
 		if(empty($results)) {
-			$results["error"] = array(
+			$results['error'] = array(
 				"message" => "No results found. Please try with less or different keywords!"
 				"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)  {
     public static function print_results($results, $opts)  {
-		if($opts->raw_output == "on") {
-			echo '<pre>Results: ';
-			print_r($results);
-			echo '</pre>';
-		}
-
-		echo "<section class=\"main-column\">";
-		echo "<ol>";
+/*
+		echo '<pre>Results: ';
+		print_r($results);
+		echo '</pre>';
+*/
 
 
-		// Elapsed time
 		if(array_key_exists("search", $results)) {
 		if(array_key_exists("search", $results)) {
-			$number_of_results = count($results['search']);
-			echo "<li class=\"meta-time\">Fetched ".$number_of_results." results in ".$results['time']." seconds.</li>";
-		}
+			echo "<ol>";
 
 
-		// No results found
-        if(array_key_exists("error", $results)) {
-            echo "<li class=\"meta-error\">".$results['error']['message']."</li>";
-        }
+			// Elapsed time
+			$number_of_results = count($results['search']);
+			echo "<li class=\"meta\">Fetched ".$number_of_results." results in ".$results['time']." seconds.</li>";
 
 
-		// Did you mean/Search suggestion
-		if(array_key_exists("search", $results)) {
-			$specific_result = "";
+			// Did you mean/Search suggestion
+			if(array_key_exists("did_you_mean", $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
 					// 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;
 					$search_specific_url = "./results.php?q="  . urlencode($search_specific)."&t=".$opts->type."&a=".$opts->hash;
-					$specific_result = "<br /><small>Or instead search for <a href=\"$search_specific_url\">$search_specific</a>.</small>";
+					
+					// Specific search
+					$specific_result = "<br /><small>Or instead search for <a href=\"".$search_specific_url."\">".$search_specific."</a>.</small>";
 		
 		
-					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 "<li class=\"meta-did-you-mean\">Did you mean <a href=\"$didyoumean_url\">$didyoumean</a>?$specific_result</li>";
+				echo "<li class=\"meta\">Did you mean <a href=\"".$didyoumean_url."\">".$results['did_you_mean']."</a>?".$specific_result."</li>";
 	
 	
-				unset($results['search'][0], $didyoumean, $didyoumean_url, $specific_result);
-			}
-		}
-
-		// Special result
-		if(array_key_exists("special", $results)) {
-			echo "<li class=\"special-result\"><article>";
-			// 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'] .= '. <a href="'.$results['special']['source'].'" target="_blank">[...]</a>';
+				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 = "<img src=\"".$results['special']['image']."\" align=\"right\" width=\"".$width."\" height=\"".$height."\" />";
-				$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].") - <a href=\"./results.php?q=".$imdb_result[0][1]."&a=".$opts->hash."&t=9\">search for magnet links</a>?<br /><sub>An IMDb ID is detected when a TV Show is present in the results. The first match is highlighted here.</sub>"
+						);
+						$found = true;
+					}
+				}
 			}
 			}
-			echo "<div class=\"title\"><h2>".$results['special']['title']."</h2></div>";
-			echo "<div class=\"text\">".$results['special']['text']."</div>";
-			if(array_key_exists("source", $results['special'])) {
-				echo "<div class=\"source\"><a href=\"".$results['special']['source']."\" target=\"_blank\">".$results['special']['source']."</a></div>";
+			if(array_key_exists("special", $results)) {
+				echo "<li class=\"special-result\"><article>";
+				// 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'] .= '. <a href="'.$results['special']['source'].'" target="_blank">[...]</a>';
+				}
+	
+				// 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 = "<img src=\"".$results['special']['image']."\" align=\"right\" width=\"".$width."\" height=\"".$height."\" />";
+					$results['special']['text'] = $special_image.$results['special']['text'];
+	
+					unset($image_specs, $width, $height, $special_image);
+				}
+				echo "<div class=\"title\"><h2>".$results['special']['title']."</h2></div>";
+				echo "<div class=\"text\">".$results['special']['text']."</div>";
+				if(array_key_exists("source", $results['special'])) {
+					echo "<div class=\"source\"><a href=\"".$results['special']['source']."\" target=\"_blank\">".$results['special']['source']."</a></div>";
+				}
+				echo "</article></li>";
 			}
 			}
-			echo "</article></li>";
-		}
 
 
-		// Search results
-		if(array_key_exists("search", $results)) {
+			// Search results
 	        foreach($results['search'] as $result) {
 	        foreach($results['search'] as $result) {
 		        if(array_key_exists("did_you_mean", $result)) continue;
 		        if(array_key_exists("did_you_mean", $result)) continue;
 	
 	
 				// Put result together
 				// Put result together
 				echo "<li class=\"result\"><article>";
 				echo "<li class=\"result\"><article>";
-				echo "<div class=\"url\"><a href=\"".$result["url"]."\" target=\"_blank\">".get_formatted_url($result["url"])."</a></div>";
-				echo "<div class=\"title\"><a href=\"".$result["url"]."\" target=\"_blank\"><h2>".$result["title"]."</h2></a></div>";
-				echo "<div class=\"description\">".$result["description"]."</div>";
+				echo "<div class=\"url\"><a href=\"".$result['url']."\" target=\"_blank\">".get_formatted_url($result['url'])."</a></div>";
+				echo "<div class=\"title\"><a href=\"".$result['url']."\" target=\"_blank\"><h2>".$result['title']."</h2></a></div>";
+				echo "<div class=\"description\">".$result['description']."</div>";
 				echo "</article></li>";
 				echo "</article></li>";
+
+				unset($result);
 	        }
 	        }
+
+			echo "</ol>";
 		}
 		}
- 
-        echo "</ol>";
-        echo "</section>";
+
+		// No results found
+        if(array_key_exists("error", $results)) {
+            echo "<div class=\"error\">".$results['error']['message']."</div>";
+        }
     }
     }
 }
 }
 ?>
 ?>

+ 1 - 1
engines/special/currency.php

@@ -18,7 +18,7 @@ class CurrencyRequest extends EngineRequest {
         $json_response = json_decode($response, true);
         $json_response = json_decode($response, true);
 
 
 		if(!empty($json_response)) {
 		if(!empty($json_response)) {
-	        $result = $json_response["rates"];
+	        $result = $json_response['rates'];
 
 
 			// Process query
 			// Process query
 			// [0] = AMOUNT
 			// [0] = AMOUNT

+ 1 - 1
engines/special/wikipedia.php

@@ -51,7 +51,7 @@ class WikipediaRequest extends EngineRequest {
 			);
 			);
 			
 			
 			if (array_key_exists("thumbnail",  $result)) {
 			if (array_key_exists("thumbnail",  $result)) {
-				$response["image"] = strip_tags(trim($result["thumbnail"]["source"]));
+				$response['image'] = strip_tags(trim($result['thumbnail']['source']));
 			}
 			}
 			
 			
 			return $response;
 			return $response;

+ 38 - 33
engines/torrent/1337x.php

@@ -10,17 +10,17 @@
 *  liability that might arise from its use.
 *  liability that might arise from its use.
 ------------------------------------------------------------------------------------ */
 ------------------------------------------------------------------------------------ */
 class LeetxRequest extends EngineRequest {
 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(
 		$categories = array(
 			1 => "DVD",
 			1 => "DVD",
 			2 => "Divx/Xvid",
 			2 => "Divx/Xvid",
@@ -105,38 +105,43 @@ class LeetxRequest extends EngineRequest {
 		);
 		);
 
 
 		// Scrape the page
 		// 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;
+		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 = 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])));
 			$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);
-			$size = $size_unformatted[0] . " " . preg_replace("/[0-9]+/", "", $size_unformatted[1]);
+			
+			// 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 (
 			array_push($results, array (
-                // Required
+				// Required
 				"source" => "1337x.to",
 				"source" => "1337x.to",
-				"name" => sanitize($name),
+				"name" => $name,
 				"magnet" => "./engines/torrent/magnetize_1337x.php?url=".$url,
 				"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
 				// Optional values
-				"category" => $categories[sanitize($category[2])],
+				"category" => $categories[$category],
 				"url" => $url,
 				"url" => $url,
 				"date_added" => $date_added
 				"date_added" => $date_added
 			));
 			));
-        }
 
 
-        return $results;
-    }
+			unset($name, $seeders, $leechers, $size_unformatted, $size, $category, $url, $date_added);
+		}
+		
+		return $results;
+	}
 }
 }
 ?>
 ?>

+ 88 - 0
engines/torrent/eztv.php

@@ -0,0 +1,88 @@
+<?php
+/* ------------------------------------------------------------------------------------
+*  Goosle - A meta search engine for private and fast internet fun.
+*
+*  COPYRIGHT NOTICE
+*  Copyright 2023-2024 Arnan de Gans. All Rights Reserved.
+*
+*  COPYRIGHT NOTICES AND ALL THE COMMENTS SHOULD REMAIN INTACT.
+*  By using this code you agree to indemnify Arnan de Gans from any 
+*  liability that might arise from its use.
+------------------------------------------------------------------------------------ */
+class EZTVRequest extends EngineRequest {
+	public function get_request_url() {
+		// Make reasonably sure it's an IMDb id and abort if it's not
+		$query_terms = explode(" ", $this->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;
+	}
+}
+?>

+ 44 - 37
engines/torrent/nyaa.php

@@ -10,47 +10,54 @@
 *  liability that might arise from its use.
 *  liability that might arise from its use.
 ------------------------------------------------------------------------------------ */
 ------------------------------------------------------------------------------------ */
 class NyaaRequest extends EngineRequest {
 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
 		// 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]));
 			$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
 				// 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
 				"date_added" => $date_added
-            ));
-        }
+			));
+
+			unset($name, $magnet, $seeders, $leechers, $size, $category, $url, $date_added, $meta);
+		}
 
 
-        return $results;
-    }
+		return $results;
+	}
 }
 }
 ?>
 ?>

+ 45 - 33
engines/torrent/thepiratebay.php

@@ -10,17 +10,17 @@
 *  liability that might arise from its use.
 *  liability that might arise from its use.
 ------------------------------------------------------------------------------------ */
 ------------------------------------------------------------------------------------ */
 class PirateBayRequest extends EngineRequest {
 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
 		// No response
-        if(empty($json_response)) return $results;
-
+		if(empty($json_response)) return $results;
+		
 		$categories = array(
 		$categories = array(
 			100 => "Audio",
 			100 => "Audio",
 			101 => "Music",
 			101 => "Music",
@@ -84,32 +84,44 @@ class PirateBayRequest extends EngineRequest {
 		);
 		);
 
 
 		// Use API result
 		// Use API result
-        foreach($json_response as $response) {
+		foreach($json_response as $response) {
 			// Nothing found
 			// 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
 				// 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,
+ 			));
+
+		   	unset($name, $magnet, $seeders, $leechers, $size, $category, $id, $date_added);
+		}
 
 
-        return $results;
-    }
+		return $results;
+	}
 }
 }
 ?>
 ?>

+ 56 - 38
engines/torrent/yts.php

@@ -10,55 +10,73 @@
 *  liability that might arise from its use.
 *  liability that might arise from its use.
 ------------------------------------------------------------------------------------ */
 ------------------------------------------------------------------------------------ */
 class YTSRequest extends EngineRequest {
 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
 		// No response
-        if(empty($json_response)) return $results;
-
+		if(empty($json_response)) return $results;
+		
 		// Nothing found
 		// 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
 		// Use API result
-        foreach ($json_response["data"]["movies"] as $movie) {
+		foreach ($json_response['data']['movies'] as $movie) {
 			// Prevent gaps
 			// 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'] = '';
-
-            // Block these categories
-           	if(array_intersect($movie["genres"], $this->opts->yts_categories_blocked)) continue;
+			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']);
 
 
-            $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",
 					"source" => "yts.mx",
 					"name" => $name,
 					"name" => $name,
 					"magnet" => $magnet,
 					"magnet" => $magnet,
-					"seeders" => sanitize($torrent["seeds"]),
-					"leechers" => sanitize($torrent["peers"]),
-					"size" => sanitize($torrent["size"]),
+					"seeders" => $seeders,
+					"leechers" => $leechers,
+					"size" => $size,
 					// Optional
 					// 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;
 		return $results;
 	}
 	}

+ 51 - 42
functions/search_engine.php

@@ -75,11 +75,13 @@ abstract class EngineRequest {
 	--------------------------------------*/
 	--------------------------------------*/
 	public function get_results() {
 	public function get_results() {
 
 
+		// It's a torrent search?
 		if(!isset($this->url)) return $this->parse_results(null);
 		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);
 		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);
 		if(!isset($this->ch)) return $this->parse_results(null);
 		$response = ($this->mh) ? curl_multi_getcontent($this->ch) : curl_exec($this->ch);
 		$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
 // Load and make config available, pass around variables
 --------------------------------------*/
 --------------------------------------*/
 function load_opts() {
 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)
 	// 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) {
 function fetch_search_results($opts) {
     $start_time = microtime(true);
     $start_time = microtime(true);
 
 
-	// 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);
+	echo "<section class=\"main-column\">";
 
 
-    $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);
+	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 "<div class=\"warning\">Search query can not be empty!<br />Not sure what went wrong? Learn more about <a href=\"./help.php?a=".$opts->hash."\">how to use Goosle</a>.</div>";
+    }
 
 
-    return $results;
+	echo "</section>";
 }
 }
 
 
 /*--------------------------------------
 /*--------------------------------------
@@ -158,7 +167,7 @@ function special_search_request($opts) {
     $query_terms = explode(" ", $opts->query);
     $query_terms = explode(" ", $opts->query);
 
 
 	// Currency converter
 	// 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";
         require "engines/special/currency.php";
         $special_request = new CurrencyRequest($opts, null);
         $special_request = new CurrencyRequest($opts, null);
 	}
 	}

+ 17 - 17
functions/tools.php

@@ -20,35 +20,35 @@ function verify_hash($opts, $auth) {
 }
 }
 
 
 /*--------------------------------------
 /*--------------------------------------
-// Strip all extras from an url
+// Load pages into a DOM
 --------------------------------------*/
 --------------------------------------*/
-function get_base_url($url) {
-    $parsed = parse_url($url);
+function get_xpath($response) {
+    if(!$response)
+        return null;
 
 
-    return $parsed["scheme"] . "://" . $parsed["host"] . "/";
+    $htmlDom = new DOMDocument;
+    @$htmlDom->loadHTML($response);
+    $xpath = new DOMXPath($htmlDom);
+
+    return $xpath;
 }
 }
 
 
 /*--------------------------------------
 /*--------------------------------------
-// Format search result urls
+// Strip all extras from an url
 --------------------------------------*/
 --------------------------------------*/
-function get_formatted_url($url) {
-    $parsed = parse_url($url);
+function get_base_url($url) {
+    $url = parse_url($url);
 
 
-    return $parsed["scheme"] . "://" . $parsed["host"] . str_replace('/', ' &rsaquo; ', str_replace('%20', ' ', rtrim($parsed['path'], '/')));
+    return $url['scheme'] . "://" . $url['host'] . "/";
 }
 }
 
 
 /*--------------------------------------
 /*--------------------------------------
-// Load pages into a DOM
+// Format search result urls
 --------------------------------------*/
 --------------------------------------*/
-function get_xpath($response) {
-    if(!$response)
-        return null;
-
-    $htmlDom = new DOMDocument;
-    @$htmlDom->loadHTML($response);
-    $xpath = new DOMXPath($htmlDom);
+function get_formatted_url($url) {
+    $url = parse_url($url);
 
 
-    return $xpath;
+    return $url['scheme'] . "://" . $url['host'] . str_replace('/', ' &rsaquo; ', str_replace('%20', ' ', rtrim($url['path'], '/')));
 }
 }
 
 
 /*--------------------------------------
 /*--------------------------------------

+ 19 - 10
help.php

@@ -35,7 +35,7 @@ if(verify_hash($opts, $auth)) {
 	<div class="header-wrap">
 	<div class="header-wrap">
 		<form action="results.php" method="get" autocomplete="off">
 		<form action="results.php" method="get" autocomplete="off">
 		    <h1 class="logo"><a class="no-decoration" href="./?a=<?php echo $opts->hash; ?>"><span class="G">G</span>oosle</a></h1>        
 		    <h1 class="logo"><a class="no-decoration" href="./?a=<?php echo $opts->hash; ?>"><span class="G">G</span>oosle</a></h1>        
-		    <input tabindex="1" class="search" type="text" value="<?php echo (strlen($opts->query) > 0) ? htmlspecialchars($opts->query) : "" ; ?>" name="q" required /><input tabindex="2" class="button" type="submit" value="Search" />
+		    <input tabindex="1" class="search" type="search" value="<?php echo (strlen($opts->query) > 0) ? htmlspecialchars($opts->query) : "" ; ?>" name="q" /><input tabindex="2" class="button" type="submit" value="Search" />
 		
 		
 	        <input type="hidden" name="t" value="<?php echo $opts->type; ?>"/>
 	        <input type="hidden" name="t" value="<?php echo $opts->type; ?>"/>
 		    <input type="hidden" name="a" value="<?php echo $opts->hash; ?>">
 		    <input type="hidden" name="a" value="<?php echo $opts->hash; ?>">
@@ -63,12 +63,12 @@ if(verify_hash($opts, $auth)) {
 			<h2>Google features</h2>
 			<h2>Google features</h2>
 			<p>Searching defaults to Moderate Safe mode. You can override safe mode by adding the prefix <strong>safe:on</strong> or <strong>safe:off</strong> to your search query.<br /><strong>On</strong> will use 'Strict' mode, while <strong>off</strong> will disable safe searching. this may yield results that are unsuitable for work or minors.</p>
 			<p>Searching defaults to Moderate Safe mode. You can override safe mode by adding the prefix <strong>safe:on</strong> or <strong>safe:off</strong> to your search query.<br /><strong>On</strong> will use 'Strict' mode, while <strong>off</strong> will disable safe searching. this may yield results that are unsuitable for work or minors.</p>
 			<p>Google results are not personalized by default, using Google's option for it.</p>
 			<p>Google results are not personalized by default, using Google's option for it.</p>
-			<p>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 <strong>lang fr</strong> for French or <strong>lang:es</strong> 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.</p>
+			<p>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 <strong>lang:fr</strong> for French or <strong>lang:es</strong> 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.</p>
 			<p>To do a category search, prefix your search with one of the following keywords; <strong>app</strong>, <strong>book</strong>, <strong>news</strong>, <strong>shop</strong> or <strong>patent</strong>. This will tell Google to look for results in that specific category.<br />For example: Searching for <strong>book trainspotting</strong> will (or should) show results related to the book Trainspotting.</p>
 			<p>To do a category search, prefix your search with one of the following keywords; <strong>app</strong>, <strong>book</strong>, <strong>news</strong>, <strong>shop</strong> or <strong>patent</strong>. This will tell Google to look for results in that specific category.<br />For example: Searching for <strong>book trainspotting</strong> will (or should) show results related to the book Trainspotting.</p>
 
 
 			<?php if($opts->enable_image_search == "on") { ?>
 			<?php if($opts->enable_image_search == "on") { ?>
 				<h2>Image Search</h2>
 				<h2>Image Search</h2>
-				<p>Search for images through Qwant Image Search.<br />The number of results is limited to 50.</p>
+				<p>Search for images through Yahoo! Image Search.<br />The number of results is limited to 100. So up to 100 images may appear.</p>
 				<p>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.</p>
 				<p>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.</p>
 				<p>You can search for images in a general size by adding <strong>size:small</strong>, <strong>size:medium</strong> or <strong>size:large</strong> to your search query.</p>
 				<p>You can search for images in a general size by adding <strong>size:small</strong>, <strong>size:medium</strong> or <strong>size:large</strong> to your search query.</p>
 			<?php } ?>
 			<?php } ?>
@@ -77,7 +77,7 @@ if(verify_hash($opts, $auth)) {
 			<?php if($opts->special['currency'] == "on") { ?>
 			<?php if($opts->special['currency'] == "on") { ?>
 				<h3>Currency converter</h3>
 				<h3>Currency converter</h3>
 				<p>Convert currency with a simple query.<br />
 				<p>Convert currency with a simple query.<br />
-				For example: Search for <strong>20 EUR in HKD</strong> or <strong>20 USD to MXN</strong> and DuckDuckGo or Google will search for it, but also a local conversion is done in a highlighted result.</p>
+				For example: Search for <strong>20 EUR in HKD</strong> or <strong>14 USD to MXN</strong> and DuckDuckGo or Google will search for it, but also a local conversion is done in a highlighted result.</p>
 			<?php } ?>
 			<?php } ?>
 			
 			
 			<?php if($opts->special['wikipedia'] == "on") { ?>
 			<?php if($opts->special['wikipedia'] == "on") { ?>
@@ -89,24 +89,33 @@ if(verify_hash($opts, $auth)) {
 			<?php if($opts->special['phpnet'] == "on") { ?>
 			<?php if($opts->special['phpnet'] == "on") { ?>
 				<h3>PHP.net Search</h3>
 				<h3>PHP.net Search</h3>
 				<p>Prefix your search with <strong>php</strong> to search on php.net for a PHP function.<br />
 				<p>Prefix your search with <strong>php</strong> to search on php.net for a PHP function.<br />
-				For example: Searching for <strong>php in_array</strong> or <strong>php trim</strong> will show you a brief description, compatible PHP versions and a usage example for that function.</p>
+				For example: Searching for <strong>php in_array</strong> or <strong>php trim</strong> will show you a brief description, compatible PHP versions and the basic syntax for that function.</p>
 			<?php } ?>
 			<?php } ?>
 			
 			
 			<?php if($opts->special['definition'] == "on") { ?>
 			<?php if($opts->special['definition'] == "on") { ?>
 				<h3>Word Definition</h3>
 				<h3>Word Definition</h3>
-				<p>You can easily look up the meaning of single words. Prefix the word you want to look up with any of the following keywords; <strong>d</strong>, <strong>define</strong>, <strong>mean</strong>, <strong>meaning</strong>.<br />
-				For example: Searching for <strong>define search</strong> will search for that on Google or DuckDuckGo, but also show the definition highlighted above the search results.</p>
+				<p>You can easily look up the meaning of single words. Prefix the word you want to look up with any of the following keywords; <strong>d</strong>, <strong>define</strong>, <strong>mean</strong> or <strong>meaning</strong>.<br />
+				For example: Searching for <strong>define search</strong> will search for that on Google or DuckDuckGo, but also show the dictionary definition highlighted above the search results.</p>
 				<p><em><strong>Note:</strong> Special Searches do not work for torrent searches.</em></p>
 				<p><em><strong>Note:</strong> Special Searches do not work for torrent searches.</em></p>
 			<?php } ?>
 			<?php } ?>
 			
 			
 			<?php if($opts->enable_torrent_search == "on") { ?>
 			<?php if($opts->enable_torrent_search == "on") { ?>
 				<h2>Torrent Search</h2>
 				<h2>Torrent Search</h2>
-				<p>Search for anything torrent sites have on offer the direct search result should give you the magnet link.<br />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.</p>
+				<p>Search for anything torrent sites have on offer the direct search result should give you the magnet link.<br />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.</p>
 				<p>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.</p>
 				<p>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.</p>
-				<p><em><strong>Disclaimer:</strong> If you like, or found a use for, what you downloaded, you should probably buy a legal copy of it.</em></p>
+				<?php if($opts->special['imdb_id_search'] == "on") { ?>
+					<h3>Searching TV Shows</h3>
+					<p>To do a specific search on The Pirate Bay and EZTV you can search for IMDb Title IDs. These are numeric IDs prefixed with <strong>tt</strong>. This kind of search is useful if you're looking a tv show that doesn't have a unique name.</p>
+					<p>	If you already know the Title ID you can enter it directly in the Torrent search as your search query.<br />
+					If you don't know the Title ID you can search DuckDuckGo or Google for <strong>imdb [tv show name]</strong>, for example <strong>imdb game of thrones</strong>.<br />
+					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.</p>
+					
+					<p>To help you narrow down seasons and episodes you can search for <strong>tt0944947 S08</strong> and get filtered results from EZTV for Game of Thrones Season 8. Searching for <strong>tt0944947 S08E05</strong> should find Season 8 Episode 5 and so on.</p>
+				<?php } ?>
+				<p><em><strong>Note:</strong> If you like, or found a use for, what you downloaded, you should probably buy a legal copy of it.</em></p>
 			<?php } ?>
 			<?php } ?>
 
 
-			<p><small><strong>Acknowledgements:</strong><br />Goosle started as a fork of LibreY, and takes some design cues from DuckDuckGo.com. Goosle is created by <a href="https://ajdg.solutions/" target="_blank">Arnan de Gans</a>.</small></p>
+			<p><small><strong>Acknowledgements:</strong><br />Goosle started as a fork of LibreY, and takes some design cues from DuckDuckGo.com. Goosle is created by <a href="https://ajdg.solutions/" target="_blank">Arnan de Gans</a> with the intent to make search fun again.</small></p>
 		</section>
 		</section>
 	</div>
 	</div>
 </div>
 </div>

+ 1 - 1
index.php

@@ -36,7 +36,7 @@ if(verify_hash($opts, $auth)) {
 	    <form action="results.php" method="get" autocomplete="off">
 	    <form action="results.php" method="get" autocomplete="off">
 	        <h1><span class="G">G</span>oosle</h1>
 	        <h1><span class="G">G</span>oosle</h1>
 	
 	
-	        <input tabindex="10" type="text" class="search" name="q" autofocus required />
+	        <input tabindex="10" type="search" class="search" name="q" autofocus />
 
 
 	        <input type="hidden" name="t" value="0"/>
 	        <input type="hidden" name="t" value="0"/>
 	        <input type="hidden" name="a" value="<?php echo $opts->hash; ?>"/>
 	        <input type="hidden" name="a" value="<?php echo $opts->hash; ?>"/>

+ 38 - 14
readme.md

@@ -1,5 +1,5 @@
 # Goosle
 # 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.
 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.
 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
 ## Features
 - Search on DuckDuckGo
 - Search on DuckDuckGo
 - Search on Google.com
 - Search on Google.com
-- Image search through Qwant
+- Image search through Yahoo! Images
 - Search for magnet links on popular Torrent sites
 - Search for magnet links on popular Torrent sites
 - Special searches for; Currency conversion, Dictionary, Wikipedia and php.net
 - Special searches for; Currency conversion, Dictionary, Wikipedia and php.net
 - Instant password generator on the home page
 - Instant password generator on the home page
@@ -27,22 +27,25 @@ What Goosle does *not* have.
 
 
 And yet it just works...
 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
 ## 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 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 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)
 [![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
 ## 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.
 Tested to work on Apache with PHP8.2.
 
 
 ## Installation
 ## Installation
 1. Unzip the download.
 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:
 ### Notes:
 - The .htaccess file has a redirect to force HTTPS as well as browser caching instructions ready to go.
 - 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!
 Have fun finding things!
 
 
-
 ## Disclaimer
 ## 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.
 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
 ## 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
 ## 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
 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] More useful error response when search doesn't work.
 - [change] EngineRequest::request_successful() now provides a boolean response.
 - [change] EngineRequest::request_successful() now provides a boolean response.
 - [change] Removed versioning indicator from help page.
 - [change] Removed versioning indicator from help page.
 - [change] Added version indicator to results.php and help.php footer.
 - [change] Added version indicator to results.php and help.php footer.
 - [change] 'Nope, Go away!' for unauthorized users changed to 'Goosle'.
 - [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
 1.0.1 - December 5, 2023
 - [fix] mktime() getting intermittent strings in 1337x crawler.
 - [fix] mktime() getting intermittent strings in 1337x crawler.

+ 3 - 2
results.php

@@ -35,8 +35,9 @@ if(verify_hash($opts, $auth)) {
 	<div class="header-wrap">
 	<div class="header-wrap">
 		<form action="results.php" method="get" autocomplete="off">
 		<form action="results.php" method="get" autocomplete="off">
 		    <h1 class="logo"><a class="no-decoration" href="./?a=<?php echo $opts->hash; ?>"><span class="G">G</span>oosle</a></h1>        
 		    <h1 class="logo"><a class="no-decoration" href="./?a=<?php echo $opts->hash; ?>"><span class="G">G</span>oosle</a></h1>        
-		    <input tabindex="1" class="search" type="text" value="<?php echo (strlen($opts->query) > 0) ? htmlspecialchars($opts->query) : "" ; ?>" name="q" required /><input tabindex="2" class="button" type="submit" value="Search" />
-		
+		    <input tabindex="1" class="search" type="search" value="<?php echo (strlen($opts->query) > 0) ? htmlspecialchars($opts->query) : "" ; ?>" name="q" /><input tabindex="2" class="button" type="submit" value="Search" />
+			
+
 	        <input type="hidden" name="t" value="<?php echo $opts->type; ?>"/>
 	        <input type="hidden" name="t" value="<?php echo $opts->type; ?>"/>
 		    <input type="hidden" name="a" value="<?php echo $opts->hash; ?>">
 		    <input type="hidden" name="a" value="<?php echo $opts->hash; ?>">