option('channel') || $this->option('topic') || $this->option('playlist')) { $youTubeService = new YouTubeService( channelId: $this->option('channel') ?? config('services.youtube.channel_id'), topicChannelId: $this->option('topic') ?? config('services.youtube.topic_channel_id'), playlistId: $this->option('playlist') ?? config('services.youtube.playlist_id'), ); } if (! $youTubeService->isConfigured()) { $this->error(__('music_productions.youtube_not_configured')); return Command::FAILURE; } $this->info(__('music_productions.youtube_sync_started')); $this->line(__('music_productions.youtube_sync_source', [ 'source' => $youTubeService->getReleasesSourceLabel(), ])); $this->line('Playlist ID: ' . $youTubeService->resolveReleasesPlaylistId()); try { $releases = $youTubeService->getReleases(); } catch (\Throwable $exception) { $this->error($exception->getMessage()); return Command::FAILURE; } if (empty($releases)) { $this->warn(__('music_productions.youtube_no_releases')); return Command::SUCCESS; } $created = 0; $updated = 0; $dryRun = (bool) $this->option('dry-run'); foreach ($releases as $index => $release) { $videoId = $release['video_id'] ?? null; if (blank($videoId)) { continue; } $title = $release['title'] ?? __('music_productions.youtube_untitled_release'); $releaseDate = filled($release['release_date'] ?? null) ? Carbon::parse($release['release_date']) : null; $youtubeUrl = $release['youtube_url'] ?? null; $coverUrl = $release['cover_url'] ?? null; $description = $release['description'] ?? ''; $production = $this->findMatchingProduction($videoId, $title); $action = $production ? 'update' : 'create'; $this->line(sprintf( '[%d/%d] %s: %s (%s)', $index + 1, count($releases), $action === 'create' ? 'Yeni' : 'Güncelle', $title, $releaseDate?->format('Y-m-d') ?? '-' )); if ($dryRun) { continue; } $attributes = [ 'youtube_video_id' => $videoId, 'youtube_url' => $youtubeUrl, 'youtube_cover_url' => $coverUrl, 'youtube_description' => $description, 'youtube_synced_at' => now(), ]; if (! $production) { $attributes['title'] = $title; $attributes['slug'] = YouTubeService::uniqueSlug($title); $attributes['production_date'] = $releaseDate; $attributes['content'] = $youTubeService->buildDefaultContent($release); $attributes['is_active'] = true; $attributes['sort_order'] = 0; $coverPath = $youTubeService->downloadCoverImage($coverUrl, $videoId); if ($coverPath) { $attributes['cover_image'] = $coverPath; } MusicProduction::create($attributes); $created++; continue; } if (blank($production->title)) { $attributes['title'] = $title; } if ($releaseDate) { $attributes['production_date'] = $releaseDate; } if (blank($production->content)) { $attributes['content'] = $youTubeService->buildDefaultContent($release); } if (blank($production->cover_image) && filled($coverUrl)) { $coverPath = $youTubeService->downloadCoverImage($coverUrl, $videoId); if ($coverPath) { $attributes['cover_image'] = $coverPath; } } $production->update($attributes); $updated++; usleep(150000); } $message = __('music_productions.youtube_sync_completed', [ 'created' => $created, 'updated' => $updated, 'total' => count($releases), ]); $this->info($message); Log::info('YouTube releases sync completed', [ 'created' => $created, 'updated' => $updated, 'total' => count($releases), 'dry_run' => $dryRun, ]); return Command::SUCCESS; } protected function findMatchingProduction(string $videoId, string $title): ?MusicProduction { $byYoutubeId = MusicProduction::withTrashed() ->where('youtube_video_id', $videoId) ->first(); if ($byYoutubeId) { return $byYoutubeId->trashed() ? null : $byYoutubeId; } $slug = Str::slug($title); $bySlug = MusicProduction::query() ->whereNull('youtube_video_id', 'and', false) ->where('slug', '=', $slug) ->first(); if ($bySlug) { return $bySlug; } return MusicProduction::query() ->whereNull('youtube_video_id', 'and', false) ->whereRaw('LOWER(title) = ?', [Str::lower($title)], 'and') ->first(); } }