<?php
|
declare(strict_types=1);
|
|
namespace App\Service;
|
|
use Psr\Log\LoggerInterface;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Target;
|
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
/**
|
* Downloads covers from LibraryThing (https://www.librarything.com/services/covers.php).
|
*/
|
class LibraryThingCovers {
|
private const int MAX_BYTES = 10 * 1024 * 1024;
|
|
public function __construct(
|
#[Target('librarything.client')]
|
private readonly HttpClientInterface $libraryThingClient,
|
private readonly LoggerInterface $logger,
|
#[Autowire('%app.librarything_api_key%')]
|
private readonly ?string $apiKey,
|
) {
|
}
|
|
public function isEnabled(): bool {
|
return trim((string)$this->apiKey) !== '';
|
}
|
|
/**
|
* @return string|null the image data, or null when there is no cover or it could not be fetched
|
*/
|
public function download(string $isbn): ?string {
|
if (!$this->isEnabled() || preg_match('/^[\dX]{10,13}$/', $isbn) !== 1) {
|
return null;
|
}
|
|
try {
|
$response = $this->libraryThingClient->request(
|
'GET',
|
\sprintf('/devkey/%s/large/isbn/%s', rawurlencode(trim((string)$this->apiKey)), $isbn),
|
);
|
|
if ($response->getStatusCode() !== 200) {
|
$this->logger->warning('LibraryThing returned HTTP {status} for ISBN {isbn}.', [
|
'status' => $response->getStatusCode(),
|
'isbn' => $isbn,
|
]);
|
|
return null;
|
}
|
|
$data = $response->getContent();
|
} catch (ExceptionInterface $exception) {
|
// The message would contain the URL, and with it the key.
|
$this->logger->warning('The LibraryThing cover for ISBN {isbn} could not be downloaded.', ['isbn' => $isbn]);
|
|
return null;
|
}
|
|
if (\strlen($data) > self::MAX_BYTES) {
|
return null;
|
}
|
|
$size = @getimagesizefromstring($data);
|
|
return $size === false || $size[0] <= 1 || $size[1] <= 1 ? null : $data;
|
}
|
}
|