- Remove projetos mortos: VideoStudy.Native (MAUI), Controllers/LicenseController - Remove serviços não usados: FFmpegService, HardwareIdService, LicenseManager, PdfGeneratorService, ScreenshotService, TranscriptionService do UI - Remove dependências pesadas do UI: Whisper.net, YoutubeExplode, QuestPDF - Remove PuppeteerSharp e SkiaSharp do API (150MB Chromium não é mais necessário) - Screenshots agora usam FFmpeg diretamente (mais simples, mais confiável) - YouTubeService reescrito para chamar /api/video-info em vez de YoutubeExplode - Adiciona campo UserContext em AnalysisRequest (contexto livre do usuário) - UI traduzida para português; aba de arquivo local removida (nunca funcionou) - YouTubeProcessor simplificado: sem modo Advanced/Whisper - GetYtDlpPath busca yt-dlp.exe subindo até 7 níveis de diretório - Cookies do yt-dlp configuráveis via YtDlp:CookiesBrowser no appsettings - Chave Groq agora lida de env var GROQ_API_KEY (appsettings.json sem segredos) - VideoStudy.Linux (Photino) adicionado à solução como host multiplataforma - yt-dlp atualizado de 2025.01.26 para 2026.03.17 (fix do nsig extraction) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
150 lines
4.7 KiB
Plaintext
150 lines
4.7 KiB
Plaintext
@page "/library"
|
|
@inject PersistenceService PersistenceService
|
|
@using VideoStudy.Shared
|
|
@using System.Diagnostics
|
|
|
|
<div class="container-fluid py-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2 class="fw-bold text-dark">📚 Minha Biblioteca</h2>
|
|
@if (!string.IsNullOrEmpty(currentFolder))
|
|
{
|
|
<button class="btn btn-outline-secondary" @onclick="GoBack">
|
|
⬅️ Voltar
|
|
</button>
|
|
}
|
|
</div>
|
|
|
|
@if (string.IsNullOrEmpty(currentFolder))
|
|
{
|
|
<!-- MODO PASTAS -->
|
|
<div class="row g-3">
|
|
<!-- Pasta Geral (Padrão) -->
|
|
<div class="col-md-3">
|
|
<div class="card h-100 shadow-sm folder-card" @onclick='() => OpenFolder("Geral")'>
|
|
<div class="card-body text-center p-4">
|
|
<div class="display-1 mb-2">📁</div>
|
|
<h5 class="fw-bold">Geral</h5>
|
|
<small class="text-muted">@GetCount("Geral") arquivos</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Outras Pastas do Disco -->
|
|
@foreach (var folder in folders)
|
|
{
|
|
if (folder == "Geral") continue;
|
|
<div class="col-md-3">
|
|
<div class="card h-100 shadow-sm folder-card" @onclick='() => OpenFolder(folder)'>
|
|
<div class="card-body text-center p-4">
|
|
<div class="display-1 mb-2">📁</div>
|
|
<h5 class="fw-bold">@folder</h5>
|
|
<small class="text-muted">@GetCount(folder) arquivos</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<!-- MODO ARQUIVOS (DENTRO DA PASTA) -->
|
|
<h5 class="mb-3 text-muted">Pasta: <strong>@currentFolder</strong></h5>
|
|
|
|
<div class="list-group shadow-sm">
|
|
@foreach (var session in currentSessions)
|
|
{
|
|
<div class="list-group-item list-group-item-action d-flex align-items-center p-3">
|
|
<div class="me-3 fs-1">📄</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-1 fw-bold">@session.Title</h6>
|
|
<small class="text-muted">
|
|
📅 @session.CreatedAt.ToString("g") •
|
|
<span class="text-truncate d-inline-block" style="max-width: 300px;">@session.FilePath</span>
|
|
</small>
|
|
</div>
|
|
<button class="btn btn-primary btn-sm px-4 fw-bold" @onclick="() => OpenPdf(session.FilePath)">
|
|
Abrir PDF ↗️
|
|
</button>
|
|
</div>
|
|
}
|
|
|
|
@if (!currentSessions.Any())
|
|
{
|
|
<div class="text-center p-5 text-muted">
|
|
<h5>Esta pasta está vazia 🕸️</h5>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<style>
|
|
.folder-card {
|
|
cursor: pointer;
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
border: none;
|
|
background: #f8f9fa;
|
|
}
|
|
.folder-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 10px 20px rgba(0,0,0,0.1) !important;
|
|
background: #fff;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
</style>
|
|
|
|
@code {
|
|
private List<string> folders = new();
|
|
private List<VideoStudySession> allSessions = new();
|
|
private List<VideoStudySession> currentSessions = new();
|
|
private string? currentFolder = null;
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
LoadData();
|
|
}
|
|
|
|
private void LoadData()
|
|
{
|
|
// Ler estrutura física e banco
|
|
folders = PersistenceService.GetFolders();
|
|
if (!folders.Contains("Geral")) folders.Insert(0, "Geral"); // Garantir Geral
|
|
|
|
allSessions = PersistenceService.GetAllSessions();
|
|
}
|
|
|
|
private int GetCount(string folder)
|
|
{
|
|
return allSessions.Count(s => s.FolderName == folder);
|
|
}
|
|
|
|
private void OpenFolder(string folderName)
|
|
{
|
|
currentFolder = folderName;
|
|
currentSessions = allSessions.Where(s => s.FolderName == folderName).ToList();
|
|
}
|
|
|
|
private void GoBack()
|
|
{
|
|
currentFolder = null;
|
|
}
|
|
|
|
private void OpenPdf(string path)
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
new Process
|
|
{
|
|
StartInfo = new ProcessStartInfo(path) { UseShellExecute = true }
|
|
}.Start();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Tratamento simples, idealmente mostraria um toast
|
|
}
|
|
}
|
|
}
|