using Google.Apis.Services; using Google.Apis.YouTube.v3; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Postall.Domain.Services.Contracts; using Postall.Infra.Dtos; using System.Security.Claims; namespace Postall.Infra.Services { public class YouTubeServiceChannel: IYouTubeServiceChannel { private readonly IConfiguration _configuration; private readonly IHttpContextAccessor _httpContextAccessor; private readonly string _apiKey; private readonly string _clientId; private readonly string _clientSecret; public YouTubeServiceChannel(IConfiguration configuration, IHttpContextAccessor httpContextAccessor) { _configuration = configuration; _httpContextAccessor = httpContextAccessor; _apiKey = _configuration["YouTube:ApiKey"]; _clientId = _configuration["Authentication:Google:ClientId"]; _clientSecret = _configuration["Authentication:Google:ClientSecret"]; } /// /// Obtém o ID do usuário atual a partir das claims /// private string GetCurrentUserId() { var user = _httpContextAccessor.HttpContext.User; // Primeiro tentar obter o NameIdentifier (que foi adicionado no ExternalLoginCallback) var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; // Se não encontrou, tenta usar o email como identificador if (string.IsNullOrEmpty(userId)) { var email = user.FindFirst(ClaimTypes.Email)?.Value; if (!string.IsNullOrEmpty(email)) { // Gerar ID baseado no email (mesmo método usado no ExternalLoginCallback) userId = Convert.ToBase64String( System.Text.Encoding.UTF8.GetBytes(email) ).Replace("/", "_").Replace("+", "-").Replace("=", ""); } } return userId; } /// /// Obtém os vídeos dos canais que o usuário tem acesso, ordenados por data /// public async Task> GetUserChannelVideosAsync(int maxResults = 10) { // Obter o ID do usuário das claims var userId = GetCurrentUserId(); if (string.IsNullOrEmpty(userId)) return new List(); // Obter os canais do usuário do banco de dados var userChannels = await GetUserChannelsAsync(userId); if (!userChannels.Any()) return new List(); var videos = new List(); // Para cada canal, buscar os vídeos recentes foreach (var channelId in userChannels) { var channelVideos = await GetChannelVideosAsync(channelId, maxResults); videos.AddRange(channelVideos); } // Ordenar por data de publicação e pegar os mais recentes return videos .OrderByDescending(v => v.PublishedAt) .Take(maxResults) .ToList(); } /// /// Obtém os vídeos de um canal específico /// public async Task> GetChannelVideosAsync(string channelId, int maxResults = 10) { try { var youtubeService = new YouTubeBaseServiceWrapper(_apiKey); // Primeiro, buscar os IDs dos últimos vídeos do canal var searchRequest = youtubeService.Search.List("snippet"); searchRequest.ChannelId = channelId; searchRequest.Order = SearchResource.ListRequest.OrderEnum.Date; searchRequest.MaxResults = maxResults; searchRequest.Type = "video"; var searchResponse = await searchRequest.ExecuteAsync(); if (searchResponse.Items == null || searchResponse.Items.Count == 0) return new List(); // Obter os IDs dos vídeos var videoIds = searchResponse.Items.Select(i => i.Id.VideoId).ToList(); // Buscar detalhes dos vídeos var videosRequest = youtubeService.Videos.List("snippet,contentDetails,statistics"); videosRequest.Id = string.Join(",", videoIds); var videosResponse = await videosRequest.ExecuteAsync(); // Converter para o modelo var videos = new List(); foreach (var videoItem in videosResponse.Items) { var searchItem = searchResponse.Items.FirstOrDefault(i => i.Id.VideoId == videoItem.Id); videos.Add(new VideoItemResponse { Id = videoItem.Id, Title = videoItem.Snippet.Title, Description = videoItem.Snippet.Description, ThumbnailUrl = videoItem.Snippet.Thumbnails.High.Url, PublishedAt = videoItem.Snippet.PublishedAt.Value, ChannelId = videoItem.Snippet.ChannelId, ChannelTitle = videoItem.Snippet.ChannelTitle }); } return videos; } catch (Exception ex) { // Em produção, use um logger adequado Console.WriteLine($"Erro ao obter vídeos do canal {channelId}: {ex.Message}"); return new List(); } } /// /// Método para buscar os canais que o usuário gerencia ou tem acesso /// Em um cenário real, isso viria do banco de dados /// private async Task> GetUserChannelsAsync(string userId) { // TODO: Substituir isso por uma consulta real ao banco de dados // Em produção, você deve implementar uma tabela que relacione // os usuários com seus canais do YouTube // Para teste, aqui estão alguns canais populares // Na implementação real, você deve armazenar os canais que o usuário tem permissão return new List { "UC_x5XG1OV2P6uZZ5FSM9Ttw", // Google Developers "UCt84aUC9OG6di8kSdKzEHTQ", // Outro canal de exemplo "UCkw4JCwteGrDHIsyIIKo4tQ" // Google }; } /// /// Salva um vídeo para o usuário atual /// Em produção, isso salvaria no banco de dados /// public async Task SaveVideoForUserAsync(string videoId) { var userId = GetCurrentUserId(); if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(videoId)) return; // TODO: Salvar no banco de dados a relação entre o usuário e o vídeo // Código de exemplo: // await _dbContext.UserVideos.AddAsync(new UserVideo // { // UserId = userId, // VideoId = videoId, // DateAdded = DateTime.UtcNow // }); // await _dbContext.SaveChangesAsync(); } /// /// Remove um vídeo do usuário atual /// Em produção, isso removeria do banco de dados /// public async Task RemoveVideoForUserAsync(string videoId) { var userId = GetCurrentUserId(); if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(videoId)) return; // TODO: Remover do banco de dados a relação entre o usuário e o vídeo // Código de exemplo: // var userVideo = await _dbContext.UserVideos // .FirstOrDefaultAsync(uv => uv.UserId == userId && uv.VideoId == videoId); // if (userVideo != null) // { // _dbContext.UserVideos.Remove(userVideo); // await _dbContext.SaveChangesAsync(); // } } } /// /// Wrapper para o serviço base do YouTube /// public class YouTubeBaseServiceWrapper : YouTubeService { public YouTubeBaseServiceWrapper(string apiKey) : base(new BaseClientService.Initializer() { ApiKey = apiKey, ApplicationName = "PostAll" }) { } } }