From 564e066fcac7acce0abd1ff9d5519ae2cecccfe7 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Wed, 30 Apr 2025 11:16:15 +0200 Subject: [PATCH] Use https and enable popporn --- Blacklight/XXX.php | 6 +- Blacklight/processing/adult/ADE.php | 2 +- Blacklight/processing/adult/ADM.php | 2 +- Blacklight/processing/adult/AEBN.php | 2 +- Blacklight/processing/adult/Hotmovies.php | 2 +- Blacklight/processing/adult/Popporn.php | 536 ++++++++++++++++------ 6 files changed, 395 insertions(+), 155 deletions(-) diff --git a/Blacklight/XXX.php b/Blacklight/XXX.php index 6ba6c5a2e..2218e1f1c 100755 --- a/Blacklight/XXX.php +++ b/Blacklight/XXX.php @@ -377,13 +377,13 @@ class XXX $this->colorCli->info('Checking AEBN for movie info'); $res = $mov->processSite($movie); - /*if ($res === false) { + if ($res === false) { $this->whichClass = 'pop'; - $mov = new Popporn(); + $mov = new Popporn; $mov->cookie = $this->cookie; $this->colorCli->info('Checking PopPorn for movie info'); $res = $mov->processSite($movie); - }*/ + } if ($res === false) { $this->whichClass = 'adm'; diff --git a/Blacklight/processing/adult/ADE.php b/Blacklight/processing/adult/ADE.php index 90510ce84..08b18e58e 100755 --- a/Blacklight/processing/adult/ADE.php +++ b/Blacklight/processing/adult/ADE.php @@ -20,7 +20,7 @@ class ADE extends AdultMovies /** * Define ADE Url here. */ - private const ADE = 'http://www.adultdvdempire.com'; + private const ADE = 'https://www.adultdvdempire.com'; /** * Direct Url returned in getAll method. diff --git a/Blacklight/processing/adult/ADM.php b/Blacklight/processing/adult/ADM.php index 985456e6c..803cfdbda 100755 --- a/Blacklight/processing/adult/ADM.php +++ b/Blacklight/processing/adult/ADM.php @@ -11,7 +11,7 @@ class ADM extends AdultMovies * Define Adult DVD Marketplace url * Needed Search Queries Constant. */ - private const ADMURL = 'http://www.adultdvdmarketplace.com'; + private const ADMURL = 'https://www.adultdvdmarketplace.com'; private const TRAILINGSEARCH = '/xcart/adult_dvd/advanced_search.php?sort_by=relev&title='; diff --git a/Blacklight/processing/adult/AEBN.php b/Blacklight/processing/adult/AEBN.php index 50f096e93..87ce3c837 100755 --- a/Blacklight/processing/adult/AEBN.php +++ b/Blacklight/processing/adult/AEBN.php @@ -14,7 +14,7 @@ class AEBN extends AdultMovies /** * Url Constants used within this class. */ - private const AEBNSURL = 'http://straight.theater.aebn.net'; + private const AEBNSURL = 'https://straight.theater.aebn.net'; private const TRAILINGSEARCH = '/dispatcher/fts?theaterId=13992&genreId=101&locale=en&count=30&imageType=Large&targetSearchMode=basic&isAdvancedSearch=false&isFlushAdvancedSearchCriteria=false&sortType=Relevance&userQuery=title%3A+%2B'; diff --git a/Blacklight/processing/adult/Hotmovies.php b/Blacklight/processing/adult/Hotmovies.php index 22b5e536d..3a3eb80d5 100755 --- a/Blacklight/processing/adult/Hotmovies.php +++ b/Blacklight/processing/adult/Hotmovies.php @@ -10,7 +10,7 @@ class Hotmovies extends AdultMovies */ private const EXTRASEARCH = '&complete=on&search_in=video_title'; - private const HMURL = 'http://www.hotmovies.com'; + private const HMURL = 'https://www.hotmovies.com'; private const TRAILINGSEARCH = '/search.php?words='; diff --git a/Blacklight/processing/adult/Popporn.php b/Blacklight/processing/adult/Popporn.php index f10e7dba3..4fac595d3 100755 --- a/Blacklight/processing/adult/Popporn.php +++ b/Blacklight/processing/adult/Popporn.php @@ -6,84 +6,150 @@ class Popporn extends AdultMovies { /** * Define a cookie file location for curl. - * - * @var string string */ public string $cookie = ''; /** - * Set this for what you are searching for. + * Base URL for the site */ - protected string $searchTerm = ''; + private const BASE_URL = 'https://www.popporn.com'; /** - * Override if 18 years+ or older - * Define PopPorn url - * Needed Search Queries Constant. + * Search endpoint */ - private const IF18 = 'https://www.popporn.com/popporn/4'; - - private const POPURL = 'https://www.popporn.com'; - - private const TRAILINGSEARCH = '/search&q='; + private const SEARCH_ENDPOINT = '/search&q='; /** - * Sets the direct url for the return results array. + * Age verification URL + */ + private const AGE_VERIFICATION_URL = 'https://www.popporn.com/popporn/4'; + + /** + * Minimum similarity percentage to consider a match + */ + protected float $minimumSimilarity = 90.0; + + /** + * Direct URL for the movie */ protected string $_directUrl = ''; /** - * Curl Raw Html. + * Raw HTML response */ protected $_response; /** - * Results returned from each method. + * Results array */ protected array $_res = []; /** - * This is set in the getAll method. + * Movie title */ protected string $_title = ''; /** - * Add this to pop url to get results. + * Temporary URL for internal operations */ protected string $_trailUrl = ''; + /** + * POST parameters for API requests + */ private $_postParams; /** * Get Box Cover Images. - * - * @return array - box cover,back cover */ - protected function covers(): array + protected function covers(): array|false { - if ($ret = $this->_html->find('div[id=box-art], a[rel=box-art]', 1)) { - $this->_res['boxcover'] = trim($ret->href); - if (stripos(trim($ret->href), '_aa') !== false) { - $this->_res['backcover'] = str_ireplace('_aa', '_bb', trim($ret->href)); + // Method 1: Try structured data + if (preg_match('/"image":\s*"(.*?)"/is', $this->_response, $match)) { + $this->_res['boxcover'] = trim($match[1]); + // Try to determine backcover from boxcover pattern + if (stripos(trim($match[1]), '_aa') !== false) { + $this->_res['backcover'] = str_ireplace('_aa', '_bb', trim($match[1])); } else { - $this->_res['backcover'] = str_ireplace('.jpg', '_b.jpg', trim($ret->href)); + $this->_res['backcover'] = str_ireplace('.jpg', '_b.jpg', trim($match[1])); } - } else { - if ($ret = $this->_html->findOne('img.front')) { - $this->_res['boxcover'] = $ret->src; - } - if ($ret = $this->_html->findOne('img.back')) { - $this->_res['backcover'] = $ret->src; + + return $this->_res; + } + + // Method 2: Try multiple selectors + $selectors = [ + 'div[id=box-art], a[rel=box-art]', + 'img.front', + 'div.box-cover img', + 'div.product-image img', + ]; + + foreach ($selectors as $selector) { + if ($ret = $this->_html->findOne($selector)) { + $this->_res['boxcover'] = $ret->href ?? $ret->src; + + // Try to determine backcover + if (stripos($this->_res['boxcover'], '_aa') !== false) { + $this->_res['backcover'] = str_ireplace('_aa', '_bb', $this->_res['boxcover']); + } else { + $this->_res['backcover'] = str_ireplace('.jpg', '_b.jpg', $this->_res['boxcover']); + } + + // Also check for explicit back cover + if (! isset($this->_res['backcover']) && $back = $this->_html->findOne('img.back')) { + $this->_res['backcover'] = $back->src; + } + + return $this->_res; } } - return $this->_res; + return false; } + /** + * Gets the movie synopsis/description + */ protected function synopsis(): array { + // Method 1: Try structured data + if (preg_match('/"description":\s*"(.*?)"/is', $this->_response, $match)) { + $this->_res['synopsis'] = trim(html_entity_decode(str_replace('\u', '\\u', $match[1]))); + + return $this->_res; + } + + // Method 2: Try multiple selectors + $selectors = [ + 'div[id=product-info] h3[class=highlight] + *', + 'div.product-description', + 'div.synopsis', + 'meta[name="description"]', + ]; + + foreach ($selectors as $selector) { + if ($ret = $this->_html->findOne($selector)) { + $text = $ret->plaintext ?? $ret->content; + + // Filter out "POPPORN EXCLUSIVE" text + if (stripos(trim($text), 'POPPORN EXCLUSIVE') !== false) { + if ($ret->next_sibling()) { + $text = trim($ret->next_sibling()->plaintext); + } + } + + if (! empty($text)) { + $this->_res['synopsis'] = trim($text); + + return $this->_res; + } + } + } + + // Original method as fallback if ($ret = $this->_html->find('div[id=product-info] ,h3[class=highlight]', 1)) { - if ($ret->next_sibling()->plaintext) { + if ($ret->next_sibling() && $ret->next_sibling()->plaintext) { if (stripos(trim($ret->next_sibling()->plaintext), 'POPPORN EXCLUSIVE') === false) { $this->_res['synopsis'] = trim($ret->next_sibling()->plaintext); } else { @@ -99,8 +165,33 @@ class Popporn extends AdultMovies return $this->_res; } + /** + * Gets movie trailers + */ protected function trailers(): array { + // Method 1: Try structured data + if (preg_match('/"contentUrl":\s*"(.*?)"/is', $this->_response, $match)) { + $this->_res['trailers']['url'] = trim($match[1]); + + return $this->_res; + } + + // Method 2: Modern video embeds + $videoSelectors = [ + 'video source', + 'iframe[src*="trailer"]', + ]; + + foreach ($videoSelectors as $selector) { + if ($ret = $this->_html->findOne($selector)) { + $this->_res['trailers']['url'] = $ret->src; + + return $this->_res; + } + } + + // Method 3: Original flash-based trailer extraction if ($ret = $this->_html->findOne('input#thickbox-trailer-link')) { $ret->value = trim($ret->value); $ret->value = str_replace('..', '', $ret->value); @@ -111,10 +202,16 @@ class Popporn extends AdultMovies $random = ((float) mt_rand() / (float) mt_getrandmax()) * 5400000000000000; $this->_trailUrl = '/com/tlavideo/vod/FlvAjaxSupportService.cfc?random='.$random; $this->_postParams = 'method=pipeStreamLoc&productID='.$productid; - $ret = json_decode(json_decode($this->_response, true), true); - $this->_res['trailers']['baseurl'] = self::POPURL.'/flashmediaserver/trailerPlayer.swf'; - $this->_res['trailers']['flashvars'] = 'subscribe=false&image=&file='.self::POPURL.'/'.$ret['LOC'].'&autostart=false'; - unset($this->_response); + $response = getRawHtml(self::BASE_URL.$this->_trailUrl, $this->cookie, $this->_postParams); + if (! empty($response)) { + $ret = json_decode(json_decode($response, true), true); + if ($ret && isset($ret['LOC'])) { + $this->_res['trailers']['baseurl'] = self::BASE_URL.'/flashmediaserver/trailerPlayer.swf'; + $this->_res['trailers']['flashvars'] = 'subscribe=false&image=&file='.self::BASE_URL.'/'.$ret['LOC'].'&autostart=false'; + // Also provide a modern URL if possible + $this->_res['trailers']['url'] = self::BASE_URL.'/'.$ret['LOC']; + } + } $this->_response = $tmprsp; } } @@ -122,165 +219,308 @@ class Popporn extends AdultMovies return $this->_res; } + /** + * Gets product information + */ protected function productInfo(bool $extras = false): array { - $country = false; - if ($ret = $this->_html->findOne('div#lside')) { - foreach ($ret->find('text') as $e) { - $e = trim($e->innertext); - $e = str_replace([', ', '...', ' '], '', $e); - if (stripos($e, 'Country:') !== false) { - $country = true; - } - if ($country === true) { - if (stripos($e, 'addthis_config') === false) { - if (! empty($e)) { - $this->_res['productinfo'][] = $e; - } - } else { - break; + $productInfo = []; + $director = ''; + + // Method 1: Try structured data + if (preg_match('/"director":\s*{[^}]*"name":\s*"(.*?)"/is', $this->_response, $match)) { + $director = trim($match[1]); + } + + // Method 2: Look for product details in various formats + $selectors = [ + 'div#lside', + 'div.product-details', + 'div.product-info', + ]; + + foreach ($selectors as $selector) { + if ($ret = $this->_html->findOne($selector)) { + // Extract country information + $country = false; + $rawInfo = []; + + foreach ($ret->find('text') as $e) { + $e = trim($e->innertext); + $e = str_replace([', ', '...', ' '], '', $e); + + if (stripos($e, 'Country:') !== false) { + $country = true; } + + if ($country === true) { + if (stripos($e, 'addthis_config') === false) { + if (! empty($e)) { + $rawInfo[] = $e; + } + } else { + break; + } + } + } + + if (! empty($rawInfo)) { + $productInfo = array_chunk($rawInfo, 2, false); + break; } } } - $this->_res['productinfo'] = array_chunk($this->_res['productinfo'], 2, false); + $this->_res['productinfo'] = $productInfo; + $this->_res['director'] = $director; + // Get extras if requested if ($extras === true) { $features = false; - if ($this->_html->findOne('ul.stock-information')) { - foreach ($this->_html->find('ul.stock-information') as $ul) { - foreach ($ul->find('li') as $e) { - $e = trim($e->plaintext); - if ($e === 'Features:') { + $extrasData = []; + + $featureSelectors = [ + 'ul.stock-information', + 'div.features', + 'div.extras', + ]; + + foreach ($featureSelectors as $selector) { + if ($ret = $this->_html->findOne($selector)) { + foreach ($ret->find('li') as $e) { + $text = trim($e->plaintext); + if ($text === 'Features:') { $features = true; - $e = null; + + continue; } - if ($features === true) { - if (! empty($e)) { - $this->_res['extras'][] = $e; - } + + if ($features === true && ! empty($text)) { + $extrasData[] = $text; } } - } - } - } - return $this->_res; - } - - protected function cast(): array - { - $cast = false; - $director = false; - $er = []; - if ($ret = $this->_html->findOne('div#lside')) { - foreach ($ret->find('text') as $e) { - $e = trim($e->innertext); - $e = str_replace([',', ' '], '', $e); - if (stripos($e, 'Cast') !== false) { - $cast = true; - } - $e = str_replace('Cast:', '', $e); - if ($cast === true) { - if (stripos($e, 'Director:') !== false) { - $director = true; - $e = null; - } - - if (($director === true) && ! empty($e)) { - $this->_res['director'] = $e; - $director = false; - $e = null; - } - if (stripos($e, 'Country:') === false) { - if (! empty($e)) { - $er[] = $e; - } - } else { + if (! empty($extrasData)) { + $this->_res['extras'] = $extrasData; break; } } } } - $this->_res['cast'] = $er; return $this->_res; } /** - * Gets categories. + * Gets the cast members + */ + protected function cast(): array + { + $cast = []; + $director = ''; + + // Method 1: Try structured data + if (preg_match_all('/"actor":\s*{[^}]*"name":\s*"(.*?)"/is', $this->_response, $matches)) { + foreach ($matches[1] as $actor) { + $cast[] = trim($actor); + } + } + + if (preg_match('/"director":\s*{[^}]*"name":\s*"(.*?)"/is', $this->_response, $match)) { + $director = trim($match[1]); + } + + // Method 2: Try multiple selectors + if (empty($cast)) { + $castSelectors = [ + 'div.cast a', + 'div.stars a', + 'div.performers a', + ]; + + foreach ($castSelectors as $selector) { + $elements = $this->_html->find($selector); + if (! empty($elements)) { + foreach ($elements as $element) { + $cast[] = trim($element->plaintext); + } + break; + } + } + } + + // Method 3: Original method (fallback) + if (empty($cast)) { + $castFound = false; + $directorFound = false; + $rawCast = []; + + if ($ret = $this->_html->findOne('div#lside')) { + foreach ($ret->find('text') as $e) { + $e = trim($e->innertext); + $e = str_replace([',', ' '], '', $e); + + if (stripos($e, 'Cast') !== false) { + $castFound = true; + + continue; + } + + $e = str_replace('Cast:', '', $e); + + if ($castFound === true) { + if (stripos($e, 'Director:') !== false) { + $directorFound = true; + + continue; + } + + if ($directorFound === true && ! empty($e)) { + $director = $e; + $directorFound = false; + + continue; + } + + if (stripos($e, 'Country:') === false && ! empty($e)) { + $rawCast[] = $e; + } else { + break; + } + } + } + + $cast = $rawCast; + } + } + + $this->_res['cast'] = array_unique(array_filter($cast)); + $this->_res['director'] = $director; + + return $this->_res; + } + + /** + * Gets categories/genres */ protected function genres(): array { $genres = []; - if ($ret = $this->_html->find('div[id=thekeywords], p[class=keywords]', 1)) { - foreach ($ret->find('a') as $e) { - $genres[] = trim($e->plaintext); + + // Method 1: Try structured data + if (preg_match_all('/"genre":\s*"(.*?)"/is', $this->_response, $matches)) { + foreach ($matches[1] as $genre) { + $genres[] = trim($genre); } } - $this->_res['genres'] = $genres; + + // Method 2: Try multiple selectors + if (empty($genres)) { + $selectors = [ + 'div[id=thekeywords] a', + 'p[class=keywords] a', + 'div.categories a', + 'div.tags a', + ]; + + foreach ($selectors as $selector) { + $elements = $this->_html->find($selector); + if (! empty($elements)) { + foreach ($elements as $e) { + $genres[] = trim($e->plaintext); + } + break; + } + } + } + + $this->_res['genres'] = array_unique(array_filter($genres)); return $this->_res; } /** * Searches for match against search term. - * - * @return bool , true if search >= 90% */ public function processSite(string $movie): bool { - if (! empty($movie)) { - $this->_trailUrl = self::TRAILINGSEARCH.$movie; - $this->_response = getRawHtml(self::POPURL.$this->_trailUrl, $this->cookie); - if ($this->_response !== false) { - if ($ret = $this->_html->loadHtml($this->_response)->find('div.product-info, div.title', 1)) { - if (! empty($ret->plaintext)) { - $this->_title = trim($ret->plaintext); - $title = str_replace('XXX', '', $ret->plaintext); - $title = trim(preg_replace('/\(.*?\)|[._-]/i', ' ', $title)); - similar_text(strtolower($movie), strtolower($title), $p); - if ($p >= 90) { - if ($ret = $ret->findOne('a')) { - $this->_trailUrl = trim($ret->href); - unset($this->_response); - $this->_response = getRawHtml(self::POPURL.$this->_trailUrl, $this->cookie); - if ($this->_response !== false) { - $this->_html->loadHtml($this->_response); - if ($ret = $this->_html->findOne('#link-to-this')) { - $this->_directUrl = trim($ret->href); - unset($this->_response); - $this->_response = getRawHtml($this->_directUrl, $this->cookie); - $this->_html->loadHtml($this->_response); + if (empty($movie)) { + return false; + } - return true; - } + $searchUrl = self::BASE_URL.self::SEARCH_ENDPOINT.urlencode($movie); + $this->_response = getRawHtml($searchUrl, $this->cookie); - return false; - } - } + if (empty($this->_response)) { + // Try age verification URL + $this->_response = getRawHtml(self::AGE_VERIFICATION_URL, $this->cookie); + if (! empty($this->_response)) { + $this->_html->loadHtml($this->_response); - return true; - } - } - - return false; - } - } else { - $this->_response = getRawHtml(self::IF18, $this->cookie); - if ($this->_response !== false) { - $this->_html->loadHtml($this->_response); - - return true; - } - - return false; + return false; // Need to verify age first } return false; } + $this->_html->loadHtml($this->_response); + $bestMatch = null; + $highestSimilarity = 0; + + // Try multiple selector patterns for search results + $resultSelectors = [ + 'div.product-info a, div.title a', + 'div.product-title a', + 'h3.product-title a', + ]; + + foreach ($resultSelectors as $selector) { + $results = $this->_html->find($selector); + + if (! empty($results)) { + foreach ($results as $result) { + $title = $result->title ?? $result->plaintext; + $url = $result->href; + + if (! empty($title)) { + // Clean title for better comparison + $cleanTitle = str_replace('XXX', '', $title); + $cleanTitle = preg_replace('/\(.*?\)|[._\-]/i', ' ', $cleanTitle); + $cleanTitle = trim($cleanTitle); + + // Compare titles + similar_text(strtolower($movie), strtolower($cleanTitle), $similarity); + + if ($similarity > $highestSimilarity) { + $highestSimilarity = $similarity; + $bestMatch = [ + 'title' => $title, + 'url' => $url, + ]; + } + } + } + + break; // If we found results with this selector, no need to try others + } + } + + // If we found a match above our threshold + if ($bestMatch && $highestSimilarity >= $this->minimumSimilarity) { + $this->_title = trim($bestMatch['title']); + $this->_directUrl = str_starts_with($bestMatch['url'], 'http') + ? $bestMatch['url'] + : self::BASE_URL.$bestMatch['url']; + + // Fetch the movie details page + $this->_response = getRawHtml($this->_directUrl, $this->cookie); + if (! empty($this->_response)) { + $this->_html->loadHtml($this->_response); + + return true; + } + } + return false; } }