Files
animexe/app/Services/AniListService.php
2026-07-14 00:01:48 +03:00

153 lines
4.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services;
use App\Models\Anime;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class AniListService
{
const ENDPOINT = 'https://graphql.anilist.co';
const CACHE_TTL = 60 * 60 * 24 * 7;
// Maksimum genişlik (px) — oran korunur, WebP'ye çevrilir
const COVER_MAX_W = 600;
const BANNER_MAX_W = 1920;
const WEBP_QUALITY = 90;
private function query(array $variables, string $gql): ?array
{
try {
$res = Http::timeout(10)
->withHeaders(['Content-Type' => 'application/json', 'Accept' => 'application/json'])
->post(self::ENDPOINT, ['query' => $gql, 'variables' => $variables]);
if (!$res->ok()) return null;
if (!empty($res->json('errors'))) return null;
return $res->json('data.Media');
} catch (\Throwable $e) {
Log::debug('AniList query failed', ['err' => $e->getMessage()]);
return null;
}
}
public function fetchByMalId(int $malId): ?array
{
return Cache::remember("anilist_mal_{$malId}", self::CACHE_TTL, function () use ($malId) {
return $this->query(
['malId' => $malId],
'query($malId:Int){Media(idMal:$malId,type:ANIME){coverImage{extraLarge}bannerImage}}'
);
});
}
public function fetchByTitle(string $title): ?array
{
return Cache::remember('anilist_title_' . md5($title), self::CACHE_TTL, function () use ($title) {
return $this->query(
['search' => $title],
'query($search:String){Media(search:$search,type:ANIME){coverImage{extraLarge}bannerImage}}'
);
});
}
/**
* Resmi indir, yeniden boyutlandır, WebP olarak storage'a kaydet.
* Başarılıysa storage-relative yolu döner (örn. anime/covers/123.webp).
*/
/**
* Resmi indir, max genişliğe orantılı küçült (asla büyütme), WebP kaydet.
* Orijinalden küçükse olduğu gibi bırakır.
*/
private function downloadAndResize(string $url, string $storagePath, int $maxW): ?string
{
try {
$response = Http::timeout(20)->withHeaders([
'User-Agent' => 'Mozilla/5.0',
'Referer' => 'https://anilist.co/',
])->get($url);
if (!$response->ok()) return null;
$raw = $response->body();
$src = @imagecreatefromstring($raw);
if (!$src) return null;
$srcW = imagesx($src);
$srcH = imagesy($src);
if ($srcW > $maxW) {
// Orantılı küçült
$newW = $maxW;
$newH = (int) round($srcH * ($maxW / $srcW));
$dst = imagecreatetruecolor($newW, $newH);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $newW, $newH, $srcW, $srcH);
imagedestroy($src);
} else {
// Zaten küçük — olduğu gibi kullan
$dst = $src;
}
$absPath = storage_path('app/public/' . $storagePath);
@mkdir(dirname($absPath), 0755, true);
$ok = imagewebp($dst, $absPath, self::WEBP_QUALITY);
imagedestroy($dst);
return $ok ? $storagePath : null;
} catch (\Throwable $e) {
Log::debug('AniList image download failed', ['url' => $url, 'err' => $e->getMessage()]);
return null;
}
}
/**
* Anime'nin boş kapak/banner alanlarını AniList'ten doldur.
* Resimleri indirir, boyutlandırır, WebP olarak storage'a kaydeder.
* Dolu alanların üzerine yazmaz.
*/
public function fillImages(Anime $anime): bool
{
$needCover = empty($anime->cover_image);
$needBanner = empty($anime->banner_image);
if (!$needCover && !$needBanner) return false;
$data = null;
if ($anime->mal_id) {
$data = $this->fetchByMalId((int) $anime->mal_id);
}
if (!$data) {
$data = $this->fetchByTitle($anime->title);
}
if (!$data) return false;
$updates = [];
if ($needCover && !empty($data['coverImage']['extraLarge'])) {
$path = $this->downloadAndResize(
$data['coverImage']['extraLarge'],
"anime/covers/{$anime->id}.webp",
self::COVER_MAX_W
);
if ($path) $updates['cover_image'] = $path;
}
if ($needBanner && !empty($data['bannerImage'])) {
$path = $this->downloadAndResize(
$data['bannerImage'],
"anime/banners/{$anime->id}.webp",
self::BANNER_MAX_W
);
if ($path) $updates['banner_image'] = $path;
}
if (empty($updates)) return false;
$anime->update($updates);
return true;
}
}