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 YouTubeServiceVideo: IVideoService
{
private readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly string _apiKey;
private readonly string _clientId;
private readonly string _clientSecret;
public YouTubeServiceVideo(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"
})
{
}
}
}