Compare commits
No commits in common. "5695b2287ca23c5193b9176978f8813d87747977" and "0443cc652758caec6a3a24d21a0db0bcb3c2e21e" have entirely different histories.
5695b2287c
...
0443cc6527
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,23 +1,6 @@
|
|||||||
# Build results
|
bin/
|
||||||
[Bb]in/
|
obj/
|
||||||
.bin/
|
/packages/
|
||||||
[Dd]ebug/
|
riderModule.iml
|
||||||
[Rr]elease/
|
/_ReSharper.Caches/
|
||||||
[Rr]eleases/
|
|
||||||
[Oo]bj/
|
|
||||||
[Oo]ut/
|
|
||||||
[Ll]og/
|
|
||||||
[Ll]ogs/
|
|
||||||
nuget/
|
|
||||||
|
|
||||||
# IDE files
|
|
||||||
.vs/
|
|
||||||
.vscode/
|
|
||||||
.vshistory/
|
|
||||||
.idea/
|
.idea/
|
||||||
.editorconfig
|
|
||||||
*.user
|
|
||||||
*.DotSettings
|
|
||||||
|
|
||||||
#backups
|
|
||||||
.old*/
|
|
||||||
|
|||||||
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal 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
1
DTLib
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 3ebb5be5819fa46d36705a5752aecd885c406a8b
|
||||||
@ -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();
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
2
VkAudioDownloader.sln.DotSettings
Normal file
2
VkAudioDownloader.sln.DotSettings
Normal 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>
|
||||||
4
VkAudioDownloader.sln.DotSettings.user
Normal file
4
VkAudioDownloader.sln.DotSettings.user
Normal 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"><AssemblyExplorer>
|
||||||
|
<Assembly Path="C:\Users\User\.nuget\packages\vknet\1.72.0\lib\net6.0\VkNet.dll" />
|
||||||
|
</AssemblyExplorer></s:String></wpf:ResourceDictionary>
|
||||||
@ -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)
|
||||||
@ -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(
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
{
|
{
|
||||||
@ -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)
|
||||||
{
|
{
|
||||||
@ -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="))
|
||||||
{
|
{
|
||||||
|
|||||||
1
VkNet.AudioBypass
Submodule
1
VkNet.AudioBypass
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit e3e704c40bb568953415a2c74a397197fafb5988
|
||||||
5
pack.sh
5
pack.sh
@ -1,5 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
set -ex
|
|
||||||
rm -rf nuget
|
|
||||||
dotnet pack VkAudioDownloader/VkAudioDownloader.csproj -o ./nuget/
|
|
||||||
ls nuget
|
|
||||||
@ -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
1
vknet
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 5989ed812a93dec5d44f60bc50d119a5a46047a1
|
||||||
Loading…
Reference in New Issue
Block a user