mirror of
https://github.com/Stone-Red-Code/HyperbolicDownloader.git
synced 2026-09-04 00:56:09 +02:00
- Add command system
- Add basic file sharing
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace HyperbolicDownloader.FileProcessing;
|
||||
|
||||
internal class FileCompressor
|
||||
internal static class FileCompressor
|
||||
{
|
||||
private static void CompressFile(string inputFilePath, string compressedFilePath)
|
||||
public static void CompressFile(string inputFilePath, string compressedFilePath)
|
||||
{
|
||||
using FileStream originalFileStream = File.Open(inputFilePath, FileMode.Open);
|
||||
using FileStream compressedFileStream = File.Create(compressedFilePath);
|
||||
@@ -12,11 +12,25 @@ internal class FileCompressor
|
||||
originalFileStream.CopyTo(compressor);
|
||||
}
|
||||
|
||||
private static void DecompressFile(string compressedFilePath, string outputFilePath)
|
||||
public static void DecompressFile(string compressedFilePath, string outputFilePath)
|
||||
{
|
||||
using FileStream compressedFileStream = File.Open(compressedFilePath, FileMode.Open);
|
||||
using FileStream outputFileStream = File.Create(outputFilePath);
|
||||
using GZipStream? decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
|
||||
decompressor.CopyTo(outputFileStream);
|
||||
}
|
||||
|
||||
public static IEnumerable<byte[]> ReadChunks(string path, int chunkSize)
|
||||
{
|
||||
byte[] buffer = new byte[chunkSize];
|
||||
int bytesRead;
|
||||
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
|
||||
using (BufferedStream bs = new BufferedStream(fs))
|
||||
{
|
||||
while ((bytesRead = bs.Read(buffer, 0, chunkSize)) != 0)
|
||||
{
|
||||
yield return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,18 @@ internal class FileValidator
|
||||
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
public static string CalculateHash(string filePath)
|
||||
{
|
||||
return CalculateHashAsync(filePath).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public static async Task<bool> ValidateHashAsync(string filePath, string hash)
|
||||
{
|
||||
return await CalculateHashAsync(filePath) == hash;
|
||||
}
|
||||
|
||||
public static bool ValidateHash(string filePath, string hash)
|
||||
{
|
||||
return CalculateHash(filePath) == hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace HyperbolicDownloader.FileProcessing;
|
||||
|
||||
internal class FilesManager
|
||||
{
|
||||
private readonly List<HyperFileInfo> files = new List<HyperFileInfo>();
|
||||
|
||||
public bool TryAdd(string filePath, out HyperFileInfo? fileInfo, out string? errorMessage)
|
||||
{
|
||||
string fullPath = Path.GetFullPath(filePath);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
fileInfo = null;
|
||||
errorMessage = "Invalid file path!";
|
||||
return false;
|
||||
}
|
||||
|
||||
string hash = FileValidator.CalculateHash(filePath);
|
||||
|
||||
if (Contains(hash))
|
||||
{
|
||||
fileInfo = null;
|
||||
errorMessage = "File already tracked!";
|
||||
return false;
|
||||
}
|
||||
|
||||
fileInfo = new HyperFileInfo(hash, fullPath);
|
||||
|
||||
files.Add(fileInfo);
|
||||
|
||||
SaveFiles();
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<HyperFileInfo> fileInfos)
|
||||
{
|
||||
foreach (HyperFileInfo fileInfo in fileInfos)
|
||||
{
|
||||
if (File.Exists(fileInfo.FilePath) && !Contains(fileInfo.Hash))
|
||||
{
|
||||
files.Add(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
SaveFiles();
|
||||
}
|
||||
|
||||
public bool TryGet(string hash, out HyperFileInfo? fileInfo)
|
||||
{
|
||||
if (Contains(hash))
|
||||
{
|
||||
fileInfo = files.First(x => x.Hash == hash);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileInfo = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRemove(string hash)
|
||||
{
|
||||
files.RemoveAll(f => f.Hash == hash);
|
||||
|
||||
SaveFiles();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Contains(string? hash)
|
||||
{
|
||||
return files.Any(f => f.Hash == hash);
|
||||
}
|
||||
|
||||
public List<HyperFileInfo> ToList()
|
||||
{
|
||||
return files.ToList();
|
||||
}
|
||||
|
||||
private void SaveFiles()
|
||||
{
|
||||
File.WriteAllText(Program.FilesInfoPath, JsonSerializer.Serialize(files));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace HyperbolicDownloader.FileProcessing;
|
||||
|
||||
internal class HyperFileInfo
|
||||
{
|
||||
public string Hash { get; set; } = string.Empty;
|
||||
public string FilePath { get; set; } = string.Empty;
|
||||
|
||||
public HyperFileInfo(string hash, string filePath)
|
||||
{
|
||||
Hash = hash;
|
||||
FilePath = filePath;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Console.Commander.NET" Version="1.0.0" />
|
||||
<PackageReference Include="Open.NAT" Version="2.1.0" />
|
||||
<PackageReference Include="Stone_Red-C-Sharp-Utilities" Version="1.0.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
using System.Net;
|
||||
using HyperbolicDownloader.FileProcessing;
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace HyperbolicDownloader
|
||||
namespace HyperbolicDownloader.Networking
|
||||
{
|
||||
internal class NetworkClient
|
||||
{
|
||||
public bool IsListening { get; private set; } = false;
|
||||
|
||||
private TcpListener? tcpListener;
|
||||
private FilesManager filesManager;
|
||||
private readonly Dictionary<string, (Type type, Delegate method)> events = new();
|
||||
|
||||
public NetworkClient()
|
||||
public NetworkClient(FilesManager filesManager)
|
||||
{
|
||||
this.filesManager = filesManager;
|
||||
}
|
||||
|
||||
public static async Task<T?> SendAsync<T>(IPAddress remoteIp, int remotePort, string eventName, object data)
|
||||
@@ -93,22 +97,28 @@ namespace HyperbolicDownloader
|
||||
tcpListener.Start();
|
||||
IsListening = true;
|
||||
|
||||
Task.Run(() =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (IsListening)
|
||||
{
|
||||
try
|
||||
{
|
||||
using TcpClient client = tcpListener.AcceptTcpClient();
|
||||
TcpClient client = tcpListener.AcceptTcpClient();
|
||||
NetworkStream nwStream = client.GetStream();
|
||||
byte[] buffer = new byte[client.ReceiveBufferSize];
|
||||
|
||||
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
|
||||
int bytesRead = await nwStream.ReadAsync(buffer, 0, client.ReceiveBufferSize);
|
||||
|
||||
string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
|
||||
|
||||
if (dataReceived.StartsWith("Download"))
|
||||
{
|
||||
_ = Upload(client, dataReceived[8..]);
|
||||
continue;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(dataReceived))
|
||||
{
|
||||
client.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -128,6 +138,8 @@ namespace HyperbolicDownloader
|
||||
|
||||
method?.DynamicInvoke(this, eventArgs);
|
||||
}
|
||||
|
||||
client.Close();
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
@@ -141,6 +153,48 @@ namespace HyperbolicDownloader
|
||||
});
|
||||
}
|
||||
|
||||
private async Task Upload(TcpClient client, string hash)
|
||||
{
|
||||
byte[] bytesToSend;
|
||||
hash = hash.Trim();
|
||||
NetworkStream nwStream = client.GetStream();
|
||||
|
||||
if (filesManager.TryGet(hash, out HyperFileInfo? hyperFileInfo) && File.Exists(hyperFileInfo?.FilePath))
|
||||
{
|
||||
if (hash != await FileValidator.CalculateHashAsync(hyperFileInfo.FilePath))
|
||||
{
|
||||
bytesToSend = Encoding.ASCII.GetBytes("Hash does not match!");
|
||||
await nwStream.WriteAsync(bytesToSend);
|
||||
return;
|
||||
}
|
||||
|
||||
//string compressedFilePath = $"{hash + DateTime.Now.Millisecond}.temp";
|
||||
|
||||
//FileCompressor.CompressFile(hyperFileInfo.FilePath, compressedFilePath);
|
||||
|
||||
FileInfo compressedFileInfo = new FileInfo(hyperFileInfo.FilePath);
|
||||
|
||||
bytesToSend = Encoding.ASCII.GetBytes(compressedFileInfo.Length.ToString());
|
||||
await nwStream.WriteAsync(bytesToSend);
|
||||
|
||||
foreach (byte[]? chunk in FileCompressor.ReadChunks(hyperFileInfo.FilePath, 1000))
|
||||
{
|
||||
if (chunk is not null)
|
||||
{
|
||||
await nwStream.WriteAsync(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
// File.Delete(compressedFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesToSend = Encoding.ASCII.GetBytes("File not found!");
|
||||
await nwStream.WriteAsync(bytesToSend);
|
||||
}
|
||||
client.Close();
|
||||
}
|
||||
|
||||
public void StopListening()
|
||||
{
|
||||
tcpListener?.Stop();
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
internal class NetworkSocket
|
||||
{
|
||||
public string IPAddress { get; set; }
|
||||
public int Port { get; set; }
|
||||
|
||||
public NetworkSocket(string ipAddress, int port)
|
||||
{
|
||||
IPAddress = ipAddress;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public string IPAddress { get; set; }
|
||||
public int Port { get; set; }
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
using HyperbolicDownloader.FileProcessing;
|
||||
using HyperbolicDownloader.Networking;
|
||||
using HyperbolicDownloader.UserInterface;
|
||||
|
||||
using Open.Nat;
|
||||
|
||||
@@ -15,15 +17,16 @@ namespace HyperbolicDownloader;
|
||||
internal class Program
|
||||
{
|
||||
private const int BroadcastPort = 2155;
|
||||
|
||||
public const string HostsFilePath = "Hosts.json";
|
||||
public const string FilesInfoPath = "Files.json";
|
||||
|
||||
private static int publicPort;
|
||||
private static readonly int privatePort = 3055;
|
||||
private static NatDevice? device;
|
||||
private static Mapping? portMapping;
|
||||
private static readonly HostsManager hosts = new();
|
||||
private static readonly NetworkClient networkClient = new();
|
||||
private static readonly HostsManager hostsManager = new();
|
||||
private static readonly FilesManager filesManager = new FilesManager();
|
||||
private static readonly NetworkClient networkClient = new(filesManager);
|
||||
private static readonly Random random = new Random();
|
||||
|
||||
private static async Task Main()
|
||||
@@ -33,7 +36,13 @@ internal class Program
|
||||
if (File.Exists(HostsFilePath))
|
||||
{
|
||||
string hostsJson = await File.ReadAllTextAsync(HostsFilePath);
|
||||
hosts.AddRange(JsonSerializer.Deserialize<List<NetworkSocket>>(hostsJson) ?? new());
|
||||
hostsManager.AddRange(JsonSerializer.Deserialize<List<NetworkSocket>>(hostsJson) ?? new());
|
||||
}
|
||||
|
||||
if (File.Exists(FilesInfoPath))
|
||||
{
|
||||
string filesJson = await File.ReadAllTextAsync(FilesInfoPath);
|
||||
filesManager.AddRange(JsonSerializer.Deserialize<List<HyperFileInfo>>(filesJson) ?? new());
|
||||
}
|
||||
|
||||
Console.WriteLine("Searching for a UPnP/NAT-PMP device...");
|
||||
@@ -48,6 +57,8 @@ internal class Program
|
||||
{
|
||||
networkClient.ListenTo<NetworkSocket>("GetHostsList", GetHostList);
|
||||
networkClient.ListenTo<List<NetworkSocket>>("DiscoverAnswer", DiscoverAnswer);
|
||||
networkClient.ListenTo<string>("Message", ReciveMessage);
|
||||
networkClient.ListenTo<string>("HasFile", HasFile);
|
||||
networkClient.StartListening(privatePort);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
@@ -62,31 +73,33 @@ internal class Program
|
||||
broadcastClient.Send(BroadcastPort, privatePort.ToString());
|
||||
await Task.Delay(5000);
|
||||
|
||||
if (hosts.Count > 0)
|
||||
if (hostsManager.Count > 0)
|
||||
{
|
||||
Console.WriteLine("Checking if hosts are active...");
|
||||
hosts.RemoveInactiveHosts();
|
||||
hostsManager.RemoveInactiveHosts();
|
||||
}
|
||||
|
||||
if (hosts.Count == 0)
|
||||
if (hostsManager.Count == 0)
|
||||
{
|
||||
hosts.AddRange(await Setup.ConfigureHost());
|
||||
hostsManager.AddRange(await UserInterface.Setup.ConfigureHost());
|
||||
Console.WriteLine("Checking if hosts are active...");
|
||||
hosts.RemoveInactiveHosts();
|
||||
hostsManager.RemoveInactiveHosts();
|
||||
}
|
||||
|
||||
Console.WriteLine($"{hosts.Count} active host(s).");
|
||||
Console.WriteLine($"{hostsManager.Count} active host(s).");
|
||||
Console.WriteLine("Starting broadcast listener...");
|
||||
broadcastClient.StartListening(BroadcastPort);
|
||||
broadcastClient.OnBroadcastRecived += BroadcastClient_OnBroadcastRecived;
|
||||
ConsoleExt.WriteLine("Done", ConsoleColor.Green);
|
||||
await Task.Delay(-1);
|
||||
|
||||
new InputHandler(hostsManager, filesManager).ReadInput();
|
||||
ClosePorts();
|
||||
}
|
||||
|
||||
private static async void BroadcastClient_OnBroadcastRecived(object? sender, BroadcastRecivedEventArgs recivedEventArgs)
|
||||
{
|
||||
Debug.WriteLine($"Received broadcast \"{recivedEventArgs.Message}\" from {recivedEventArgs.IPEndPoint.Address}");
|
||||
List<NetworkSocket> hostsToSend = hosts.ToList();
|
||||
List<NetworkSocket> hostsToSend = hostsManager.ToList();
|
||||
|
||||
NetworkSocket? localSocket = GetLocalSocket();
|
||||
|
||||
@@ -102,7 +115,7 @@ internal class Program
|
||||
|
||||
if (success)
|
||||
{
|
||||
hosts.Add(new NetworkSocket(recivedEventArgs.IPEndPoint.Address.ToString(), remotePort));
|
||||
hostsManager.Add(new NetworkSocket(recivedEventArgs.IPEndPoint.Address.ToString(), remotePort));
|
||||
try
|
||||
{
|
||||
await NetworkClient.SendAsync(recivedEventArgs.IPEndPoint.Address, remotePort, "DiscoverAnswer", hostsToSend);
|
||||
@@ -117,12 +130,17 @@ internal class Program
|
||||
private static void DiscoverAnswer(object? sender, MessageRecivedEventArgs<List<NetworkSocket>> recivedEventArgs)
|
||||
{
|
||||
Console.WriteLine($"Received answer from {recivedEventArgs.IpAddress}. Returned {recivedEventArgs.Data.Count} host(s).");
|
||||
hosts.AddRange(recivedEventArgs.Data);
|
||||
hostsManager.AddRange(recivedEventArgs.Data);
|
||||
}
|
||||
|
||||
private static void ReciveMessage(object? sender, MessageRecivedEventArgs<string> recivedEventArgs)
|
||||
{
|
||||
Console.WriteLine($"Received \"{recivedEventArgs.Data}\" from {recivedEventArgs.IpAddress}.");
|
||||
}
|
||||
|
||||
private static async void GetHostList(object? sender, MessageRecivedEventArgs<NetworkSocket> recivedEventArgs)
|
||||
{
|
||||
List<NetworkSocket> hostsToSend = hosts.ToList();
|
||||
List<NetworkSocket> hostsToSend = hostsManager.ToList();
|
||||
|
||||
NetworkSocket? localSocket = GetLocalSocket();
|
||||
|
||||
@@ -136,12 +154,17 @@ internal class Program
|
||||
|
||||
if (recivedEventArgs.Data.Port != 0)
|
||||
{
|
||||
hosts.Add(recivedEventArgs.Data);
|
||||
hostsManager.Add(recivedEventArgs.Data);
|
||||
}
|
||||
|
||||
await recivedEventArgs.SendResponseAsync(hostsToSend);
|
||||
}
|
||||
|
||||
private static async void HasFile(object? sender, MessageRecivedEventArgs<string> recivedEventArgs)
|
||||
{
|
||||
await recivedEventArgs.SendResponseAsync(filesManager.Contains(recivedEventArgs.Data));
|
||||
}
|
||||
|
||||
public static async Task<bool> OpenPorts()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
using Commander_Net;
|
||||
|
||||
using HyperbolicDownloader.FileProcessing;
|
||||
using HyperbolicDownloader.Networking;
|
||||
|
||||
using Stone_Red_Utilities.ConsoleExtentions;
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace HyperbolicDownloader.UserInterface;
|
||||
|
||||
internal class InputHandler
|
||||
{
|
||||
private readonly HostsManager hostsManager;
|
||||
private readonly FilesManager filesManager;
|
||||
private readonly Commander commander = new Commander();
|
||||
private bool exit = false;
|
||||
|
||||
public InputHandler(HostsManager hostsManager, FilesManager filesManager)
|
||||
{
|
||||
this.hostsManager = hostsManager;
|
||||
commander.Register((_) => Console.Clear(), "clear");
|
||||
commander.Register(Exit, "exit");
|
||||
commander.Register(GetFile, "get");
|
||||
commander.Register(AddFile, "add");
|
||||
commander.Register(ListFiles, "list");
|
||||
this.filesManager = filesManager;
|
||||
}
|
||||
|
||||
public void ReadInput()
|
||||
{
|
||||
while (!exit)
|
||||
{
|
||||
Console.Write("> ");
|
||||
string input = Console.ReadLine() ?? string.Empty;
|
||||
|
||||
if (!commander.Execute(input))
|
||||
{
|
||||
ConsoleExt.WriteLine("Unknown command!", ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetFile(string hash)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hash))
|
||||
{
|
||||
Console.WriteLine("No hash value specified!");
|
||||
}
|
||||
|
||||
foreach (NetworkSocket host in hostsManager.ToList())
|
||||
{
|
||||
bool validIpAdress = IPAddress.TryParse(host.IPAddress, out IPAddress? ipAddress);
|
||||
|
||||
if (validIpAdress)
|
||||
{
|
||||
ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow);
|
||||
try
|
||||
{
|
||||
Console.CursorLeft = 0;
|
||||
|
||||
Task<bool> sendTask = NetworkClient.SendAsync<bool>(ipAddress!, host.Port, "HasFile", hash);
|
||||
|
||||
_ = sendTask.Wait(1000);
|
||||
|
||||
if (sendTask.Result)
|
||||
{
|
||||
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green);
|
||||
Console.WriteLine("Requesting file...");
|
||||
|
||||
TcpClient tcpClient = new TcpClient();
|
||||
tcpClient.Connect(ipAddress!, host.Port);
|
||||
|
||||
NetworkStream nwStream = tcpClient.GetStream();
|
||||
byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
|
||||
byte[] reciveBuffer = new byte[1000];
|
||||
|
||||
byte[] bytesToSend = Encoding.ASCII.GetBytes($"Download {hash}");
|
||||
nwStream.Write(bytesToSend);
|
||||
int bytesRead = nwStream.Read(buffer, 0, tcpClient.ReceiveBufferSize);
|
||||
|
||||
string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
|
||||
|
||||
if (int.TryParse(dataReceived, out int fileSize)) //If received data is not a size an error occurred
|
||||
{
|
||||
Console.WriteLine($"Starting download...");
|
||||
|
||||
int totalBytesRead = 0;
|
||||
|
||||
using FileStream? fileStream = new FileStream($"{hash}.txt", FileMode.Create);
|
||||
|
||||
while (totalBytesRead < fileSize)
|
||||
{
|
||||
bytesRead = nwStream.Read(reciveBuffer, 0, reciveBuffer.Length);
|
||||
|
||||
bytesRead = Math.Min(reciveBuffer.Length, fileSize - totalBytesRead);
|
||||
|
||||
fileStream.Write(reciveBuffer, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
Console.CursorLeft = 0;
|
||||
Console.Out.WriteAsync($"Downloading: {Math.Ceiling(100d / fileSize * totalBytesRead)}% {totalBytesRead}/{fileSize}");
|
||||
}
|
||||
|
||||
fileStream.Close();
|
||||
//FileCompressor.DecompressFile($"{hash}.gz", $"result.txt");
|
||||
Console.WriteLine();
|
||||
ConsoleExt.WriteLine("Done", ConsoleColor.Green);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleExt.WriteLine(dataReceived, ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
Console.CursorLeft = 0;
|
||||
ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hostsManager.Remove(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFile(string args)
|
||||
{
|
||||
if (filesManager.TryAdd(args, out HyperFileInfo? fileInfo, out string? message))
|
||||
{
|
||||
ConsoleExt.WriteLine($"Added file: {fileInfo!.FilePath}", ConsoleColor.Green);
|
||||
Console.WriteLine($"Hash: {fileInfo.Hash}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleExt.WriteLine(message, ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
|
||||
private void ListFiles(string _)
|
||||
{
|
||||
int index = 0;
|
||||
foreach (HyperFileInfo fileInfo in filesManager.ToList())
|
||||
{
|
||||
index++;
|
||||
Console.WriteLine($"{index}) {fileInfo.FilePath}");
|
||||
Console.WriteLine($"Hash: {fileInfo.Hash}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
private void Exit(string _)
|
||||
{
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Stone_Red_Utilities.ConsoleExtentions;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace HyperbolicDownloader;
|
||||
namespace HyperbolicDownloader.UserInterface;
|
||||
|
||||
internal static class Setup
|
||||
{
|
||||
Reference in New Issue
Block a user