generated from ricardo/MVCLogin
feat: meus resumos com estrutura codificada
This commit is contained in:
parent
7b3c63ff37
commit
511b538ad3
@ -1,4 +1,4 @@
|
||||
using SumaTube.Infra.MongoDB.Documents;
|
||||
using SumaTube.Infra.MongoDB.Documents.UserPlan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
using SumaTube.Domain.Entities.Videos;
|
||||
|
||||
namespace SumaTube.Infra.Contracts.Repositories.Videos
|
||||
{
|
||||
public interface IVideoSummaryRepository
|
||||
{
|
||||
Task<VideoSummary> GetByIdAsync(string id);
|
||||
Task<List<VideoSummary>> GetByUserIdAsync(string userId);
|
||||
Task AddAsync(VideoSummary videoSummary);
|
||||
Task UpdateAsync(VideoSummary videoSummary);
|
||||
Task<bool> ExistsAsync(string videoId, string userId, string language);
|
||||
Task<VideoSummary> GetByVideoIdAndUserIdAndLanguageAsync(string videoId, string userId, string language);
|
||||
}
|
||||
}
|
||||
20
SumaTuba.Infra/Messages.cs
Normal file
20
SumaTuba.Infra/Messages.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra
|
||||
{
|
||||
public class VideoProcessingCompletedMessage
|
||||
{
|
||||
public string SessionId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string Transcription { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra.MongoDB.Documents
|
||||
namespace SumaTube.Infra.MongoDB.Documents.UserPlan
|
||||
{
|
||||
public class PersonUserDocument
|
||||
{
|
||||
@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra.MongoDB.Documents
|
||||
namespace SumaTube.Infra.MongoDB.Documents.UserPlan
|
||||
{
|
||||
public class UserPaymentDocument
|
||||
{
|
||||
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra.MongoDB.Documents.Videos
|
||||
{
|
||||
public class VideoProcessingMessageDocument
|
||||
{
|
||||
public string SessionId { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Language { get; set; }
|
||||
|
||||
public VideoProcessingMessageDocument(string sessionId, string url, string language)
|
||||
{
|
||||
SessionId = sessionId;
|
||||
Url = url;
|
||||
Language = language;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using SumaTube.Domain.Entities.UserPlan;
|
||||
using SumaTube.Infra.MongoDB.Documents;
|
||||
using SumaTube.Crosscutting.Mappers;
|
||||
using SumaTube.Infra.MongoDB.Documents.UserPlan;
|
||||
|
||||
namespace SumaTube.Infra.MongoDB.Mappers.UserPlan
|
||||
{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using MongoDB.Driver;
|
||||
using SumaTube.Infra.MongoDB.Documents;
|
||||
using SumaTube.Infra.MongoDB.Documents.UserPlan;
|
||||
|
||||
namespace SumaTube.Infra.MongoDB.Repositories
|
||||
namespace SumaTube.Infra.MongoDB.Repositories.UserPlan
|
||||
{
|
||||
public class PersonUserRepository
|
||||
{
|
||||
@ -0,0 +1,73 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using SumaTube.Domain.Entities.Videos;
|
||||
using SumaTube.Infra.Contracts.Repositories.Videos;
|
||||
|
||||
namespace SumaTube.Infra.MongoDB.Repositories.Videos
|
||||
{
|
||||
public class VideoSummaryRepository : IVideoSummaryRepository
|
||||
{
|
||||
private readonly IMongoCollection<VideoSummary> _collection;
|
||||
private readonly ILogger<VideoSummaryRepository> _logger;
|
||||
|
||||
public VideoSummaryRepository(IMongoDatabase database, ILogger<VideoSummaryRepository> logger)
|
||||
{
|
||||
_collection = database.GetCollection<VideoSummary>("VideoSummaries");
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<VideoSummary> GetByIdAsync(string id)
|
||||
{
|
||||
_logger.LogInformation("Obtendo resumo por ID: {Id}", id);
|
||||
return await _collection.Find(x => x.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<VideoSummary>> GetByUserIdAsync(string userId)
|
||||
{
|
||||
_logger.LogInformation("Obtendo resumos do usuário: {UserId}", userId);
|
||||
return await _collection.Find(x => x.UserId == userId)
|
||||
.SortByDescending(x => x.RequestDate)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task AddAsync(VideoSummary videoSummary)
|
||||
{
|
||||
_logger.LogInformation("Adicionando novo resumo. ID: {Id}, VideoId: {VideoId}",
|
||||
videoSummary.Id, videoSummary.VideoId);
|
||||
await _collection.InsertOneAsync(videoSummary);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(VideoSummary videoSummary)
|
||||
{
|
||||
_logger.LogInformation("Atualizando resumo. ID: {Id}, Status: {Status}",
|
||||
videoSummary.Id, videoSummary.Status);
|
||||
await _collection.ReplaceOneAsync(x => x.Id == videoSummary.Id, videoSummary);
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(string videoId, string userId, string language)
|
||||
{
|
||||
_logger.LogInformation("Verificando existência de resumo. VideoId: {VideoId}, UserId: {UserId}, Language: {Language}",
|
||||
videoId, userId, language);
|
||||
|
||||
var count = await _collection.CountDocumentsAsync(x =>
|
||||
x.VideoId == videoId &&
|
||||
x.UserId == userId &&
|
||||
x.Language == language &&
|
||||
(x.Status == "REALIZADO" || x.Status == "PROCESSANDO"));
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
public async Task<VideoSummary> GetByVideoIdAndUserIdAndLanguageAsync(string videoId, string userId, string language)
|
||||
{
|
||||
_logger.LogInformation("Obtendo resumo por VideoId: {VideoId}, UserId: {UserId}, Language: {Language}",
|
||||
videoId, userId, language);
|
||||
|
||||
return await _collection.Find(x =>
|
||||
x.VideoId == videoId &&
|
||||
x.UserId == userId &&
|
||||
x.Language == language)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
SumaTuba.Infra/Register/InfraServicesRegister.cs
Normal file
39
SumaTuba.Infra/Register/InfraServicesRegister.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDB.Driver;
|
||||
using SumaTube.Infra.Contracts.Repositories.Videos;
|
||||
using SumaTube.Infra.MongoDB.Repositories.Videos;
|
||||
using SumaTube.Infra.VideoSumarizer.Contracts;
|
||||
using SumaTube.Infra.VideoSumarizer.Videos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra.Register
|
||||
{
|
||||
public static class InfraServicesRegister
|
||||
{
|
||||
public static IServiceCollection AddInfraServices(this IServiceCollection services, ConfigurationManager configuration)
|
||||
{
|
||||
var mongoConnectionString = configuration.GetConnectionString("MongoDB") ?? "mongodb://localhost:27017";
|
||||
var mongoDatabaseName = configuration.GetValue<string>("MongoDB:DatabaseName") ?? "SumaTube";
|
||||
|
||||
services.AddSingleton<IMongoClient>(sp =>
|
||||
{
|
||||
return new MongoClient(mongoConnectionString);
|
||||
});
|
||||
|
||||
services.AddScoped<IMongoDatabase>(sp =>
|
||||
{
|
||||
var client = sp.GetRequiredService<IMongoClient>();
|
||||
return client.GetDatabase(mongoDatabaseName);
|
||||
});
|
||||
|
||||
services.AddScoped<IVideoSumarizerService, VideoSumarizerService>();
|
||||
services.AddScoped<IVideoSummaryRepository, VideoSummaryRepository>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra.VideoSumarizer.Contracts
|
||||
{
|
||||
public interface IVideoSumarizerService
|
||||
{
|
||||
Task RequestVideoSummarization(string sessionId, string url, string language);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RabbitMQ.Client.Events;
|
||||
using RabbitMQ.Client;
|
||||
using SumaTube.Infra.Contracts.Repositories.Videos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static MongoDB.Driver.WriteConcern;
|
||||
|
||||
namespace SumaTube.Infra.VideoSumarizer.Videos
|
||||
{
|
||||
public class VideoProcessingCompletedListener : BackgroundService
|
||||
{
|
||||
private readonly IVideoSummaryRepository _repository;
|
||||
private readonly ILogger<VideoProcessingCompletedListener> _logger;
|
||||
private IConnection _connection;
|
||||
//private IModel _channel;
|
||||
|
||||
private static string _hostName = Environment.GetEnvironmentVariable("RABBITMQ_HOST") ?? "localhost";
|
||||
private static string _userName = Environment.GetEnvironmentVariable("RABBITMQ_USER") ?? "guest";
|
||||
private static string _password = Environment.GetEnvironmentVariable("RABBITMQ_PASSWORD") ?? "guest";
|
||||
private static string _queueName = "video-processing-completed";
|
||||
|
||||
public VideoProcessingCompletedListener(
|
||||
IVideoSummaryRepository repository,
|
||||
ILogger<VideoProcessingCompletedListener> logger)
|
||||
{
|
||||
_repository = repository;
|
||||
_logger = logger;
|
||||
|
||||
InitializeRabbitMQ();
|
||||
}
|
||||
|
||||
private void InitializeRabbitMQ()
|
||||
{
|
||||
_logger.LogInformation("Inicializando conexão com RabbitMQ em {HostName}...", _hostName);
|
||||
|
||||
try
|
||||
{
|
||||
//var factory = new ConnectionFactory()
|
||||
//{
|
||||
// HostName = _hostName,
|
||||
// UserName = _userName,
|
||||
// Password = _password,
|
||||
// DispatchConsumersAsync = true
|
||||
//};
|
||||
|
||||
//_connection = factory.CreateConnection();
|
||||
//_channel = _connection.CreateModel();
|
||||
|
||||
//_channel.QueueDeclare(
|
||||
// queue: _queueName,
|
||||
// durable: true,
|
||||
// exclusive: false,
|
||||
// autoDelete: false,
|
||||
// arguments: null);
|
||||
|
||||
_logger.LogInformation("Conexão com RabbitMQ estabelecida. Aguardando mensagens na fila: {QueueName}", _queueName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro ao inicializar conexão com RabbitMQ: {Message}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
//stoppingToken.ThrowIfCancellationRequested();
|
||||
|
||||
//var consumer = new AsyncEventingBasicConsumer(_channel);
|
||||
|
||||
//consumer.Received += async (model, ea) =>
|
||||
//{
|
||||
// var body = ea.Body.ToArray();
|
||||
// var message = Encoding.UTF8.GetString(body);
|
||||
|
||||
// _logger.LogInformation("Mensagem recebida: {Message}", message);
|
||||
|
||||
// try
|
||||
// {
|
||||
// var completedMessage = JsonConvert.DeserializeObject<VideoProcessingCompletedMessage>(message);
|
||||
|
||||
// // Buscar o resumo pelo sessionId
|
||||
// var summaries = await _repository.GetByUserIdAsync(completedMessage.UserId);
|
||||
// var summary = summaries.Find(s => s.SessionId == completedMessage.SessionId);
|
||||
|
||||
// if (summary != null)
|
||||
// {
|
||||
// _logger.LogInformation("Atualizando resumo. ID: {Id}, SessionId: {SessionId}",
|
||||
// summary.Id, completedMessage.SessionId);
|
||||
|
||||
// if (completedMessage.Success)
|
||||
// {
|
||||
// summary.SetAsCompleted(
|
||||
// completedMessage.Title,
|
||||
// completedMessage.Summary,
|
||||
// completedMessage.Transcription,
|
||||
// completedMessage.Duration);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// summary.SetAsFailed(completedMessage.ErrorMessage);
|
||||
// }
|
||||
|
||||
// await _repository.UpdateAsync(summary);
|
||||
// _logger.LogInformation("Resumo atualizado com sucesso. ID: {Id}, Status: {Status}",
|
||||
// summary.Id, summary.Status);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// _logger.LogWarning("Resumo não encontrado para SessionId: {SessionId}", completedMessage.SessionId);
|
||||
// }
|
||||
|
||||
// _channel.BasicAck(ea.DeliveryTag, false);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// _logger.LogError(ex, "Erro ao processar mensagem: {Message}", ex.Message);
|
||||
// _channel.BasicNack(ea.DeliveryTag, false, true);
|
||||
// }
|
||||
//};
|
||||
|
||||
//_channel.BasicConsume(queue: _queueName, autoAck: false, consumer: consumer);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
//_channel?.Close();
|
||||
//_connection?.Close();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
using RabbitMQ.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RabbitMQ.Client;
|
||||
using SumaTube.Infra.VideoSumarizer.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -8,17 +10,24 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Infra.VideoSumarizer.Videos
|
||||
{
|
||||
public class VideoSumarizerService
|
||||
public class VideoSumarizerService : IVideoSumarizerService
|
||||
{
|
||||
private static string _hostName = Environment.GetEnvironmentVariable("RABBITMQ_HOST") ?? "localhost";
|
||||
private static string _userName = Environment.GetEnvironmentVariable("RABBITMQ_USER") ?? "guest";
|
||||
private static string _password = Environment.GetEnvironmentVariable("RABBITMQ_PASSWORD") ?? "guest";
|
||||
private static string _queueName = "video-processing-queue";
|
||||
|
||||
private readonly ILogger<VideoSumarizerService> _logger;
|
||||
|
||||
public VideoSumarizerService(ILogger<VideoSumarizerService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task RequestVideoSummarization(string sessionId, string url, string language)
|
||||
{
|
||||
Console.WriteLine("### Video Processor Publisher ###");
|
||||
Console.WriteLine($"Conectando ao RabbitMQ em {_hostName}...");
|
||||
_logger.LogInformation("### Video Processor Publisher ###");
|
||||
_logger.LogInformation("Conectando ao RabbitMQ em {HostName}...", _hostName);
|
||||
|
||||
try
|
||||
{
|
||||
@ -44,12 +53,8 @@ namespace SumaTube.Infra.VideoSumarizer.Videos
|
||||
Persistent = true
|
||||
};
|
||||
|
||||
Console.WriteLine("Conexão estabelecida com RabbitMQ");
|
||||
Console.WriteLine("Pressione 'Ctrl+C' para sair");
|
||||
_logger.LogInformation("Conexão estabelecida com RabbitMQ");
|
||||
|
||||
// Loop para publicar mensagens
|
||||
while (true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(language))
|
||||
language = "pt";
|
||||
|
||||
@ -64,24 +69,21 @@ namespace SumaTube.Infra.VideoSumarizer.Videos
|
||||
var body = Encoding.UTF8.GetBytes(messageJson);
|
||||
|
||||
// Publicar mensagem
|
||||
await channel.BasicPublishAsync(exchange: string.Empty,
|
||||
await channel.BasicPublishAsync(
|
||||
exchange: string.Empty,
|
||||
routingKey: _queueName,
|
||||
mandatory: true,
|
||||
basicProperties: properties,
|
||||
body: body);
|
||||
|
||||
Console.WriteLine($"[x] Mensagem enviada: {messageJson}");
|
||||
}
|
||||
_logger.LogInformation("Mensagem enviada: {Message}", messageJson);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Erro: {ex.Message}");
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
}
|
||||
|
||||
Console.WriteLine("Publicador finalizado.");
|
||||
Console.ReadLine();
|
||||
_logger.LogError(ex, "Erro ao enviar mensagem para RabbitMQ: {Message}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
SumaTube.Application/Register/ApplicationServiceRegister.cs
Normal file
20
SumaTube.Application/Register/ApplicationServiceRegister.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SumaTube.Application.Videos.ApplicationServices;
|
||||
using SumaTube.Application.Videos.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Application.Register
|
||||
{
|
||||
public static class ApplicationServiceRegister
|
||||
{
|
||||
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IVideoApplicationService, VideoApplicationService>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
SumaTube.Application/SumaTube.Application.csproj
Normal file
20
SumaTube.Application/SumaTube.Application.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BaseDomain\BaseDomain.csproj" />
|
||||
<ProjectReference Include="..\SumaTuba.Infra\SumaTube.Infra.csproj" />
|
||||
<ProjectReference Include="..\SumaTube.Crosscutting\SumaTube.Crosscutting.csproj" />
|
||||
<ProjectReference Include="..\SumaTube.Domain\SumaTube.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="UserPlan\Contracts\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,23 @@
|
||||
using SumaTube.Domain.Entities.UserPlan;
|
||||
using SumaTube.Infra.Contracts.Repositories.UserPlan;
|
||||
using SumaTube.Infra.MongoDB.Mappers.UserPlan;
|
||||
|
||||
namespace SumaTube.Application.UserPlan.ApplicationServices
|
||||
{
|
||||
public class PersonUserAppService
|
||||
{
|
||||
private readonly IPersonUserRepository _personUserRepository;
|
||||
|
||||
public PersonUserAppService(IPersonUserRepository personUserRepository)
|
||||
{
|
||||
_personUserRepository = personUserRepository;
|
||||
}
|
||||
|
||||
public async Task<PersonUser> GetPersonUserByEmailAsync(string email)
|
||||
{
|
||||
var document = await _personUserRepository.GetByEmailAsync(email);
|
||||
|
||||
return document.ToDomain();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SumaTube.Application.Videos.Contracts;
|
||||
using SumaTube.Domain.Entities.Videos;
|
||||
using SumaTube.Infra.Contracts.Repositories.Videos;
|
||||
using SumaTube.Infra.VideoSumarizer.Contracts;
|
||||
using SumaTube.Infra.VideoSumarizer.Videos;
|
||||
|
||||
namespace SumaTube.Application.Videos.ApplicationServices
|
||||
{
|
||||
public class VideoApplicationService : IVideoApplicationService
|
||||
{
|
||||
private readonly IVideoSummaryRepository _repository;
|
||||
private readonly IVideoSumarizerService _sumarizerService;
|
||||
private readonly ILogger<VideoApplicationService> _logger;
|
||||
|
||||
public VideoApplicationService(
|
||||
IVideoSummaryRepository repository,
|
||||
IVideoSumarizerService sumarizerService,
|
||||
ILogger<VideoApplicationService> logger)
|
||||
{
|
||||
_repository = repository;
|
||||
_sumarizerService = sumarizerService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<VideoSummary>> GetUserVideosAsync(string userId)
|
||||
{
|
||||
_logger.LogInformation("Obtendo videos do usuário: {UserId}", userId);
|
||||
return await _repository.GetByUserIdAsync(userId);
|
||||
}
|
||||
|
||||
public async Task<VideoSummary> GetVideoSummaryByIdAsync(string id, string userId)
|
||||
{
|
||||
_logger.LogInformation("Obtendo resumo com ID: {SummaryId} para usuário: {UserId}", id, userId);
|
||||
var summary = await _repository.GetByIdAsync(id);
|
||||
|
||||
if (summary == null || summary.UserId != userId)
|
||||
{
|
||||
_logger.LogWarning("Resumo não encontrado ou acesso não autorizado. ID: {SummaryId}, UserId: {UserId}", id, userId);
|
||||
return null;
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
public async Task<VideoSummary> RequestVideoSummaryAsync(string youtubeUrl, string language, string userId)
|
||||
{
|
||||
_logger.LogInformation("Solicitando resumo para URL: {Url}, idioma: {Language}, usuário: {UserId}", youtubeUrl, language, userId);
|
||||
|
||||
try
|
||||
{
|
||||
// Extrair ID do vídeo
|
||||
string videoId = ExtractVideoId(youtubeUrl);
|
||||
|
||||
if (string.IsNullOrEmpty(videoId))
|
||||
{
|
||||
_logger.LogWarning("URL do YouTube inválida: {Url}", youtubeUrl);
|
||||
throw new ArgumentException("URL do YouTube inválida");
|
||||
}
|
||||
|
||||
// Verificar se já existe um resumo para este vídeo/usuário/idioma
|
||||
bool exists = await _repository.ExistsAsync(videoId, userId, language);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
_logger.LogInformation("Resumo já existente para vídeo: {VideoId}, usuário: {UserId}, idioma: {Language}",
|
||||
videoId, userId, language);
|
||||
return await _repository.GetByVideoIdAndUserIdAndLanguageAsync(videoId, userId, language);
|
||||
}
|
||||
|
||||
// Criar novo resumo
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var summary = VideoSummary.Create(videoId, userId, language, sessionId);
|
||||
|
||||
// Salvar no repositório
|
||||
await _repository.AddAsync(summary);
|
||||
|
||||
// Enviar para processamento via RabbitMQ
|
||||
_logger.LogInformation("Enviando solicitação para processamento. SessionId: {SessionId}", sessionId);
|
||||
await _sumarizerService.RequestVideoSummarization(sessionId, youtubeUrl, language);
|
||||
|
||||
return summary;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro ao solicitar resumo de vídeo: {Message}", ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<object> CheckSummaryStatusAsync(string id, string userId)
|
||||
{
|
||||
_logger.LogInformation("Verificando status do resumo. ID: {SummaryId}, usuário: {UserId}", id, userId);
|
||||
|
||||
var summary = await _repository.GetByIdAsync(id);
|
||||
|
||||
if (summary == null || summary.UserId != userId)
|
||||
{
|
||||
_logger.LogWarning("Resumo não encontrado ou acesso não autorizado. ID: {SummaryId}, UserId: {UserId}", id, userId);
|
||||
return new { status = "NOT_FOUND" };
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
status = summary.Status,
|
||||
title = summary.Title,
|
||||
thumbnailUrl = summary.ThumbnailUrl,
|
||||
errorMessage = summary.ErrorMessage
|
||||
};
|
||||
}
|
||||
|
||||
private string ExtractVideoId(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
|
||||
if (uri.Host.Contains("youtube.com"))
|
||||
{
|
||||
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
|
||||
return query["v"];
|
||||
}
|
||||
else if (uri.Host.Contains("youtu.be"))
|
||||
{
|
||||
var segments = uri.Segments;
|
||||
return segments[segments.Length - 1].TrimEnd('/');
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro ao extrair ID do vídeo da URL: {Url}", url);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using SumaTube.Domain.Entities.Videos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Application.Videos.Contracts
|
||||
{
|
||||
public interface IVideoApplicationService
|
||||
{
|
||||
Task<List<VideoSummary>> GetUserVideosAsync(string userId);
|
||||
Task<VideoSummary> GetVideoSummaryByIdAsync(string id, string userId);
|
||||
Task<VideoSummary> RequestVideoSummaryAsync(string youtubeUrl, string language, string userId);
|
||||
Task<object> CheckSummaryStatusAsync(string id, string userId);
|
||||
}
|
||||
}
|
||||
62
SumaTube.Domain/Entities/Videos/VideoSummary.cs
Normal file
62
SumaTube.Domain/Entities/Videos/VideoSummary.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SumaTube.Domain.Entities.Videos
|
||||
{
|
||||
public class VideoSummary
|
||||
{
|
||||
public string Id { get; private set; }
|
||||
public string VideoId { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string ThumbnailUrl { get; private set; }
|
||||
public string Status { get; private set; }
|
||||
public DateTime RequestDate { get; private set; }
|
||||
public string Language { get; private set; }
|
||||
public string UserId { get; private set; }
|
||||
public string Summary { get; private set; }
|
||||
public string Transcription { get; private set; }
|
||||
public int Duration { get; private set; }
|
||||
public string ErrorMessage { get; private set; }
|
||||
public string SessionId { get; private set; }
|
||||
|
||||
// Factory method
|
||||
public static VideoSummary Create(string videoId, string userId, string language, string sessionId)
|
||||
{
|
||||
return new VideoSummary
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
VideoId = videoId,
|
||||
Title = "Carregando...",
|
||||
ThumbnailUrl = $"https://img.youtube.com/vi/{videoId}/maxresdefault.jpg",
|
||||
Status = "PROCESSANDO",
|
||||
RequestDate = DateTime.UtcNow,
|
||||
Language = language,
|
||||
UserId = userId,
|
||||
SessionId = sessionId
|
||||
};
|
||||
}
|
||||
|
||||
// Methods to update state
|
||||
public void SetAsCompleted(string title, string summary, string transcription, int duration)
|
||||
{
|
||||
Title = title;
|
||||
Summary = summary;
|
||||
Transcription = transcription;
|
||||
Duration = duration;
|
||||
Status = "REALIZADO";
|
||||
ErrorMessage = null;
|
||||
}
|
||||
|
||||
public void SetAsFailed(string errorMessage)
|
||||
{
|
||||
Status = "ERRO";
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
// For MongoDB/ORM
|
||||
private VideoSummary() { }
|
||||
}
|
||||
}
|
||||
10
SumaTube.sln
10
SumaTube.sln
@ -13,7 +13,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseDomain", "BaseDomain\Ba
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SumaTube.Crosscutting", "SumaTube.Crosscutting\SumaTube.Crosscutting.csproj", "{46EE417D-A974-4011-9799-646F31C9C146}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SumaTube.Application", "..\SumaTube.Application\SumaTube.Application.csproj", "{C598FDC6-26F5-44B2-AB7C-A5471DBE4168}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SumaTube.Application", "SumaTube.Application\SumaTube.Application.csproj", "{142F15AA-CD0E-CC29-6972-3FE9A1DB283F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -41,10 +41,10 @@ Global
|
||||
{46EE417D-A974-4011-9799-646F31C9C146}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{46EE417D-A974-4011-9799-646F31C9C146}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{46EE417D-A974-4011-9799-646F31C9C146}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C598FDC6-26F5-44B2-AB7C-A5471DBE4168}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C598FDC6-26F5-44B2-AB7C-A5471DBE4168}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C598FDC6-26F5-44B2-AB7C-A5471DBE4168}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C598FDC6-26F5-44B2-AB7C-A5471DBE4168}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{142F15AA-CD0E-CC29-6972-3FE9A1DB283F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{142F15AA-CD0E-CC29-6972-3FE9A1DB283F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{142F15AA-CD0E-CC29-6972-3FE9A1DB283F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{142F15AA-CD0E-CC29-6972-3FE9A1DB283F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
114
SumaTube/Controllers/VideoController.cs
Normal file
114
SumaTube/Controllers/VideoController.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SumaTube.Application.Videos.ApplicationServices;
|
||||
using SumaTube.Application.Videos.Contracts;
|
||||
using SumaTube.Domain.Entities.Videos;
|
||||
using SumaTube.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SumaTube.Controllers
|
||||
{
|
||||
public class VideoController : Controller
|
||||
{
|
||||
private readonly IVideoApplicationService _videoApplicationService;
|
||||
private readonly ILogger<VideoController> _logger;
|
||||
|
||||
public VideoController(
|
||||
IVideoApplicationService videoApplicationService,
|
||||
ILogger<VideoController> logger)
|
||||
{
|
||||
_videoApplicationService = videoApplicationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult Extract()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> VideoSummary(string id)
|
||||
{
|
||||
// Aqui você chamaria sua WebAPI para processar o vídeo
|
||||
// e retornaria o modelo para a view
|
||||
var model = new VideoSummaryViewModel
|
||||
{
|
||||
VideoId = id,
|
||||
VideoTitle = "Título do Vídeo",
|
||||
ChannelName = "Nome do Canal",
|
||||
ChannelThumbnail = "URL da Thumbnail do Canal",
|
||||
PublishedDate = "Data de Publicação",
|
||||
ViewCount = "Quantidade de Visualizações",
|
||||
LikeCount = "Quantidade de Curtidas",
|
||||
CommentCount = "Quantidade de Comentarios",
|
||||
SummaryText = "Resumo do Vídeo",
|
||||
KeyPoints = new List<object> { "Ponto 1", "Ponto 2", "Ponto 3" },
|
||||
Captions = new List<object> { "Legenda 1", "Legenda 2", "Legenda 3" },
|
||||
Keywords = new List<string> { "Palavra 1", "Palavra 2", "Palavra 3" },
|
||||
RelatedTopics = new List<string> { "Tópico 1", "Tópico 2", "Tópico 3" },
|
||||
RelatedVideos = new List<object> { "Vídeo 1", "Vídeo 2", "Vídeo 3" }
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> MySummary()
|
||||
{
|
||||
var userId = User.Identity.Name; // Ou outra forma de obter o ID do usuário
|
||||
_logger.LogInformation("Carregando página 'Meus Resumos' para usuário: {UserId}", userId);
|
||||
|
||||
var videos = new List<VideoSummary>();
|
||||
if (userId != null)
|
||||
videos = await _videoApplicationService.GetUserVideosAsync(userId);
|
||||
|
||||
return View(videos);
|
||||
}
|
||||
|
||||
// Endpoint para enviar solicitação de novo resumo
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> RequestSummary(string youtubeUrl, string language)
|
||||
{
|
||||
var userId = User.Identity.Name; // Ou outra forma de obter o ID do usuário
|
||||
_logger.LogInformation("Recebido pedido de resumo. URL: {Url}, Idioma: {Language}, Usuário: {UserId}",
|
||||
youtubeUrl, language, userId);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _videoApplicationService.RequestVideoSummaryAsync(youtubeUrl, language, userId);
|
||||
_logger.LogInformation("Resumo solicitado com sucesso. ID: {Id}", result.Id);
|
||||
|
||||
return Json(new { success = true, videoId = result.VideoId, summaryId = result.Id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro ao solicitar resumo: {Message}", ex.Message);
|
||||
return Json(new { success = false, error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// Página para exibir um resumo específico
|
||||
public async Task<IActionResult> Summary(string id)
|
||||
{
|
||||
var userId = User.Identity.Name; // Ou outra forma de obter o ID do usuário
|
||||
_logger.LogInformation("Acessando resumo. ID: {Id}, Usuário: {UserId}", id, userId);
|
||||
|
||||
var summary = await _videoApplicationService.GetVideoSummaryByIdAsync(id, userId);
|
||||
|
||||
if (summary == null)
|
||||
{
|
||||
_logger.LogWarning("Resumo não encontrado. ID: {Id}", id);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(summary);
|
||||
}
|
||||
|
||||
// Endpoint para verificar status de processamento
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> CheckStatus(string id)
|
||||
{
|
||||
var userId = User.Identity.Name; // Ou outra forma de obter o ID do usuário
|
||||
_logger.LogInformation("Verificando status do resumo. ID: {Id}, Usuário: {UserId}", id, userId);
|
||||
|
||||
var status = await _videoApplicationService.CheckSummaryStatusAsync(id, userId);
|
||||
return Json(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
SumaTube/Models/VideoSummaryItem.cs
Normal file
23
SumaTube/Models/VideoSummaryItem.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace SumaTube.Models
|
||||
{
|
||||
public class VideoSummaryItem
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string VideoId { get; set; } // ID do vídeo do YouTube
|
||||
public string Title { get; set; }
|
||||
public string ThumbnailUrl { get; set; }
|
||||
public string Status { get; set; } // PROCESSANDO, REALIZADO, ERRO
|
||||
public DateTime RequestDate { get; set; }
|
||||
public string Language { get; set; }
|
||||
public string UserId { get; set; } // ID do usuário que solicitou o resumo
|
||||
|
||||
// Propriedades para quando o resumo for concluído
|
||||
public string Summary { get; set; }
|
||||
public string Transcription { get; set; }
|
||||
public int Duration { get; set; } // Duração do vídeo em segundos
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
20
SumaTube/Models/VideoSummaryViewModel.cs
Normal file
20
SumaTube/Models/VideoSummaryViewModel.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace SumaTube.Models
|
||||
{
|
||||
public class VideoSummaryViewModel
|
||||
{
|
||||
public string VideoId { get; set; }
|
||||
public string VideoTitle { get; set; }
|
||||
public string ChannelName { get; set; }
|
||||
public string ChannelThumbnail { get; set; }
|
||||
public string PublishedDate { get; set; }
|
||||
public string ViewCount { get; set; }
|
||||
public string LikeCount { get; set; }
|
||||
public string CommentCount { get; set; }
|
||||
public string SummaryText { get; set; }
|
||||
public List<object> KeyPoints { get; set; }
|
||||
public List<object> Captions { get; set; }
|
||||
public List<string> Keywords { get; set; }
|
||||
public List<string> RelatedTopics { get; set; }
|
||||
public List<object> RelatedVideos { get; set; }
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,8 @@ using Stripe.Forwarding;
|
||||
using System.Globalization;
|
||||
using System.Security.Policy;
|
||||
using SumaTube.Crosscutting.Logging.Configuration;
|
||||
using SumaTube.Application.Register;
|
||||
using SumaTube.Infra.Register;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -23,6 +25,9 @@ builder.Host.UseSerilog((context, services, configuration) =>
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
|
||||
builder.Services.AddApplicationServices();
|
||||
builder.Services.AddInfraServices(builder.Configuration);
|
||||
|
||||
var config = builder.Configuration;
|
||||
|
||||
//builder.Services.AddAuthentication()
|
||||
|
||||
@ -9,6 +9,13 @@
|
||||
<DockerfileContext>.</DockerfileContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Views\CartHome\**" />
|
||||
<Content Remove="Views\CartHome\**" />
|
||||
<EmbeddedResource Remove="Views\CartHome\**" />
|
||||
<None Remove="Views\CartHome\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.7" />
|
||||
@ -26,11 +33,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Views\CartHome\" />
|
||||
<Folder Include="wwwroot\img\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SumaTube.Application\SumaTube.Application.csproj" />
|
||||
<ProjectReference Include="..\SumaTube.Crosscutting\SumaTube.Crosscutting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -1,79 +1,279 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
ViewBag.Title = "Resumos Inteligentes de Vídeos";
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
}
|
||||
<!-- Hero Banner -->
|
||||
<div class="hero-banner mb-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<h1>Transforme qualquer vídeo em um resumo inteligente</h1>
|
||||
<p class="lead">Extraímos as legendas e criamos resumos precisos para você economizar tempo e maximizar o aprendizado.</p>
|
||||
|
||||
@section Styles {
|
||||
<style type="text/css">
|
||||
.text-responsive {
|
||||
font-size: calc(100% + 1vw + 1vh);
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
<div class="div-body">
|
||||
<div class="container">
|
||||
<div id="myCarousel" class="carousel slide" data-ride="carousel">
|
||||
<ol class="carousel-indicators">
|
||||
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
|
||||
<li data-target="#myCarousel" data-slide-to="1"></li>
|
||||
<li data-target="#myCarousel" data-slide-to="2"></li>
|
||||
<li data-target="#myCarousel" data-slide-to="3"></li>
|
||||
</ol>
|
||||
<div class="carousel-inner" role="listbox">
|
||||
<div class="carousel-item active">
|
||||
<img class="first-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="First slide">
|
||||
<div class="container">
|
||||
<div class="carousel-caption d-md-block text-left">
|
||||
<h1 class="text-responsive">Example headline.</h1>
|
||||
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
|
||||
<p><a class="btn btn-lg btn-primary" href="#" role="button">Sign up today</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<img class="second-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Second slide">
|
||||
<div class="container">
|
||||
<div class="carousel-caption d-md-block">
|
||||
<h1 class="text-responsive">Another example headline.</h1>
|
||||
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
|
||||
<p><a class="btn btn-lg btn-primary" href="#" role="button">Learn more</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<img class="third-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Third slide">
|
||||
<div class="container">
|
||||
<div class="carousel-caption d-md-block text-right">
|
||||
<h1 class="text-responsive">One more for good measure.</h1>
|
||||
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
|
||||
<p><a class="btn btn-lg btn-primary" href="#" role="button">Browse gallery</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<img class="fourth-slide" src="/img/teste1.png" alt="Fourth slide">
|
||||
<div class="container">
|
||||
<div class="carousel-caption d-md-block text-right text-dark">
|
||||
<h1 class="text-responsive">One more for good measure.</h1>
|
||||
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
|
||||
<p><a class="btn btn-lg btn-primary" href="#" role="button">Browse gallery</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#myCarousel" role="button" data-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">Anterior</span>
|
||||
<a href="@Url.Action("Index", "Login")" class="btn btn-hero">
|
||||
<i class="bi bi-magic mr-2"></i> Fazer login e resumir um vídeo
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#myCarousel" role="button" data-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">Próximo</span>
|
||||
</div>
|
||||
<div class="col-lg-6 mt-4 mt-lg-0 text-center">
|
||||
<div class="hero-image">
|
||||
<img src="/img/video-summary-demo.png" alt="Ilustração de resumo de vídeo" class="img-fluid" onerror="this.src='https://via.placeholder.com/600x350?text=SumaTube';" />
|
||||
<span class="demo-badge">
|
||||
<i class="bi bi-play-circle mr-1"></i> Ver demonstração
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Como funciona -->
|
||||
<section class="mb-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center mb-4">Como funciona</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100 text-center">
|
||||
<div class="mb-3">
|
||||
<i class="bi bi-youtube text-danger" style="font-size: 3rem;"></i>
|
||||
</div>
|
||||
<h3 class="suma-card-title">1. Cole o link do YouTube</h3>
|
||||
<p class="suma-card-body">
|
||||
Basta colar o URL do vídeo do YouTube que você deseja resumir. Funciona com qualquer vídeo que tenha legendas disponíveis.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100 text-center">
|
||||
<div class="mb-3">
|
||||
<i class="bi bi-cpu text-danger" style="font-size: 3rem;"></i>
|
||||
</div>
|
||||
<h3 class="suma-card-title">2. Processamento inteligente</h3>
|
||||
<p class="suma-card-body">
|
||||
Nossa tecnologia extrai as legendas e utiliza inteligência artificial para identificar os pontos mais importantes do vídeo.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100 text-center">
|
||||
<div class="mb-3">
|
||||
<i class="bi bi-file-earmark-text text-danger" style="font-size: 3rem;"></i>
|
||||
</div>
|
||||
<h3 class="suma-card-title">3. Receba seu resumo</h3>
|
||||
<p class="suma-card-body">
|
||||
Em poucos segundos, você recebe um resumo completo e organizado com os principais pontos abordados no vídeo.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Recursos -->
|
||||
<section class="mb-5 bg-light py-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center mb-4">Economize tempo com nossos recursos</h2>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6 mb-4 mb-lg-0 order-lg-2">
|
||||
@*
|
||||
<img src="/images/features-illustration.png" alt="Recursos do SumaTube" class="img-fluid rounded shadow" onerror="this.src='https://via.placeholder.com/600x400?text=Recursos+SumaTube';" />
|
||||
|
||||
*@
|
||||
</div>
|
||||
<div class="col-lg-6 order-lg-1">
|
||||
<div class="d-flex mb-4">
|
||||
<div class="mr-3">
|
||||
<i class="bi bi-clock text-danger" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Economize tempo</h3>
|
||||
<p>Assista apenas o que realmente importa com nossos resumos personalizados.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex mb-4">
|
||||
<div class="mr-3">
|
||||
<i class="bi bi-translate text-danger" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Suporte multi-idioma</h3>
|
||||
<p>Resuma vídeos em diversos idiomas com a mesma precisão e qualidade.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex mb-4">
|
||||
<div class="mr-3">
|
||||
<i class="bi bi-bookmark-check text-danger" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Pontos-chave destacados</h3>
|
||||
<p>Identificamos automaticamente os conceitos e informações mais importantes em cada vídeo.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="mr-3">
|
||||
<i class="bi bi-cloud-download text-danger" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Salve para consultar depois</h3>
|
||||
<p>Mantenha todos seus resumos organizados em sua biblioteca pessoal.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Planos -->
|
||||
<section class="mb-5">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2>Escolha o plano ideal para você</h2>
|
||||
<p class="lead">Oferecemos opções flexíveis para atender às suas necessidades</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100 text-center">
|
||||
<span class="badge badge-pill badge-light mb-3">Gratuito</span>
|
||||
<h3 class="suma-card-title">Basic</h3>
|
||||
<div class="my-4">
|
||||
<span class="h1">R$ 0</span>
|
||||
<span class="text-muted">/mês</span>
|
||||
</div>
|
||||
<ul class="list-unstyled mb-4">
|
||||
<li class="mb-2">3 resumos por mês</li>
|
||||
<li class="mb-2">Resumos básicos</li>
|
||||
<li class="mb-2">Suporte por e-mail</li>
|
||||
<li class="mb-2 text-muted"><del>Biblioteca personalizada</del></li>
|
||||
<li class="mb-2 text-muted"><del>Exportação em PDF</del></li>
|
||||
</ul>
|
||||
<a href="#" class="btn btn-outline-danger">Começar agora</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100 text-center" style="transform: scale(1.05); border: 2px solid var(--suma-red);">
|
||||
<span class="badge badge-pill badge-danger mb-3">Popular</span>
|
||||
<h3 class="suma-card-title">Pro</h3>
|
||||
<div class="my-4">
|
||||
<span class="h1">R$ 19,90</span>
|
||||
<span class="text-muted">/mês</span>
|
||||
</div>
|
||||
<ul class="list-unstyled mb-4">
|
||||
<li class="mb-2">30 resumos por mês</li>
|
||||
<li class="mb-2">Resumos detalhados</li>
|
||||
<li class="mb-2">Suporte prioritário</li>
|
||||
<li class="mb-2">Biblioteca personalizada</li>
|
||||
<li class="mb-2">Exportação em PDF</li>
|
||||
</ul>
|
||||
<a href="#" class="btn btn-danger">Escolher Pro</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100 text-center">
|
||||
<span class="badge badge-pill badge-light mb-3">Premium</span>
|
||||
<h3 class="suma-card-title">Business</h3>
|
||||
<div class="my-4">
|
||||
<span class="h1">R$ 49,90</span>
|
||||
<span class="text-muted">/mês</span>
|
||||
</div>
|
||||
<ul class="list-unstyled mb-4">
|
||||
<li class="mb-2">Resumos ilimitados</li>
|
||||
<li class="mb-2">Resumos avançados</li>
|
||||
<li class="mb-2">Suporte VIP</li>
|
||||
<li class="mb-2">Biblioteca com categorias</li>
|
||||
<li class="mb-2">Exportação em vários formatos</li>
|
||||
</ul>
|
||||
<a href="#" class="btn btn-outline-danger">Escolher Business</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Depoimentos -->
|
||||
<section class="mb-5 bg-light py-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center mb-5">O que nossos usuários dizem</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100">
|
||||
<div class="d-flex mb-3 align-items-center">
|
||||
<div class="rounded-circle overflow-hidden mr-3" style="width: 50px; height: 50px;">
|
||||
<img src="https://via.placeholder.com/50x50" alt="Usuário" class="img-fluid" />
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Mariana Silva</h5>
|
||||
<small class="text-muted">Estudante de Medicina</small>
|
||||
</div>
|
||||
</div>
|
||||
<p class="suma-card-body">
|
||||
"O SumaTube revolucionou meus estudos. Consigo extrair o essencial de aulas longas em minutos, o que me ajudou a otimizar muito meu tempo de estudo."
|
||||
</p>
|
||||
<div class="text-warning">
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100">
|
||||
<div class="d-flex mb-3 align-items-center">
|
||||
<div class="rounded-circle overflow-hidden mr-3" style="width: 50px; height: 50px;">
|
||||
<img src="https://via.placeholder.com/50x50" alt="Usuário" class="img-fluid" />
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Carlos Mendes</h5>
|
||||
<small class="text-muted">Profissional de Marketing</small>
|
||||
</div>
|
||||
</div>
|
||||
<p class="suma-card-body">
|
||||
"Uso o SumaTube para acompanhar webinars e palestras do meu setor. Os resumos são precisos e me ajudam a extrair insights valiosos sem gastar horas assistindo vídeos."
|
||||
</p>
|
||||
<div class="text-warning">
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-half"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="suma-card h-100">
|
||||
<div class="d-flex mb-3 align-items-center">
|
||||
<div class="rounded-circle overflow-hidden mr-3" style="width: 50px; height: 50px;">
|
||||
<img src="https://via.placeholder.com/50x50" alt="Usuário" class="img-fluid" />
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Júlia Santos</h5>
|
||||
<small class="text-muted">Professora</small>
|
||||
</div>
|
||||
</div>
|
||||
<p class="suma-card-body">
|
||||
"Recomendo o SumaTube para todos meus alunos. É uma ferramenta incrível para complementar os estudos e revisar conteúdos de forma eficiente."
|
||||
</p>
|
||||
<div class="text-warning">
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA -->
|
||||
<section class="mb-5">
|
||||
<div class="container">
|
||||
<div class="suma-card bg-danger text-white text-center py-5">
|
||||
<h2 class="mb-4">Comece a economizar tempo hoje mesmo</h2>
|
||||
<p class="lead mb-4">Transforme a maneira como você assiste vídeos e absorve conhecimento</p>
|
||||
<a href="@Url.Action("Index", "Login")" class="btn btn-light btn-lg px-4">
|
||||
<i class="bi bi-play-circle mr-2"></i> Começar gratuitamente
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -1,102 +0,0 @@
|
||||
.btn-primary {
|
||||
background-color: darkgreen;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-red {
|
||||
background-color: darkred;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.div-body {
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
color: #5a5a5a;
|
||||
}
|
||||
|
||||
.bg-inverse {
|
||||
background-color: #292b2c !important;
|
||||
}
|
||||
/* CUSTOMIZE THE CAROUSEL
|
||||
-------------------------------------------------- */
|
||||
|
||||
/* Carousel base class */
|
||||
.carousel {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
/* Since positioning the image, we need to help out the caption */
|
||||
.carousel-caption {
|
||||
z-index: 10;
|
||||
bottom: 3rem;
|
||||
}
|
||||
|
||||
/* Declare heights because of positioning of img element */
|
||||
.carousel-item {
|
||||
height: 32rem;
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
.carousel-item > img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
height: 32rem;
|
||||
}
|
||||
|
||||
|
||||
/* MARKETING CONTENT
|
||||
-------------------------------------------------- */
|
||||
|
||||
/* Center align the text within the three columns below the carousel */
|
||||
.marketing .col-lg-4 {
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.marketing h2 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.marketing .col-lg-4 p {
|
||||
margin-right: .75rem;
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
|
||||
/* Featurettes
|
||||
------------------------- */
|
||||
|
||||
.featurette-divider {
|
||||
margin: 5rem 0; /* Space out the Bootstrap <hr> more */
|
||||
}
|
||||
|
||||
/* Thin out the marketing headings */
|
||||
.featurette-heading {
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
letter-spacing: -.05rem;
|
||||
}
|
||||
|
||||
|
||||
/* RESPONSIVE CSS
|
||||
-------------------------------------------------- */
|
||||
|
||||
@media (min-width: 40em) {
|
||||
/* Bump up size of carousel content */
|
||||
.carousel-caption p {
|
||||
margin-bottom: 1.25rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.featurette-heading {
|
||||
font-size: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 62em) {
|
||||
.featurette-heading {
|
||||
margin-top: 7rem;
|
||||
}
|
||||
}
|
||||
@ -1,127 +1,111 @@
|
||||
@{
|
||||
ViewBag.Title = "Login / Registro";
|
||||
}
|
||||
|
||||
@section Styles {
|
||||
<style>
|
||||
.box {
|
||||
width: 500px;
|
||||
margin: 200px 0;
|
||||
.login-container {
|
||||
max-width: 500px;
|
||||
margin: 60px auto;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shape1 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
float: left;
|
||||
margin-right: -50px;
|
||||
.login-icon {
|
||||
font-size: 2.5rem;
|
||||
color: var(--suma-red);
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.shape2 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
margin-top: -30px;
|
||||
float: left;
|
||||
.login-title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--suma-red);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.shape3 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
margin-top: -30px;
|
||||
float: left;
|
||||
margin-left: -31px;
|
||||
.login-subtitle {
|
||||
font-size: 16px;
|
||||
color: var(--suma-dark-gray);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.shape4 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
margin-top: -25px;
|
||||
float: left;
|
||||
margin-left: -32px;
|
||||
.login-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: white;
|
||||
color: #444;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 12px 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.shape5 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
float: left;
|
||||
margin-right: -48px;
|
||||
margin-left: -32px;
|
||||
margin-top: -30px;
|
||||
.login-button:hover {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.shape6 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
float: left;
|
||||
margin-right: -20px;
|
||||
margin-top: -35px;
|
||||
.login-button i {
|
||||
margin-right: 10px;
|
||||
color: #4285F4;
|
||||
}
|
||||
|
||||
.shape7 {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #0074d9;
|
||||
border-radius: 80px;
|
||||
float: left;
|
||||
margin-right: -20px;
|
||||
margin-top: -57px;
|
||||
.login-footer {
|
||||
margin-top: 30px;
|
||||
font-size: 14px;
|
||||
color: var(--suma-dark-gray);
|
||||
}
|
||||
|
||||
.float {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
.login-footer a {
|
||||
color: var(--suma-red);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-left: 145px;
|
||||
.login-footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.video-icon-container {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--suma-red);
|
||||
}
|
||||
|
||||
.video-icon-container i {
|
||||
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
</style>
|
||||
|
||||
}
|
||||
|
||||
<div id="login-row" class="row justify-content-center align-items-center">
|
||||
<div id="login-column" class="col-md-6">
|
||||
<div class="box">
|
||||
<div class="shape1"></div>
|
||||
<div class="shape2"></div>
|
||||
<div class="shape3"></div>
|
||||
<div class="shape4"></div>
|
||||
<div class="shape5"></div>
|
||||
<div class="shape6"></div>
|
||||
<div class="shape7"></div>
|
||||
<div class="float">
|
||||
<br />
|
||||
<br />
|
||||
@using (Html.BeginForm("ExternalLogin", "Login", new { provider = "Microsoft" }, FormMethod.Post, true, new { id = "externalLoginForm", style = "margin-left: 150px" }))
|
||||
<div class="login-container">
|
||||
<div class="login-icon">
|
||||
<i class="bi bi-play-btn-fill"></i>
|
||||
</div>
|
||||
<h1 class="login-title">Bem-vindo ao SumaTube</h1>
|
||||
<p class="login-subtitle">Faça login para criar resumos inteligentes de vídeos do YouTube</p>
|
||||
<div class="video-icon-container">
|
||||
<i class="bi bi-file-earmark-text"></i>
|
||||
<i class="bi bi-arrow-left-right mx-2"></i>
|
||||
<i class="bi bi-youtube"></i>
|
||||
</div>
|
||||
@using (Html.BeginForm("ExternalLoginGoogle", "Login", new { provider = "Google" }, FormMethod.Post, true, new { id = "googleLoginForm" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-success btn-block login">Login with Microsoft</button>
|
||||
}
|
||||
<br />
|
||||
@using (Html.BeginForm("ExternalLoginGoogle", "Login", new { provider = "Google" }, FormMethod.Post, true, new { id = "externalLoginForm", style = "margin-left: 150px" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-success btn-block login">Login with Google</button>
|
||||
<button type="submit" class="login-button">
|
||||
<i class="bi bi-google"></i>
|
||||
Continuar/Entrar com Google
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-footer">
|
||||
Ao fazer login, você concorda com nossos <a href="/termos">Termos de Serviço</a> e <a href="/privacidade">Política de Privacidade</a>
|
||||
</div>
|
||||
</div>
|
||||
109
SumaTube/Views/Plans/Index - Copy.cshtml
Normal file
109
SumaTube/Views/Plans/Index - Copy.cshtml
Normal file
@ -0,0 +1,109 @@
|
||||
@{
|
||||
ViewBag.Title = "Planos";
|
||||
}
|
||||
|
||||
<div class="row db-padding-btm db-attached">
|
||||
<div class="col-4 col-xs-4 col-sm-4 col-md-4 col-lg-4">
|
||||
<div class="db-wrapper">
|
||||
<div class="db-pricing-eleven db-bk-color-one">
|
||||
<div class="price text-price-responsive">
|
||||
<sup>R$</sup> 0,00
|
||||
<small>para sempre!</small>
|
||||
</div>
|
||||
<div class="type text-type-responsive">
|
||||
Básico
|
||||
</div>
|
||||
<ul class="text-responsive">
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Bio/Links <span class="hide-in-cell">pessoal virtual</span></li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Exibir imagem </span>Whatsapp *</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Até 3 links </li>
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Sem página de</span> Produtos</li>
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Sem contador de </span>Visualizações</span></li>
|
||||
</ul>
|
||||
<div class="pricing-footer">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<div class="btn db-button-color-square btn-lg">Plano Inicial</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="btn db-button-color-square btn-lg">Comece com este!</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-xs-4 col-sm-4 col-md-4 col-lg-4">
|
||||
<div class="db-wrapper">
|
||||
<div class="db-pricing-eleven db-bk-color-two popular">
|
||||
<div class="price text-price-responsive">
|
||||
<sup>R$</sup>13
|
||||
<small>por mês</small>
|
||||
</div>
|
||||
<div class="type text-type-responsive">
|
||||
BIO
|
||||
</div>
|
||||
<ul>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Bio/Links <span class="hide-in-cell">pessoal virtual</span></li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Exibir cartão </span>Whatsapp *</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>100 links</li>
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Sem página de</span> Produtos</li>
|
||||
@*
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Link agendado **</li>
|
||||
*@
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Contador de</span> Visualizações</li>
|
||||
@*
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Registro de contatos</span> </li>
|
||||
*@
|
||||
</ul>
|
||||
<div class="pricing-footer">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<a href="/Pay/?plan=bio" class="btn db-button-color-square btn-lg">Assinar</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="/Login" class="btn db-button-color-square btn-lg">Login / Registro</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-xs-4 col-sm-4 col-md-4 col-lg-4">
|
||||
<div class="db-wrapper">
|
||||
<div class="db-pricing-eleven db-bk-color-three">
|
||||
<div class="price text-price-responsive">
|
||||
<sup>R$</sup>27
|
||||
<small>por mês</small>
|
||||
</div>
|
||||
<div class="type text-type-responsive">
|
||||
CATÁLOGO
|
||||
</div>
|
||||
<ul>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Bio/Cartão <span class="hide-in-cell">pessoal virtual</span></li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Exibir cartão </span>Whatsapp *</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Sem limite de links </li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Links de Produtos</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Contador<span class="hide-in-cell"> de visualizações</span> </li>
|
||||
@*
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Registro de contatos</span> </li>
|
||||
*@
|
||||
</ul>
|
||||
<div class="pricing-footer">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<button type="button" class="btn db-button-color-square btn-lg" onclick="waitingDialog.show('Custom message');window.location.href = '/Pay/?plan=catalogo';">Assinar</button>
|
||||
|
||||
@*
|
||||
<a href="/Pay/?plan=catalogo" class="btn db-button-color-square btn-lg">Assinar</a>
|
||||
*@
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="/Login" class="btn db-button-color-square btn-lg">Login / Registro</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
225
SumaTube/Views/Plans/Index - Copy.cshtml.css
Normal file
225
SumaTube/Views/Plans/Index - Copy.cshtml.css
Normal file
@ -0,0 +1,225 @@
|
||||
/*=============================================================
|
||||
|
||||
Authour URL: www.designbootstrap.com
|
||||
|
||||
http://www.designbootstrap.com/
|
||||
|
||||
License: MIT
|
||||
======================================================== */
|
||||
|
||||
/*============================================================
|
||||
BACKGROUND COLORS
|
||||
============================================================*/
|
||||
.db-bk-color-one {
|
||||
background-color: #D9EDD2;
|
||||
}
|
||||
|
||||
.db-bk-color-two {
|
||||
background-color: #B9CEA0;
|
||||
}
|
||||
|
||||
.db-bk-color-three {
|
||||
background-color: #D9EDD2;
|
||||
}
|
||||
|
||||
.db-bk-color-six {
|
||||
background-color: #F59B24;
|
||||
}
|
||||
|
||||
.db-padding-btm {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.db-button-color-square {
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.50);
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
-webkit-border-radius: 0px;
|
||||
-moz-border-radius: 0px;
|
||||
}
|
||||
|
||||
.db-button-color-square:hover {
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.50);
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
.db-pricing-eleven {
|
||||
margin-bottom: 30px;
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, .5);
|
||||
border-radius: 7px 7px 7px 7px;
|
||||
color: #101211;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.db-pricing-eleven ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.db-pricing-eleven ul li {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.db-pricing-eleven ul li i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
.db-pricing-eleven .price {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
color: #353000;
|
||||
}
|
||||
|
||||
.db-pricing-eleven .price small {
|
||||
color: #63783F;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.db-pricing-eleven .type {
|
||||
background-color: #63783F;
|
||||
padding: 50px 20px;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.text-price-responsive {
|
||||
font-size: 30px;
|
||||
padding: 20px 10px 10px 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-type-responsive {
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.text-responsive {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.hide-in-cell {
|
||||
display: none
|
||||
}
|
||||
|
||||
@media (min-width: 544px) {
|
||||
.db-pricing-eleven ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.text-price-responsive {
|
||||
font-size: 30px;
|
||||
padding: 20px 10px 10px 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-type-responsive {
|
||||
font-size: 30px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-responsive {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.hide-in-cell {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
/* Medium devices (tablets, 768px and up) */
|
||||
@media (min-width: 768px) {
|
||||
.text-price-responsive {
|
||||
padding: 40px 20px 20px 20px;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.text-type-responsive {
|
||||
font-size: 30px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-responsive {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large devices (desktops, 992px and up) */
|
||||
@media (min-width: 992px) {
|
||||
.text-price-responsive {
|
||||
padding: 40px 20px 20px 20px;
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
.text-type-responsive {
|
||||
font-size: 30px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-responsive {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra large devices (large desktops, 1200px and up) */
|
||||
@media (min-width: 1200px) {
|
||||
.db-pricing-eleven ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.text-price-responsive {
|
||||
padding: 40px 20px 20px 20px;
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
.text-type-responsive {
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.text-responsive {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.hide-in-cell {
|
||||
display: inline
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.db-pricing-eleven .pricing-footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.db-attached > .col-lg-4,
|
||||
.db-attached > .col-lg-3,
|
||||
.db-attached > .col-md-4,
|
||||
.db-attached > .col-md-3,
|
||||
.db-attached > .col-sm-4,
|
||||
.db-attached > .col-sm-3 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.db-pricing-eleven.popular {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.db-pricing-eleven.popular .price {
|
||||
padding-top: 80px;
|
||||
}
|
||||
@ -1,109 +1,394 @@
|
||||
@{
|
||||
ViewBag.Title = "Planos";
|
||||
ViewData["Title"] = "Planos";
|
||||
}
|
||||
|
||||
<div class="row db-padding-btm db-attached">
|
||||
<div class="col-4 col-xs-4 col-sm-4 col-md-4 col-lg-4">
|
||||
<div class="db-wrapper">
|
||||
<div class="db-pricing-eleven db-bk-color-one">
|
||||
<div class="price text-price-responsive">
|
||||
<sup>R$</sup> 0,00
|
||||
<small>para sempre!</small>
|
||||
</div>
|
||||
<div class="type text-type-responsive">
|
||||
Básico
|
||||
</div>
|
||||
<ul class="text-responsive">
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Bio/Links <span class="hide-in-cell">pessoal virtual</span></li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Exibir imagem </span>Whatsapp *</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Até 3 links </li>
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Sem página de</span> Produtos</li>
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Sem contador de </span>Visualizações</span></li>
|
||||
</ul>
|
||||
<div class="pricing-footer">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<div class="btn db-button-color-square btn-lg">Plano Inicial</div>
|
||||
@section Styles {
|
||||
<style type="text/css">
|
||||
.plans-header {
|
||||
background-color: var(--suma-beige);
|
||||
padding: 3rem 0;
|
||||
border-radius: 0 0 50% 50% / 20px;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="btn db-button-color-square btn-lg">Comece com este!</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-xs-4 col-sm-4 col-md-4 col-lg-4">
|
||||
<div class="db-wrapper">
|
||||
<div class="db-pricing-eleven db-bk-color-two popular">
|
||||
<div class="price text-price-responsive">
|
||||
<sup>R$</sup>13
|
||||
<small>por mês</small>
|
||||
</div>
|
||||
<div class="type text-type-responsive">
|
||||
BIO
|
||||
</div>
|
||||
<ul>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Bio/Links <span class="hide-in-cell">pessoal virtual</span></li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Exibir cartão </span>Whatsapp *</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>100 links</li>
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Sem página de</span> Produtos</li>
|
||||
@*
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Link agendado **</li>
|
||||
*@
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Contador de</span> Visualizações</li>
|
||||
@*
|
||||
<li><i class="bi bi-square text-success"></i><span class="hide-in-cell">Registro de contatos</span> </li>
|
||||
*@
|
||||
</ul>
|
||||
<div class="pricing-footer">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<a href="/Pay/?plan=bio" class="btn db-button-color-square btn-lg">Assinar</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="/Login" class="btn db-button-color-square btn-lg">Login / Registro</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-xs-4 col-sm-4 col-md-4 col-lg-4">
|
||||
<div class="db-wrapper">
|
||||
<div class="db-pricing-eleven db-bk-color-three">
|
||||
<div class="price text-price-responsive">
|
||||
<sup>R$</sup>27
|
||||
<small>por mês</small>
|
||||
</div>
|
||||
<div class="type text-type-responsive">
|
||||
CATÁLOGO
|
||||
</div>
|
||||
<ul>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>1 Bio/Cartão <span class="hide-in-cell">pessoal virtual</span></li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Exibir cartão </span>Whatsapp *</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Sem limite de links </li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Links de Produtos</li>
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i>Contador<span class="hide-in-cell"> de visualizações</span> </li>
|
||||
@*
|
||||
<li><i class="bi bi-plus-square-fill text-success"></i><span class="hide-in-cell">Registro de contatos</span> </li>
|
||||
*@
|
||||
</ul>
|
||||
<div class="pricing-footer">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<button type="button" class="btn db-button-color-square btn-lg" onclick="waitingDialog.show('Custom message');window.location.href = '/Pay/?plan=catalogo';">Assinar</button>
|
||||
|
||||
@*
|
||||
<a href="/Pay/?plan=catalogo" class="btn db-button-color-square btn-lg">Assinar</a>
|
||||
*@
|
||||
.pricing-card {
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
transition: all 0.3s ease;
|
||||
height: 100%;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="/Login" class="btn db-button-color-square btn-lg">Login / Registro</a>
|
||||
|
||||
.pricing-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 15px 30px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.pricing-card.popular {
|
||||
border-color: var(--suma-red);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.popular-badge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: var(--suma-red);
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0 12px 0 12px;
|
||||
}
|
||||
|
||||
.pricing-header {
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--suma-gray);
|
||||
}
|
||||
|
||||
.pricing-price {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin: 1rem 0;
|
||||
color: var(--suma-red);
|
||||
}
|
||||
|
||||
.pricing-period {
|
||||
font-size: 0.9rem;
|
||||
color: var(--suma-dark-gray);
|
||||
}
|
||||
|
||||
.pricing-features {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.pricing-feature {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pricing-feature i {
|
||||
color: var(--suma-red);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.pricing-feature.disabled {
|
||||
color: var(--suma-dark-gray);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.pricing-action {
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-outline-red {
|
||||
color: var(--suma-red);
|
||||
border-color: var(--suma-red);
|
||||
}
|
||||
|
||||
.btn-outline-red:hover {
|
||||
background-color: var(--suma-red);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.faq-question {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--suma-red);
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
<!-- Header da página -->
|
||||
<div class="plans-header">
|
||||
<div class="container text-center">
|
||||
<h1 class="display-4 mb-3">Planos de Assinatura</h1>
|
||||
<p class="lead mb-4">Escolha o plano ideal para suas necessidades de resumo de vídeos</p>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-light active" id="monthlyBtn">Mensal</button>
|
||||
<button type="button" class="btn btn-light" id="yearlyBtn">Anual (20% desconto)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cards de planos -->
|
||||
<div class="container mb-5">
|
||||
<div class="row">
|
||||
<!-- Plano Gratuito -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="pricing-card">
|
||||
<div class="pricing-header">
|
||||
<h3>Gratuito</h3>
|
||||
<div class="pricing-price">R$ 0</div>
|
||||
<div class="pricing-period">para sempre</div>
|
||||
</div>
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>3 resumos por mês</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Resumos básicos</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Vídeos de até 10 minutos</span>
|
||||
</div>
|
||||
<div class="pricing-feature disabled">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
<span>Download de resumos</span>
|
||||
</div>
|
||||
<div class="pricing-feature disabled">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
<span>Transcrição completa</span>
|
||||
</div>
|
||||
<div class="pricing-feature disabled">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
<span>Suporte prioritário</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pricing-action">
|
||||
<a href="#" class="btn btn-outline-red btn-lg btn-block">Começar grátis</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plano Premium -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="pricing-card popular">
|
||||
<div class="popular-badge">Mais popular</div>
|
||||
<div class="pricing-header">
|
||||
<h3>Premium</h3>
|
||||
<div class="pricing-price" id="premiumPrice">R$ 29,90</div>
|
||||
<div class="pricing-period" id="premiumPeriod">por mês</div>
|
||||
</div>
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Resumos ilimitados</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Resumos detalhados</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Vídeos de até 3 horas</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Download de resumos (PDF)</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Transcrição completa</span>
|
||||
</div>
|
||||
<div class="pricing-feature disabled">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
<span>Suporte prioritário</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pricing-action">
|
||||
<a href="#" class="btn btn-primary btn-lg btn-block">Assinar agora</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plano Pro -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="pricing-card">
|
||||
<div class="pricing-header">
|
||||
<h3>Profissional</h3>
|
||||
<div class="pricing-price" id="proPrice">R$ 59,90</div>
|
||||
<div class="pricing-period" id="proPeriod">por mês</div>
|
||||
</div>
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Tudo do plano Premium</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Resumos para vídeos de qualquer duração</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Análise avançada de conteúdo</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Extração de pontos-chave personalizados</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>API para integração</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<i class="bi bi-check-circle-fill"></i>
|
||||
<span>Suporte prioritário 24/7</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pricing-action">
|
||||
<a href="#" class="btn btn-outline-red btn-lg btn-block">Experimente 7 dias grátis</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabela comparativa -->
|
||||
<div class="mt-5 mb-5">
|
||||
<h3 class="text-center mb-4">Comparação detalhada dos planos</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Recurso</th>
|
||||
<th class="text-center">Gratuito</th>
|
||||
<th class="text-center">Premium</th>
|
||||
<th class="text-center">Profissional</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Resumos mensais</td>
|
||||
<td class="text-center">3</td>
|
||||
<td class="text-center">Ilimitados</td>
|
||||
<td class="text-center">Ilimitados</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Duração máxima dos vídeos</td>
|
||||
<td class="text-center">10 minutos</td>
|
||||
<td class="text-center">3 horas</td>
|
||||
<td class="text-center">Sem limite</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transcrição completa</td>
|
||||
<td class="text-center"><i class="bi bi-x text-danger"></i></td>
|
||||
<td class="text-center"><i class="bi bi-check text-success"></i></td>
|
||||
<td class="text-center"><i class="bi bi-check text-success"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Download de resumos</td>
|
||||
<td class="text-center"><i class="bi bi-x text-danger"></i></td>
|
||||
<td class="text-center"><i class="bi bi-check text-success"></i></td>
|
||||
<td class="text-center"><i class="bi bi-check text-success"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Palavras-chave e tópicos</td>
|
||||
<td class="text-center">Básico</td>
|
||||
<td class="text-center">Avançado</td>
|
||||
<td class="text-center">Personalizado</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Acesso a API</td>
|
||||
<td class="text-center"><i class="bi bi-x text-danger"></i></td>
|
||||
<td class="text-center"><i class="bi bi-x text-danger"></i></td>
|
||||
<td class="text-center"><i class="bi bi-check text-success"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Suporte</td>
|
||||
<td class="text-center">Email</td>
|
||||
<td class="text-center">Email e chat</td>
|
||||
<td class="text-center">Prioritário 24/7</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FAQs -->
|
||||
<div class="container mb-5">
|
||||
<h3 class="text-center mb-4">Perguntas Frequentes</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="faq-item">
|
||||
<h5 class="faq-question">Como funciona o SumaTube?</h5>
|
||||
<p>O SumaTube usa tecnologia avançada de IA para extrair e analisar as legendas dos vídeos do YouTube, criando resumos inteligentes que capturam os pontos principais do conteúdo.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h5 class="faq-question">Posso cancelar minha assinatura a qualquer momento?</h5>
|
||||
<p>Sim, você pode cancelar sua assinatura a qualquer momento. Não há contratos de longo prazo ou taxas de cancelamento.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h5 class="faq-question">Como são processados os pagamentos?</h5>
|
||||
<p>Processamos pagamentos via cartão de crédito, débito, PayPal e Pix. Todas as transações são seguras e criptografadas.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="faq-item">
|
||||
<h5 class="faq-question">E se o vídeo não tiver legendas?</h5>
|
||||
<p>O SumaTube pode gerar legendas automaticamente para vídeos que não as possuem, embora a precisão possa variar dependendo da qualidade do áudio.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h5 class="faq-question">Quais idiomas são suportados?</h5>
|
||||
<p>Atualmente suportamos resumos em português, inglês e espanhol. Estamos constantemente adicionando suporte para novos idiomas.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h5 class="faq-question">Como posso entrar em contato com o suporte?</h5>
|
||||
<p>Você pode entrar em contato conosco através do email suporte@sumatube.com ou pelo chat disponível no site para usuários Premium e Profissional.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Call to Action -->
|
||||
<div class="text-center mt-5">
|
||||
<h3 class="mb-3">Pronto para começar?</h3>
|
||||
<p class="mb-4">Escolha o plano ideal para suas necessidades e comece a usar o SumaTube hoje mesmo.</p>
|
||||
<a href="#" class="btn btn-primary btn-lg">Criar conta gratuita</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
// Toggle entre planos mensais e anuais
|
||||
$('#monthlyBtn').click(function() {
|
||||
$(this).addClass('active');
|
||||
$('#yearlyBtn').removeClass('active');
|
||||
|
||||
// Atualizar preços para mensais
|
||||
$('#premiumPrice').text('R$ 29,90');
|
||||
$('#premiumPeriod').text('por mês');
|
||||
$('#proPrice').text('R$ 59,90');
|
||||
$('#proPeriod').text('por mês');
|
||||
});
|
||||
|
||||
$('#yearlyBtn').click(function() {
|
||||
$(this).addClass('active');
|
||||
$('#monthlyBtn').removeClass('active');
|
||||
|
||||
// Atualizar preços para anuais com desconto
|
||||
$('#premiumPrice').text('R$ 287,04');
|
||||
$('#premiumPeriod').text('por ano (R$ 23,92/mês)');
|
||||
$('#proPrice').text('R$ 575,04');
|
||||
$('#proPeriod').text('por ano (R$ 47,92/mês)');
|
||||
});
|
||||
|
||||
// Animação aos cards
|
||||
$('.pricing-card').hover(
|
||||
function() {
|
||||
$(this).addClass('shadow-lg');
|
||||
},
|
||||
function() {
|
||||
$(this).removeClass('shadow-lg');
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
@using System.Security.Claims
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="pt-br">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@ -9,110 +9,181 @@
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/css/custom.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/SumaTube.styles.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
@await RenderSectionAsync("Styles", required: false)
|
||||
</head>
|
||||
<body class="hide-body">
|
||||
<body class="d-flex flex-column min-vh-100 hide-body">
|
||||
<partial name="_Busy" />
|
||||
<div id="wrapper">
|
||||
|
||||
<header>
|
||||
<nav id="nav-bar" class="navbar navbar-expand-lg navbar-dark">
|
||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">SumaTube</a>
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center" asp-area="" asp-controller="Home" asp-action="Index">
|
||||
<i class="bi bi-play-btn-fill mr-2"></i> SumaTube
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<!-- Campo de busca central -->
|
||||
<div class="d-none d-md-block mx-auto">
|
||||
<form class="form-inline my-2 my-lg-0">
|
||||
<div class="input-group">
|
||||
<input class="form-control search-input" type="search" placeholder="Buscar vídeos..." aria-label="Buscar">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-light" type="submit">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="navbar-collapse collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Home" asp-action="Index">
|
||||
<i class="bi bi-house-door"></i> Home
|
||||
</a>
|
||||
</li>
|
||||
@*
|
||||
Exibir apenas são não estiver logado.
|
||||
*@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Plans" asp-action="Index">
|
||||
<i class="bi bi-star"></i> Planos
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Video" asp-action="MySummary">
|
||||
<i class="bi bi-file-text"></i> Meus resumos
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Plans" asp-action="Index">Planos</a>
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Video" asp-action="Extract">
|
||||
<i class="bi bi-magic"></i> Extract
|
||||
</a>
|
||||
</li>
|
||||
<partial name="_Language" />
|
||||
</ul>
|
||||
|
||||
@if (User != null && User.Identity != null && !User.Identity.IsAuthenticated)
|
||||
{
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<partial name="_Language" />
|
||||
<li class="nav-item" style="margin-right: 10px">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Login" asp-action="Index"><i class="bi bi-person"></i> Login</a>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Login" asp-action="Index">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Login
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<partial name="_Language"/>
|
||||
<li class="nav-item dropdown" style="margin-right: 10px">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="bi bi-person"></i> @(User.FindFirst("FullName")!=null ? User.FindFirst("FullName").Value : "N/A")
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" asp-area="" asp-controller="Login" asp-action="Logout">Sair</a>
|
||||
@*
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
Criar uma tela que permita alterar o plano.
|
||||
*@
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle text-white" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="bi bi-person-circle"></i> @(User.FindFirst("FullName") != null ? User.FindFirst("FullName").Value : "N/A")
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" asp-controller="Account" asp-action="Profile">
|
||||
<i class="bi bi-person"></i> Meu Perfil
|
||||
</a>
|
||||
<a class="dropdown-item" asp-controller="Videos" asp-action="History">
|
||||
<i class="bi bi-clock-history"></i> Histórico
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" asp-area="" asp-controller="Login" asp-action="Logout">
|
||||
<i class="bi bi-box-arrow-right"></i> Sair
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
@*
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" asp-area="" asp-controller="Login" asp-action="Logout">@User.FindFirst("FullName").Value
|
||||
</a>
|
||||
</li>
|
||||
*@
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
@RenderBody()
|
||||
@*
|
||||
<main role="main" class="pb-3">
|
||||
</main>
|
||||
*@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<!-- Campo de busca para dispositivos móveis -->
|
||||
<div class="container d-md-none mt-3 mb-3">
|
||||
<form class="form-inline">
|
||||
<div class="input-group w-100">
|
||||
<input class="form-control" type="search" placeholder="Buscar vídeos..." aria-label="Buscar">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-light" type="submit">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Conteúdo principal -->
|
||||
<main class="flex-grow-1">
|
||||
<div class="container py-4">
|
||||
@RenderBody()
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer mt-auto py-3">
|
||||
<div class="container">
|
||||
© 2024 - SumaTube - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4 mb-3 mb-md-0">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-play-btn-fill mr-2" style="color: var(--suma-red); font-size: 1.2rem;"></i>
|
||||
<span class="font-weight-bold">SumaTube</span>
|
||||
<span class="mx-2">|</span>
|
||||
<small>Resumos inteligentes de vídeos</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3 mb-md-0 text-center">
|
||||
<div class="d-flex justify-content-center">
|
||||
<a href="#" class="text-muted mx-2"><i class="bi bi-facebook"></i></a>
|
||||
<a href="#" class="text-muted mx-2"><i class="bi bi-twitter"></i></a>
|
||||
<a href="#" class="text-muted mx-2"><i class="bi bi-instagram"></i></a>
|
||||
<a href="#" class="text-muted mx-2"><i class="bi bi-youtube"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 text-md-right">
|
||||
<div class="text-center text-md-right">
|
||||
<small class="text-muted">© 2024 - Todos os direitos reservados</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script>
|
||||
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
<script src="~/js/wait.js" asp-append-version="true"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(document).ready(function () {
|
||||
$('.loading').hide();
|
||||
//$('body').fadeIn(1000);
|
||||
$('body').slideDown('slow');
|
||||
$('body').fadeIn(800);
|
||||
});
|
||||
|
||||
$('a[href="#search"]').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
$('#search').addClass('open');
|
||||
$('#search > form > input[type="search"]').focus();
|
||||
inactivateActiveOption();
|
||||
searchActive();
|
||||
});
|
||||
// Animação para links do menu
|
||||
$('.navbar-nav .nav-link').hover(
|
||||
function() { $(this).addClass('pulse'); },
|
||||
function() { $(this).removeClass('pulse'); }
|
||||
);
|
||||
|
||||
$('#search, #search button.close').on('click keyup', function (event) {
|
||||
if (event.target == this || event.target.className == 'close' || event.keyCode == 27) {
|
||||
$(this).removeClass('open');
|
||||
// Ativar o item de menu atual
|
||||
$(document).ready(function () {
|
||||
setActiveByLocation();
|
||||
searchInactive();
|
||||
}
|
||||
});
|
||||
|
||||
// Controle de carregamento de página
|
||||
$(window).on('beforeunload', function () {
|
||||
displayBusyIndicator();
|
||||
});
|
||||
@ -121,20 +192,10 @@
|
||||
displayBusyIndicator();
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#wrapper').fadeIn('slow');
|
||||
$('a.nav-link').click(function () {
|
||||
$('#wrapper').fadeOut('slow');
|
||||
});
|
||||
setActiveByLocation();
|
||||
});
|
||||
|
||||
|
||||
|
||||
function setActiveByLocation() {
|
||||
$('ul.navbar-nav').find('a[href="' + location.pathname + '"]')
|
||||
.closest('li')
|
||||
.addClass("active")
|
||||
.addClass("active");
|
||||
|
||||
$('ul.navbar-nav').find('a[href="' + location.pathname + '"]')
|
||||
.closest('a')
|
||||
@ -142,37 +203,9 @@
|
||||
.addClass('text-black');
|
||||
}
|
||||
|
||||
function searchActive() {
|
||||
$('#searchLi').addClass("active");
|
||||
$('#searchLi > a')
|
||||
.removeClass('text-white')
|
||||
.addClass('text-black');
|
||||
}
|
||||
|
||||
function searchInactive() {
|
||||
$('#searchLi').removeClass("active");
|
||||
$('#searchLi > a')
|
||||
.removeClass('text-black')
|
||||
.addClass('text-white');
|
||||
}
|
||||
|
||||
function inactivateActiveOption() {
|
||||
$('ul.navbar-nav li.active')
|
||||
.closest('li')
|
||||
.removeClass("active")
|
||||
|
||||
$('ul.navbar-nav').find('a[href="' + location.pathname + '"]')
|
||||
.closest('a')
|
||||
.removeClass('text-black')
|
||||
.addClass('text-white');
|
||||
}
|
||||
|
||||
function displayBusyIndicator() {
|
||||
//$('body').fadeOut(1000);
|
||||
$('body').slideUp('slow');
|
||||
$('body').fadeOut(300);
|
||||
}
|
||||
|
||||
//$(".hide-body").fadeOut(2000);
|
||||
});
|
||||
</script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
|
||||
@ -44,14 +44,6 @@ button.accept-policy {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
#search {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
|
||||
273
SumaTube/Views/Video/Extract.cshtml
Normal file
273
SumaTube/Views/Video/Extract.cshtml
Normal file
@ -0,0 +1,273 @@
|
||||
@{
|
||||
ViewData["Title"] = "Extrair Resumo";
|
||||
}
|
||||
|
||||
@section Styles {
|
||||
<style type="text/css">
|
||||
.extract-container {
|
||||
max-width: 800px;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.extract-card {
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-control-lg {
|
||||
font-size: 1.1rem;
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 30px 0 0 30px;
|
||||
}
|
||||
|
||||
.btn-extract {
|
||||
border-radius: 0 30px 30px 0;
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
background-color: var(--suma-beige);
|
||||
border-radius: 50%;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem auto;
|
||||
}
|
||||
|
||||
.feature-icon i {
|
||||
font-size: 2rem;
|
||||
color: var(--suma-red);
|
||||
}
|
||||
|
||||
.step-number {
|
||||
background-color: var(--suma-red);
|
||||
color: white;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
display: none;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
<div class="container extract-container">
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-4 mb-3">Resumir Vídeo do YouTube</h1>
|
||||
<p class="lead">Cole o link de qualquer vídeo do YouTube para obter um resumo inteligente</p>
|
||||
</div>
|
||||
|
||||
<div class="extract-card mb-5">
|
||||
<form id="extractForm">
|
||||
<div class="form-group">
|
||||
<label for="youtubeUrl"><strong>URL do vídeo</strong></label>
|
||||
<div class="input-group input-group-lg">
|
||||
<input type="url" class="form-control form-control-lg" id="youtubeUrl" placeholder="https://www.youtube.com/watch?v=..." required>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-extract" id="extractButton">
|
||||
<i class="bi bi-magic mr-2"></i> Resumir
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">Ex: https://www.youtube.com/watch?v=abcde12345</small>
|
||||
</div>
|
||||
|
||||
<div class="progress-container" id="progressContainer">
|
||||
<p class="text-center mb-2" id="processingStatus">Processando vídeo...</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated bg-danger" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Como funciona -->
|
||||
<div class="mb-5">
|
||||
<h3 class="text-center mb-4">Como funciona</h3>
|
||||
<div class="row">
|
||||
<!-- Passo 1 -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="feature-icon">
|
||||
<i class="bi bi-link-45deg"></i>
|
||||
</div>
|
||||
<h5 class="text-center mb-3">Passo 1</h5>
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="step-number">1</div>
|
||||
<div>
|
||||
Cole o link do vídeo do YouTube que deseja resumir no campo acima.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Passo 2 -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="feature-icon">
|
||||
<i class="bi bi-cpu"></i>
|
||||
</div>
|
||||
<h5 class="text-center mb-3">Passo 2</h5>
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="step-number">2</div>
|
||||
<div>
|
||||
Nossa IA extrai as legendas e processa o conteúdo do vídeo automaticamente.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Passo 3 -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="feature-icon">
|
||||
<i class="bi bi-file-earmark-text"></i>
|
||||
</div>
|
||||
<h5 class="text-center mb-3">Passo 3</h5>
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="step-number">3</div>
|
||||
<div>
|
||||
Receba um resumo completo com os principais pontos, timestamps e transcrição.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recursos -->
|
||||
<div class="row">
|
||||
<div class="col-12 text-center mb-4">
|
||||
<h3>Recursos incríveis do SumaTube</h3>
|
||||
</div>
|
||||
|
||||
<!-- Recurso 1 -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="summary-section d-flex">
|
||||
<div class="mr-4">
|
||||
<i class="bi bi-clock" style="font-size: 2rem; color: var(--suma-red);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Economize tempo</h5>
|
||||
<p>Extraia o conteúdo mais importante de vídeos longos em questão de minutos.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recurso 2 -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="summary-section d-flex">
|
||||
<div class="mr-4">
|
||||
<i class="bi bi-translate" style="font-size: 2rem; color: var(--suma-red);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Suporte a múltiplos idiomas</h5>
|
||||
<p>Resumos disponíveis em português, inglês, espanhol e outros idiomas.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recurso 3 -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="summary-section d-flex">
|
||||
<div class="mr-4">
|
||||
<i class="bi bi-lightning-charge" style="font-size: 2rem; color: var(--suma-red);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Processamento rápido</h5>
|
||||
<p>Nossa tecnologia avançada processa os vídeos em segundos.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recurso 4 -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="summary-section d-flex">
|
||||
<div class="mr-4">
|
||||
<i class="bi bi-download" style="font-size: 2rem; color: var(--suma-red);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Download de resumos</h5>
|
||||
<p>Salve os resumos em PDF para referência futura e compartilhamento.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#extractForm').submit(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Validar URL
|
||||
var youtubeUrl = $('#youtubeUrl').val();
|
||||
if (!isValidYouTubeUrl(youtubeUrl)) {
|
||||
alert('Por favor, insira uma URL válida do YouTube.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar progresso
|
||||
$('#extractButton').prop('disabled', true);
|
||||
$('#progressContainer').show();
|
||||
simulateProgress();
|
||||
|
||||
// Simulação de processamento - em um projeto real, aqui você faria a chamada AJAX para sua WebAPI
|
||||
setTimeout(function() {
|
||||
// Redirecionar para a página de resultado (em um caso real, você redirecionaria após receber a resposta da API)
|
||||
window.location.href = '/Video/Summary?id=' + extractVideoId(youtubeUrl);
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
function isValidYouTubeUrl(url) {
|
||||
var regex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+/;
|
||||
return regex.test(url);
|
||||
}
|
||||
|
||||
function extractVideoId(url) {
|
||||
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
|
||||
var match = url.match(regExp);
|
||||
return (match && match[7].length == 11) ? match[7] : false;
|
||||
}
|
||||
|
||||
function simulateProgress() {
|
||||
var progress = 0;
|
||||
var interval = setInterval(function() {
|
||||
progress += 5;
|
||||
$('#progressBar').css('width', progress + '%');
|
||||
$('#progressBar').attr('aria-valuenow', progress);
|
||||
|
||||
// Atualizar mensagem de status
|
||||
if (progress <= 20) {
|
||||
$('#processingStatus').text('Extraindo informações do vídeo...');
|
||||
} else if (progress <= 40) {
|
||||
$('#processingStatus').text('Processando legendas...');
|
||||
} else if (progress <= 60) {
|
||||
$('#processingStatus').text('Identificando pontos principais...');
|
||||
} else if (progress <= 80) {
|
||||
$('#processingStatus').text('Gerando resumo...');
|
||||
} else {
|
||||
$('#processingStatus').text('Finalizando...');
|
||||
}
|
||||
|
||||
if (progress >= 100) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
83
SumaTube/Views/Video/MySummary.cshtml
Normal file
83
SumaTube/Views/Video/MySummary.cshtml
Normal file
@ -0,0 +1,83 @@
|
||||
@using SumaTube.Domain.Entities.Videos
|
||||
@model List<VideoSummary>
|
||||
@{
|
||||
ViewData["Title"] = "Meus Resumos";
|
||||
}
|
||||
|
||||
<div class="container my-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Meus Resumos</h1>
|
||||
<a href="@Url.Action("Extract", "Video")" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Novo Resumo
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if (Model == null || !Model.Any())
|
||||
{
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-file-earmark-text" style="font-size: 3rem; color: var(--suma-red);"></i>
|
||||
<h3 class="mt-3">Você ainda não possui resumos</h3>
|
||||
<p class="text-muted">Clique em "Novo Resumo" para extrair o conteúdo de um vídeo do YouTube</p>
|
||||
<a href="@Url.Action("Extract", "Video")" class="btn btn-primary mt-3">
|
||||
<i class="bi bi-plus-circle"></i> Criar Meu Primeiro Resumo
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
@foreach (var summary in Model)
|
||||
{
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="position-relative">
|
||||
<img src="@summary.ThumbnailUrl" class="card-img-top" alt="@summary.Title">
|
||||
@if (summary.Status == "PROCESSANDO")
|
||||
{
|
||||
<div class="position-absolute top-0 end-0 p-2 bg-warning text-dark rounded-bottom-left">
|
||||
<i class="bi bi-hourglass-split"></i> Processando
|
||||
</div>
|
||||
}
|
||||
else if (summary.Status == "REALIZADO")
|
||||
{
|
||||
<div class="position-absolute top-0 end-0 p-2 bg-success text-white rounded-bottom-left">
|
||||
<i class="bi bi-check-circle"></i> Concluído
|
||||
</div>
|
||||
}
|
||||
else if (summary.Status == "ERRO")
|
||||
{
|
||||
<div class="position-absolute top-0 end-0 p-2 bg-danger text-white rounded-bottom-left">
|
||||
<i class="bi bi-exclamation-triangle"></i> Erro
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-truncate" title="@summary.Title">@summary.Title</h5>
|
||||
<p class="card-text text-muted small">
|
||||
<i class="bi bi-calendar"></i> @summary.RequestDate.ToString("dd/MM/yyyy HH:mm")
|
||||
<br>
|
||||
<i class="bi bi-translate"></i> @summary.Language
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer bg-white border-top-0">
|
||||
<a href="@Url.Action("Summary", "Video", new { id = summary.Id })" class="btn btn-sm btn-outline-primary w-100">
|
||||
@if (summary.Status == "PROCESSANDO")
|
||||
{
|
||||
<span>Ver Progresso</span>
|
||||
}
|
||||
else if (summary.Status == "REALIZADO")
|
||||
{
|
||||
<span>Ver Resumo</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Ver Detalhes</span>
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
260
SumaTube/Views/Video/VideoSummary.cshtml
Normal file
260
SumaTube/Views/Video/VideoSummary.cshtml
Normal file
@ -0,0 +1,260 @@
|
||||
@using SumaTube.Models;
|
||||
|
||||
@model VideoSummaryViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Resumo do Vídeo";
|
||||
}
|
||||
|
||||
@section Styles {
|
||||
<style type="text/css">
|
||||
.timestamp-link {
|
||||
color: var(--suma-red);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.timestamp-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.summary-section {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tag-pill {
|
||||
background-color: var(--suma-beige);
|
||||
border-radius: 20px;
|
||||
padding: 5px 15px;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
display: inline-block;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.summary-tab-content {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.video-frame {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.transcription-text {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
background-color: var(--suma-light-gray);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<!-- Coluna do vídeo -->
|
||||
<div class="col-lg-8">
|
||||
<div class="video-frame mb-4">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<iframe class="embed-responsive-item" src="https://www.youtube.com/embed/@Model.VideoId" allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-section">
|
||||
<h1>@Model.VideoTitle</h1>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="@Model.ChannelThumbnail" alt="@Model.ChannelName" class="rounded-circle mr-2" style="width: 40px; height: 40px;">
|
||||
<div>
|
||||
<h6 class="mb-0">@Model.ChannelName</h6>
|
||||
<small class="text-muted">@Model.PublishedDate • @Model.ViewCount visualizações</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-3 mb-3">
|
||||
<div>
|
||||
<span class="mr-3">
|
||||
<i class="bi bi-hand-thumbs-up"></i> @Model.LikeCount
|
||||
</span>
|
||||
<span>
|
||||
<i class="bi bi-chat-left-text"></i> @Model.CommentCount
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-dark mr-2">
|
||||
<i class="bi bi-share"></i> Compartilhar
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-dark">
|
||||
<i class="bi bi-bookmark"></i> Salvar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs de navegação -->
|
||||
<ul class="nav nav-tabs" id="summaryTabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="summary-tab" data-toggle="tab" href="#summary" role="tab" aria-controls="summary" aria-selected="true">
|
||||
<i class="bi bi-journal-text"></i> Resumo
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="transcription-tab" data-toggle="tab" href="#transcription" role="tab" aria-controls="transcription" aria-selected="false">
|
||||
<i class="bi bi-body-text"></i> Transcrição
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="keywords-tab" data-toggle="tab" href="#keywords" role="tab" aria-controls="keywords" aria-selected="false">
|
||||
<i class="bi bi-tags"></i> Palavras-chave
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Conteúdo das tabs -->
|
||||
<div class="tab-content summary-tab-content p-3 bg-white border border-top-0 rounded-bottom mb-4" id="summaryTabsContent">
|
||||
<!-- Tab Resumo -->
|
||||
<div class="tab-pane fade show active" id="summary" role="tabpanel" aria-labelledby="summary-tab">
|
||||
<h4 class="mb-3">Principais pontos</h4>
|
||||
|
||||
@foreach (var point in Model.KeyPoints)
|
||||
{
|
||||
<div class="mb-3">
|
||||
<p>
|
||||
@* <a class="timestamp-link" data-time="@point.TimestampSeconds">@point.TimestampFormatted</a>
|
||||
<strong>@point.Title</strong>
|
||||
</p>
|
||||
<p>@point.Description</p>
|
||||
|
||||
*@
|
||||
<a class="timestamp-link" data-time="30">30s</a>
|
||||
<strong>Titulo</strong>
|
||||
</p>
|
||||
<p>Description</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<h4 class="mt-4 mb-3">Resumo geral</h4>
|
||||
<p>@Model.SummaryText</p>
|
||||
</div>
|
||||
|
||||
<!-- Tab Transcrição -->
|
||||
<div class="tab-pane fade" id="transcription" role="tabpanel" aria-labelledby="transcription-tab">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h4 class="mb-3">Transcrição completa</h4>
|
||||
<button class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-download"></i> Baixar
|
||||
</button>
|
||||
</div>
|
||||
<div class="transcription-text">
|
||||
@foreach (var caption in Model.Captions)
|
||||
{
|
||||
<p>
|
||||
@*
|
||||
<a class="timestamp-link" data-time="@caption.TimestampSeconds">
|
||||
@caption.TimestampFormatted
|
||||
</a>
|
||||
@caption.Text
|
||||
|
||||
*@
|
||||
<a class="timestamp-link" data-time="30s">
|
||||
30s
|
||||
</a>
|
||||
caption.Text
|
||||
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Palavras-chave -->
|
||||
<div class="tab-pane fade" id="keywords" role="tabpanel" aria-labelledby="keywords-tab">
|
||||
<h4 class="mb-3">Tópicos principais</h4>
|
||||
<div class="mb-4">
|
||||
@foreach (var keyword in Model.Keywords)
|
||||
{
|
||||
<span class="tag-pill">@keyword</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<h4 class="mb-3">Temas relacionados</h4>
|
||||
<div>
|
||||
@foreach (var topic in Model.RelatedTopics)
|
||||
{
|
||||
<span class="tag-pill">@topic</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coluna lateral -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Vídeos recomendados -->
|
||||
<div class="summary-section">
|
||||
<h5 class="mb-3">Vídeos relacionados</h5>
|
||||
|
||||
@foreach (var video in Model.RelatedVideos)
|
||||
{
|
||||
<div class="media mb-3">
|
||||
@*
|
||||
<img src="@video.ThumbnailUrl" class="mr-3" alt="@video.Title" style="width: 120px; border-radius: 4px;">
|
||||
<div class="media-body">
|
||||
<h6 class="mt-0">@video.Title</h6>
|
||||
<small class="text-muted">@video.ChannelName</small><br>
|
||||
<small class="text-muted">@video.ViewCount visualizações</small>
|
||||
</div>
|
||||
*@
|
||||
<img src="#ThumbnailUrl" class="mr-3" alt="#Title" style="width: 120px; border-radius: 4px;">
|
||||
<div class="media-body">
|
||||
<h6 class="mt-0">Title</h6>
|
||||
<small class="text-muted">video.ChannelName</small><br>
|
||||
<small class="text-muted">x visualizações</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Call to action para planos premium -->
|
||||
<div class="summary-section text-center" style="background-color: var(--suma-beige);">
|
||||
<i class="bi bi-star-fill mb-3" style="font-size: 2rem; color: var(--suma-red);"></i>
|
||||
<h5>Obtenha mais recursos com o plano Premium</h5>
|
||||
<p class="small mb-3">Acesso ilimitado a resumos, download de textos e muito mais.</p>
|
||||
<a href="#" class="btn btn-primary btn-block">Ver planos</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
// Função para clicar nos timestamps e pular para o momento do vídeo
|
||||
$('.timestamp-link').click(function() {
|
||||
var time = $(this).data('time');
|
||||
var videoFrame = $('iframe')[0];
|
||||
var videoUrl = videoFrame.src;
|
||||
|
||||
// Garantir que a URL tem os parâmetros necessários
|
||||
if (videoUrl.indexOf('?') === -1) {
|
||||
videoUrl += '?';
|
||||
} else {
|
||||
videoUrl += '&';
|
||||
}
|
||||
|
||||
// Adicionar comando para pular para o tempo específico
|
||||
videoUrl += 'start=' + time;
|
||||
|
||||
// Atualizar o iframe
|
||||
videoFrame.src = videoUrl;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
255
SumaTube/wwwroot/css/custom.css
Normal file
255
SumaTube/wwwroot/css/custom.css
Normal file
@ -0,0 +1,255 @@
|
||||
/* Cores principais do SumaTube */
|
||||
:root {
|
||||
--suma-red: #cc0000;
|
||||
--suma-light-red: #e60000;
|
||||
--suma-dark-red: #990000;
|
||||
--suma-beige: #f5f5dc;
|
||||
--suma-light-gray: #f8f8f8;
|
||||
--suma-gray: #e0e0e0;
|
||||
--suma-dark-gray: #606060;
|
||||
--suma-black: #212121;
|
||||
}
|
||||
|
||||
/* Estilos Gerais */
|
||||
html, body {
|
||||
height: 100%;
|
||||
font-family: 'Roboto', 'Segoe UI', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--suma-light-gray);
|
||||
color: var(--suma-black);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.hide-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Barra de Navegação */
|
||||
#nav-bar {
|
||||
background-color: var(--suma-red) !important;
|
||||
padding: 0.5rem 1rem;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-item {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-item.active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link {
|
||||
padding: 8px 16px !important;
|
||||
border-radius: 3px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link.text-black {
|
||||
color: white !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Conteúdo principal */
|
||||
main {
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px 15px;
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
flex-shrink: 0;
|
||||
background-color: var(--suma-beige);
|
||||
color: var(--suma-dark-gray);
|
||||
padding: 20px 0;
|
||||
width: 100%;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* Login container */
|
||||
.login-container {
|
||||
max-width: 500px;
|
||||
margin: 60px auto;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Seção de hero banner */
|
||||
.hero-banner {
|
||||
background: linear-gradient(135deg, var(--suma-red), var(--suma-dark-red));
|
||||
color: white;
|
||||
padding: 2.5rem 0;
|
||||
margin-bottom: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-banner::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 60%;
|
||||
height: 200%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: rotate(-15deg);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-banner h1 {
|
||||
font-weight: 700;
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 1rem;
|
||||
text-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.hero-banner .lead {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 1.5rem;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Botões */
|
||||
.btn-primary, .btn-red {
|
||||
background-color: var(--suma-red) !important;
|
||||
border-color: var(--suma-dark-red) !important;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .btn-red:hover {
|
||||
background-color: var(--suma-light-red) !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-hero {
|
||||
background-color: white;
|
||||
color: var(--suma-red);
|
||||
border: none;
|
||||
padding: 0.8rem 1.5rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-hero:hover {
|
||||
transform: translateY(-2px);
|
||||
background-color: #f8f8f8;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
|
||||
color: var(--suma-dark-red);
|
||||
}
|
||||
|
||||
/* Responsividade */
|
||||
@media (max-width: 768px) {
|
||||
.hero-banner {
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-banner h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.btn-hero {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animações */
|
||||
.pulse {
|
||||
animation: pulse 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Efeito de loading */
|
||||
.loading {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
overflow: show;
|
||||
margin: auto;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.loading:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(245, 245, 220, 0.7);
|
||||
}
|
||||
|
||||
.loading:not(:required):after {
|
||||
content: '';
|
||||
display: block;
|
||||
font-size: 10px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-top: -0.5em;
|
||||
animation: spinner 1500ms infinite linear;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: rgba(204, 0, 0, 0.75) 1.5em 0 0 0, rgba(204, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(204, 0, 0, 0.75) 0 1.5em 0 0, rgba(204, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(204, 0, 0, 0.75) -1.5em 0 0 0, rgba(204, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(204, 0, 0, 0.75) 0 -1.5em 0 0, rgba(204, 0, 0, 0.75) 1.1em -1.1em 0 0;
|
||||
}
|
||||
|
||||
/* Animação do spinner */
|
||||
@keyframes spinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
BIN
SumaTube/wwwroot/img/video-summary-demo.png
Normal file
BIN
SumaTube/wwwroot/img/video-summary-demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Loading…
Reference in New Issue
Block a user