isConfigured()) { $this->error('DeepSeek API anahtarı ayarlanmamış. Admin > Ayarlar > deepseek_api_key'); return self::FAILURE; } $limit = (int) $this->option('limit'); $animeId = $this->option('anime'); $force = $this->option('force'); $dry = $this->option('dry-run'); // İşlenecek animeleri belirle if ($animeId) { $animes = Anime::where('id', $animeId)->with('genres')->get(); } else { $query = Anime::with('genres'); if (!$force) { $query->where(function ($q) { $q->whereNull('description')->orWhere('description', '') ->orWhereNull('release_year') ->orWhereNull('studio')->orWhere('studio', '') ->orWhereNull('type')->orWhere('type', '') ->orWhereNull('status')->orWhere('status', ''); })->orDoesntHave('genres'); } $query->orderBy('id'); if ($limit > 0) $query->limit($limit); $animes = $query->get(); } if ($animes->isEmpty()) { $this->info('İşlenecek anime bulunamadı (tüm alanlar dolu).'); Log::channel('daily')->info('[FillAnimeMeta] İşlenecek anime yok.'); return self::SUCCESS; } $this->info("Toplam {$animes->count()} anime işlenecek" . ($dry ? ' (dry-run)' : '') . '...'); Log::channel('daily')->info("[FillAnimeMeta] Başladı. {$animes->count()} anime, limit={$limit}, force=" . ($force ? 'evet' : 'hayır')); $done = 0; $skipped = 0; $failed = 0; foreach ($animes as $anime) { $missing = $this->missingFields($anime); if (!$force && empty($missing)) { $this->line(" ATLA {$anime->title} — tüm alanlar dolu"); $skipped++; continue; } $label = $force ? 'tüm alanlar' : implode(', ', $missing); $this->line(" İŞLE [{$anime->id}] {$anime->title} — eksik: {$label}"); if ($dry) { $done++; continue; } $meta = $ai->generateAnimeMeta($anime->title, $anime->title_jp ?? ''); if (!$meta) { $this->warn(" HATA {$anime->title} — AI boş yanıt döndürdü"); Log::channel('daily')->warning("[FillAnimeMeta] HATA [{$anime->id}] {$anime->title}: AI boş yanıt"); $failed++; sleep(3); continue; } // Sadece boş alanları doldur (force modunda hepsini güncelle) $updates = []; $fillIfEmpty = function (string $field, $value) use ($anime, $force, &$updates) { if ($value === null || $value === '') return; if ($force || empty($anime->$field)) { $updates[$field] = $value; } }; $fillIfEmpty('description', $meta['description'] ?? null); $fillIfEmpty('release_year', $meta['release_year'] ?? null); $fillIfEmpty('studio', $meta['studio'] ?? null); $fillIfEmpty('type', $meta['type'] ?? null); $fillIfEmpty('status', $meta['status'] ?? null); $fillIfEmpty('title_en', $meta['title_en'] ?? null); $fillIfEmpty('title_jp', $meta['title_jp'] ?? null); // Rating: sadece boşsa veya 0 ise doldur if (!empty($meta['rating']) && ($force || !$anime->rating)) { $updates['rating'] = min(10, max(0, (float) $meta['rating'])); } if (!empty($updates)) { $anime->update($updates); } // Genres: boşsa ekle if (!empty($meta['genres']) && ($force || $anime->genres->isEmpty())) { $genreIds = []; foreach ($meta['genres'] as $genreName) { $genre = Genre::firstOrCreate( ['name' => $genreName], ['slug' => \Illuminate\Support\Str::slug($genreName)] ); $genreIds[] = $genre->id; } if ($genreIds) { $force ? $anime->genres()->sync($genreIds) : $anime->genres()->syncWithoutDetaching($genreIds); } } $updatedFields = array_keys($updates); if (!empty($meta['genres']) && ($force || $anime->genres->isEmpty())) { $updatedFields[] = 'genres(' . implode(',', $meta['genres'] ?? []) . ')'; } $summary = empty($updatedFields) ? 'Yeni alan yok' : implode(', ', $updatedFields); $this->info(" ✓ OK [{$anime->id}] {$anime->title} → {$summary}"); Log::channel('daily')->info("[FillAnimeMeta] OK [{$anime->id}] {$anime->title} → {$summary}"); $done++; // API rate limit — DeepSeek'i boğma sleep(2); } $summary = "Tamamlandı: {$done} işlendi, {$skipped} atlandı, {$failed} hata."; $this->info($summary); Log::channel('daily')->info("[FillAnimeMeta] {$summary}"); return $failed > 0 ? self::FAILURE : self::SUCCESS; } private function missingFields(Anime $anime): array { $missing = []; if (empty($anime->description)) $missing[] = 'description'; if (empty($anime->release_year)) $missing[] = 'release_year'; if (empty($anime->studio)) $missing[] = 'studio'; if (empty($anime->type)) $missing[] = 'type'; if (empty($anime->status)) $missing[] = 'status'; if (!$anime->rating) $missing[] = 'rating'; if (empty($anime->title_en)) $missing[] = 'title_en'; if ($anime->genres->isEmpty()) $missing[] = 'genres'; return $missing; } }