]> 91.132.146.200 Git - bibliotheca-php.git/commitdiff
Updated PHP-IMDB-Grabber develop
authorBanana <mail@bananas-playground.net>
Sun, 20 Jul 2025 10:45:52 +0000 (12:45 +0200)
committerBanana <mail@bananas-playground.net>
Sun, 20 Jul 2025 10:45:52 +0000 (12:45 +0200)
Signed-off-by: Banana <mail@bananas-playground.net>
CHANGELOG
sources/imdb.class.php.txt
webclient/lib/imdbwebparser.class.php

index 8f05c98f81caaab93bf6f249f87749930d128410..e4b56083c4b48546d64044b329ddf0ce0d3a5011 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,8 @@
     * Fixed: Usage of default sorting in dashboard and collection view.
     * Updated to uikit-3.23.11
     * Upgrade information files are markdown files.
+    * Updated PHP-IMDB-Grabber to https://github.com/FabianBeiner/PHP-IMDB-Grabber/tree/v6.2.6
+        with some heavy modifications.
 
 1.7 - The Ceremonial Chambers 2024-06-01
     * Added i18n. Please read the upgrade file for more information. English and german are available
index 86a8a55850ed400f546e248e44840795438de9c0..f19db269377fa48d90654da63835f3c7fe5ef19e 100644 (file)
@@ -13,7 +13,7 @@
  * @author  Fabian Beiner <fb@fabianbeiner.de>
  * @license https://opensource.org/licenses/MIT The MIT License
  * @link    https://github.com/FabianBeiner/PHP-IMDB-Grabber/ GitHub Repository
- * @version 6.2.0
+ * @version 6.2.6
  */
 class IMDB
 {
@@ -53,60 +53,60 @@ class IMDB
      * These are the regular expressions used to extract the data.
      * If you don’t know what you’re doing, you shouldn’t touch them.
      */
-    const IMDB_AKA           = '~<td[^>]*>\s*Also\s*Known\s*As\s*</td>\s*<td>(.+)</td>~Uis';
-    const IMDB_ASPECT_RATIO  = '~<td[^>]*>Aspect\s*Ratio</td>\s*<td>(.+)</td>~Uis';
-    const IMDB_AWARDS        = '~<div\s*class="titlereference-overview-section">\s*Awards:(.+)</div>~Uis';
-    const IMDB_BUDGET        = '~<td[^>]*>Budget<\/td>\s*<td>\s*(.*)(?:\(estimated\))\s*<\/td>~Ui';
-    const IMDB_CAST          = '~<td[^>]*itemprop="actor"[^>]*>\s*<a\s*href="/name/([^/]*)/\?[^"]*"[^>]*>\s*<span.+>(.+)</span~Ui';
+    const IMDB_AKA           = '~=ttrv_dt_aka.*<span[^>]+>(.*)</span>~Ui';
+    const IMDB_ASPECT_RATIO  = '~aspect ratio.*<span.*>(.*)</span>~Uis';
+    const IMDB_AWARDS        = '~Awards</a>.*span\sclass.*>(.*)</div>~Uis';
+    const IMDB_BUDGET        = '~budget</span>.*<span.*>\s*(.*)(?:\s*\(estimated\))\s*</span>~Ui';
+    const IMDB_CAST          = '~<\/div><a class="ipc-lockup-overlay ipc-focusable" href="\/name\/([^\/]*)\/.*href.*>(.*)<\/a>~Ui';
     const IMDB_CAST_IMAGE    = '~(loadlate="(.*)"[^>]*><\/a>\s+<\/td>\s+)?<td[^>]*itemprop="actor"[^>]*>\s*<a\s*href="\/name\/([^/]*)\/\?[^"]*"[^>]*>\s*<span.+>(.+)<\/span+~Uis';
-    const IMDB_CERTIFICATION = '~<td[^>]*>\s*Certification\s*</td>\s*<td>(.+)</td>~Ui';
-    const IMDB_CHAR          = '~<td class="character">(?:\s+)<div>(.*)(?:\s+)(?: /| \(.*\)|<\/div>)~Ui';
-    const IMDB_COLOR         = '~<a href="\/search\/title\?colors=(?:.*)">(.*)<\/a>~Ui';
-    const IMDB_COMPANIES     = '~production_companies&ref_=(?:.*)">Edit</a>\s+</header>\s+<ul class="simpleList">(.*)Distributors</h4>~Uis';
-    const IMDB_COMPANY       = '~<li>\s+<a href="\/company\/(co[0-9]+)\/">(.*?)</a>~';
-    const IMDB_COUNTRY       = '~<a href="/country/(\w+)">(.*)</a>~Ui';
-    const IMDB_CREATOR       = '~<div[^>]*>\s*(?:Creator|Creators)\s*:\s*<ul[^>]*>(.+)</ul>~Uxsi';
-    const IMDB_DISTRIBUTOR   = '@href="[^"]*update=[t0-9]+:distributors[^"]*">Edit</a>\s*</header>\s*<ul\s*class="simpleList">(.*):special_effects_companies@Uis';
-    const IMDB_DISTRIBUTORS  = '@\/company\/(co[0-9]+)\/">(.*?)<\/a>\s+(?:\(([0-9]+)\))?\s+(?:\((.*?)\))?\s+(?:\((.*?)\))?\s+(?:\((?:.*?)\))?\s+</li>@';
-    const IMDB_DIRECTOR      = '~<div[^>]*>\s*(?:Director|Directors)\s*:\s*<ul[^>]*>(.+)</ul>~Uxsi';
-    const IMDB_GENRE         = '~href="/genre/([a-zA-Z_-]*)/?">([a-zA-Z_ -]*)</a>~Ui';
-    const IMDB_GROSS         = '~pl-zebra-list__label">Cumulative Worldwide Gross<\/td>\s+<td>\s+(.*)\s+<~Uxsi';
-    const IMDB_ID            = '~((?:tt\d{6,})|(?:itle\?\d{6,}))~';
+    const IMDB_CERTIFICATION = '~\?certificates=.*ref_=ttrv_stry">(.+)(?:</span></li></ul></div>|<a\sclass[^>]+\/parentalguide\/[^>]+>)~Ui';
+    const IMDB_CHAR          = '~\/characters\/nm\d+\/.*>(.*)<\/a>~Ui';
+    const IMDB_COLOR         = '~href="/search/title/\?colors(?:.*)">(.*)<\/a>~Ui';
+    const IMDB_COMPANIES     = '~id="production">[\w\s]*?(.*)</section>~Ui';
+    const IMDB_COMPANY       = '~href="/company/(co\d+)/\?ref_=ttrv_cmpy_\d">([^<svg].*?)</a><div~';
+    const IMDB_COUNTRY       = '~country_of_origin=(.*)&amp;ref_=ttrv_dt_cnt">(.*)<\/a~Ui';
+    const IMDB_CREATOR       = '~>\s*(?:Creator|Creators|Producer|Producers).*<ul[^>]*>(.+)</ul>~Uxsi';
+    const IMDB_DISTRIBUTOR   = '@<span\sid="distribution".*<ul\sclass[^>]+>(.*)</section>@Uis';
+    const IMDB_DISTRIBUTORS  = '@<li.*\/company\/(.*)\/[^>]+>(.*)<.*\((.*),\s*([\d-]+?).*\((.*)\).*</li>@Uis';
+    const IMDB_DIRECTOR      = '~id="director".*<ul[^>]*>(.+)</section>~Uxsi';
+    const IMDB_GENRE         = '~genres=([a-zA-Z_-]*)&amp;.*<span class="ipc-chip__text">([a-zA-Z_ -]*)<\/span><\/a~Ui';
+    const IMDB_GROSS         = '~pl-zebra-list__label">Cumulative Worldwide Gross<\/td>\s*<td>\s*(.*?)\s*<\/td>~i';
+    const IMDB_ID            = '~((?:tt\d+)|(?:itle\?\d+))~';
     const IMDB_LANGUAGE      = '~<a href="\/language\/(\w+)">(.*)<\/a>~Ui';
-    const IMDB_LOCATION      = '~href="\/search\/title\?locations=(.*)">(.*)<\/a>~Ui';
-    const IMDB_LOCATIONS     = '~href="\/search\/title\?locations=[^>]*>\s?(.*)\s?<\/a>[^"]*<dd>\s?(.*)\s<\/dd>~Ui';
-    const IMDB_MPAA          = '~<li class="ipl-inline-list__item">(?:\s+)(TV-Y|TV-Y7|TV-G|TV-PG|TV-14|TV-MA|G|PG|PG-13|R|NC-17|NR|UR)(?:\s+)<\/li>~Ui';
-    const IMDB_MUSIC         = '~Music by\s*<\/h4>.*<table class=.*>(.*)</table>~Us';
+    const IMDB_LOCATION      = '~href="/search/title/\?locations=(.*)&amp.*">(.*)<\/a>~Ui';
+    const IMDB_LOCATIONS     = '~href="(?<url>\/search\/title\/\?locations=[^>]*)">\s?(?<location>.*)\s?<\/a><p(.*)>\((?<specification>.*)\)<\/p>~Ui';
+    const IMDB_MPAA          = '~<li class="ipl-inline-list__item">(?:\s+)(TV-Y|TV-Y7|TV-Y7-FV|TV-G|TV-PG|TV-14|TV-MA|TV-MA-L|TV-MA-S|TV-MA-V|G|PG|PG-13|R|NC-17|NR|UR|M|X)(?:\s+)<\/li>~Ui';
+    const IMDB_MUSIC         = '~id="composer">.*<ul\sclass[^>]+>(.*)</section>~Uxsi';
     const IMDB_NAME          = '~href="/name/(.+)/?(?:\?[^"]*)?"[^>]*>(.+)</a>~Ui';
     const IMDB_MOVIE_DESC    = '~<section class="titlereference-section-overview">\s+<div>\s+(.*)\s*?</div>\s+<hr>\s+<div class="titlereference-overview-section">~Ui';
     const IMDB_SERIES_DESC   = '~<div>\s+(?:.*?</a>\s+</span>\s+</div>\s+<hr>\s+<div>\s+)(.*)\s+</div>\s+<hr>\s+<div class="titlereference-overview-section">~Ui';
     const IMDB_SERIESEP_DESC = '~All Episodes(?:.*?)</li>\s+(?:.*?)?</ul>\s+</span>\s+<hr>\s+</div>\s+<div>\s+(.*?)\s+</div>\s+<hr>~';
-    const IMDB_NOT_FOUND_ADV = '~<span>No results.</span>~Ui';
+    const IMDB_NOT_FOUND_ADV = '~"results-section-empty-results-msg"~Ui';
     const IMDB_NOT_FOUND_DES = 'Know what this is about';
     const IMDB_NOT_FOUND_ORG = '~<h1 class="findHeader">No results found for ~Ui';
-    const IMDB_PLOT          = '~<td[^>]*>\s*Plot\s*Summary\s*</td>\s*<td>\s*<p>\s*(.*)\s*</p>~Ui';
-    const IMDB_PLOT_KEYWORDS = '~<td[^>]*>Plot\s*Keywords</td>\s*<td>(.+)(?:<a\s*href="/title/[^>]*>[^<]*</a>\s*</li>\s*</ul>\s*)?</td>~Ui';
-    const IMDB_POSTER        = '~<link\s*rel=\'image_src\'\s*href="(.*)">~Ui';
-    const IMDB_RATING        = '~class="ipl-rating-star__rating">(.*)<~Ui';
-    const IMDB_RATING_COUNT  = '~class="ipl-rating-star__total-votes">\((.*)\)<~Ui';
-    const IMDB_RELEASE_DATE  = '~href="/title/[t0-9]*/releaseinfo">(.*)<~Ui';
-    const IMDB_RUNTIME       = '~<td[^>]*>\s*Runtime\s*</td>\s*<td>(.+)</td>~Ui';
-    const IMDB_SEARCH_ADV    = '~text-primary">1[.]</span>\s*<a.href="\/title\/(tt\d{6,})\/(?:.*?)"(?:\s*)>(?:.*?)<\/a>~Ui';
+    const IMDB_PLOT          = '~data-testid="plot-l".*>(.*)<\/span>~Ui';
+    const IMDB_PLOT_KEYWORDS = '~explore=keywords.*<span class="ipc-chip__text">(.*)<\/span>~Ui';
+    const IMDB_POSTER        = '~<meta property="og:image" content="(.*)"\/>~Ui';
+    const IMDB_RATING        = '~"ratingsSummary":{"aggregateRating":(.*),.*}~Ui';
+    const IMDB_RATING_COUNT  = '~"ratingsSummary":{.*"voteCount":(\d+),.*}~Ui';
+    const IMDB_RELEASE_DATE  = '~\/title\/tt\d+\/releaseinfo\/\?ref_=ttrv_ov_rdat">(.*)</a>~Ui';
+    const IMDB_RUNTIME       = '~id="runtime".*<ul[^>]+>(.*)</ul>~Ui';
+    const IMDB_SEARCH_ADV    = '~<a href="/title/(tt\d+).*?ipc-title-link-wrapper~i';
     const IMDB_SEARCH_ORG    = '~find-title-result">(?:.*?)alt="(.*?)"(?:.*?)href="\/title\/(tt\d{6,})\/(?:.*?)">(.*?)<\/a>~';
-    const IMDB_SEASONS       = '~episodes\?season=(?:\d+)">(\d+)<~Ui';
-    const IMDB_SOUND_MIX     = '~<td[^>]*>\s*Sound\s*Mix\s*</td>\s*<td>(.+)</td>~Ui';
-    const IMDB_TAGLINE       = '~<td[^>]*>\s*Taglines\s*</td>\s*<td>(.+)</td>~Ui';
-    const IMDB_TITLE         = '~itemprop="name">(.*)(<\/h3>|<span)~Ui';
+    const IMDB_SEASONS       = '~episodes/\?season=[^>]+>(\d+)<~Ui';
+    const IMDB_SOUND_MIX     = '~/search/title/\?sound_mixes.*ref_=spec_2">(.*)</a>~Ui';
+    const IMDB_TAGLINE       = '~"taglines":{"edges":\[{"node":{"text":"(.*)","__typename":"Tagline"}~Ui';
+    const IMDB_TITLE         = '~<title>(.*)\s*\(.*\)\s*-\sReference\s*view \s*-\s*IMDb</title>~Ui';
     const IMDB_TITLE_EP      = '~titlereference-watch-ribbon"(?:.*)itemprop="name">(.*?)\s+<span\sclass="titlereference-title-year">~Ui';
-    const IMDB_TITLE_ORIG    = '~</h3>(?:\s+)(.*)(?:\s+)<span class=\"titlereference-original-title-label~Ui';
+    const IMDB_TITLE_ORIG    = '~hero__pageTitle.*hero__primary-text">(.*)</span>~Ui';
     const IMDB_TOP250        = '~href="/chart/top(?:tv)?".class(?:.*?)#([0-9]{1,})</a>~Ui';
     const IMDB_TRAILER       = '~href="/title/(?:tt\d+)/videoplayer/(vi[0-9]*)"~Ui';
-    const IMDB_TYPE          = '~href="/genre/(?:[a-zA-Z_-]*)/?">(?:[a-zA-Z_ -]*)</a>\s+</li>\s+(?:.*item">)\s+(?:<a href="(?:.*)</a>\s+</li>\s+(?:.*item">)\s+)?([a-zA-Z_ -]*)\s+</li>~Ui';
+    const IMDB_TYPE          = '~"titleType":.*"text":"(.*)",~Ui';
     const IMDB_URL           = '~https?://(?:.*\.|.*)imdb.com/(?:t|T)itle(?:\?|/)(..\d+)~i';
-    const IMDB_USER_REVIEW   = '~href="/title/[t0-9]*/reviews"[^>]*>([^<]*)\s*User~Ui';
-    const IMDB_VOTES         = '~"ipl-rating-star__total-votes">\s*\((.*)\)\s*<~Ui';
-    const IMDB_WRITER        = '~<div[^>]*>\s*(?:Writer|Writers)\s*:\s*<ul[^>]*>(.+)</ul>~Ui';
-    const IMDB_YEAR          = '~og:title\' content="(?:.*)\((?:.*)(\d{4})(?:.*)\)~Ui';
+    const IMDB_USER_REVIEW   = '~href="/title/tt\d+/reviews/\?ref_=ttrv_ov_ururv">(.*)</a>~Ui';
+    const IMDB_VOTES         = '~"ratingsSummary":{.*"voteCount":(\d+),.*}~Ui';
+    const IMDB_WRITER        = '~>\s*(?:Writer|Writers).*<ul[^>]*>(.+)</ul>~Uxsi';
+    const IMDB_YEAR          = '~<title>.*\s*\((?:[^()]+ )?(\d{4}(?:–\d{4})?)\)\s*-\sReference\s*view \s*-\s*IMDb</title>~iU';
 
     /**
      * @var string The string returned, if nothing is found.
@@ -334,7 +334,7 @@ class IMDB
         }
 
         $aCurlInfo = IMDBHelper::runCurl($this->sUrl);
-        $sSource   = $aCurlInfo['contents'];
+        $sSource   = isset($aCurlInfo['contents']) ? $aCurlInfo['contents'] : false;
 
         if (false === $sSource) {
             if (true === self::IMDB_DEBUG) {
@@ -549,7 +549,7 @@ class IMDB
 
                 return IMDBHelper::arrayOutput($this->bArrayOutput, $this->sSeparator, self::$sNotFound, $aReturn);
             } else {
-                $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo', $this->iId);
+                $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo/', $this->iId);
                 $aCurlInfo = IMDBHelper::runCurl($fullAkas);
                 $sSource   = $aCurlInfo['contents'] ?? false;
 
@@ -561,7 +561,7 @@ class IMDB
                     return false;
                 }
 
-                $aReturned = IMDBHelper::matchRegex($sSource, "~<td class=\"aka-item__name\">(.*?)<\/td>\s+<td class=\"aka-item__title\">(.*?)<\/td>~");
+                $aReturned = IMDBHelper::matchRegex($sSource, '~<span class="ipc-metadata-list-item__label"[^>]*>([^<]+)</span>.*?<span class="ipc-metadata-list-item__list-content-item"[^>]*>([^<]+)</span>(?:\s*<span class="ipc-metadata-list-item__list-content-item--subText"[^>]*>\(([^)]+)\)</span>)?~s');
 
                 if ($aReturned) {
                     $aReturn = [];
@@ -610,7 +610,7 @@ class IMDB
 
                 return IMDBHelper::arrayOutput($this->bArrayOutput, $this->sSeparator, self::$sNotFound, $aReturn);
             } else {
-                $fullCritics  = sprintf('https://www.imdb.com/title/tt%s/criticreviews', $this->iId);
+                $fullCritics  = sprintf('https://www.imdb.com/title/tt%s/criticreviews/', $this->iId);
                 $aCurlInfo = IMDBHelper::runCurl($fullCritics);
                 $sSource   = $aCurlInfo['contents'] ?? false;
 
@@ -619,19 +619,19 @@ class IMDB
                         echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                     }
 
-                    return false;
+                    return IMDB::$sNotFound;
                 }
 
                 $aReturned = IMDBHelper::matchRegex(
                     $sSource,
-                    '~metascore_wrap(?:.*)\s+(?:.*)\s+(?:.*)ratingValue\">([0-9]+)<\/span>(?:\s+(?:.*)){4}ratingCount\">([0-9]+)~'
+                    '~<div class="sc-8c54a7fa-1 gTQbcw">(\d+)</div>.*?(\d+) reviews~s'
                 );
 
                 if ($aReturned) {
                     $aReturn = [];
                     $aReturn[] = [
-                        'metascore' => IMDBHelper::cleanString($aReturned[1][0]),
-                        'reviews' => IMDBHelper::cleanString($aReturned[2][0]),
+                        'metascore' => isset($aReturned[1][0]) ? IMDBHelper::cleanString($aReturned[1][0]) : '',
+                        'reviews' => isset($aReturned[2][0]) ? IMDBHelper::cleanString($aReturned[2][0]) : '',
                     ];
 
                     file_put_contents($sCacheFile, serialize($aReturn));
@@ -674,7 +674,7 @@ class IMDB
 
                 return IMDBHelper::arrayOutput($this->bArrayOutput, $this->sSeparator, self::$sNotFound, $aReturn);
             } else {
-                $fullCritics  = sprintf('https://www.imdb.com/title/tt%s/criticreviews', $this->iId);
+                $fullCritics  = sprintf('https://www.imdb.com/title/tt%s/criticreviews/', $this->iId);
                 $aCurlInfo = IMDBHelper::runCurl($fullCritics);
                 $sSource   = $aCurlInfo['contents'] ?? false;
 
@@ -683,12 +683,12 @@ class IMDB
                         echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                     }
 
-                    return false;
+                    return IMDB::$sNotFound;
                 }
 
                 $aReturned = IMDBHelper::matchRegex(
                     $sSource,
-                    '~"ratingValue\">([0-9]+)<\/span>\s+<\/div>\s+<\/td>\s+(?:.*)\s+(?:<a\s+href=\"(.*)\"\s+)?(?:.*)\"name\">(.*)<\/span>(?:.*)\"name\">(.*)<\/span><\/span>(?:<\/a>)?\s+(?:.*)\"reviewbody\"> (.*)<\/div>~'
+                    '~<div class="sc-d8486f96-2 (?:fgepEK|kPhAAe|crRWFG)">(\d+)</div>.*?<span class="sc-d8486f96-5 jyAgZO">(.*?)</span>(?:<a.*?href="(.*?)".*?>)?(.*?)</(?:span|a)>.*?<div>(.*?)</div>~'
                 );
 
                 if ($aReturned) {
@@ -915,7 +915,8 @@ class IMDB
                     if (0 !== $iLimit && $i >= $iLimit) {
                         break;
                     }
-                    $sChar = str_replace(' / ', ' and ', $aMatchChar[1][$i]);
+                    $sCharRaw = $aMatchChar[1][$i] ?? 'Unknown';
+                    $sChar = str_replace(' / ', ' and ', $sCharRaw);
                     $aReturn[] = '<a href="https://www.imdb.com/name/' . IMDBHelper::cleanString(
                             $aMatch[1][$i]
                         ) . '/"' . ($sTarget ? ' target="' . $sTarget . '"' : '') . '>' . IMDBHelper::cleanString(
@@ -956,7 +957,9 @@ class IMDB
                     if (0 !== $iLimit && $i >= $iLimit) {
                         break;
                     }
-                    $sChar = str_replace(' / ', ' and ', $aMatchChar[1][$i]);
+                    $sCharRaw = $aMatchChar[1][$i] ?? 'Unknown';
+                    $sChar = str_replace(' / ', ' and ', $sCharRaw);
+
                     $aReturn[] = IMDBHelper::cleanString($sName) . ' as ' . IMDBHelper::cleanString($sChar);
                 }
 
@@ -1441,8 +1444,7 @@ class IMDB
     /**
      * Returns all locations
      *
-     * @return string location
-     * @return string specification
+     * @return string|array locations
      */
     public function getLocations()
     {
@@ -1464,7 +1466,7 @@ class IMDB
 
                 return IMDBHelper::arrayOutput($this->bArrayOutput, $this->sSeparator, self::$sNotFound, $aReturn);
             } else {
-                $fullLocations = sprintf('https://www.imdb.com/title/tt%s/locations', $this->iId);
+                $fullLocations = sprintf('https://www.imdb.com/title/tt%s/locations/', $this->iId);
                 $aCurlInfo     = IMDBHelper::runCurl($fullLocations);
                 $sSource       = $aCurlInfo['contents'] ?? false;
 
@@ -1473,22 +1475,19 @@ class IMDB
                         echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                     }
 
-                    return false;
+                    return IMDB::$sNotFound;
                 }
 
                 $aReturned = IMDBHelper::matchRegex($sSource, self::IMDB_LOCATIONS);
 
                 if ($aReturned) {
                     $aReturn = [];
-                    foreach ($aReturned[1] as $i => $strName) {
+                    foreach ($aReturned['url'] as $i => $strName) {
                         if (strpos($strName, '(') === false) {
                             $aReturn[] = [
-                                'location' => IMDBHelper::cleanString($strName),
-                            ];
-                        }
-                        if (strpos($aReturned[2][$i], '(') !== false) {
-                            $aReturn[] = [
-                                'specification' => IMDBHelper::cleanString($aReturned[2][$i]),
+                                'url' => IMDBHelper::cleanString($aReturned['url'][$i]),
+                                'location' => IMDBHelper::cleanString($aReturned['location'][$i]),
+                                'specification' => IMDBHelper::cleanString($aReturned['specification'][$i]),
                             ];
                         }
                     }
@@ -1565,7 +1564,7 @@ class IMDB
      * @param bool   $bMore     Add … if there are more cast members than printed.
      * @param string $sSize     small or big images
      *
-     * @return array Array with title and url.
+     * @return string Array with title and url.
      */
     public function getPhotos($iLimit = 0, $bMore = true, $sSize = 'small')
     {
@@ -1610,14 +1609,14 @@ class IMDB
                 while ($isPage) {
                     $fullPhotos  = sprintf('https://www.imdb.com/title/tt%s/mediaindex?page=%d', $this->iId, $page);
                     $aCurlInfo = IMDBHelper::runCurl($fullPhotos);
-                    $sSource   = $aCurlInfo['contents'];
+                    $sSource   = isset($aCurlInfo['contents']) ? $aCurlInfo['contents'] : false;
 
                     if (false === $sSource) {
                         if (true === self::IMDB_DEBUG) {
                             echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                         }
 
-                        return false;
+                        return IMDB::$sNotFound;
                     }
 
                     $aReturned = IMDBHelper::matchRegex($sSource, '~title="(.*?)"\s+><img(?:.*)\s+src="(.*?)"\s+\/>~');
@@ -1829,7 +1828,7 @@ class IMDB
 
                 return IMDBHelper::arrayOutput($this->bArrayOutput, $this->sSeparator, self::$sNotFound, $aReturn);
             } else {
-                $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo', $this->iId);
+                $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo/', $this->iId);
                 $aCurlInfo = IMDBHelper::runCurl($fullAkas);
                 $sSource   = $aCurlInfo['contents'] ?? false;
 
@@ -1838,12 +1837,12 @@ class IMDB
                         echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                     }
 
-                    return false;
+                    return IMDB::$sNotFound;
                 }
 
                 $aReturned = IMDBHelper::matchRegex(
                     $sSource,
-                    '~>(.*)\s+<\/a><\/td>\s+<td class=\"release-date-item__date\" align=\"right\">(.*)<\/td>~'
+                    '~<a class="ipc-metadata-list-item__label[^>]*>([^<]+)</a>.*?<span class="ipc-metadata-list-item__list-content-item"[^>]*>([^<]+)</span>~s'
                 );
 
                 if ($aReturned) {
@@ -2064,42 +2063,96 @@ class IMDB
                     $fullEpisodes  = sprintf('https://www.imdb.com/title/tt%s/episodes/?season=%d', $this->iId, $page);
 
                     $aCurlInfo = IMDBHelper::runCurl($fullEpisodes);
-                    $sSource   = $aCurlInfo['contents'];
+                    $sSource   = isset($aCurlInfo['contents']) ? $aCurlInfo['contents'] : false;
 
                     if (false === $sSource) {
                         if (true === self::IMDB_DEBUG) {
                             echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                         }
+                        return IMDB::$sNotFound;
+                    }
 
-                        return false;
+                    $aSeasonsLinks = IMDBHelper::matchRegex($sSource, '~tab-season-entry" href="/title/tt\d+/episodes/\?season=(\d)~s');
+                    $aFoundSeasons = [];
+                    if ($aSeasonsLinks) {
+                        foreach ($aSeasonsLinks[1] as $i => $aSeasonNumber) {
+                            $aFoundSeasons[] = $aSeasonNumber;
+                        }
+
+                    }
+
+                    if (!in_array($page, $aFoundSeasons)) {
+                        break;
                     }
 
                     $aSplit = IMDBHelper::matchRegex($sSource, '~<article class=.+?episode-item-wrapper(.+?)ipc-rating-star--rate">Rate</span>~s');
 
                     if ($aSplit) {
                         foreach ($aSplit[1] as $i => $text) {
-                            $aReturned = IMDBHelper::matchRegex($aSplit[1][$i], '~h4.+/title/(tt\d+)/[?]ref_.+ttep_ep(\d+).+?S\d+\.E\d+ ∙ (.+?)<\/div>.+?<span class=".+?">(.+?)</span>.+?<div class="ipc-html-content-inner-div">(.+?)</div>.+?ratingGroup--imdb-rating.+?</svg>(.+?)<span.+?ipc-rating-star--voteCount">.+?>(.+?)<~s');
-                            if ($aReturned) {
-                                foreach ($aReturned[1] as $n => $episode) {
-                                    $aReturn[] = [
-                                        'season'    => $page,
-                                        'episode' => IMDBHelper::cleanString($aReturned[2][$n]),
-                                        'title'   => IMDBHelper::cleanString($aReturned[3][$n]),
-                                        'rating'  => IMDBHelper::cleanString($aReturned[6][$n]),
-                                        'votes'   => IMDBHelper::cleanString($aReturned[7][$n]),
-                                        'airdate' => IMDBHelper::cleanString($aReturned[4][$n]),
-                                        'plot'    => IMDBHelper::cleanString($aReturned[5][$n]),
-                                        'id'      => IMDBHelper::cleanString($aReturned[1][$n]),
-                                    ];
-                                }
+
+                            # Set default values
+                            $dEpisode = self::$sNotFound;
+                            $dTitle = self::$sNotFound;
+                            $dRating = self::$sNotFound;
+                            $dVotes = self::$sNotFound;
+                            $dAirdate = self::$sNotFound;
+                            $dPlot = self::$sNotFound;
+                            $dId = self::$sNotFound;
+
+                            # Find values
+                            $fId = IMDBHelper::matchRegex($aSplit[1][$i], '~h4.+/title/(tt\d+)~s');
+                            $fEpisode = IMDBHelper::matchRegex($aSplit[1][$i], '~ref_=ttep_ep_(\d+)~s');
+                            $fTitle = IMDBHelper::matchRegex($aSplit[1][$i], '~S\d+\.E\d+ ∙ (.+?)<\/div>~s');
+                            $fAirdate = IMDBHelper::matchRegex($aSplit[1][$i], '~<span class="sc-ccd6e31b-10 fVspdm">(.+?)<\/span>~s');
+                            $fPlot = IMDBHelper::matchRegex($aSplit[1][$i], '~"ipc-html-content-inner-div" role="presentation">(.+?)<\/div>~s');
+                            $fRaiting = IMDBHelper::matchRegex($aSplit[1][$i], '~IMDb rating: (\d\.\d)~s');
+                            $fVotes = IMDBHelper::matchRegex($aSplit[1][$i], '~voteCount.+?-->(.+?)<~s');
+
+                            # Update values if not empty
+                            if (!empty($fId[1][0])) {
+                                $dId = IMDBHelper::cleanString($fId[1][0]);
+                            }
+
+                            if (!empty($fEpisode[1][0])) {
+                                $dEpisode = IMDBHelper::cleanString($fEpisode[1][0]);
+                            }
+
+                            if (!empty($fTitle[1][0])) {
+                                $dTitle = IMDBHelper::cleanString($fTitle[1][0]);
+                            }
+
+                            if (!empty($fRaiting[1][0])) {
+                                $dRating = IMDBHelper::cleanString($fRaiting[1][0]);
                             }
+
+                            if (!empty($fVotes[1][0])) {
+                                $dVotes = IMDBHelper::cleanString($fVotes[1][0]);
+                            }
+
+                            if (!empty($fAirdate[1][0])) {
+                                $dAirdate = IMDBHelper::cleanString($fAirdate[1][0]);
+                            }
+
+                            if (!empty($fPlot[1][0])) {
+                                $dPlot = IMDBHelper::cleanString($fPlot[1][0]);
+                            }
+
+
+                            $aReturn[] = [
+                                'season'  => $page,
+                                'episode' => $dEpisode,
+                                'title'   => $dTitle,
+                                'rating'  => $dRating,
+                                'votes'   => $dVotes,
+                                'airdate' => $dAirdate,
+                                'plot'    => $dPlot,
+                                'id'      => $dId,
+                            ];
+
                         }
                     }
 
                     file_put_contents($sCacheFile, serialize($aReturn));
-                    if (preg_match('~href="\?season=-1~s', $sSource) || !preg_match('~id="load_next_episodes"~', $sSource)) {
-                        break;
-                    }
 
                     $page++;
                 }
@@ -2309,29 +2362,24 @@ class IMDBHelper extends IMDB
      */
     public static function arrayOutput($bArrayOutput, $sSeparator, $sNotFound, $aReturn = null, $bHaveMore = false)
     {
-        if ($bArrayOutput ?? false) {
-            if ($aReturn == null || ! is_array($aReturn)) {
-                return [];
-            }
-
-            if ($bHaveMore) {
-                $aReturn[] = '…';
-            }
+        if ($aReturn === null) {
+            return $bArrayOutput ? [] : $sNotFound;
+        }
 
-            return $aReturn;
-        } else {
-            if ($aReturn == null || ! is_array($aReturn)) {
-                return $sNotFound;
-            }
+        if ($bArrayOutput) {
+            return $bHaveMore ? [...$aReturn, '…'] : $aReturn;
+        }
 
-            foreach ($aReturn as $i => $value) {
-                if (is_array($value)) {
-                    $aReturn[$i] = implode($sSeparator, $value);
-                }
-            }
+        $processValue = fn(mixed $value): string => match (true) {
+            is_array($value) => empty(array_filter($value, fn($v) => $v !== '' && $v !== null))
+                ? $sNotFound
+                : implode($sSeparator, array_map(fn($v) => $v ?: $sNotFound, $value)),
+            $value === '' || $value === null => $sNotFound,
+            default => (string)$value,
+        };
 
-            return implode($sSeparator, $aReturn) . (($bHaveMore) ? '…' : '');
-        }
+        $result = implode($sSeparator, array_map($processValue, $aReturn));
+        return $bHaveMore ? $result . '…' : $result;
     }
 
     /**
@@ -2439,7 +2487,7 @@ class IMDBHelper extends IMDB
                 CURLOPT_BINARYTRANSFER => ($bDownload ? true : false),
                 CURLOPT_CONNECTTIMEOUT => self::IMDB_TIMEOUT,
                 CURLOPT_ENCODING       => '',
-                CURLOPT_FOLLOWLOCATION => 0,
+                CURLOPT_FOLLOWLOCATION => 1,
                 CURLOPT_FRESH_CONNECT  => 0,
                 CURLOPT_HEADER         => ($bDownload ? false : true),
                 CURLOPT_HTTPHEADER     => [
@@ -2463,7 +2511,7 @@ class IMDBHelper extends IMDB
 
         if (200 !== $aCurlInfo['http_code'] && 302 !== $aCurlInfo['http_code']) {
             if (true === self::IMDB_DEBUG) {
-                echo '<pre><b>cURL returned wrong HTTP code “' . $aCurlInfo['http_code'] . '”, aborting.</b></pre>';
+                echo '<pre><b>cURL returned wrong HTTP code “' . $aCurlInfo['http_code'] . '” for “' . $aCurlInfo['url'] . '”, aborting.</b></pre>';
             }
 
             return false;
index bae9e908154cdc81637afcbffd01b6bba570a4e9..fb93a71a7c937f43c643dec44a6c9db2cfa9eb64 100644 (file)
@@ -12,7 +12,7 @@
  * @author  Fabian Beiner <fb@fabianbeiner.de>
  * @license https://opensource.org/licenses/MIT The MIT License
  * @link    https://github.com/FabianBeiner/PHP-IMDB-Grabber/ GitHub Repository
- * @version 6.2.0
+ * @version 6.2.6
  *
  *
  * Functionality is the same but modified heavily to remove the does-not-make-sense static helper
@@ -24,7 +24,7 @@ class IMDB
        /**
         * Set this to true if you run into problems.
         */
-       private bool $IMDB_DEBUG = false;
+       private bool $IMDB_DEBUG = true;
 
        /**
         * @var string Set the preferred language for the User Agent.
@@ -67,60 +67,60 @@ class IMDB
         * These are the regular expressions used to extract the data.
         * If you don’t know what you’re doing, you shouldn’t touch them.
         */
-       const IMDB_AKA           = '~<td[^>]*>\s*Also\s*Known\s*As\s*</td>\s*<td>(.+)</td>~Uis';
-       const IMDB_ASPECT_RATIO  = '~<td[^>]*>Aspect\s*Ratio</td>\s*<td>(.+)</td>~Uis';
-       const IMDB_AWARDS        = '~<div\s*class="titlereference-overview-section">\s*Awards:(.+)</div>~Uis';
-    const IMDB_BUDGET        = '~<td[^>]*>Budget<\/td>\s*<td>\s*(.*)(?:\(estimated\))\s*<\/td>~Ui';
-       const IMDB_CAST          = '~<td[^>]*itemprop="actor"[^>]*>\s*<a\s*href="/name/([^/]*)/\?[^"]*"[^>]*>\s*<span.+>(.+)</span~Ui';
+    const IMDB_AKA           = '~=ttrv_dt_aka.*<span[^>]+>(.*)</span>~Ui';
+    const IMDB_ASPECT_RATIO  = '~aspect ratio.*<span.*>(.*)</span>~Uis';
+    const IMDB_AWARDS        = '~Awards</a>.*span\sclass.*>(.*)</div>~Uis';
+    const IMDB_BUDGET        = '~budget</span>.*<span.*>\s*(.*)(?:\s*\(estimated\))\s*</span>~Ui';
+    const IMDB_CAST          = '~<\/div><a class="ipc-lockup-overlay ipc-focusable" href="\/name\/([^\/]*)\/.*href.*>(.*)<\/a>~Ui';
     const IMDB_CAST_IMAGE    = '~(loadlate="(.*)"[^>]*><\/a>\s+<\/td>\s+)?<td[^>]*itemprop="actor"[^>]*>\s*<a\s*href="\/name\/([^/]*)\/\?[^"]*"[^>]*>\s*<span.+>(.+)<\/span+~Uis';
-       const IMDB_CERTIFICATION = '~<td[^>]*>\s*Certification\s*</td>\s*<td>(.+)</td>~Ui';
-    const IMDB_CHAR          = '~<td class="character">(?:\s+)<div>(.*)(?:\s+)(?: /| \(.*\)|<\/div>)~Ui';
-    const IMDB_COLOR         = '~<a href="\/search\/title\?colors=(?:.*)">(.*)<\/a>~Ui';
-    const IMDB_COMPANIES     = '~production_companies&ref_=(?:.*)">Edit</a>\s+</header>\s+<ul class="simpleList">(.*)Distributors</h4>~Uis';
-    const IMDB_COMPANY       = '~<li>\s+<a href="\/company\/(co[0-9]+)\/">(.*?)</a>~';
-       const IMDB_COUNTRY       = '~<a href="/country/(\w+)">(.*)</a>~Ui';
-       const IMDB_CREATOR       = '~<div[^>]*>\s*(?:Creator|Creators)\s*:\s*<ul[^>]*>(.+)</ul>~Uxsi';
-    const IMDB_DISTRIBUTOR   = '@href="[^"]*update=[t0-9]+:distributors[^"]*">Edit</a>\s*</header>\s*<ul\s*class="simpleList">(.*):special_effects_companies@Uis';
-    const IMDB_DISTRIBUTORS  = '@\/company\/(co[0-9]+)\/">(.*?)<\/a>\s+(?:\(([0-9]+)\))?\s+(?:\((.*?)\))?\s+(?:\((.*?)\))?\s+(?:\((?:.*?)\))?\s+</li>@';
-       const IMDB_DIRECTOR      = '~<div[^>]*>\s*(?:Director|Directors)\s*:\s*<ul[^>]*>(.+)</ul>~Uxsi';
-       const IMDB_GENRE         = '~href="/genre/([a-zA-Z_-]*)/?">([a-zA-Z_ -]*)</a>~Ui';
-    const IMDB_GROSS         = '~pl-zebra-list__label">Cumulative Worldwide Gross<\/td>\s+<td>\s+(.*)\s+<~Uxsi';
-       const IMDB_ID            = '~((?:tt\d{6,})|(?:itle\?\d{6,}))~';
+    const IMDB_CERTIFICATION = '~\?certificates=.*ref_=ttrv_stry">(.+)(?:</span></li></ul></div>|<a\sclass[^>]+\/parentalguide\/[^>]+>)~Ui';
+    const IMDB_CHAR          = '~\/characters\/nm\d+\/.*>(.*)<\/a>~Ui';
+    const IMDB_COLOR         = '~href="/search/title/\?colors(?:.*)">(.*)<\/a>~Ui';
+    const IMDB_COMPANIES     = '~id="production">[\w\s]*?(.*)</section>~Ui';
+    const IMDB_COMPANY       = '~href="/company/(co\d+)/\?ref_=ttrv_cmpy_\d">([^<svg].*?)</a><div~';
+    const IMDB_COUNTRY       = '~country_of_origin=(.*)&amp;ref_=ttrv_dt_cnt">(.*)<\/a~Ui';
+    const IMDB_CREATOR       = '~>\s*(?:Creator|Creators|Producer|Producers).*<ul[^>]*>(.+)</ul>~Uxsi';
+    const IMDB_DISTRIBUTOR   = '@<span\sid="distribution".*<ul\sclass[^>]+>(.*)</section>@Uis';
+    const IMDB_DISTRIBUTORS  = '@<li.*\/company\/(.*)\/[^>]+>(.*)<.*\((.*),\s*([\d-]+?).*\((.*)\).*</li>@Uis';
+    const IMDB_DIRECTOR      = '~id="director".*<ul[^>]*>(.+)</section>~Uxsi';
+    const IMDB_GENRE         = '~genres=([a-zA-Z_-]*)&amp;.*<span class="ipc-chip__text">([a-zA-Z_ -]*)<\/span><\/a~Ui';
+    const IMDB_GROSS         = '~pl-zebra-list__label">Cumulative Worldwide Gross<\/td>\s*<td>\s*(.*?)\s*<\/td>~i';
+    const IMDB_ID            = '~((?:tt\d+)|(?:itle\?\d+))~';
     const IMDB_LANGUAGE      = '~<a href="\/language\/(\w+)">(.*)<\/a>~Ui';
-    const IMDB_LOCATION      = '~href="\/search\/title\?locations=(.*)">(.*)<\/a>~Ui';
-    const IMDB_LOCATIONS     = '~href="\/search\/title\?locations=[^>]*>\s?(.*)\s?<\/a>[^"]*<dd>\s?(.*)\s<\/dd>~Ui';
-    const IMDB_MPAA          = '~<li class="ipl-inline-list__item">(?:\s+)(TV-Y|TV-Y7|TV-G|TV-PG|TV-14|TV-MA|G|PG|PG-13|R|NC-17|NR|UR)(?:\s+)<\/li>~Ui';
-    const IMDB_MUSIC         = '~Music by\s*<\/h4>.*<table class=.*>(.*)</table>~Us';
-       const IMDB_NAME          = '~href="/name/(.+)/?(?:\?[^"]*)?"[^>]*>(.+)</a>~Ui';
+    const IMDB_LOCATION      = '~href="/search/title/\?locations=(.*)&amp.*">(.*)<\/a>~Ui';
+    const IMDB_LOCATIONS     = '~href="(?<url>\/search\/title\/\?locations=[^>]*)">\s?(?<location>.*)\s?<\/a><p(.*)>\((?<specification>.*)\)<\/p>~Ui';
+    const IMDB_MPAA          = '~<li class="ipl-inline-list__item">(?:\s+)(TV-Y|TV-Y7|TV-Y7-FV|TV-G|TV-PG|TV-14|TV-MA|TV-MA-L|TV-MA-S|TV-MA-V|G|PG|PG-13|R|NC-17|NR|UR|M|X)(?:\s+)<\/li>~Ui';
+    const IMDB_MUSIC         = '~id="composer">.*<ul\sclass[^>]+>(.*)</section>~Uxsi';
+    const IMDB_NAME          = '~href="/name/(.+)/?(?:\?[^"]*)?"[^>]*>(.+)</a>~Ui';
     const IMDB_MOVIE_DESC    = '~<section class="titlereference-section-overview">\s+<div>\s+(.*)\s*?</div>\s+<hr>\s+<div class="titlereference-overview-section">~Ui';
     const IMDB_SERIES_DESC   = '~<div>\s+(?:.*?</a>\s+</span>\s+</div>\s+<hr>\s+<div>\s+)(.*)\s+</div>\s+<hr>\s+<div class="titlereference-overview-section">~Ui';
     const IMDB_SERIESEP_DESC = '~All Episodes(?:.*?)</li>\s+(?:.*?)?</ul>\s+</span>\s+<hr>\s+</div>\s+<div>\s+(.*?)\s+</div>\s+<hr>~';
-    const IMDB_NOT_FOUND_ADV = '~<span>No results.</span>~Ui';
+    const IMDB_NOT_FOUND_ADV = '~"results-section-empty-results-msg"~Ui';
     const IMDB_NOT_FOUND_DES = 'Know what this is about';
     const IMDB_NOT_FOUND_ORG = '~<h1 class="findHeader">No results found for ~Ui';
-    const IMDB_PLOT          = '~<td[^>]*>\s*Plot\s*Summary\s*</td>\s*<td>\s*<p>\s*(.*)\s*</p>~Ui';
-       const IMDB_PLOT_KEYWORDS = '~<td[^>]*>Plot\s*Keywords</td>\s*<td>(.+)(?:<a\s*href="/title/[^>]*>[^<]*</a>\s*</li>\s*</ul>\s*)?</td>~Ui';
-       const IMDB_POSTER        = '~<link\s*rel=\'image_src\'\s*href="(.*)">~Ui';
-       const IMDB_RATING        = '~class="ipl-rating-star__rating">(.*)<~Ui';
-       const IMDB_RATING_COUNT  = '~class="ipl-rating-star__total-votes">\((.*)\)<~Ui';
-       const IMDB_RELEASE_DATE  = '~href="/title/[t0-9]*/releaseinfo">(.*)<~Ui';
-       const IMDB_RUNTIME       = '~<td[^>]*>\s*Runtime\s*</td>\s*<td>(.+)</td>~Ui';
-    const IMDB_SEARCH_ADV    = '~text-primary">1[.]</span>\s*<a.href="\/title\/(tt\d{6,})\/(?:.*?)"(?:\s*)>(?:.*?)<\/a>~Ui';
+    const IMDB_PLOT          = '~data-testid="plot-l".*>(.*)<\/span>~Ui';
+    const IMDB_PLOT_KEYWORDS = '~explore=keywords.*<span class="ipc-chip__text">(.*)<\/span>~Ui';
+    const IMDB_POSTER        = '~<meta property="og:image" content="(.*)"\/>~Ui';
+    const IMDB_RATING        = '~"ratingsSummary":{"aggregateRating":(.*),.*}~Ui';
+    const IMDB_RATING_COUNT  = '~"ratingsSummary":{.*"voteCount":(\d+),.*}~Ui';
+    const IMDB_RELEASE_DATE  = '~\/title\/tt\d+\/releaseinfo\/\?ref_=ttrv_ov_rdat">(.*)</a>~Ui';
+    const IMDB_RUNTIME       = '~id="runtime".*<ul[^>]+>(.*)</ul>~Ui';
+    const IMDB_SEARCH_ADV    = '~<a href="/title/(tt\d+).*?ipc-title-link-wrapper~i';
     const IMDB_SEARCH_ORG    = '~find-title-result">(?:.*?)alt="(.*?)"(?:.*?)href="\/title\/(tt\d{6,})\/(?:.*?)">(.*?)<\/a>~';
-       const IMDB_SEASONS       = '~episodes\?season=(?:\d+)">(\d+)<~Ui';
-       const IMDB_SOUND_MIX     = '~<td[^>]*>\s*Sound\s*Mix\s*</td>\s*<td>(.+)</td>~Ui';
-       const IMDB_TAGLINE       = '~<td[^>]*>\s*Taglines\s*</td>\s*<td>(.+)</td>~Ui';
-       const IMDB_TITLE         = '~itemprop="name">(.*)(<\/h3>|<span)~Ui';
+    const IMDB_SEASONS       = '~episodes/\?season=[^>]+>(\d+)<~Ui';
+    const IMDB_SOUND_MIX     = '~/search/title/\?sound_mixes.*ref_=spec_2">(.*)</a>~Ui';
+    const IMDB_TAGLINE       = '~"taglines":{"edges":\[{"node":{"text":"(.*)","__typename":"Tagline"}~Ui';
+    const IMDB_TITLE         = '~<title>(.*)\s*\(.*\)\s*-\sReference\s*view \s*-\s*IMDb</title>~Ui';
     const IMDB_TITLE_EP      = '~titlereference-watch-ribbon"(?:.*)itemprop="name">(.*?)\s+<span\sclass="titlereference-title-year">~Ui';
-       const IMDB_TITLE_ORIG    = '~</h3>(?:\s+)(.*)(?:\s+)<span class=\"titlereference-original-title-label~Ui';
+    const IMDB_TITLE_ORIG    = '~hero__pageTitle.*hero__primary-text">(.*)</span>~Ui';
     const IMDB_TOP250        = '~href="/chart/top(?:tv)?".class(?:.*?)#([0-9]{1,})</a>~Ui';
     const IMDB_TRAILER       = '~href="/title/(?:tt\d+)/videoplayer/(vi[0-9]*)"~Ui';
-    const IMDB_TYPE          = '~href="/genre/(?:[a-zA-Z_-]*)/?">(?:[a-zA-Z_ -]*)</a>\s+</li>\s+(?:.*item">)\s+(?:<a href="(?:.*)</a>\s+</li>\s+(?:.*item">)\s+)?([a-zA-Z_ -]*)\s+</li>~Ui';
+    const IMDB_TYPE          = '~"titleType":.*"text":"(.*)",~Ui';
     const IMDB_URL           = '~https?://(?:.*\.|.*)imdb.com/(?:t|T)itle(?:\?|/)(..\d+)~i';
-       const IMDB_USER_REVIEW   = '~href="/title/[t0-9]*/reviews"[^>]*>([^<]*)\s*User~Ui';
-       const IMDB_VOTES         = '~"ipl-rating-star__total-votes">\s*\((.*)\)\s*<~Ui';
-       const IMDB_WRITER        = '~<div[^>]*>\s*(?:Writer|Writers)\s*:\s*<ul[^>]*>(.+)</ul>~Ui';
-       const IMDB_YEAR          = '~og:title\' content="(?:.*)\((?:.*)(\d{4})(?:.*)\)~Ui';
+    const IMDB_USER_REVIEW   = '~href="/title/tt\d+/reviews/\?ref_=ttrv_ov_ururv">(.*)</a>~Ui';
+    const IMDB_VOTES         = '~"ratingsSummary":{.*"voteCount":(\d+),.*}~Ui';
+    const IMDB_WRITER        = '~>\s*(?:Writer|Writers).*<ul[^>]*>(.+)</ul>~Uxsi';
+    const IMDB_YEAR          = '~<title>.*\s*\((?:[^()]+ )?(\d{4}(?:–\d{4})?)\)\s*-\sReference\s*view \s*-\s*IMDb</title>~iU';
 
        /**
         * @var string The string returned, if nothing is found.
@@ -328,7 +328,7 @@ class IMDB
                }
 
                $aCurlInfo = $this->runCurl($this->sUrl);
-               $sSource = is_bool($aCurlInfo) ?  $aCurlInfo : $aCurlInfo['contents'] ;
+               $sSource = isset($aCurlInfo['contents']) ? $aCurlInfo['contents'] : false;
 
                if (false === $sSource) {
                        if ($this->IMDB_DEBUG) {
@@ -440,7 +440,7 @@ class IMDB
 
                                return $this->arrayOutput($this->bArrayOutput, $this->sSeparator, $this->sNotFound, $aReturn);
                        } else {
-                               $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo', $this->iId);
+                               $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo/', $this->iId);
                                $aCurlInfo = $this->runCurl($fullAkas);
                                $sSource   = $aCurlInfo['contents'];
 
@@ -1121,16 +1121,13 @@ class IMDB
 
                                if ($aReturned) {
                                        $aReturn = [];
-                                       foreach ($aReturned[1] as $i => $strName) {
+                    foreach ($aReturned['url'] as $i => $strName) {
                                                if (strpos($strName, '(') === false) {
-                                                       $aReturn[] = [
-                                                               'location' => $this->cleanString($strName),
-                                                       ];
-                                               }
-                                               if (strpos($aReturned[2][$i], '(') !== false) {
-                                                       $aReturn[] = [
-                                                               'specification' => $this->cleanString($aReturned[2][$i]),
-                                                       ];
+                            $aReturn[] = [
+                                'url' => IMDBHelper::cleanString($aReturned['url'][$i]),
+                                'location' => IMDBHelper::cleanString($aReturned['location'][$i]),
+                                'specification' => IMDBHelper::cleanString($aReturned['specification'][$i]),
+                            ];
                                                }
                                        }
 
@@ -1323,7 +1320,7 @@ class IMDB
 
                                return $this->arrayOutput($this->bArrayOutput, $this->sSeparator, $this->sNotFound, $aReturn);
                        } else {
-                               $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo', $this->iId);
+                               $fullAkas  = sprintf('https://www.imdb.com/title/tt%s/releaseinfo/', $this->iId);
                                $aCurlInfo = $this->runCurl($fullAkas);
                                $sSource   = $aCurlInfo['contents'];
 
@@ -1332,12 +1329,12 @@ class IMDB
                                                echo '<pre><b>cURL error:</b> ' . var_dump($aCurlInfo) . '</pre>';
                                        }
 
-                                       return false;
+                    return $this->sNotFound;
                                }
 
                                $aReturned = $this->matchRegex(
                                        $sSource,
-                                       '~>(.*)<\/a><\/td>\s+<td class="release_date">(.*)<\/td>~'
+                    '~<a class="ipc-metadata-list-item__label[^>]*>([^<]+)</a>.*?<span class="ipc-metadata-list-item__list-content-item"[^>]*>([^<]+)</span>~s'
                                );
 
                                if ($aReturned) {
@@ -1642,29 +1639,24 @@ class IMDB
         */
        private function arrayOutput($bArrayOutput, $sSeparator, $sNotFound, $aReturn = '', $bHaveMore = false)
        {
-               if ($bArrayOutput) {
-                       if (empty($aReturn) || ! is_array($aReturn)) {
-                               return [];
-                       }
-
-                       if ($bHaveMore) {
-                               $aReturn[] = '…';
-                       }
-
-                       return $aReturn;
-               } else {
-                       if (empty($aReturn) || ! is_array($aReturn)) {
-                               return $sNotFound;
-                       }
-
-                       foreach ($aReturn as $i => $value) {
-                               if (is_array($value)) {
-                                       $aReturn[$i] = implode($sSeparator, $value);
-                               }
-                       }
-
-                       return implode($sSeparator, $aReturn) . (($bHaveMore) ? '…' : '');
-               }
+        if ($aReturn === null) {
+            return $bArrayOutput ? [] : $sNotFound;
+        }
+
+        if ($bArrayOutput) {
+            return $bHaveMore ? [...$aReturn, '…'] : $aReturn;
+        }
+
+        $processValue = fn(mixed $value): string => match (true) {
+            is_array($value) => empty(array_filter($value, fn($v) => $v !== '' && $v !== null))
+                ? $sNotFound
+                : implode($sSeparator, array_map(fn($v) => $v ?: $sNotFound, $value)),
+            $value === '' || $value === null => $sNotFound,
+            default => (string)$value,
+        };
+
+        $result = implode($sSeparator, array_map($processValue, $aReturn));
+        return $bHaveMore ? $result . '…' : $result;
        }
 
        /**
@@ -1771,7 +1763,7 @@ class IMDB
                        [
                                CURLOPT_CONNECTTIMEOUT => $this->IMDB_TIMEOUT,
                                CURLOPT_ENCODING       => '',
-                               CURLOPT_FOLLOWLOCATION => true,
+                               CURLOPT_FOLLOWLOCATION => 1,
                                CURLOPT_FRESH_CONNECT  => 0,
                                CURLOPT_HEADER         => ($bDownload ? false : true),
                                CURLOPT_HTTPHEADER     => [
@@ -1794,7 +1786,7 @@ class IMDB
 
                if (200 !== $aCurlInfo['http_code']) {
                        if ($this->IMDB_DEBUG) {
-                               echo '<pre><b>cURL returned wrong HTTP code “' . $aCurlInfo['http_code'] . '”, aborting.</b></pre>';
+                echo '<pre><b>cURL returned wrong HTTP code “' . $aCurlInfo['http_code'] . '” for “' . $aCurlInfo['url'] . '”, aborting.</b></pre>';
                        }
 
                        return false;