Completed
Push — dev ( b248ae...c3389d )
by Darko
06:23 queued 10s
created

TMDB::getPoster()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 17
ccs 0
cts 10
cp 0
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 12
1
<?php
2
3
namespace Blacklight\processing\tv;
4
5
use Blacklight\ReleaseImage;
6
use Tmdb\Laravel\Facades\Tmdb as TmdbClient;
7
8
class TMDB extends TV
9
{
10
    protected const MATCH_PROBABILITY = 75;
11
12
    /**
13
     * @var string The URL for the image for poster
14
     */
15
    public $posterUrl;
16
17
    /**
18
     * Construct. Instantiate TMDB Class.
19
     *
20
     * @param array $options Class instances.
21
     *
22
     * @throws \Exception
23
     */
24
    public function __construct(array $options = [])
25
    {
26
        parent::__construct($options);
27
    }
28
29
    /**
30
     * Fetch banner from site.
31
     *
32
     * @param $videoId
33
     * @param $siteID
34
     *
35
     * @return bool
36
     */
37
    public function getBanner($videoId, $siteID): bool
38
    {
39
        return false;
40
    }
41
42
    /**
43
     * Main processing director function for TMDB
44
     * Calls work query function and initiates processing.
45
     *
46
     * @param      $groupID
47
     * @param      $guidChar
48
     * @param      $process
49
     * @param bool $local
50
     */
51
    public function processSite($groupID, $guidChar, $process, $local = false): void
52
    {
53
        $res = $this->getTvReleases($groupID, $guidChar, $process, parent::PROCESS_TMDB);
54
55
        $tvcount = \count($res);
0 ignored issues
show
Bug introduced by
It seems like $res can also be of type integer; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

55
        $tvcount = \count(/** @scrutinizer ignore-type */ $res);
Loading history...
56
        $lookupSetting = true;
57
58
        if ($this->echooutput && $tvcount > 0) {
59
            $this->colorCli->header('Processing TMDB lookup for '.number_format($tvcount).' release(s).', true);
60
        }
61
62
        if ($res instanceof \Traversable) {
63
            $this->titleCache = [];
64
65
            foreach ($res as $row) {
66
                $tmdbid = false;
67
68
                // Clean the show name for better match probability
69
                $release = $this->parseInfo($row['searchname']);
70
71
                if (\is_array($release) && $release['name'] !== '') {
72
                    if (\in_array($release['cleanname'], $this->titleCache, false)) {
73
                        if ($this->echooutput) {
74
                            $this->colorCli->headerOver('Title: ').
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->headerOver('Title: ') targeting Blacklight\ColorCLI::headerOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->headerOver('Title: ') of type void can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
                            /** @scrutinizer ignore-type */ $this->colorCli->headerOver('Title: ').
Loading history...
75
                                    $this->colorCli->warningOver($release['cleanname']).
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->warning...($release['cleanname']) targeting Blacklight\ColorCLI::warningOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
76
                                    $this->colorCli->header(' already failed lookup for this site.  Skipping.', true);
0 ignored issues
show
Bug introduced by
Are you sure $this->colorCli->header(...ite. Skipping.', true) of type void can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

76
                                    /** @scrutinizer ignore-type */ $this->colorCli->header(' already failed lookup for this site.  Skipping.', true);
Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->header(...ite. Skipping.', true) targeting Blacklight\ColorCLI::header() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
77
                        }
78
                        $this->setVideoNotFound(parent::PROCESS_TRAKT, $row['id']);
79
                        continue;
80
                    }
81
82
                    // Find the Video ID if it already exists by checking the title against stored TMDB titles
83
                    $videoId = $this->getByTitle($release['cleanname'], parent::TYPE_TV, parent::SOURCE_TMDB);
84
85
                    // Force local lookup only
86
                    if ($local === true) {
87
                        $lookupSetting = false;
88
                    }
89
90
                    // If lookups are allowed lets try to get it.
91
                    if ($videoId === false && $lookupSetting) {
92
                        if ($this->echooutput) {
93
                            $this->colorCli->primaryOver('Checking TMDB for previously failed title: ').
0 ignored issues
show
Bug introduced by
Are you sure $this->colorCli->primary...iously failed title: ') of type void can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
                            /** @scrutinizer ignore-type */ $this->colorCli->primaryOver('Checking TMDB for previously failed title: ').
Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->primary...iously failed title: ') targeting Blacklight\ColorCLI::primaryOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
94
                                    $this->colorCli->headerOver($release['cleanname']).
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->headerO...($release['cleanname']) targeting Blacklight\ColorCLI::headerOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
95
                                    $this->colorCli->primary('.', true);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->primary('.', true) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->primary('.', true) of type void can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

95
                                    /** @scrutinizer ignore-type */ $this->colorCli->primary('.', true);
Loading history...
96
                        }
97
98
                        // Get the show from TMDB
99
                        $tmdbShow = $this->getShowInfo((string) $release['cleanname']);
100
101
                        if (\is_array($tmdbShow)) {
102
                            // Check if we have the TMDB ID already, if we do use that Video ID
103
                            $dupeCheck = $this->getVideoIDFromSiteID('tvdb', $tmdbShow['tvdb']);
104
                            if ($dupeCheck === false) {
105
                                $videoId = $this->add($tmdbShow);
106
                                $tmdbid = $tmdbShow['tmdb'];
107
                            } else {
108
                                $videoId = $dupeCheck;
109
                                // Update any missing fields and add site IDs
110
                                $this->update($videoId, $tmdbShow);
111
                                $tmdbid = $this->getSiteIDFromVideoID('tmdb', $videoId);
112
                            }
113
                        }
114
                    } else {
115
                        if ($this->echooutput) {
116
                            $this->colorCli->primaryOver('Found local TMDB match for: '.$release['cleanname']);
117
                            $this->colorCli->primary('.  Attempting episode lookup!', true);
118
                        }
119
                        $tmdbid = $this->getSiteIDFromVideoID('tmdb', $videoId);
0 ignored issues
show
Bug introduced by
It seems like $videoId can also be of type false; however, parameter $videoID of Blacklight\processing\Vi...:getSiteIDFromVideoID() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
                        $tmdbid = $this->getSiteIDFromVideoID('tmdb', /** @scrutinizer ignore-type */ $videoId);
Loading history...
120
                    }
121
122
                    if (is_numeric($videoId) && $videoId > 0 && is_numeric($tmdbid) && $tmdbid > 0) {
123
                        // Now that we have valid video and tmdb ids, try to get the poster
124
                        $this->getPoster($videoId);
125
126
                        $seasonNo = preg_replace('/^S0*/i', '', $release['season']);
127
                        $episodeNo = preg_replace('/^E0*/i', '', $release['episode']);
128
129
                        if ($episodeNo === 'all') {
130
                            // Set the video ID and leave episode 0
131
                            $this->setVideoIdFound($videoId, $row['id'], 0);
132
                            $this->colorCli->primary('Found TMDB Match for Full Season!', true);
133
                            continue;
134
                        }
135
136
                        // Download all episodes if new show to reduce API usage
137
                        if ($this->countEpsByVideoID($videoId) === false) {
138
                            $this->getEpisodeInfo($tmdbid, -1, -1, '', $videoId);
139
                        }
140
141
                        // Check if we have the episode for this video ID
142
                        $episode = $this->getBySeasonEp($videoId, $seasonNo, $episodeNo, $release['airdate']);
143
144
                        if ($episode === false) {
145
                            // Send the request for the episode to TMDB
146
                            $tmdbEpisode = $this->getEpisodeInfo(
147
                                $tmdbid,
148
                                $seasonNo,
149
                                $episodeNo,
150
                                $release['airdate']
151
                            );
152
153
                            if ($tmdbEpisode) {
154
                                $episode = $this->addEpisode($videoId, $tmdbEpisode);
155
                            }
156
                        }
157
158
                        if ($episode !== false && is_numeric($episode) && $episode > 0) {
159
                            // Mark the releases video and episode IDs
160
                            $this->setVideoIdFound($videoId, $row['id'], $episode);
161
                            if ($this->echooutput) {
162
                                $this->colorCli->primary('Found TMDB Match!', true);
163
                            }
164
                            continue;
165
                        }
166
                        //Processing failed, set the episode ID to the next processing group
167
                        $this->setVideoNotFound(parent::PROCESS_TRAKT, $row['id']);
168
                    } else {
169
                        //Processing failed, set the episode ID to the next processing group
170
                        $this->setVideoNotFound(parent::PROCESS_TRAKT, $row['id']);
171
                        $this->titleCache[] = $release['cleanname'];
172
                    }
173
                } else {
174
                    //Processing failed, set the episode ID to the next processing group
175
                    $this->setVideoNotFound(parent::PROCESS_TRAKT, $row['id']);
176
                    $this->titleCache[] = $release['cleanname'];
177
                }
178
            }
179
        }
180
    }
181
182
    /**
183
     * Calls the API to perform initial show name match to TMDB title
184
     * Returns a formatted array of show data or false if no match.
185
     *
186
     * @param $cleanName
187
     *
188
     * @return array|false
189
     */
190
    protected function getShowInfo($cleanName)
191
    {
192
        $return = $response = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
193
194
        $response = TmdbClient::getSearchApi()->searchTv($cleanName);
195
196
        sleep(1);
197
198
        if (\is_array($response) && ! empty($response['results'])) {
199
            $return = $this->matchShowInfo($response['results'], $cleanName);
200
        }
201
202
        return $return;
203
    }
204
205
    /**
206
     * @param array $shows
207
     * @param string $cleanName
208
     *
209
     * @return array|false
210
     */
211
    private function matchShowInfo($shows, $cleanName)
212
    {
213
        $return = false;
214
        $highestMatch = 0;
215
216
        $show = [];
217
        foreach ($shows as $show) {
218
            if ($this->checkRequiredAttr($show, 'tmdbS')) {
0 ignored issues
show
Bug introduced by
'tmdbS' of type string is incompatible with the type integer expected by parameter $type of Blacklight\processing\tv\TV::checkRequiredAttr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

218
            if ($this->checkRequiredAttr($show, /** @scrutinizer ignore-type */ 'tmdbS')) {
Loading history...
219
                // Check for exact title match first and then terminate if found
220
                if (strtolower($show['name']) === strtolower($cleanName)) {
221
                    $highest = $show;
222
                    break;
223
                }
224
                // Check each show title for similarity and then find the highest similar value
225
                $matchPercent = $this->checkMatch(strtolower($show['name']), strtolower($cleanName), self::MATCH_PROBABILITY);
226
227
                // If new match has a higher percentage, set as new matched title
228
                if ($matchPercent > $highestMatch) {
229
                    $highestMatch = $matchPercent;
230
                    $highest = $show;
231
                }
232
            }
233
        }
234
        if (! empty($highest)) {
235
            $showAlternativeTitles = TmdbClient::getTvApi()->getAlternativeTitles($highest['id']);
236
            $showExternalIds = TmdbClient::getTvApi()->getExternalIds($highest['id']);
237
238
239
            if ($showAlternativeTitles !== null && \is_array($showAlternativeTitles)) {
240
                foreach ($showAlternativeTitles as $aka) {
241
                    $highest['alternative_titles'][] = $aka['title'];
242
                }
243
                $highest['network'] = $show['networks'][0]['name'] ?? '';
244
                $highest['external_ids'] = $showExternalIds;
245
            }
246
            $return = $this->formatShowInfo($highest);
247
        }
248
249
        return $return;
250
    }
251
252
    /**
253
     * Retrieves the poster art for the processed show.
254
     *
255
     * @param int $videoId -- the local Video ID
256
     *
257
     * @return int
258
     */
259
    public function getPoster($videoId): int
260
    {
261
        $ri = new ReleaseImage();
262
263
        $hascover = 0;
264
265
        // Try to get the Poster
266
        if (! empty($this->posterUrl)) {
267
            $hascover = $ri->saveImage($videoId, $this->posterUrl, $this->imgSavePath);
268
269
            // Mark it retrieved if we saved an image
270
            if ($hascover === 1) {
271
                $this->setCoverFound($videoId);
272
            }
273
        }
274
275
        return $hascover;
276
    }
277
278
    /**
279
     * Gets the specific episode info for the parsed release after match
280
     * Returns a formatted array of episode data or false if no match.
281
     *
282
     * @param int $tmdbid
283
     * @param int $season
284
     * @param int $episode
285
     * @param string  $airdate
286
     * @param int $videoId
287
     *
288
     * @return array|false
289
     */
290
    protected function getEpisodeInfo($tmdbid, $season, $episode, $airdate = '', $videoId = 0)
291
    {
292
        $return = false;
293
294
        try {
295
            $response = TmdbClient::getTvEpisodeApi()->getEpisode($tmdbid, $season, $episode);
296
        } catch (TmdbApiException $e) {
0 ignored issues
show
Bug introduced by
The type Blacklight\processing\tv\TmdbApiException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
297
            return false;
298
        }
299
300
        sleep(1);
301
302
        //Handle Single Episode Lookups
303
        if (\is_array($response) && $this->checkRequiredAttr($response, 'tmdbE')) {
0 ignored issues
show
Bug introduced by
'tmdbE' of type string is incompatible with the type integer expected by parameter $type of Blacklight\processing\tv\TV::checkRequiredAttr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

303
        if (\is_array($response) && $this->checkRequiredAttr($response, /** @scrutinizer ignore-type */ 'tmdbE')) {
Loading history...
304
            $return = $this->formatEpisodeInfo($response);
305
        }
306
307
        return $return;
308
    }
309
310
    /**
311
     * Assigns API show response values to a formatted array for insertion
312
     * Returns the formatted array.
313
     *
314
     * @param $show
315
     *
316
     * @return array
317
     */
318
    protected function formatShowInfo($show): array
319
    {
320
        $this->posterUrl = isset($show['poster_path']) ? 'https:'.$this->helper->getUrl($show['poster_path']) : '';
0 ignored issues
show
Bug Best Practice introduced by
The property helper does not exist on Blacklight\processing\tv\TMDB. Did you maybe forget to declare it?
Loading history...
321
322
        if (isset($show['external_ids']['imdb_id'])) {
323
            preg_match('/tt(?P<imdbid>\d{6,7})$/i', $show['external_ids']['imdb_id'], $imdb);
324
        }
325
326
        return [
327
                'type'      => parent::TYPE_TV,
328
                'title'     => (string) $show['name'],
329
                'summary'   => (string) $show['overview'],
330
                'started'   => (string) $show['first_air_date'],
331
                'publisher' => isset($show['network']) ? (string) $show['network'] : '',
332
                'country'   => $show['origin_country'][0] ?? '',
333
                'source'    => parent::SOURCE_TMDB,
334
                'imdb'      => isset($imdb['imdbid']) ? (int) $imdb['imdbid'] : 0,
335
                'tvdb'      => isset($show['external_ids']['tvdb_id']) ? (int) $show['external_ids']['tvdb_id'] : 0,
336
                'trakt'     => 0,
337
                'tvrage'    => isset($show['external_ids']['tvrage_id']) ? (int) $show['external_ids']['tvrage_id'] : 0,
338
                'tvmaze'    => 0,
339
                'tmdb'      => (int) $show['id'],
340
                'aliases'   => ! empty($show['alternative_titles']) ? (array) $show['alternative_titles'] : '',
341
                'localzone' => "''",
342
        ];
343
    }
344
345
    /**
346
     * Assigns API episode response values to a formatted array for insertion
347
     * Returns the formatted array.
348
     *
349
     * @param $episode
350
     *
351
     * @return array
352
     */
353
    protected function formatEpisodeInfo($episode): array
354
    {
355
        return [
356
                'title'       => (string) $episode['name'],
357
                'series'      => (int) $episode['season_number'],
358
                'episode'     => (int) $episode['episode_number'],
359
                'se_complete' => 'S'.sprintf('%02d', $episode['season_number']).'E'.sprintf('%02d', $episode['episode_number']),
360
                'firstaired'  => (string) $episode['air_date'],
361
                'summary'     => (string) $episode['overview'],
362
        ];
363
    }
364
}
365