Compare commits
No commits in common. "dd7dd54ca29441544560ead8ee73b3b4e84dd375" and "242b4596ff637e6d5f1a7fcbd730f20303102836" have entirely different histories.
dd7dd54ca2
...
242b4596ff
25
YTExtractor.sln
Normal file
25
YTExtractor.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.11.35327.3
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YTExtractor", "YTExtractor\YTExtractor.csproj", "{7DA7D783-153F-42EF-87E4-239DEC80F91A}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7DA7D783-153F-42EF-87E4-239DEC80F91A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7DA7D783-153F-42EF-87E4-239DEC80F91A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7DA7D783-153F-42EF-87E4-239DEC80F91A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7DA7D783-153F-42EF-87E4-239DEC80F91A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {0A995D46-30D9-42A9-BA92-225340DFC214}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
47
YTExtractor/Data/MongoDbConnector.cs
Normal file
47
YTExtractor/Data/MongoDbConnector.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace YTExtractor.Data
|
||||||
|
{
|
||||||
|
public class MongoDBConnector
|
||||||
|
{
|
||||||
|
private readonly IMongoDatabase _database;
|
||||||
|
private readonly IMongoCollection<VideoData> _collection;
|
||||||
|
|
||||||
|
public MongoDBConnector(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var connectionString = configuration.GetSection("MongoDbConnaction").Value;
|
||||||
|
var client = new MongoClient(connectionString);
|
||||||
|
_database = client.GetDatabase("YTExtractor");
|
||||||
|
_collection = _database.GetCollection<VideoData>("videos");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InsertVideo(VideoData video)
|
||||||
|
{
|
||||||
|
await _collection.InsertOneAsync(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<VideoData> GetVideoById(string id)
|
||||||
|
{
|
||||||
|
return await _collection.Find(x => x.Id == id).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<VideoData>> GetAllVideos()
|
||||||
|
{
|
||||||
|
return await _collection.Find(_ => true).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateVideo(VideoData video)
|
||||||
|
{
|
||||||
|
await _collection.ReplaceOneAsync(x => x.Id == video.Id, video);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteVideo(string id)
|
||||||
|
{
|
||||||
|
await _collection.DeleteOneAsync(x => x.Id == id);
|
||||||
|
}
|
||||||
|
public async Task<VideoData> GetVideoByUrl(string url)
|
||||||
|
{
|
||||||
|
return await _collection.Find(x => x.Url == url).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
YTExtractor/Data/VideoData.cs
Normal file
17
YTExtractor/Data/VideoData.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace YTExtractor.Data
|
||||||
|
{
|
||||||
|
public class VideoData
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.String)]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string Titulo { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string ThumbnailUrl { get; set; }
|
||||||
|
public string TranscText { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
YTExtractor/Dockerfile
Normal file
19
YTExtractor/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
RUN dotnet restore
|
||||||
|
RUN dotnet publish -c Release -o /app
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app .
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y python3-pip && \
|
||||||
|
pip3 install yt-dlp && \
|
||||||
|
mkdir /app/temp && \
|
||||||
|
chmod 777 /app/temp && \
|
||||||
|
which yt-dlp || (echo "yt-dlp not found" && exit 1)
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
ENTRYPOINT ["dotnet", "YTExtractor.dll"]
|
||||||
87
YTExtractor/Program.cs
Normal file
87
YTExtractor/Program.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using YTExtractor;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Sinks.InfluxDB;
|
||||||
|
using YTExtractor.Data;
|
||||||
|
|
||||||
|
// App configuration and endpoints
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
var environment = builder.Environment.EnvironmentName;
|
||||||
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.WriteTo.InfluxDB(
|
||||||
|
address: "http://192.168.0.76",
|
||||||
|
dbName: "telegraf",
|
||||||
|
source: "YTExtractor-{environment}")
|
||||||
|
.Enrich.WithProperty("Environment", environment)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
builder.Services.AddSingleton<MongoDBConnector>();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
|
||||||
|
app.MapPost("/api/video-info", async (VideoRequest request, MongoDBConnector mongo) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!YoutubeService.IsValidYouTubeUrl(request.Url))
|
||||||
|
return Results.BadRequest("Invalid YouTube URL");
|
||||||
|
|
||||||
|
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||||
|
Directory.CreateDirectory(tempDir);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Information($"Obtendo dados do video: {request.Url}");
|
||||||
|
|
||||||
|
var videoExists = await mongo.GetVideoByUrl(request.Url);
|
||||||
|
if (videoExists != null)
|
||||||
|
{
|
||||||
|
return Results.Ok(new VideoInfo(
|
||||||
|
videoExists.Url,
|
||||||
|
videoExists.Titulo,
|
||||||
|
videoExists.ThumbnailUrl,
|
||||||
|
videoExists.TranscText
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = await YoutubeService.GetVideoInfo(request.Url, tempDir);
|
||||||
|
var subtitles = await YoutubeService.GetSubtitles(request.Url, request.Language, tempDir);
|
||||||
|
|
||||||
|
await mongo.InsertVideo(new VideoData
|
||||||
|
{
|
||||||
|
Url = request.Url,
|
||||||
|
Titulo = info.Title,
|
||||||
|
ThumbnailUrl = info.ThumbnailUrl,
|
||||||
|
TranscText = subtitles
|
||||||
|
});
|
||||||
|
|
||||||
|
Log.Information($"Dados de video obtidos: {request.Url}");
|
||||||
|
return Results.Ok(new VideoInfo(
|
||||||
|
request.Url,
|
||||||
|
info.Title,
|
||||||
|
info.ThumbnailUrl,
|
||||||
|
subtitles
|
||||||
|
));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (Directory.Exists(tempDir))
|
||||||
|
Directory.Delete(tempDir, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Failed to get video info");
|
||||||
|
return Results.Problem(ex.Message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.WithName("GetVideoInfo")
|
||||||
|
.WithOpenApi();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
41
YTExtractor/Properties/launchSettings.json
Normal file
41
YTExtractor/Properties/launchSettings.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:49221",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:5102",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "https://localhost:7082;http://localhost:5102",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
YTExtractor/VideoInfo.cs
Normal file
6
YTExtractor/VideoInfo.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YTExtractor
|
||||||
|
{
|
||||||
|
public record VideoInfo(string Url, string Title, string ThumbnailUrl, string Subtitles);
|
||||||
|
}
|
||||||
4
YTExtractor/VideoRequest.cs
Normal file
4
YTExtractor/VideoRequest.cs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
namespace YTExtractor
|
||||||
|
{
|
||||||
|
public record VideoRequest(string Url, string Language);
|
||||||
|
}
|
||||||
24
YTExtractor/YTExtractor.csproj
Normal file
24
YTExtractor/YTExtractor.csproj
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.12" />
|
||||||
|
<PackageReference Include="MongoDB.Bson" Version="3.1.0" />
|
||||||
|
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
|
||||||
|
<PackageReference Include="Serilog" Version="4.2.1-dev-02337" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.InfluxDB.DotNetCore" Version="1.0.2" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="yt-dlp.exe">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
YTExtractor/YTExtractor.csproj.user
Normal file
6
YTExtractor/YTExtractor.csproj.user
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ActiveDebugProfile>https</ActiveDebugProfile>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
6
YTExtractor/YTExtractor.http
Normal file
6
YTExtractor/YTExtractor.http
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@YTExtractor_HostAddress = http://localhost:5102
|
||||||
|
|
||||||
|
GET {{YTExtractor_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
76
YTExtractor/YoutubeService.cs
Normal file
76
YTExtractor/YoutubeService.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using Microsoft.Extensions.Hosting.Internal;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace YTExtractor
|
||||||
|
{
|
||||||
|
public class YoutubeService
|
||||||
|
{
|
||||||
|
public static bool IsValidYouTubeUrl(string urlx)
|
||||||
|
{
|
||||||
|
return Regex.IsMatch(urlx, @"^(https?\:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<YtDlpInfo> GetVideoInfo(string url, string workingDir)
|
||||||
|
{
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "yt-dlp",
|
||||||
|
Arguments = $"--dump-json {url}",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
WorkingDirectory = workingDir
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = Process.Start(startInfo);
|
||||||
|
var output = await process.StandardOutput.ReadToEndAsync();
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
throw new Exception("Failed to get video info");
|
||||||
|
|
||||||
|
var jsonDoc = JsonDocument.Parse(output);
|
||||||
|
var root = jsonDoc.RootElement;
|
||||||
|
|
||||||
|
return new YtDlpInfo(
|
||||||
|
root.GetProperty("title").GetString() ?? "",
|
||||||
|
root.GetProperty("thumbnail").GetString() ?? ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> GetSubtitles(string url, string language, string workingDir)
|
||||||
|
{
|
||||||
|
var pathExe = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
|
var exePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||||
|
? Path.Combine(pathExe, "yt-dlp.exe")
|
||||||
|
: "yt-dlp";
|
||||||
|
|
||||||
|
//var wokingExe = Path.Combine(pathExe, "yt-dlp.exe");
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = exePath,
|
||||||
|
Arguments = $"--write-sub --write-auto-sub --sub-lang {language} --skip-download {url}",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
WorkingDirectory = workingDir
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = Process.Start(startInfo);
|
||||||
|
var output = await process.StandardOutput.ReadToEndAsync();
|
||||||
|
var error = await process.StandardError.ReadToEndAsync();
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
var subtitleFile = Directory.GetFiles(workingDir, "*.vtt").FirstOrDefault();
|
||||||
|
if (subtitleFile == null)
|
||||||
|
throw new Exception("No subtitles found");
|
||||||
|
|
||||||
|
return await File.ReadAllTextAsync(subtitleFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
YTExtractor/YtDlpInfo.cs
Normal file
6
YTExtractor/YtDlpInfo.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YTExtractor
|
||||||
|
{
|
||||||
|
public record YtDlpInfo(string Title, string ThumbnailUrl);
|
||||||
|
}
|
||||||
8
YTExtractor/appsettings.Development.json
Normal file
8
YTExtractor/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
YTExtractor/appsettings.json
Normal file
10
YTExtractor/appsettings.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"MongoDbConnaction": "mongodb://admin:c4rn31r0@192.168.0.82:27017,192.168.0.81:27017/?replicaSet=rs0"
|
||||||
|
}
|
||||||
BIN
YTExtractor/yt-dlp.exe
Normal file
BIN
YTExtractor/yt-dlp.exe
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user