twitch vod downloading done

ffmpeg essentials

fix

Project reorganized

git lfs

ffmpeg removed

ffmpeg added
This commit is contained in:
2024-02-14 02:07:22 +01:00
Unverified
parent 91f9b645bd
commit e3ec5c3a48
264 changed files with 6239 additions and 4014 deletions

View File

@@ -1,30 +0,0 @@
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

@@ -1,30 +0,0 @@
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

@@ -1,97 +0,0 @@
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,26 @@
using Newtonsoft.Json;
using VDownload.Services.Data.Authentication.Models;
namespace VDownload.Services.Data.Authentication
{
public class AuthenticationData
{
#region PROPERTIES
[JsonProperty("twitch")]
public TokenAuthenticationData Twitch { get; set; }
#endregion
#region CONSTRUCTORS
public AuthenticationData()
{
Twitch = new TokenAuthenticationData();
}
#endregion
}
}

View File

@@ -0,0 +1,95 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Services.Data.Configuration;
namespace VDownload.Services.Data.Authentication
{
public interface IAuthenticationDataService
{
#region PROPERTIES
AuthenticationData Data { get; }
#endregion
#region METHODS
Task Load();
Task Save();
#endregion
}
public class AuthenticationDataService : IAuthenticationDataService
{
#region SERVICES
protected readonly IConfigurationService _configurationService;
#endregion
#region FIELDS
protected readonly string _filePath;
#endregion
#region PROPERTIES
public AuthenticationData Data { get; protected set; }
#endregion
#region CONSTRUCTORS
public AuthenticationDataService(IConfigurationService configurationService)
{
_configurationService = configurationService;
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string appdataDirectoryName = _configurationService.Common.Path.Appdata.DirectoryName;
string appdataAuthenticationFilename = _configurationService.Common.Path.Appdata.AuthenticationFile;
_filePath = Path.Combine(appdataPath, appdataDirectoryName, appdataAuthenticationFilename);
}
#endregion
#region PUBLIC METHODS
public async Task Load()
{
Data = null;
if (File.Exists(_filePath))
{
string content = await File.ReadAllTextAsync(_filePath);
Data = JsonConvert.DeserializeObject<AuthenticationData>(content);
}
Data ??= new AuthenticationData();
}
public async Task Save()
{
Directory.CreateDirectory(Path.GetDirectoryName(_filePath));
string content = JsonConvert.SerializeObject(Data);
await File.WriteAllTextAsync(_filePath, content);
}
#endregion
}
}

View File

@@ -5,9 +5,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.Authentication
namespace VDownload.Services.Data.Authentication.Models
{
public class AuthenticationDataTwitch
public class TokenAuthenticationData
{
[JsonProperty("token")]
public byte[]? Token { get; set; }

View File

@@ -7,12 +7,11 @@
</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" />
<ProjectReference Include="..\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.Configuration;
using System.Text.Json.Serialization;
using VDownload.Services.Data.Configuration.Models;
namespace VDownload.Services.Data.Configuration
{
public class CommonConfiguration
{
[ConfigurationKeyName("path")]
public Models.Path Path { get; set; }
[ConfigurationKeyName("processing")]
public Processing Processing { get; set; }
[ConfigurationKeyName("string_resources_assembly")]
public string StringResourcesAssembly { get; set; }
}
}

View File

@@ -0,0 +1,48 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Sources.Twitch.Configuration;
namespace VDownload.Services.Data.Configuration
{
public interface IConfigurationService
{
CommonConfiguration Common { get; }
TwitchConfiguration Twitch { get; }
}
public class ConfigurationService : IConfigurationService
{
#region SERVICES
protected readonly IConfiguration _configuration;
#endregion
#region PROPERTIES
public CommonConfiguration Common { get; protected set; }
public TwitchConfiguration Twitch { get; protected set; }
#endregion
#region CONSTRUCTORS
public ConfigurationService(IConfiguration configuration)
{
Common = configuration.GetSection("common").Get<CommonConfiguration>()!;
Twitch = configuration.GetSection("twitch").Get<TwitchConfiguration>()!;
}
#endregion
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VDownload.Services.Data.Configuration.Models
{
public class Appdata
{
[ConfigurationKeyName("directory_name")]
public string DirectoryName { get; set; }
[ConfigurationKeyName("authentication_file")]
public string AuthenticationFile { get; set; }
[ConfigurationKeyName("settings_file")]
public string SettingsFile { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VDownload.Services.Data.Configuration.Models
{
public class Muxer
{
[ConfigurationKeyName("extension")]
public string Extension { get; set; }
[ConfigurationKeyName("video_codecs")]
public List<string> VideoCodecs { get; } = new List<string>();
[ConfigurationKeyName("audio_codecs")]
public List<string> AudioCodecs { get; } = new List<string>();
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VDownload.Services.Data.Configuration.Models
{
public class Path
{
[ConfigurationKeyName("appdata")]
public Appdata Appdata { get; set; }
[ConfigurationKeyName("temp")]
public Temp Temp { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VDownload.Services.Data.Configuration.Models
{
public class Processing
{
[ConfigurationKeyName("muxers")]
public List<Muxer> Muxers { get; } = new List<Muxer>();
[ConfigurationKeyName("processed_filename")]
public string ProcessedFilename { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace VDownload.Services.Data.Configuration.Models
{
public class Temp
{
[ConfigurationKeyName("tasks_directory")]
public string TasksDirectory { get; set; }
}
}

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="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Configuration\VDownload.Sources.Twitch.Configuration.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,36 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Models;
using VDownload.Services.Data.Settings.Models;
namespace VDownload.Services.Data.Settings
{
public class CommonSettings
{
[JsonProperty("max_number_of_videos_to_get_from_playlist")]
public int MaxNumberOfVideosToGetFromPlaylist { get; set; } = 0;
[JsonProperty("max_number_of_running_tasks")]
public int MaxNumberOfRunningTasks { get; set; } = 5;
[JsonProperty("temp_directory")]
public string TempDirectory { get; set; } = $"{Path.GetTempPath()}\\VDownload";
[JsonProperty("delete_temp_on_error")]
public bool DeleteTempOnError { get; set; } = true;
[JsonProperty("default_task_settings")]
public DefaultTaskSettings DefaultTaskSettings { get; set; } = new DefaultTaskSettings();
[JsonProperty("notifications")]
public Notifications Notifications { get; set; } = new Notifications();
[JsonProperty("processing")]
public Processing Processing { get; set; } = new Processing();
}
}

View File

@@ -0,0 +1,28 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Models;
namespace VDownload.Services.Data.Settings.Models
{
public class DefaultTaskSettings
{
[JsonProperty("media_type")]
public MediaType MediaType { get; set; } = MediaType.Original;
[JsonProperty("video_extension")]
public VideoExtension VideoExtension { get; set; } = VideoExtension.MP4;
[JsonProperty("audio_extension")]
public AudioExtension AudioExtension { get; set; } = AudioExtension.MP3;
[JsonProperty("output_directory")]
public string OutputDirectory { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
[JsonProperty("save_last_output_directory")]
public bool SaveLastOutputDirectory { get; set; } = false;
}
}

View File

@@ -0,0 +1,18 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.Data.Settings.Models
{
public class Notifications
{
[JsonProperty("on_successful")]
public bool OnSuccessful { get; set; } = true;
[JsonProperty("on_unsuccessful")]
public bool OnUnsuccessful { get; set; } = true;
}
}

View File

@@ -0,0 +1,25 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Models;
namespace VDownload.Services.Data.Settings.Models
{
public class Processing
{
[JsonProperty("ffmpeg_location")]
public string FFmpegLocation { get; set; } = $"{AppDomain.CurrentDomain.BaseDirectory}\\FFmpeg";
[JsonProperty("use_multithreading")]
public bool UseMultithreading { get; set; } = true;
[JsonProperty("use_hardware_acceleration")]
public bool UseHardwareAcceleration { get; set; } = true;
[JsonProperty("speed")]
public ProcessingSpeed Speed { get; set; } = ProcessingSpeed.UltraFast;
}
}

View File

@@ -0,0 +1,30 @@
using Newtonsoft.Json;
using VDownload.Sources.Twitch.Settings;
namespace VDownload.Services.Data.Settings
{
public class SettingsData
{
#region PROPERTIES
[JsonProperty("common")]
public CommonSettings Common { get; set; }
[JsonProperty("twitch")]
public TwitchSettings Twitch { get; set; }
#endregion
#region CONSTRUCTORS
internal SettingsData()
{
Common = new CommonSettings();
Twitch = new TwitchSettings();
}
#endregion
}
}

View File

@@ -0,0 +1,104 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Services.Data.Configuration;
namespace VDownload.Services.Data.Settings
{
public interface ISettingsService
{
#region PROPERTIES
SettingsData Data { get; }
#endregion
#region METHODS
Task Load();
Task Save();
Task Restore();
#endregion
}
public class SettingsService : ISettingsService
{
#region SERVICES
protected readonly IConfigurationService _configurationService;
#endregion
#region FIELDS
protected readonly string _filePath;
#endregion
#region PROPERTIES
public SettingsData Data { get; private set; }
#endregion
#region CONSTRUCTORS
public SettingsService(IConfigurationService configurationService)
{
_configurationService = configurationService;
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string appdataDirectoryName = _configurationService.Common.Path.Appdata.DirectoryName;
string appdataAuthenticationFilename = _configurationService.Common.Path.Appdata.SettingsFile;
_filePath = Path.Combine(appdataPath, appdataDirectoryName, appdataAuthenticationFilename);
}
#endregion
#region PUBLIC METHODS
public async Task Load()
{
Data = null;
if (File.Exists(_filePath))
{
string content = await File.ReadAllTextAsync(_filePath);
Data = JsonConvert.DeserializeObject<SettingsData>(content);
}
Data ??= new SettingsData();
}
public async Task Save()
{
Directory.CreateDirectory(Path.GetDirectoryName(_filePath));
string content = JsonConvert.SerializeObject(Data);
await File.WriteAllTextAsync(_filePath, content);
}
public async Task Restore()
{
Data = new SettingsData();
await Save();
}
#endregion
}
}

View File

@@ -0,0 +1,19 @@
<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" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" />
<ProjectReference Include="..\..\..\VDownload.Sources\VDownload.Sources.Twitch\VDownload.Sources.Twitch.Settings\VDownload.Sources.Twitch.Settings.csproj" />
<ProjectReference Include="..\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,93 +0,0 @@
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

@@ -1,14 +0,0 @@
<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

@@ -1,101 +0,0 @@
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

@@ -1,14 +0,0 @@
<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,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.Dialogs
{
public enum DialogResult
{
Primary,
Secondary,
Cancelled
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.Dialogs
{
public enum DialogResultOkCancel
{
Ok,
Cancel
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.Dialogs
{
public enum DialogResultYesNo
{
Yes,
No
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.Dialogs
{
public enum DialogResultYesNoCancel
{
Yes,
No,
Cancelled
}
}

View File

@@ -0,0 +1,121 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.Dialogs
{
public interface IDialogsService
{
#region PROPERTIES
XamlRoot DefaultRoot { get; set; }
#endregion
#region METHODS
Task ShowClose(string title, string message);
Task<DialogResult> ShowDouble(string title, string message, string primaryButtonText, string secondaryButtonText);
Task ShowOk(string title, string message);
Task<DialogResultOkCancel> ShowOkCancel(string title, string message);
Task ShowSingle(string title, string message, string buttonText);
Task<DialogResult> ShowTriple(string title, string message, string primaryButtonText, string secondaryButtonText, string cancelButtonText);
Task<DialogResultYesNo> ShowYesNo(string title, string message);
Task<DialogResultYesNoCancel> ShowYesNoCancel(string title, string message);
#endregion
}
public class DialogsService : IDialogsService
{
#region PROPERTIES
public XamlRoot DefaultRoot { get; set; }
#endregion
#region PUBLIC METHODS
public async Task ShowOk(string title, string message) => await ShowSingle(title, message, "OK");
public async Task ShowClose(string title, string message) => await ShowSingle(title, message, "Close");
public async Task ShowSingle(string title, string message, string buttonText)
{
ContentDialog contentDialog = BuildDialog(title, message);
contentDialog.CloseButtonText = buttonText;
await ShowDialog(contentDialog);
}
public async Task<DialogResultOkCancel> ShowOkCancel(string title, string message) => await ShowDouble(title, message, "OK", "Cancel") switch
{
DialogResult.Primary => DialogResultOkCancel.Ok,
_ => DialogResultOkCancel.Cancel
};
public async Task<DialogResultYesNo> ShowYesNo(string title, string message) => await ShowDouble(title, message, "Yes", "No") switch
{
DialogResult.Primary => DialogResultYesNo.Yes,
_ => DialogResultYesNo.No
};
public async Task<DialogResult> ShowDouble(string title, string message, string primaryButtonText, string secondaryButtonText)
{
ContentDialog contentDialog = BuildDialog(title, message);
contentDialog.PrimaryButtonText = primaryButtonText;
contentDialog.SecondaryButtonText = secondaryButtonText;
return await ShowDialog(contentDialog);
}
public async Task<DialogResultYesNoCancel> ShowYesNoCancel(string title, string message) => await ShowTriple(title, message, "Yes", "No", "Cancel") switch
{
DialogResult.Primary => DialogResultYesNoCancel.Yes,
DialogResult.Secondary => DialogResultYesNoCancel.Yes,
_ => DialogResultYesNoCancel.Cancelled
};
public async Task<DialogResult> ShowTriple(string title, string message, string primaryButtonText, string secondaryButtonText, string cancelButtonText)
{
ContentDialog contentDialog = BuildDialog(title, message);
contentDialog.PrimaryButtonText = primaryButtonText;
contentDialog.SecondaryButtonText = secondaryButtonText;
contentDialog.CloseButtonText = cancelButtonText;
return await ShowDialog(contentDialog);
}
#endregion
#region PRIVATE METHODS
private ContentDialog BuildDialog(string title, string message)
{
return new ContentDialog()
{
Title = title,
Content = message,
XamlRoot = DefaultRoot
};
}
private async Task<DialogResult> ShowDialog(ContentDialog dialog)
{
ContentDialogResult result = await dialog.ShowAsync();
return result switch
{
ContentDialogResult.Primary => DialogResult.Primary,
ContentDialogResult.Secondary => DialogResult.Secondary,
_ => DialogResult.Cancelled
}; ;
}
#endregion
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>VDownload.Services.UI.Dialogs</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,41 @@
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.DictionaryResources
{
public interface IDictionaryResourcesService
{
T Get<T>(string key);
}
public class DictionaryResourcesService : IDictionaryResourcesService
{
#region CONSTRUCTORS
public DictionaryResourcesService() { }
#endregion
#region PUBLIC METHODS
public T Get<T>(string key)
{
Application.Current.Resources.TryGetValue(key, out object value);
if (value is not null && value is T cast)
{
return cast;
}
throw new KeyNotFoundException();
}
#endregion
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>VDownload.Services.UI.DictionaryResources</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.230913002" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,36 @@
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.Notifications
{
public interface INotificationsService
{
void SendNotification(string title, IEnumerable<string> message);
}
public class NotificationsService : INotificationsService
{
#region PUBLIC METHODS
public void SendNotification(string title, IEnumerable<string> message)
{
AppNotificationBuilder builder = new AppNotificationBuilder();
builder.AddText(title);
foreach (string text in message)
{
builder.AddText(text);
}
AppNotification notification = builder.BuildNotification();
AppNotificationManager.Default.Show(notification);
}
#endregion
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>VDownload.Services.UI.Notifications</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.StoragePicker
{
public class FileSavePickerFileTypeChoice
{
#region PROPERTIES
public string Description { get; private set; }
public IEnumerable<string> Extensions { get; private set; }
#endregion
#region CONSTRUCTORS
public FileSavePickerFileTypeChoice(string description, string[] extensions)
{
Description = description;
Extensions = extensions.Select(x => x.StartsWith('.') ? x : $".{x}");
}
#endregion
}
}

View File

@@ -0,0 +1,166 @@
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Pickers;
using WinRT.Interop;
namespace VDownload.Services.UI.StoragePicker
{
public interface IStoragePickerService
{
#region PROPERTIES
Window DefaultRoot { get; set; }
#endregion
#region METHODS
Task<string?> OpenDirectory();
Task<string?> OpenDirectory(StoragePickerStartLocation startLocation);
Task<IEnumerable<string>> OpenMultipleFiles();
Task<IEnumerable<string>> OpenMultipleFiles(StoragePickerStartLocation startLocation);
Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes);
Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes, StoragePickerStartLocation startLocation);
Task<string?> OpenSingleFile();
Task<string?> OpenSingleFile(StoragePickerStartLocation startLocation);
Task<string?> OpenSingleFile(string[] fileTypes);
Task<string?> OpenSingleFile(string[] fileTypes, StoragePickerStartLocation startLocation);
Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType);
Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType, StoragePickerStartLocation startLocation);
#endregion
}
public class StoragePickerService : IStoragePickerService
{
#region PROPERTIES
public Window DefaultRoot { get; set; }
#endregion
#region PUBLIC METHODS
public async Task<string?> OpenDirectory() => await OpenDirectory(StoragePickerStartLocation.Unspecified);
public async Task<string?> OpenDirectory(StoragePickerStartLocation startLocation)
{
FolderPicker picker = new FolderPicker();
InitializePicker(picker);
ConfigureFolderPicker(picker, startLocation);
StorageFolder directory = await picker.PickSingleFolderAsync();
return directory?.Path;
}
public async Task<string?> OpenSingleFile() => await OpenSingleFile(["*"], StoragePickerStartLocation.Unspecified);
public async Task<string?> OpenSingleFile(string[] fileTypes) => await OpenSingleFile(fileTypes, StoragePickerStartLocation.Unspecified);
public async Task<string?> OpenSingleFile(StoragePickerStartLocation startLocation) => await OpenSingleFile(["*"], startLocation);
public async Task<string?> OpenSingleFile(string[] fileTypes, StoragePickerStartLocation startLocation)
{
FileOpenPicker picker = new FileOpenPicker();
InitializePicker(picker);
ConfigureFileOpenPicker(picker, fileTypes, startLocation);
StorageFile storageFile = await picker.PickSingleFileAsync();
return storageFile?.Path;
}
public async Task<IEnumerable<string>> OpenMultipleFiles() => await OpenMultipleFiles(["*"], StoragePickerStartLocation.Unspecified);
public async Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes) => await OpenMultipleFiles(fileTypes, StoragePickerStartLocation.Unspecified);
public async Task<IEnumerable<string>> OpenMultipleFiles(StoragePickerStartLocation startLocation) => await OpenMultipleFiles(["*"], startLocation);
public async Task<IEnumerable<string>> OpenMultipleFiles(string[] fileTypes, StoragePickerStartLocation startLocation)
{
FileOpenPicker picker = new FileOpenPicker();
InitializePicker(picker);
ConfigureFileOpenPicker(picker, fileTypes, startLocation);
IEnumerable<StorageFile> list = await picker.PickMultipleFilesAsync();
return list.Select(x => x.Path);
}
public async Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType) => await SaveFile(fileTypes, defaultFileType, StoragePickerStartLocation.Unspecified);
public async Task<string?> SaveFile(FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType, StoragePickerStartLocation startLocation)
{
FileSavePicker picker = new FileSavePicker();
InitializePicker(picker);
ConfigureFileSavePicker(picker, fileTypes, defaultFileType, startLocation);
StorageFile file = await picker.PickSaveFileAsync();
return file?.Path;
}
#endregion
#region PRIVATE METHODS
protected void InitializePicker(object picker)
{
var hwnd = WindowNative.GetWindowHandle(DefaultRoot);
InitializeWithWindow.Initialize(picker, hwnd);
}
protected void ConfigureFolderPicker(FolderPicker picker, StoragePickerStartLocation startLocation)
{
if (startLocation != StoragePickerStartLocation.Unspecified)
{
picker.SuggestedStartLocation = (PickerLocationId)startLocation;
}
}
protected void ConfigureFileOpenPicker(FileOpenPicker picker, string[] fileTypes, StoragePickerStartLocation startLocation)
{
foreach (string fileType in fileTypes)
{
picker.FileTypeFilter.Add(fileType);
}
if (startLocation != StoragePickerStartLocation.Unspecified)
{
picker.SuggestedStartLocation = (PickerLocationId)startLocation;
}
}
protected void ConfigureFileSavePicker(FileSavePicker picker, FileSavePickerFileTypeChoice[] fileTypes, string defaultFileType, StoragePickerStartLocation startLocation)
{
if (startLocation != StoragePickerStartLocation.Unspecified)
{
picker.SuggestedStartLocation = (PickerLocationId)startLocation;
}
foreach (FileSavePickerFileTypeChoice fileType in fileTypes)
{
picker.FileTypeChoices.Add(fileType.Description, fileType.Extensions.ToList());
}
if (!defaultFileType.StartsWith('.'))
{
defaultFileType = $".{defaultFileType}";
}
if (!fileTypes.Any(x => x.Extensions.Contains(defaultFileType)))
{
picker.FileTypeChoices.Add("Default", [defaultFileType]);
}
picker.DefaultFileExtension = defaultFileType;
}
#endregion
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.StoragePicker
{
public enum StoragePickerStartLocation
{
Documents = 0,
Computer = 1,
Desktop = 2,
Downloads = 3,
Music = 5,
Pictures = 6,
Videos = 7,
Unspecified = 999
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>VDownload.Services.UI.StoragePicker</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,37 @@
using Windows.ApplicationModel.Resources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.StringResources
{
public class StringResources
{
#region FIELDS
protected ResourceLoader _resourceLoader;
#endregion
#region CONSTRUCTORS
internal StringResources(ResourceLoader resourceLoader)
{
_resourceLoader = resourceLoader;
}
#endregion
#region PUBLIC METHODS
public string Get(string key) => _resourceLoader.GetString(key);
#endregion
}
}

View File

@@ -0,0 +1,65 @@
using VDownload.Services.Data.Configuration;
using Windows.ApplicationModel.Resources;
namespace VDownload.Services.UI.StringResources
{
public interface IStringResourcesService
{
StringResources BaseViewResources { get; }
StringResources HomeViewResources { get; }
StringResources HomeVideoViewResources { get; }
StringResources HomeDownloadsViewResources { get; }
StringResources AuthenticationViewResources { get; }
StringResources NotificationsResources { get; }
}
public class StringResourcesService : IStringResourcesService
{
#region SERVICES
protected readonly IConfigurationService _configurationService;
#endregion
#region PROPERTIES
public StringResources BaseViewResources { get; protected set; }
public StringResources HomeViewResources { get; protected set; }
public StringResources HomeVideoViewResources { get; protected set; }
public StringResources HomeDownloadsViewResources { get; protected set; }
public StringResources AuthenticationViewResources { get; protected set; }
public StringResources NotificationsResources { get; protected set; }
#endregion
#region CONSTRUCTORS
public StringResourcesService(IConfigurationService configurationService)
{
_configurationService = configurationService;
BaseViewResources = BuildResource("BaseViewResources");
HomeViewResources = BuildResource("HomeViewResources");
HomeVideoViewResources = BuildResource("HomeVideoViewResources");
HomeDownloadsViewResources = BuildResource("HomeDownloadsViewResources");
AuthenticationViewResources = BuildResource("AuthenticationViewResources");
NotificationsResources = BuildResource("NotificationsResources");
}
#endregion
#region PRIVATE METHODS
protected StringResources BuildResource(string resourceName) => new StringResources(ResourceLoader.GetForViewIndependentUse($"{_configurationService.Common.StringResourcesAssembly}/{resourceName}"));
#endregion
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>VDownload.Services.UI.StringResources</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.230913002" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\VDownload.Core\VDownload.Core.Strings\VDownload.Core.Strings.csproj" />
<ProjectReference Include="..\..\VDownload.Services.Data\VDownload.Services.Data.Configuration\VDownload.Services.Data.Configuration.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>VDownload.Services.UI.WebView</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>
<ItemGroup>
<None Remove="WebViewWindow.xaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
</ItemGroup>
<ItemGroup>
<Page Update="WebViewWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.UI.WebView
{
public interface IWebViewService
{
Task<string> Show(Uri url, Predicate<string> closePredicate, string name);
}
public class WebViewService : IWebViewService
{
#region METHODS
public async Task<string> Show(Uri url, Predicate<string> closePredicate, string name)
{
WebViewWindow window = new WebViewWindow(name);
return await window.Show(url, closePredicate);
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="VDownload.Services.UI.WebView.WebViewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VDownload.Services.UI.WebView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Closed="Window_Closed">
<WebView2 x:Name="WebView" NavigationCompleted="WebView_NavigationCompleted"/>
</Window>

View File

@@ -0,0 +1,89 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Web.WebView2.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
namespace VDownload.Services.UI.WebView
{
public sealed partial class WebViewWindow : Window
{
#region FIEDLS
private readonly Predicate<string> _defaultClosePredicate = args => false;
private bool _isOpened;
private Predicate<string> _closePredicate;
#endregion
#region CONSTRUCTORS
public WebViewWindow(string name)
{
this.InitializeComponent();
this.Title = name;
_isOpened = false;
_closePredicate = _defaultClosePredicate;
}
#endregion
#region PUBLIC METHODS
internal async Task<string> Show(Uri url, Predicate<string> closePredicate)
{
this.WebView.Source = url;
_closePredicate = closePredicate;
this.Activate();
_isOpened = true;
while (_isOpened)
{
await Task.Delay(10);
}
_closePredicate = _defaultClosePredicate;
return this.WebView.Source.ToString();
}
#endregion
#region EVENT HANDLER
private void WebView_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args)
{
if (_closePredicate.Invoke(this.WebView.Source.ToString()))
{
this.Close();
}
}
private void Window_Closed(object sender, WindowEventArgs args)
{
_isOpened = false;
}
#endregion
}
}

View File

@@ -7,7 +7,7 @@ using System.Security.Cryptography;
using System.Security;
using System.Runtime.InteropServices;
namespace VDownload.Services.Encryption
namespace VDownload.Services.Utility.Encryption
{
public interface IEncryptionService
{

View File

@@ -0,0 +1,191 @@
using FFMpegCore;
using FFMpegCore.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using VDownload.Models;
using VDownload.Services.Data.Configuration;
using VDownload.Services.Data.Settings;
namespace VDownload.Services.Utility.FFmpeg
{
public class FFmpegBuilder
{
#region SERVICES
protected readonly IConfigurationService _configurationService;
protected readonly ISettingsService _settingsService;
#endregion
#region FIELDS
protected TimeSpan? _trimStart;
protected TimeSpan? _trimEnd;
protected bool _progressReporterJoined = false;
protected Action<double> _progressReporter;
protected TimeSpan _progressReporterVideoDuration;
protected bool _cancellationTokenJoined = false;
protected CancellationToken _cancellationToken;
protected MediaType _mediaType = MediaType.Original;
protected string _inputFile;
protected string _outputFile;
protected FFOptions _options;
#endregion
#region CONSTRUCTORS
internal FFmpegBuilder(IConfigurationService configurationService, ISettingsService settingsService)
{
_configurationService = configurationService;
_settingsService = settingsService;
_options = new FFOptions
{
BinaryFolder = _settingsService.Data.Common.Processing.FFmpegLocation,
};
}
#endregion
#region PUBLIC METHODS
public FFmpegBuilder SetMediaType(MediaType mediaType)
{
_mediaType = mediaType;
return this;
}
public FFmpegBuilder TrimStart(TimeSpan start)
{
_trimStart = start;
return this;
}
public FFmpegBuilder TrimEnd(TimeSpan end)
{
_trimEnd = end;
return this;
}
public FFmpegBuilder JoinProgressReporter(Action<double> progressReporter, TimeSpan videoDuration)
{
_progressReporterJoined = true;
_progressReporter = progressReporter;
_progressReporterVideoDuration = videoDuration;
return this;
}
public FFmpegBuilder JoinCancellationToken(CancellationToken cancellationToken)
{
_cancellationTokenJoined = true;
_cancellationToken = cancellationToken;
return this;
}
public async Task RunAsync(string inputFile, string outputFile)
{
_inputFile = inputFile;
_outputFile = outputFile;
FFMpegArgumentProcessor ffmpegArguments = FFMpegArguments.FromFileInput(inputFile, true, async (options) => await BuildInputArgumentOptions(options))
.OutputToFile(outputFile, true, async (options) => await BuildOutputArgumentOptions(options));
if (_cancellationTokenJoined)
{
ffmpegArguments = ffmpegArguments.CancellableThrough(_cancellationToken);
}
if (_progressReporterJoined)
{
ffmpegArguments = ffmpegArguments.NotifyOnProgress(_progressReporter, _progressReporterVideoDuration);
}
try
{
await ffmpegArguments.ProcessAsynchronously(true, _options);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
#endregion
#region PRIVATE METHODS
private async Task BuildInputArgumentOptions(FFMpegArgumentOptions options)
{
options.UsingMultithreading(_settingsService.Data.Common.Processing.UseMultithreading);
options.WithSpeedPreset((Speed)_settingsService.Data.Common.Processing.Speed);
if (_settingsService.Data.Common.Processing.UseHardwareAcceleration)
{
options.WithHardwareAcceleration(HardwareAccelerationDevice.Auto);
}
if (_trimStart is not null)
{
options.WithCustomArgument($"-ss {_trimStart}");
}
if (_trimEnd is not null)
{
options.WithCustomArgument($"-to {_trimEnd}");
}
}
private async Task BuildOutputArgumentOptions(FFMpegArgumentOptions options)
{
IMediaAnalysis analysis = await FFProbe.AnalyseAsync(_inputFile, _options);
string audioCodec = analysis.AudioStreams.First().CodecName;
string videoCodec = analysis.VideoStreams.First().CodecName;
string extension = Path.GetExtension(_outputFile).Replace(".", string.Empty);
Data.Configuration.Models.Muxer muxer = _configurationService.Common.Processing.Muxers.First(x => x.Extension == extension);
if (_mediaType != MediaType.OnlyAudio)
{
IEnumerable<string> availableCodecs = muxer.VideoCodecs;
string selectedCodec = availableCodecs.Contains(videoCodec) ? "copy" : availableCodecs.First();
options.WithCustomArgument($"-vcodec {selectedCodec}");
}
else
{
options.WithCustomArgument("-vn");
}
if (_mediaType != MediaType.OnlyVideo)
{
IEnumerable<string> availableCodecs = muxer.AudioCodecs;
string selectedCodec = availableCodecs.Contains(audioCodec) ? "copy" : availableCodecs.First();
options.WithCustomArgument($"-acodec {selectedCodec}");
}
else
{
options.WithCustomArgument("-an");
}
}
#endregion
}
}

View File

@@ -0,0 +1,48 @@
using FFMpegCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VDownload.Services.Data.Configuration;
using VDownload.Services.Data.Settings;
namespace VDownload.Services.Utility.FFmpeg
{
public interface IFFmpegService
{
FFmpegBuilder CreateBuilder();
}
public class FFmpegService : IFFmpegService
{
#region SERVICES
protected readonly IConfigurationService _configurationService;
protected readonly ISettingsService _settingsService;
#endregion
#region CONSTRUCTORS
public FFmpegService(IConfigurationService configurationService, ISettingsService settingsService)
{
_configurationService = configurationService;
_settingsService = settingsService;
}
#endregion
#region PUBLIC METHODS
public FFmpegBuilder CreateBuilder() => new FFmpegBuilder(_configurationService, _settingsService);
#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="FFMpegCore" Version="5.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\VDownload.Models\VDownload.Models.csproj" />
<ProjectReference Include="..\..\VDownload.Services.Data\VDownload.Services.Data.Settings\VDownload.Services.Data.Settings.csproj" />
</ItemGroup>
</Project>

View File

@@ -9,7 +9,7 @@ using System.Xml.Serialization;
using System.Reflection.PortableExecutable;
using System.Collections;
namespace VDownload.Services.HttpClient
namespace VDownload.Services.Utility.HttpClient
{
public interface IHttpClientService
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.HttpClient
namespace VDownload.Services.Utility.HttpClient
{
public enum HttpMethodType
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.HttpClient
namespace VDownload.Services.Utility.HttpClient
{
public class HttpRequest
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VDownload.Services.HttpClient
namespace VDownload.Services.Utility.HttpClient
{
public class Token
{

View File

@@ -8,7 +8,6 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
</Project>

View File

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

View File

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