Compare commits

..

No commits in common. "5695b2287ca23c5193b9176978f8813d87747977" and "0443cc652758caec6a3a24d21a0db0bcb3c2e21e" have entirely different histories.

20 changed files with 193 additions and 198 deletions

29
.gitignore vendored
View File

@ -1,23 +1,6 @@
# Build results bin/
[Bb]in/ obj/
.bin/ /packages/
[Dd]ebug/ riderModule.iml
[Rr]elease/ /_ReSharper.Caches/
[Rr]eleases/ .idea/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
nuget/
# IDE files
.vs/
.vscode/
.vshistory/
.idea/
.editorconfig
*.user
*.DotSettings
#backups
.old*/

9
.gitmodules vendored Normal file
View File

@ -0,0 +1,9 @@
[submodule "DTLib"]
path = DTLib
url = https://github.com/Timerix22/DTLib.git
[submodule "vknet"]
path = vknet
url = https://github.com/Timerix22/vknet
[submodule "VkNet.AudioBypass"]
path = VkNet.AudioBypass
url = https://github.com/Timerix22/VkNet.AudioBypass

1
DTLib Submodule

@ -0,0 +1 @@
Subproject commit 3ebb5be5819fa46d36705a5752aecd885c406a8b

View File

@ -1,20 +1,14 @@
using System; using System;
using System.Linq; using System.Linq;
using DTLib.Dtsod; using DTLib.Dtsod;
using DTLib.Console; using System.IO;
using DTLib.Extensions;
using DTLib.Filesystem;
using DTLib.Logging;
using VkAudioDownloader; using VkAudioDownloader;
using DTLib.Logging.New;
Console.InputEncoding = StringConverter.UTF8; using VkAudioDownloader.VkM3U8;
Console.OutputEncoding = StringConverter.UTF8;
LaunchArgumentParser argParser = new LaunchArgumentParser();
if(!File.Exists("config.dtsod")) if(!File.Exists("config.dtsod"))
{ {
File.Copy("config.dtsod.default", "config.dtsod", true); File.Copy("config.dtsod.default", "config.dtsod");
throw new Exception("No config detected, default created. Edit it!"); throw new Exception("No config detected, default created. Edit it!");
} }
@ -22,48 +16,37 @@ var config = VkClientConfig.FromDtsod(new DtsodV23(File.ReadAllText("config.dtso
var logger = new CompositeLogger(new DefaultLogFormat(true), var logger = new CompositeLogger(new DefaultLogFormat(true),
new ConsoleLogger(), new ConsoleLogger(),
new FileLogger("logs", "VkAudioDownloaer"), new FileLogger("logs", "VkAudioDownloaer"));
new FileLogger("logs", "VkAudioDownloaer_debug") { DebugLogEnabled = true}); var _logger = new LoggerContext(logger, "main");
var mainLoggerContext = new ContextLogger("Main", logger);
mainLoggerContext.LogDebug("DEBUG LOG ENABLED");
try try
{ {
#if DEBUG #if DEBUG
// checking correctness of my aes-128 decryptor on current platform AudioAesDecryptor.TestAes();
VkAudioDownloader.Helpers.AudioAesDecryptor.TestAes();
#endif #endif
mainLoggerContext.LogInfo("initializing api...");
var client = new VkClient(config, logger); var client = new VkClient(config, logger);
_logger.LogDebug("initializing api...");
await client.ConnectAsync(); await client.ConnectAsync();
argParser.Add(new LaunchArgument(new []{"s", "search"}, "search audio on vk.com", SearchAudio, "query")); // getting audio from vk
argParser.ParseAndHandle(args); var audios = client.FindAudio("сталинский костюм").ToArray();
void SearchAudio(string query) for (var i = 0; i < audios.Length; i++)
{ {
var audios = client.FindAudio(query).ToArray(); var a = audios[i];
for (var i = 0; i < audios.Length; i++) Console.WriteLine($"[{i}] {a.AudioToString()}");
{
var a = audios[i];
Console.WriteLine($"[{i}] {a.AudioToString()}");
}
Console.Write("choose audio: ");
int ain = Convert.ToInt32(Console.ReadLine());
var audio = audios[ain];
Console.WriteLine($"selected {audio.AudioToString()}");
IOPath downloadedFile = client.DownloadAudioAsync(audio, "downloads").GetAwaiter().GetResult();
mainLoggerContext.LogInfo($"audio {audio.AudioToString()} downloaded to {downloadedFile}");
} }
}
catch (LaunchArgumentParser.ExitAfterHelpException) Console.Write("choose audio: ");
{ int ain = Convert.ToInt32(Console.ReadLine());
var audio = audios[ain];
Console.WriteLine($"selected \"{audio.Title}\" -- {audio.Artist} [{TimeSpan.FromSeconds(audio.Duration)}]");
// downloading parts
string downloadedFile = await client.DownloadAudioAsync(audio, "downloads");
_logger.LogInfo($"audio {audio.AudioToString()} downloaded to {downloadedFile}");
} }
catch (Exception ex) catch (Exception ex)
{ {
mainLoggerContext.LogError(ex); _logger.LogException(ex);
} }
Console.ResetColor();

View File

@ -1,4 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
@ -8,12 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\VkAudioDownloader\VkAudioDownloader.csproj" /> <ProjectReference Include="..\VkAudioDownloader\VkAudioDownloader.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="config.dtsod.default">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

View File

@ -4,13 +4,26 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkAudioDownloader", "VkAudi
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkAudioDownloader.CLI", "VkAudioDownloader.CLI\VkAudioDownloader.CLI.csproj", "{5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkAudioDownloader.CLI", "VkAudioDownloader.CLI\VkAudioDownloader.CLI.csproj", "{5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib", "DTLib\DTLib\DTLib.csproj", "{5A02279F-F246-4101-BAA7-71EC5FAF2CAF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Dtsod", "DTLib\DTLib.Dtsod\DTLib.Dtsod.csproj", "{F6FA6507-8A20-43F8-9D88-4CB2F049780D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkNet", "vknet\VkNet\VkNet.csproj", "{123A7020-4C9B-4F7D-A27A-3002A7B21D4C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkNet.Generators", "vknet\VkNet.Generators\VkNet.Generators.csproj", "{BD698045-3408-40C1-AEEF-865219710EE2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkNet.AudioBypassService", "VkNet.AudioBypass\VkNet.AudioBypassService\VkNet.AudioBypassService.csproj", "{5650A6DD-602D-49FF-A0B0-DB59BD63EACE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sulution_items", "sulution_items", "{2CBCAE99-53A3-4ADE-A08B-5755EC471878}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sulution_items", "sulution_items", "{2CBCAE99-53A3-4ADE-A08B-5755EC471878}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore .gitignore = .gitignore
pack.sh = pack.sh .gitmodules = .gitmodules
push_packages.sh = push_packages.sh
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Logging", "DTLib\DTLib.Logging\DTLib.Logging.csproj", "{A087B535-371A-4A7E-883E-B5B290567E9A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ben.Demystifier", "DTLib\DTLib.Logging\Ben.Demystifier\src\Ben.Demystifier\Ben.Demystifier.csproj", "{42AEDE44-8CE6-4561-B83E-AAE715B9C1DE}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -25,5 +38,33 @@ Global
{5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}.Debug|Any CPU.Build.0 = Debug|Any CPU {5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}.Release|Any CPU.Build.0 = Release|Any CPU {5ECC83C5-B53F-4CA7-ABDE-E2ACE8F48FED}.Release|Any CPU.Build.0 = Release|Any CPU
{5A02279F-F246-4101-BAA7-71EC5FAF2CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A02279F-F246-4101-BAA7-71EC5FAF2CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5A02279F-F246-4101-BAA7-71EC5FAF2CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A02279F-F246-4101-BAA7-71EC5FAF2CAF}.Release|Any CPU.Build.0 = Release|Any CPU
{F6FA6507-8A20-43F8-9D88-4CB2F049780D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6FA6507-8A20-43F8-9D88-4CB2F049780D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6FA6507-8A20-43F8-9D88-4CB2F049780D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6FA6507-8A20-43F8-9D88-4CB2F049780D}.Release|Any CPU.Build.0 = Release|Any CPU
{123A7020-4C9B-4F7D-A27A-3002A7B21D4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{123A7020-4C9B-4F7D-A27A-3002A7B21D4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{123A7020-4C9B-4F7D-A27A-3002A7B21D4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{123A7020-4C9B-4F7D-A27A-3002A7B21D4C}.Release|Any CPU.Build.0 = Release|Any CPU
{BD698045-3408-40C1-AEEF-865219710EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD698045-3408-40C1-AEEF-865219710EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD698045-3408-40C1-AEEF-865219710EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD698045-3408-40C1-AEEF-865219710EE2}.Release|Any CPU.Build.0 = Release|Any CPU
{5650A6DD-602D-49FF-A0B0-DB59BD63EACE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5650A6DD-602D-49FF-A0B0-DB59BD63EACE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5650A6DD-602D-49FF-A0B0-DB59BD63EACE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5650A6DD-602D-49FF-A0B0-DB59BD63EACE}.Release|Any CPU.Build.0 = Release|Any CPU
{A087B535-371A-4A7E-883E-B5B290567E9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A087B535-371A-4A7E-883E-B5B290567E9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A087B535-371A-4A7E-883E-B5B290567E9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A087B535-371A-4A7E-883E-B5B290567E9A}.Release|Any CPU.Build.0 = Release|Any CPU
{42AEDE44-8CE6-4561-B83E-AAE715B9C1DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42AEDE44-8CE6-4561-B83E-AAE715B9C1DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42AEDE44-8CE6-4561-B83E-AAE715B9C1DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42AEDE44-8CE6-4561-B83E-AAE715B9C1DE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/SuppressUninitializedWarningFix/Enabled/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
&lt;Assembly Path="C:\Users\User\.nuget\packages\vknet\1.72.0\lib\net6.0\VkNet.dll" /&gt;&#xD;
&lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>

View File

@ -1,9 +1,10 @@
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using VkNet.Model; using VkNet.Model.Attachments;
namespace VkAudioDownloader; namespace VkAudioDownloader;
public static class ExtensionMethods public static class AudioHelper
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string AudioToString(this Audio a) public static string AudioToString(this Audio a)

View File

@ -1,25 +1,28 @@
using System;
using CliWrap; using CliWrap;
using DTLib.Logging; using DTLib.Filesystem;
using DTLib.Extensions;
using DTLib.Logging.New;
namespace VkAudioDownloader.Helpers; namespace VkAudioDownloader;
public class FFMPegHelper public class FFMPegHelper
{ {
private ContextLogger _logger; private LoggerContext _logger;
private readonly IOPath ffmpeg_exe; private readonly string ffmpeg;
public FFMPegHelper(ILogger logger, IOPath ffmpegDir) public FFMPegHelper(ILogger logger, string ffmpegDir)
{ {
_logger = new ContextLogger(nameof(FFMPegHelper), logger); _logger = new LoggerContext(logger, nameof(FFMPegHelper));
ffmpeg_exe=Path.Concat(ffmpegDir,"ffmpeg"); ffmpeg=Path.Concat(ffmpegDir,"ffmpeg");
} }
/// <summary>creates fragments list for ffmppeg concat</summary> /// <summary>creates fragments list for ffmppeg concat</summary>
/// <param name="fragmentsDir">there file list.txt will be created</param> /// <param name="fragmentsDir">there file list.txt will be created</param>
/// <param name="fragments">audio files in fragmentsDir (with or without dir in path)</param> /// <param name="fragments">audio files in fragmentsDir (with or without dir in path)</param>
/// <returns>path to list.txt file</returns> /// <returns></returns>
public IOPath CreateFragmentList(IOPath fragmentsDir, IOPath[] fragments) public string CreateFragmentList(string fragmentsDir, string[] fragments)
{ {
IOPath listFile = Path.Concat(fragmentsDir, "list.txt"); string listFile = Path.Concat(fragmentsDir, "list.txt");
using var playlistFile = File.OpenWrite(listFile); using var playlistFile = File.OpenWrite(listFile);
for (var i = 0; i < fragments.Length; i++) for (var i = 0; i < fragments.Length; i++)
{ {
@ -34,7 +37,7 @@ public class FFMPegHelper
/// <summary>converts ts files in directory to opus</summary> /// <summary>converts ts files in directory to opus</summary>
/// <param name="localDir">directory with ts fragment files</param> /// <param name="localDir">directory with ts fragment files</param>
/// <returns>paths to created opus files</returns> /// <returns>paths to created opus files</returns>
public Task<IOPath[]> ToOpus(IOPath localDir) => public Task<string[]> ToOpus(string localDir) =>
ToOpus(Directory.GetFiles(localDir, "*.ts")); ToOpus(Directory.GetFiles(localDir, "*.ts"));
/// <summary> /// <summary>
@ -42,27 +45,28 @@ public class FFMPegHelper
/// </summary> /// </summary>
/// <param name="fragments">ts fragment files</param> /// <param name="fragments">ts fragment files</param>
/// <returns>paths to created opus files</returns> /// <returns>paths to created opus files</returns>
public async Task<IOPath[]> ToOpus(IOPath[] fragments) public async Task<string[]> ToOpus(string[] fragments)
{ {
IOPath[] output = new IOPath[fragments.Length]; string[] output = new string[fragments.Length];
var tasks = new Task<CommandResult>[fragments.Length]; var tasks = new Task<CommandResult>[fragments.Length];
for (var i = 0; i < fragments.Length; i++) for (var i = 0; i < fragments.Length; i++)
{ {
IOPath tsFile = fragments[i]; string tsFile = fragments[i];
IOPath opusFile = tsFile.Replace(".ts",".opus"); string opusFile = tsFile.Replace(".ts",".opus");
_logger.LogDebug($"{tsFile} -> {opusFile}"); _logger.LogDebug($"{tsFile} -> {opusFile}");
var command = Cli.Wrap(ffmpeg_exe.Str).WithArguments(new[] var command = Cli.Wrap(ffmpeg).WithArguments(new[]
{ {
"-i", tsFile.Str, // input "-i", tsFile, // input
"-loglevel", "warning", "-hide_banner", "-nostats", // print only warnings and errors "-loglevel", "warning", "-hide_banner", "-nostats", // print only warnings and errors
"-map", "0:0", // select first audio track (sometimes there are blank buggy second thack) "-map", "0:0", // select first audio track (sometimes there are blank buggy second thack)
"-filter:a", "asetpts=PTS-STARTPTS", // fixes pts "-filter:a", "asetpts=PTS-STARTPTS", // fixes pts
"-c", "libopus", "-b:a", "96k", // encoding params "-c", "libopus", "-b:a", "96k", // encoding params
opusFile.Str // output opusFile // output
}) })
// ffmpeg prints all log to stderr, because in stdout it ptints encoded file // ffmpeg prints all log to stderr, because in stdout it ptints encoded file
.WithStandardErrorPipe(PipeTarget.ToDelegate(StdErrHandle)); .WithStandardErrorPipe(PipeTarget.ToDelegate(
msg => _logger.LogWarn(msg)));
tasks[i] = command.ExecuteAsync(); tasks[i] = command.ExecuteAsync();
output[i] = opusFile; output[i] = opusFile;
@ -72,24 +76,17 @@ public class FFMPegHelper
return output; return output;
} }
protected void StdErrHandle(string msg) public async Task Concat(string outfile, string fragmentListFile, string codec="libopus")
{
if(msg.EndsWith("start time for stream 1 is not set in estimate_timings_from_pts"))
_logger.LogDebug(msg);
else _logger.LogWarn(msg);
}
public async Task Concat(IOPath outfile, IOPath fragmentListFile, string codec="libopus")
{ {
_logger.LogDebug($"{fragmentListFile} -> {outfile}"); _logger.LogDebug($"{fragmentListFile} -> {outfile}");
var command = Cli.Wrap(ffmpeg_exe.Str).WithArguments(new[] var command = Cli.Wrap(ffmpeg).WithArguments(new[]
{ {
"-f", "concat", // mode "-f", "concat", // mode
"-i", fragmentListFile.Str, // input list "-i", fragmentListFile, // input list
"-loglevel", "warning", "-hide_banner", "-nostats", // print only warnings and errors "-loglevel", "warning", "-hide_banner", "-nostats", // print only warnings and errors
"-filter:a", "asetpts=PTS-STARTPTS", // fixes pts "-filter:a", "asetpts=PTS-STARTPTS", // fixes pts
"-c", codec, "-b:a", "96k", // encoding params "-c", codec, "-b:a", "96k", // encoding params
outfile.Str, "-y" // output override outfile, "-y" // output override
}) })
// ffmpeg prints all log to stderr, because in stdout it ptints encoded file // ffmpeg prints all log to stderr, because in stdout it ptints encoded file
.WithStandardErrorPipe(PipeTarget.ToDelegate( .WithStandardErrorPipe(PipeTarget.ToDelegate(

View File

@ -1,29 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<!--package info-->
<PackageId>VkAudioDownloader</PackageId>
<Version>0.1.0</Version>
<Authors>Timerix</Authors>
<Description>Loggers with dependency injection</Description>
<RepositoryType>GIT</RepositoryType>
<RepositoryUrl>https://github.com/Timerix22/VkAudioDownloader</RepositoryUrl>
<Configuration>Release</Configuration>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<!--compilation properties-->
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<DebugType>embedded</DebugType>
<!--language features-->
<LangVersion>10</LangVersion> <LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<!--external dependencies-->
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.4" /> <ProjectReference Include="..\DTLib\DTLib.Dtsod\DTLib.Dtsod.csproj" />
<PackageReference Include="VkNet.AudioBypassService" Version="1.7.6" /> <ProjectReference Include="..\DTLib\DTLib.Logging\DTLib.Logging.csproj" />
<PackageReference Include="VkNet" Version="1.76.0" /> <ProjectReference Include="..\DTLib\DTLib\DTLib.csproj" />
<PackageReference Include="DTLib.Dtsod" Version="1.3.0" /> <ProjectReference Include="..\VkNet.AudioBypass\VkNet.AudioBypassService\VkNet.AudioBypassService.csproj" />
<PackageReference Include="DTLib.Logging" Version="1.3.0" /> <ProjectReference Include="..\vknet\VkNet.Generators\VkNet.Generators.csproj" />
<ProjectReference Include="..\vknet\VkNet\VkNet.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.5.0" />
</ItemGroup>
</Project> </Project>

View File

@ -1,36 +1,36 @@
global using System; using System;
global using System.Threading.Tasks;
global using System.Collections.Generic;
global using DTLib.Filesystem;
global using DTLib.Extensions;
using DTLib.Logging;
using DTLib.Logging.DependencyInjection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using VkAudioDownloader.Helpers;
using VkNet; using VkNet;
using VkNet.Enums.Filters; using VkNet.Enums.Filters;
using VkNet.Model; using VkNet.Model;
using VkNet.Model.RequestParams;
using VkNet.Utils; using VkNet.Utils;
using VkNet.Model.Attachments;
using VkNet.AudioBypassService.Extensions; using VkNet.AudioBypassService.Extensions;
using DTLib.Logging.DependencyInjection;
using DTLib.Logging.New;
using DTLib.Filesystem;
using VkAudioDownloader.VkM3U8; using VkAudioDownloader.VkM3U8;
namespace VkAudioDownloader; namespace VkAudioDownloader;
public class VkClient : IDisposable public class VkClient : IDisposable
{ {
public VkApi Api; public VkApi Api;
public VkClientConfig Config; public VkClientConfig Config;
private ContextLogger _logger; private DTLib.Logging.New.ILogger _logger;
public HttpHelper Http; private HttpHelper _http;
public FFMPegHelper Ffmpeg; private FFMPegHelper _ffmpeg;
public VkClient(VkClientConfig conf, DTLib.Logging.ILogger logger) public VkClient(VkClientConfig conf, DTLib.Logging.New.ILogger logger)
{ {
Config = conf; Config = conf;
_logger = new ContextLogger(nameof(VkClient), logger); _logger = logger;
Http = new HttpHelper(); _http = new HttpHelper();
Ffmpeg = new FFMPegHelper(logger, conf.FfmpegDir); _ffmpeg = new FFMPegHelper(logger,conf.FFMPegDir);
var services = new ServiceCollection() var services = new ServiceCollection()
.Add(new LoggerService<VkApi>(logger)) .Add(new LoggerService<VkApi>(logger))
.AddAudioBypass(); .AddAudioBypass();
@ -42,16 +42,16 @@ public class VkClient : IDisposable
var authParams = new ApiAuthParams var authParams = new ApiAuthParams
{ {
ApplicationId = Config.AppId, ApplicationId = Config.AppId,
Settings = Settings.Audio, Settings = Settings.Audio
}; };
if (Config.Token is not null) if (Config.Token is not null)
{ {
_logger.LogInfo("authorizing by token"); _logger.Log(nameof(VkClient),LogSeverity.Info,"authorizing by token");
authParams.AccessToken = Config.Token; authParams.AccessToken = Config.Token;
} }
else else
{ {
_logger.LogInfo("authorizing by login and password"); _logger.Log(nameof(VkClient),LogSeverity.Info,"authorizing by login and password");
authParams.Login = Config.Login; authParams.Login = Config.Login;
authParams.Password = Config.Password; authParams.Password = Config.Password;
} }
@ -65,7 +65,7 @@ public class VkClient : IDisposable
} }
catch (Exception aex) catch (Exception aex)
{ {
_logger.LogError(aex); _logger.LogException(nameof(VkClient),aex);
} }
} }
} }
@ -77,32 +77,31 @@ public class VkClient : IDisposable
Count = maxRezults, Count = maxRezults,
}); });
///<returns>file name</returns>
public Task<IOPath> DownloadAudioAsync(Audio audio, string localDir) => public Task<string> DownloadAudioAsync(Audio audio, string localDir) =>
DownloadAudioAsync(audio, localDir,TimeSpan.FromHours(1)); DownloadAudioAsync(audio, localDir,TimeSpan.FromHours(1));
///<returns>file name</returns> public async Task<string> DownloadAudioAsync(Audio audio, string localDir, TimeSpan durationLimit)
public async Task<IOPath> DownloadAudioAsync(Audio audio, string localDir, TimeSpan durationLimit)
{ {
if (!audio.Url.ToString().StartsWith("http")) if (!audio.Url.ToString().StartsWith("http"))
throw new Exception($"incorrect audio url: {audio.Url}"); throw new Exception($"incorrect audio url: {audio.Url}");
IOPath outFile = Path.Concat(localDir, DTLib.Filesystem.Path.ReplaceRestrictedChars($"{audio.Artist}-{audio.Title}.opus")); string outFile = Path.Concat(localDir, DTLib.Filesystem.Path.CorrectString($"{audio.Artist}-{audio.Title}.opus"));
string fragmentDir = $"{outFile}_{DateTime.Now.Ticks}"; string fragmentDir = $"{outFile}_{DateTime.Now.Ticks}";
if(File.Exists(outFile)) if(File.Exists(outFile))
_logger.LogWarn( $"file {outFile} already exists"); _logger.LogWarn(nameof(VkClient), $"file {outFile} already exists");
string m3u8 = await Http.GetStringAsync(audio.Url); string m3u8 = await _http.GetStringAsync(audio.Url);
var parser = new M3U8Parser(); var parser = new M3U8Parser();
var hls = parser.Parse(audio.Url, m3u8); var hls = parser.Parse(audio.Url, m3u8);
if (hls.Duration > durationLimit.TotalSeconds) if (hls.Duration > durationLimit.TotalSeconds)
throw new Exception($"duration limit <{durationLimit}> exceeded by track <{audio}> - <{hls.Duration}>"); throw new Exception($"duration limit <{durationLimit}> exceeded by track <{audio}> - <{hls.Duration}>");
await Http.DownloadAsync(hls, fragmentDir); await _http.DownloadAsync(hls, fragmentDir);
var opusFragments = await Ffmpeg.ToOpus(fragmentDir); string[] opusFragments = await _ffmpeg.ToOpus(fragmentDir);
IOPath listFile = Ffmpeg.CreateFragmentList(fragmentDir, opusFragments); string listFile = _ffmpeg.CreateFragmentList(fragmentDir, opusFragments);
await Ffmpeg.Concat(outFile, listFile); await _ffmpeg.Concat(outFile, listFile);
Directory.Delete(fragmentDir); // Directory.Delete(fragmentDir);
return outFile; return outFile;
} }
@ -112,7 +111,7 @@ public class VkClient : IDisposable
{ {
if (_disposed) return; if (_disposed) return;
Api.Dispose(); Api.Dispose();
Http.Dispose(); _http.Dispose();
_disposed = true; _disposed = true;
} }
} }

View File

@ -5,7 +5,7 @@ namespace VkAudioDownloader;
public class VkClientConfig public class VkClientConfig
{ {
/// directory where ffmpeg and ffprobe binaries are stored /// directory where ffmpeg and ffprobe binaries are stored
public string FfmpegDir; public string FFMPegDir;
/// vk app id from https://vk.com/apps?act=manage /// vk app id from https://vk.com/apps?act=manage
public ulong AppId; public ulong AppId;
/// account password /// account password
@ -16,37 +16,27 @@ public class VkClientConfig
public string? Token; public string? Token;
public VkClientConfig(string ffmpegDir, ulong appId, string? token) public VkClientConfig(string ffmPegDir, ulong appId, string? token)
{ {
FfmpegDir = ffmpegDir; FFMPegDir = ffmPegDir;
AppId = appId; AppId = appId;
Token = token; Token = token;
} }
public VkClientConfig(string ffmpegDir, ulong appId, string? password, string? login) public VkClientConfig(string ffmPegDir, ulong appId, string? password, string? login)
{ {
FfmpegDir = ffmpegDir; FFMPegDir = ffmPegDir;
AppId = appId; AppId = appId;
Password = password; Password = password;
Login = login; Login = login;
} }
private VkClientConfig(string ffmpegDir, ulong appId) private VkClientConfig(string ffmPegDir, ulong appId)
{ {
FfmpegDir = ffmpegDir; FFMPegDir = ffmPegDir;
AppId = appId; AppId = appId;
} }
/// <summary>
/// {
/// ffmpeg_dir: "";
/// app_id: 0ul;
/// token: "";
/// #or
/// login: "";
/// password: "";
/// };
/// </summary>
public static VkClientConfig FromDtsod(DtsodV23 dtsod) public static VkClientConfig FromDtsod(DtsodV23 dtsod)
{ {
var config = new VkClientConfig(dtsod["ffmpeg_dir"], dtsod["app_id"]); var config = new VkClientConfig(dtsod["ffmpeg_dir"], dtsod["app_id"]);
@ -66,7 +56,7 @@ public class VkClientConfig
{ "password", Password ?? null }, { "password", Password ?? null },
{ "login", Login ?? null }, { "login", Login ?? null },
{ "token", Token ?? null }, { "token", Token ?? null },
{ "ffmpeg_dir", FfmpegDir} { "ffmpeg_dir", FFMPegDir}
}; };
} }

View File

@ -1,7 +1,9 @@
using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using DTLib.Filesystem;
using Stream = System.IO.Stream; using Stream = System.IO.Stream;
namespace VkAudioDownloader.Helpers; namespace VkAudioDownloader.VkM3U8;
public class AudioAesDecryptor : IDisposable public class AudioAesDecryptor : IDisposable
{ {

View File

@ -1,9 +1,10 @@
global using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using VkAudioDownloader.VkM3U8; using DTLib.Filesystem;
using Stream = System.IO.Stream; using Stream = System.IO.Stream;
namespace VkAudioDownloader.Helpers; namespace VkAudioDownloader.VkM3U8;
public class HttpHelper : HttpClient public class HttpHelper : HttpClient
{ {
@ -29,7 +30,7 @@ public class HttpHelper : HttpClient
} }
public async Task DownloadAsync(HLSFragment fragment, string localDir) => public async Task DownloadAsync(HLSFragment fragment, string localDir) =>
await WriteStreamAsync(await GetStreamAsync(fragment), Path.Concat(localDir, fragment.Name).ToString()!); await WriteStreamAsync(await GetStreamAsync(fragment), Path.Concat(localDir, fragment.Name));
public async Task DownloadAsync(HLSPlaylist playlist, string localDir) public async Task DownloadAsync(HLSPlaylist playlist, string localDir)
{ {

View File

@ -1,3 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using DTLib.Extensions;
namespace VkAudioDownloader.VkM3U8; namespace VkAudioDownloader.VkM3U8;
public class M3U8Parser public class M3U8Parser
@ -73,7 +78,7 @@ public class M3U8Parser
if(line.StartsWith("#EXTINF:")) if(line.StartsWith("#EXTINF:"))
{ {
var duration = line.After(':').Before(','); var duration = line.After(':').Before(',');
_fragmentDuration = duration.ToFloat(); _fragmentDuration = float.Parse(duration, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat);
} }
else if (line.StartsWith("#EXT-X-KEY:METHOD=")) else if (line.StartsWith("#EXT-X-KEY:METHOD="))
{ {
@ -110,4 +115,4 @@ public class M3U8Parser
_fragmentEncrypted = false; _fragmentEncrypted = false;
_fragmentEncryptionKeyUrl = null; _fragmentEncryptionKeyUrl = null;
} }
} }

1
VkNet.AudioBypass Submodule

@ -0,0 +1 @@
Subproject commit e3e704c40bb568953415a2c74a397197fafb5988

View File

@ -1,5 +0,0 @@
#!/usr/bin/bash
set -ex
rm -rf nuget
dotnet pack VkAudioDownloader/VkAudioDownloader.csproj -o ./nuget/
ls nuget

View File

@ -1,11 +0,0 @@
#!/usr/bin/bash
echo enter github api key:
read -r GHK
echo enter nuget api key:
read -r NGK
for PACK in $(find ./nuget -name '*.nupkg'); do
dotnet nuget push $PACK -k $GHK -s github --skip-duplicate
dotnet nuget push $PACK -k $NGK -s nuget.org --skip-duplicate
done

1
vknet Submodule

@ -0,0 +1 @@
Subproject commit 5989ed812a93dec5d44f60bc50d119a5a46047a1