orderBy('name')->get(['id', 'name', 'slug']); return view('frontend.discover', compact('genres')); } public function cards(Request $request) { $genreSlug = $request->input('genre', ''); $type = $request->input('type', ''); $limit = min((int) $request->input('limit', 10), 10); // Auth state'i al $isAuth = auth()->check(); $uid = $isAuth ? auth()->id() : null; try { $idQuery = Anime::where('is_published', true)->select('id'); if ($genreSlug) { $idQuery->whereHas('genres', fn($q) => $q->where('slug', $genreSlug)); } if ($type) { $idQuery->where('type', $type); } if ($isAuth && $uid) { $exclude = AnimeSwipe::where('user_id', $uid)->pluck('anime_id') ->merge(Watchlist::where('user_id', $uid)->pluck('anime_id')) ->unique(); if ($exclude->isNotEmpty()) { $idQuery->whereNotIn('id', $exclude); } } $ids = $idQuery->pluck('id'); if ($ids->isEmpty()) { return response()->json(['cards' => [], 'has_more' => false]); } $randomIds = $ids->shuffle()->take($limit); $animes = Anime::whereIn('id', $randomIds) ->with('genres:id,name') ->get() ->shuffle(); $cards = $animes->map(function (Anime $anime) { $hook = $anime->discovery_hook ?: ($anime->description ? Str::limit(strip_tags($anime->description), 130) : null); return [ 'id' => $anime->id, 'slug' => $anime->slug, 'title' => $anime->title, 'cover_url' => $anime->coverUrl, 'banner_url' => $anime->bannerUrl, 'rating' => $anime->rating ? number_format($anime->rating, 1) : null, 'year' => $anime->release_year, 'type' => $anime->type, 'status' => $anime->status, 'episode_count' => $anime->episode_count, 'genres' => $anime->genres->take(3)->pluck('name')->values(), 'hook' => $hook, 'description' => $anime->description ? Str::limit(strip_tags($anime->description), 420) : null, ]; }); return response()->json([ 'cards' => $cards, 'has_more' => $animes->count() === $limit, ]); } catch (\Exception $e) { \Log::error('Discover cards error: ' . $e->getMessage()); return response()->json(['cards' => [], 'has_more' => false, 'error' => true]); } } public function swipe(Request $request) { $data = $request->validate([ 'anime_id' => 'required|integer|exists:animes,id', 'direction' => 'required|in:like,skip', ]); if (auth()->check()) { $uid = auth()->id(); AnimeSwipe::updateOrCreate( ['user_id' => $uid, 'anime_id' => $data['anime_id']], ['direction' => $data['direction']] ); if ($data['direction'] === 'like') { Watchlist::updateOrCreate( ['user_id' => $uid, 'anime_id' => $data['anime_id']], ['status' => 'plan'] ); } } else { $seen = session('guest_swipes', []); $seen[] = $data['anime_id']; session(['guest_swipes' => array_unique(array_slice($seen, -150))]); } return response()->json(['ok' => true]); } public function reset() { if (auth()->check()) { AnimeSwipe::where('user_id', auth()->id())->delete(); } else { session()->forget('guest_swipes'); } return response()->json(['ok' => true]); } public function results(Request $request) { // Beğenilen animeler if (auth()->check()) { $uid = auth()->id(); $swipes = AnimeSwipe::where('user_id', $uid) ->with('anime:id,title,slug,cover_image,rating,release_year,type') ->orderByDesc('created_at') ->get()->filter(fn($s) => $s->anime); $likedAnimes = $swipes->where('direction', 'like') ->map(fn($s) => $s->anime) ->values(); $allSwipedIds = $swipes->pluck('anime_id'); $likeCount = $swipes->where('direction', 'like')->count(); $skipCount = $swipes->where('direction', 'skip')->count(); } else { $seen = session('guest_swipes', []); $likedAnimes = collect(); $allSwipedIds= collect($seen); $likeCount = 0; $skipCount = count($seen); } // AI önerileri — beğenilen animelerin türlerine benzer, henüz görülmemiş $recommendations = collect(); if ($likedAnimes->isNotEmpty()) { $ai = app(DeepSeekService::class); // Beğenilen animelerin genre'larını topla $likedWithGenres = Anime::whereIn('id', $likedAnimes->pluck('id')) ->with('genres:id,name') ->get(); $genreIds = $likedWithGenres->flatMap(fn($a) => $a->genres->pluck('id'))->unique(); // Benzer ama henüz görülmemiş animeler al — ID shuffle ile ORDER BY RAND() önlenir $candidateIds = Anime::where('is_published', true) ->whereNotIn('id', $allSwipedIds) ->whereHas('genres', fn($q) => $q->whereIn('id', $genreIds)) ->pluck('id') ->shuffle() ->take(30); $candidateAnimes = Anime::whereIn('id', $candidateIds) ->with('genres:id,name') ->withCount('episodes') ->get() ->shuffle(); if ($ai->isConfigured() && $candidateAnimes->isNotEmpty()) { $likedTitles = $likedAnimes->pluck('title')->take(5)->join(', '); $preferences = "Kullanıcının beğendiği animeler: {$likedTitles}. Bunlara benzer, aynı türde ya da aynı atmosferde animeler öner."; $candidateData = $candidateAnimes->map(fn($a) => [ 'id' => $a->id, 'title' => $a->title, 'type' => $a->type, 'release_year' => $a->release_year, 'rating' => $a->rating, 'genres' => $a->genres->map(fn($g) => ['name' => $g->name])->toArray(), ])->values()->toArray(); $aiRecs = Cache::remember( 'dsc_recs_' . md5($likedAnimes->pluck('id')->sort()->join(',')), 60 * 60 * 6, fn() => $ai->recommend($preferences, $candidateData) ); if ($aiRecs) { $recIds = collect($aiRecs)->pluck('id')->map('intval'); $recAnimes = $candidateAnimes->whereIn('id', $recIds)->keyBy('id'); $recommendations = collect($aiRecs)->take(6)->map(function ($rec) use ($recAnimes) { $anime = $recAnimes->get((int)$rec['id']); if (!$anime) return null; return [ 'id' => $anime->id, 'slug' => $anime->slug, 'title' => $anime->title, 'cover_url' => $anime->coverUrl, 'rating' => $anime->rating ? number_format($anime->rating, 1) : null, 'year' => $anime->release_year, 'genres' => $anime->genres->take(2)->pluck('name')->values(), 'reason' => $rec['reason'] ?? null, ]; })->filter()->values(); } } // AI yoksa genre-based fallback if ($recommendations->isEmpty()) { $recommendations = $candidateAnimes->take(6)->map(fn($a) => [ 'id' => $a->id, 'slug' => $a->slug, 'title' => $a->title, 'cover_url' => $a->coverUrl, 'rating' => $a->rating ? number_format($a->rating, 1) : null, 'year' => $a->release_year, 'genres' => $a->genres->take(2)->pluck('name')->values(), 'reason' => null, ])->values(); } } return response()->json([ 'liked' => $likedAnimes->map(fn($a) => [ 'id' => $a->id, 'slug' => $a->slug, 'title' => $a->title, 'cover_url' => $a->coverUrl, ])->values(), 'recommendations' => $recommendations, 'like_count' => $likeCount, 'skip_count' => $skipCount, 'is_auth' => auth()->check(), ]); } }