162 lines
5.5 KiB
PHP
162 lines
5.5 KiB
PHP
<?php
|
||
|
||
namespace App\Modules\Movies\Services\Omdb;
|
||
|
||
use App\Modules\Movies\Services\Contracts\MovieProvider;
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Illuminate\Support\Collection;
|
||
|
||
class OmdbMovieProvider implements MovieProvider
|
||
{
|
||
protected string $baseUrl;
|
||
protected ?string $apiKey;
|
||
protected int $cacheTtl;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->baseUrl = rtrim(config('movies.omdb.base_url', 'https://www.omdbapi.com/'), '/');
|
||
$this->apiKey = config('movies.omdb.api_key');
|
||
$this->cacheTtl = (int) config('movies.omdb.cache_ttl', 3600);
|
||
}
|
||
|
||
public function search(string $query, int $page = 1): array
|
||
{
|
||
$query = trim($query);
|
||
if ($query === '') {
|
||
return ['results' => collect(), 'total' => 0, 'page' => 1, 'has_more' => false];
|
||
}
|
||
|
||
$cacheKey = sprintf('omdb.search.%s.p%s', md5($query), $page);
|
||
$data = Cache::remember($cacheKey, $this->cacheTtl, function () use ($query, $page) {
|
||
$response = Http::acceptJson()
|
||
->timeout(6)
|
||
->retry(3, 300)
|
||
->get($this->baseUrl, [
|
||
'apikey' => $this->apiKey,
|
||
's' => $query,
|
||
'type' => 'movie',
|
||
'page' => $page,
|
||
]);
|
||
|
||
if ($response->failed()) {
|
||
abort(502, 'Couldn’t reach OMDb. Please try again.');
|
||
}
|
||
|
||
return $response->json();
|
||
});
|
||
|
||
$results = collect();
|
||
$total = 0;
|
||
$hasMore = false;
|
||
if (isset($data['Response']) && $data['Response'] === 'True' && isset($data['Search'])) {
|
||
$total = (int) ($data['totalResults'] ?? 0);
|
||
$hasMore = $page * 10 < $total; // OMDb pages of 10
|
||
$results = collect($data['Search'])->map(function ($item) {
|
||
return [
|
||
'provider' => 'omdb',
|
||
'provider_id' => $item['imdbID'] ?? null,
|
||
'title' => $item['Title'] ?? null,
|
||
'year' => $item['Year'] ?? null,
|
||
'poster_url' => ($item['Poster'] ?? 'N/A') !== 'N/A' ? $item['Poster'] : null,
|
||
];
|
||
});
|
||
}
|
||
|
||
return [
|
||
'results' => $results,
|
||
'total' => $total,
|
||
'page' => $page,
|
||
'has_more' => $hasMore,
|
||
];
|
||
}
|
||
|
||
public function details(string $id): array
|
||
{
|
||
$id = trim($id);
|
||
$cacheKey = sprintf('omdb.details.%s', $id);
|
||
$data = Cache::remember($cacheKey, $this->cacheTtl, function () use ($id) {
|
||
$response = Http::acceptJson()
|
||
->timeout(6)
|
||
->retry(3, 300)
|
||
->get($this->baseUrl, [
|
||
'apikey' => $this->apiKey,
|
||
'i' => $id,
|
||
'plot' => 'full',
|
||
'type' => 'movie',
|
||
]);
|
||
|
||
if ($response->failed()) {
|
||
abort(502, 'Couldn’t reach OMDb. Please try again.');
|
||
}
|
||
|
||
return $response->json();
|
||
});
|
||
|
||
if (!isset($data['Response']) || $data['Response'] !== 'True') {
|
||
// Map explicit OMDb errors that indicate provider-side issues differently if needed
|
||
// Default to not found for details when OMDb says False
|
||
abort(404, 'Movie not found in OMDb');
|
||
}
|
||
|
||
// Normalize to internal structure
|
||
$genres = $this->splitList($data['Genre'] ?? '');
|
||
$actors = $this->splitList($data['Actors'] ?? '');
|
||
$directors = $this->splitList($data['Director'] ?? '');
|
||
$studios = $this->splitList($data['Production'] ?? '');
|
||
$countries = $this->splitList($data['Country'] ?? '');
|
||
$languages = $this->splitList($data['Language'] ?? '');
|
||
|
||
return [
|
||
'provider' => 'omdb',
|
||
'provider_id' => $data['imdbID'] ?? null,
|
||
'external_ids' => [
|
||
'imdb' => $data['imdbID'] ?? null,
|
||
'omdb' => $data['imdbID'] ?? null,
|
||
],
|
||
'title' => $data['Title'] ?? null,
|
||
'original_title' => $data['Title'] ?? null,
|
||
'description' => $data['Plot'] ?? null,
|
||
'poster_url' => ($data['Poster'] ?? 'N/A') !== 'N/A' ? $data['Poster'] : null,
|
||
'backdrop_url' => null,
|
||
'genres' => $genres,
|
||
'rating' => $data['Rated'] ?? null,
|
||
'release_date' => $this->parseDate($data['Released'] ?? null),
|
||
'year' => isset($data['Year']) ? (int) substr($data['Year'], 0, 4) : null,
|
||
'runtime' => $this->parseRuntime($data['Runtime'] ?? null),
|
||
'actors' => $actors,
|
||
'directors' => $directors,
|
||
'studios' => $studios,
|
||
'countries' => $countries,
|
||
'languages' => $languages,
|
||
];
|
||
}
|
||
|
||
protected function splitList(?string $value): array
|
||
{
|
||
if (!$value || $value === 'N/A') return [];
|
||
return collect(explode(',', $value))
|
||
->map(fn($v) => trim($v))
|
||
->filter()
|
||
->values()
|
||
->all();
|
||
}
|
||
|
||
protected function parseRuntime(?string $runtime): ?int
|
||
{
|
||
if (!$runtime) return null;
|
||
if (preg_match('/(\d+) min/i', $runtime, $m)) {
|
||
return (int) $m[1];
|
||
}
|
||
return null;
|
||
}
|
||
|
||
protected function parseDate(?string $date): ?string
|
||
{
|
||
if (!$date || $date === 'N/A') return null;
|
||
// OMDb uses formats like "01 Jan 2000"
|
||
$ts = strtotime($date);
|
||
return $ts ? date('Y-m-d', $ts) : null;
|
||
}
|
||
}
|