new_version_init

This commit is contained in:
2024-02-13 02:59:40 +01:00
Unverified
parent e36c1404ee
commit 91f9b645bd
352 changed files with 6777 additions and 8326 deletions

View File

@@ -0,0 +1,30 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.Authentication
{
public class AuthenticationConfiguration
{
#region PROPERTIES
public string FilePath { get; private set; }
#endregion
#region CONSTRUCTORS
public AuthenticationConfiguration(IConfiguration configuration)
{
IConfigurationSection section = configuration.GetSection("authentication");
FilePath = section["file_path"];
}
#endregion
}
}

View File

@@ -0,0 +1,30 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.Authentication
{
public class AuthenticationData
{
#region PROPERTY
[JsonProperty("twitch")]
public AuthenticationDataTwitch Twitch { get; internal set; }
#endregion
#region CONSTRUCTORS
public AuthenticationData()
{
Twitch = new AuthenticationDataTwitch();
}
#endregion
}
}

View File

@@ -0,0 +1,15 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.Authentication
{
public class AuthenticationDataTwitch
{
[JsonProperty("token")]
public byte[]? Token { get; set; }
}
}

View File

@@ -0,0 +1,97 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Extensions;
namespace VDownload.Services.Authentication
{
public interface IAuthenticationService
{
#region PROPERTIES
AuthenticationData? AuthenticationData { get; }
#endregion
#region METHODS
Task Load();
Task Save();
#endregion
}
public class AuthenticationService : IAuthenticationService
{
#region SERVICES
private AuthenticationConfiguration _configuration;
#endregion
#region FIELDS
private string _authenticationDataFile;
#endregion
#region PROPERTIES
public AuthenticationData AuthenticationData { get; private set; }
public bool AuthenticationDataLoaded => AuthenticationData is not null;
#endregion
#region CONSTRUCTORS
public AuthenticationService(AuthenticationConfiguration configuration)
{
_configuration = configuration;
_authenticationDataFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), _configuration.FilePath);
}
#endregion
#region PUBLIC METHODS
public async Task Load()
{
AuthenticationData = null;
if (File.Exists(_authenticationDataFile))
{
string content = await File.ReadAllTextAsync(_authenticationDataFile);
AuthenticationData = JsonConvert.DeserializeObject<AuthenticationData>(content);
}
AuthenticationData ??= new AuthenticationData();
}
public async Task Save()
{
Directory.CreateDirectory(Path.GetDirectoryName(_authenticationDataFile));
string content = JsonConvert.SerializeObject(AuthenticationData);
await File.WriteAllTextAsync(_authenticationDataFile, content);
}
#endregion
}
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\VDownload.Extensions\VDownload.Extensions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Security;
using System.Runtime.InteropServices;
namespace VDownload.Services.Encryption
{
public interface IEncryptionService
{
byte[] Decrypt(byte[] ciphertext);
byte[] Encrypt(byte[] plaintext);
}
public class EncryptionService : IEncryptionService
{
#region PUBLIC METHODS
public byte[] Encrypt(byte[] plaintext)
{
return ProtectedData.Protect(plaintext, null, DataProtectionScope.CurrentUser);
}
public byte[] Decrypt(byte[] ciphertext)
{
return ProtectedData.Unprotect(ciphertext, null, DataProtectionScope.CurrentUser);
}
#endregion
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json;
using System.Xml.Serialization;
using System.Reflection.PortableExecutable;
using System.Collections;
namespace VDownload.Services.HttpClient
{
public interface IHttpClientService
{
Task<T?> SendRequestAsync<T>(HttpRequest request);
Task<string> SendRequestAsync(HttpRequest request);
}
public class HttpClientService : IHttpClientService
{
#region SERVICES
private readonly System.Net.Http.HttpClient _httpClient;
#endregion
#region CONSTRUCTORS
public HttpClientService(System.Net.Http.HttpClient httpClient)
{
_httpClient = httpClient;
}
#endregion
#region PUBLIC METHODS
public async Task<T?> SendRequestAsync<T>(HttpRequest request) => JsonConvert.DeserializeObject<T>(await SendRequestAsync(request));
public async Task<string> SendRequestAsync(HttpRequest request)
{
StringBuilder urlBuilder = new StringBuilder(request.Url);
if (request.Query.Count > 0)
{
Dictionary<string, object> query = request.Query.ToDictionary();
KeyValuePair<string, object> queryElement = query.ElementAt(0);
query.Remove(queryElement.Key);
urlBuilder.Append($"?{queryElement.Key}={queryElement.Value}");
foreach (KeyValuePair<string, object> queryElementLoop in query)
{
urlBuilder.Append($"&{queryElementLoop.Key}={queryElementLoop.Value}");
}
}
HttpMethod method = request.MethodType switch
{
HttpMethodType.GET => HttpMethod.Get,
HttpMethodType.POST => HttpMethod.Post,
HttpMethodType.PUT => HttpMethod.Put,
HttpMethodType.PATCH => HttpMethod.Patch,
HttpMethodType.DELETE => HttpMethod.Delete,
};
HttpRequestMessage httpRequest = new HttpRequestMessage(method, urlBuilder.ToString());
if (request.Body is not null)
{
string json = JsonConvert.SerializeObject(request.Body);
HttpContent content = new StringContent(json);
content.Headers.ContentType.MediaType = "application/json";
httpRequest.Content = content;
}
foreach (KeyValuePair<string, string> header in request.Headers)
{
httpRequest.Headers.Add(header.Key, header.Value);
}
HttpResponseMessage response = await _httpClient.SendAsync(httpRequest);
return await response.Content.ReadAsStringAsync();
}
#endregion
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.HttpClient
{
public enum HttpMethodType
{
GET,
POST,
PUT,
PATCH,
DELETE
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.HttpClient
{
public class HttpRequest
{
#region PROPERTIES
public HttpMethodType MethodType { get; private set; }
public string Url { get; private set; }
public Dictionary<string, string> Headers { get; private set; }
public Dictionary<string, object> Query { get; private set; }
public object? Body { get; set; }
#endregion
#region CONSTRUCTORS
public HttpRequest(HttpMethodType methodType, string url)
{
MethodType = methodType;
Url = url;
Headers = new Dictionary<string, string>();
Query = new Dictionary<string, object>();
}
#endregion
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.HttpClient
{
public class Token
{
#region PROPERTIES
public string Schema { get; private set; }
public byte[] TokenValue { get; private set; }
#endregion
#region CONSTRUCTORS
public Token(string schema, byte[] tokenValue)
{
Schema = schema;
TokenValue = tokenValue;
}
#endregion
#region PUBLIC METHODS
public override string ToString() => $"{Schema} {Encoding.UTF8.GetString(TokenValue)}";
#endregion
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using VDownload.Common;
using VDownload.Common.Exceptions;
using VDownload.Common.Models;
using VDownload.Common.Services;
using VDownload.Sources.Twitch;
using VDownload.Sources.Twitch.Configuration;
using VDownload.Sources.Twitch.Search;
namespace VDownload.Services.Search
{
public interface ISearchService
{
Task<Playlist> SearchPlaylist(string url, int maxVideoCount);
Task<Video> SearchVideo(string url);
}
public class SearchService : ISearchService
{
#region FIELDS
private readonly List<(Regex Regex, ISourceSearchService Service)> _urlMappings;
#endregion
#region CONSTRUCTORS
public SearchService(TwitchConfiguration twitchConfiguration, ITwitchSearchService twitchSearchService)
{
_urlMappings = new List<(Regex, ISourceSearchService)>();
_urlMappings.AddRange(twitchConfiguration.Search.GeneralRegexes.Select(x => (x, (ISourceSearchService)twitchSearchService)));
}
#endregion
#region PUBLIC METHODS
public async Task<Video> SearchVideo(string url)
{
BaseUrlCheck(url);
foreach ((Regex Regex, ISourceSearchService Service) mapping in _urlMappings)
{
if (mapping.Regex.IsMatch(url))
{
return await mapping.Service.SearchVideo(url);
}
}
throw new MediaSearchException("Source is not supported");
}
public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount)
{
BaseUrlCheck(url);
foreach ((Regex Regex, ISourceSearchService Service) mapping in _urlMappings)
{
if (mapping.Regex.IsMatch(url))
{
return await mapping.Service.SearchPlaylist(url, maxVideoCount);
}
}
throw new MediaSearchException("Source is not supported");
}
#endregion
#region PRIVATE METHODS
private void BaseUrlCheck(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
throw new MediaSearchException("Url cannot be empty");
}
}
#endregion
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\VDownload.Common\VDownload.Common.csproj" />
<ProjectReference Include="..\..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Search\VDownload.Sources.Twitch.Search.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Common;
namespace VDownload.Services.Tasks
{
public interface ITasksService
{
#region PROPERTIES
ObservableCollection<DownloadTask> Tasks { get; }
#endregion
#region METHODS
void AddTask(DownloadTask task);
#endregion
}
public class TasksService : ITasksService
{
#region FIELDS
private readonly Task _taskMonitor;
#endregion
#region PROPERTIES
public ObservableCollection<DownloadTask> Tasks { get; protected set; }
#endregion
#region CONSTRUCTORS
public TasksService()
{
Tasks = new ObservableCollection<DownloadTask>();
_taskMonitor = Task.Run(TaskMonitor);
}
#endregion
#region PUBLIC METHODS
public void AddTask(DownloadTask task)
{
Tasks.Add(task);
}
#endregion
#region PRIVATE METHODS
private void TaskMonitor()
{
int maxTaskNumber = 5; //TODO: from settings
DownloadTaskStatus[] pendingStatuses =
[
DownloadTaskStatus.Initializing,
DownloadTaskStatus.Downloading,
DownloadTaskStatus.Processing,
DownloadTaskStatus.Finalizing
];
while (true)
{
IEnumerable<DownloadTask> pendingTasks = Tasks.Where(x => pendingStatuses.Contains(x.Status));
int freeSlots = maxTaskNumber - pendingTasks.Count();
if (freeSlots > 0)
{
IEnumerable<DownloadTask> queuedTasks = Tasks.Where(x => x.Status == DownloadTaskStatus.Queued).OrderBy(x => x.CreateDate).Take(freeSlots);
foreach (DownloadTask queuedTask in queuedTasks)
{
queuedTask.Start();
}
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\VDownload.Common\VDownload.Common.csproj" />
<ProjectReference Include="..\..\VDownload.Tasks\VDownload.Tasks.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,11 @@
namespace VDownload.Services
{
public class ServiceProvider
{
#region CONSTRUCTORS
public static IServiceProvider Instance { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>