Compare commits
	
		
			2 commits
		
	
	
		
			7f19615830
			...
			47850c8804
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 47850c8804 | |||
| 182e473d1b | 
					 38 changed files with 1480 additions and 9 deletions
				
			
		
							
								
								
									
										36
									
								
								.github/main.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.github/main.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					name: CI/CD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - main
 | 
				
			||||||
 | 
					      - '*'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  build:
 | 
				
			||||||
 | 
					    runs-on: windows-latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - uses: actions/checkout@v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Setup .NET
 | 
				
			||||||
 | 
					      uses: actions/setup-dotnet@v4
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        dotnet-version: 8.0.x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Restore dependencies
 | 
				
			||||||
 | 
					      run: dotnet restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install tools
 | 
				
			||||||
 | 
					      run: dotnet tool install --global vpk
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    - name: Dotnet publish
 | 
				
			||||||
 | 
					      run: dotnet publish src/Plpext/Plpext.UI/Plpext.UI.csproj -c Release -r win-x64 -o publish
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Vpk pack
 | 
				
			||||||
 | 
					      run: vpk pack --mainExe=Plpext.exe --packId=Plpext --packVersion=1.0.0 --packDir=publish -o build/win
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    - name: Upload artifact
 | 
				
			||||||
 | 
					      uses: softprops/action-gh-release@v2
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        files: build/win/Plpext-win-Setup.exe
 | 
				
			||||||
							
								
								
									
										7
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -8,4 +8,9 @@ artifacts/
 | 
				
			||||||
*.suo
 | 
					*.suo
 | 
				
			||||||
*.userprefs
 | 
					*.userprefs
 | 
				
			||||||
*DS_Store
 | 
					*DS_Store
 | 
				
			||||||
*.sln.ide
 | 
					*.sln.ide
 | 
				
			||||||
 | 
					/build/win
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
 | 
					.vs/
 | 
				
			||||||
 | 
					Releases/
 | 
				
			||||||
 | 
					publish/
 | 
				
			||||||
| 
						 | 
					@ -4,14 +4,14 @@
 | 
				
			||||||
    <TargetFramework>net8.0</TargetFramework>
 | 
					    <TargetFramework>net8.0</TargetFramework>
 | 
				
			||||||
    <ImplicitUsings>enable</ImplicitUsings>
 | 
					    <ImplicitUsings>enable</ImplicitUsings>
 | 
				
			||||||
    <Nullable>enable</Nullable>
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
    <Platforms>x86;x64</Platforms>
 | 
					    <Platforms>x86;x64;AnyCPU</Platforms>
 | 
				
			||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <ProjectReference Include="..\Plpext.Core\Plpext.Core.csproj" />
 | 
					    <ProjectReference Include="..\Plpext.Core\Plpext.Core.csproj" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup Condition="'$(Platform)' == 'x86' Or '$(PlatformTarget)' == 'x86'">
 | 
					  <ItemGroup Condition="'$(Platform)' == 'x86' Or '$(PlatformTarget)' == 'x86' Or '$(Platform)' == 'AnyCPU' Or '$(PlatformTarget)' == 'AnyCPU' ">
 | 
				
			||||||
    <None Include="lib\x86\OpenAL32.dll">
 | 
					    <None Include="lib\x86\OpenAL32.dll">
 | 
				
			||||||
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
 | 
					      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
 | 
				
			||||||
      <TargetPath>OpenAL32.dll</TargetPath>
 | 
					      <TargetPath>OpenAL32.dll</TargetPath>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ namespace Plpext.Core.AudioConverter
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            var audioDuration = (double)(pcmData.Length / 2) / mp3Stream.Frequency;
 | 
					            var audioDuration = (double)(pcmData.Length / 2) / mp3Stream.Frequency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new AudioFile() { Name = baseFile.Name, Data = pcmData, Duration = TimeSpan.FromSeconds(audioDuration), Format = AudioFormat.Mono16, Frequency = mp3Stream.Frequency };
 | 
					            return new AudioFile() { Name = baseFile.Name, MP3Data = file, Data = pcmData, Duration = TimeSpan.FromSeconds(audioDuration), Format = AudioFormat.Mono16, Frequency = mp3Stream.Frequency };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static byte[] ResampleToMono(Span<byte> data)
 | 
					        private static byte[] ResampleToMono(Span<byte> data)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,23 +26,24 @@ public sealed class AudioPlayer : IAudioPlayer, IDisposable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public PlaybackState State { get; private set; } = PlaybackState.Stopped;
 | 
					    public PlaybackState State { get; private set; } = PlaybackState.Stopped;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async Task<bool> InitAudioPlayerAsync(AudioFile input, CancellationToken cancellationToken)
 | 
					    public async Task<bool> InitAudioPlayerAsync(AudioFile input, bool autoStart, CancellationToken cancellationToken)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _audioFile = input;
 | 
					        _audioFile = input;
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _playbackCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
 | 
					        _playbackCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            int bufferId = AL.GenBuffer();
 | 
					            int bufferId = AL.GenBuffer();
 | 
				
			||||||
            int sourceId = AL.GenSource();
 | 
					            int sourceId = AL.GenSource();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            _currentBufferId = bufferId;
 | 
					            _currentBufferId = bufferId;
 | 
				
			||||||
            _currentSourceId = sourceId;
 | 
					            _currentSourceId = sourceId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            AL.BufferData(bufferId, ALFormat.Mono16, input.Data.Span, input.Frequency);
 | 
					            AL.BufferData(bufferId, ALFormat.Mono16, input.Data.Span, input.Frequency);
 | 
				
			||||||
            AL.Source(sourceId, ALSourcei.Buffer, bufferId);
 | 
					            AL.Source(sourceId, ALSourcei.Buffer, bufferId);
 | 
				
			||||||
            return await Start();
 | 
					            if(autoStart)
 | 
				
			||||||
 | 
					                return await Start();
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (Exception ex)
 | 
					        catch (Exception ex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ namespace Plpext.Core.Interfaces;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public interface IAudioPlayer
 | 
					public interface IAudioPlayer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Task<bool> InitAudioPlayerAsync(AudioFile input, CancellationToken cancellationToken);
 | 
					    Task<bool> InitAudioPlayerAsync(AudioFile input, bool autoStart, CancellationToken cancellationToken);
 | 
				
			||||||
    void Resume();
 | 
					    void Resume();
 | 
				
			||||||
    void Pause();
 | 
					    void Pause();
 | 
				
			||||||
    void Stop();
 | 
					    void Stop();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ namespace Plpext.Core.Models
 | 
				
			||||||
    public record AudioFile
 | 
					    public record AudioFile
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public required string Name { get; init; }
 | 
					        public required string Name { get; init; }
 | 
				
			||||||
 | 
					        public ReadOnlyMemory<byte> MP3Data { get; init; } 
 | 
				
			||||||
        public ReadOnlyMemory<byte> Data { get; init; }
 | 
					        public ReadOnlyMemory<byte> Data { get; init; }
 | 
				
			||||||
        public TimeSpan Duration { get; init; }
 | 
					        public TimeSpan Duration { get; init; }
 | 
				
			||||||
        public AudioFormat Format { get; init; }
 | 
					        public AudioFormat Format { get; init; }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/Plpext/Plpext.UI/App.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Plpext/Plpext.UI/App.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					<Application xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
				
			||||||
 | 
					             x:Class="Plpext.UI.App"
 | 
				
			||||||
 | 
					             RequestedThemeVariant="Dark">
 | 
				
			||||||
 | 
					             
 | 
				
			||||||
 | 
					    <Application.Resources>
 | 
				
			||||||
 | 
					        <ResourceDictionary>
 | 
				
			||||||
 | 
					            <ResourceDictionary.MergedDictionaries>
 | 
				
			||||||
 | 
					                <ResourceInclude Source="avares://Plpext/Resources/Colors.axaml"/>
 | 
				
			||||||
 | 
					                <ResourceInclude Source="avares://Plpext/Controls/AudioPlayerControl.axaml"/>
 | 
				
			||||||
 | 
					            </ResourceDictionary.MergedDictionaries>
 | 
				
			||||||
 | 
					        </ResourceDictionary>
 | 
				
			||||||
 | 
					    </Application.Resources>
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    <Application.Styles>
 | 
				
			||||||
 | 
					        <FluentTheme />
 | 
				
			||||||
 | 
					        <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
 | 
				
			||||||
 | 
					        <StyleInclude Source="avares://Plpext/Styles/AudioPlayerControl.axaml"/>
 | 
				
			||||||
 | 
					    </Application.Styles>
 | 
				
			||||||
 | 
					</Application>
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/Plpext/Plpext.UI/App.axaml.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/Plpext/Plpext.UI/App.axaml.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Avalonia;
 | 
				
			||||||
 | 
					using Avalonia.Controls;
 | 
				
			||||||
 | 
					using Avalonia.Controls.ApplicationLifetimes;
 | 
				
			||||||
 | 
					using Avalonia.Data.Core;
 | 
				
			||||||
 | 
					using Avalonia.Data.Core.Plugins;
 | 
				
			||||||
 | 
					using Avalonia.Markup.Xaml;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Plpext.Core.AudioPlayer;
 | 
				
			||||||
 | 
					using Plpext.UI.DependencyInjection;
 | 
				
			||||||
 | 
					using Plpext.UI.ViewModels;
 | 
				
			||||||
 | 
					using Plpext.UI.Views;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class App : Application
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public override void Initialize()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            AvaloniaXamlLoader.Load(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override void OnFrameworkInitializationCompleted()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                DisableAvaloniaDataAnnotationValidation();
 | 
				
			||||||
 | 
					                Container.Services.GetRequiredService<AudioContext>().Initialize();
 | 
				
			||||||
 | 
					                desktop.MainWindow = Container.Services.GetRequiredService<MainWindow>();
 | 
				
			||||||
 | 
					                desktop.MainWindow.DataContext = Container.Services.GetRequiredService<MainWindowViewModel>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            base.OnFrameworkInitializationCompleted();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void DisableAvaloniaDataAnnotationValidation()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var dataValidationPluginsToRemove =
 | 
				
			||||||
 | 
					                BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var plugin in dataValidationPluginsToRemove)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                BindingPlugins.DataValidators.Remove(plugin);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										82
									
								
								src/Plpext/Plpext.UI/Controls/AudioPlayerControl.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/Plpext/Plpext.UI/Controls/AudioPlayerControl.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					<ResourceDictionary
 | 
				
			||||||
 | 
					    xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
				
			||||||
 | 
					    xmlns:controls="using:Plpext.UI"
 | 
				
			||||||
 | 
					    xmlns:vm="clr-namespace:Plpext.UI.ViewModels"
 | 
				
			||||||
 | 
					    xmlns:cv="clr-namespace:Plpext.UI.Controls.Converters">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!--
 | 
				
			||||||
 | 
					        Additional resources
 | 
				
			||||||
 | 
					        Using Control Themes:
 | 
				
			||||||
 | 
					        https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
 | 
				
			||||||
 | 
					        Using Theme Variants:
 | 
				
			||||||
 | 
					        https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					        <Border Padding="10" Background="Brown">
 | 
				
			||||||
 | 
					        <StackPanel Width="200" Spacing="10">
 | 
				
			||||||
 | 
					            <StackPanel Background="{DynamicResource SystemRegionBrush}">
 | 
				
			||||||
 | 
					                <controls:AudioPlayerControl IsPlaying="True" PlaybackState="Stopped" />
 | 
				
			||||||
 | 
					                <controls:AudioPlayerControl IsPlaying="True" PlaybackState="Paused" />
 | 
				
			||||||
 | 
					                <controls:AudioPlayerControl IsPlaying="True" PlaybackState="Playing" />
 | 
				
			||||||
 | 
					                <controls:AudioPlayerControl IsPlaying="False" PlaybackState="Stopped" />
 | 
				
			||||||
 | 
					            </StackPanel>
 | 
				
			||||||
 | 
					        </StackPanel>
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ControlTheme x:Key="{x:Type controls:AudioPlayerControl}" TargetType="controls:AudioPlayerControl"
 | 
				
			||||||
 | 
					                  x:DataType="vm:AudioPlayerViewModel">
 | 
				
			||||||
 | 
					        <Setter Property="Template">
 | 
				
			||||||
 | 
					            <ControlTemplate>
 | 
				
			||||||
 | 
					                <Grid Name="ButtonGrid" ColumnDefinitions="*,*, Auto">
 | 
				
			||||||
 | 
					                    <Grid.Transitions>
 | 
				
			||||||
 | 
					                        <Transitions>
 | 
				
			||||||
 | 
					                            <ThicknessTransition Property="Margin" Duration="0:0:0.3" Easing="SineEaseInOut"/>
 | 
				
			||||||
 | 
					                        </Transitions>
 | 
				
			||||||
 | 
					                    </Grid.Transitions>
 | 
				
			||||||
 | 
					                    <Button Name="PlayButton" Grid.Column="0" IsEnabled="{TemplateBinding IsEnabled}" 
 | 
				
			||||||
 | 
					                            HorizontalAlignment="Center" Width="32" Height="32" 
 | 
				
			||||||
 | 
					                            Padding="6 6 6 6"
 | 
				
			||||||
 | 
					                            CornerRadius="12"
 | 
				
			||||||
 | 
					                            FontSize="{TemplateBinding FontSize}" 
 | 
				
			||||||
 | 
					                            CommandParameter="{TemplateBinding PlayCommandParameter}" Command="{TemplateBinding PlayCommand}">
 | 
				
			||||||
 | 
					                        <Path Fill="{Binding $parent[Button].Foreground}">
 | 
				
			||||||
 | 
					                            <Path.Data>
 | 
				
			||||||
 | 
					                                <Binding Path="PlaybackState" 
 | 
				
			||||||
 | 
					                                         RelativeSource="{RelativeSource TemplatedParent}"
 | 
				
			||||||
 | 
					                                         Converter="{x:Static cv:PlaybackStateToPathConverter.Instance}"/>
 | 
				
			||||||
 | 
					                            </Path.Data>
 | 
				
			||||||
 | 
					                        </Path>
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                    <Button Name="StopButton" Grid.Column="1" IsEnabled="{TemplateBinding IsEnabled}" IsVisible="{TemplateBinding IsPlaying}" 
 | 
				
			||||||
 | 
					                            HorizontalAlignment="Center" Width="32" Height="32" 
 | 
				
			||||||
 | 
					                            Padding="6 6 6 6"
 | 
				
			||||||
 | 
					                            CornerRadius="12"
 | 
				
			||||||
 | 
					                            FontSize="{TemplateBinding FontSize}" 
 | 
				
			||||||
 | 
					                            CommandParameter="{TemplateBinding StopCommandParameter}" Command="{TemplateBinding StopCommand}">
 | 
				
			||||||
 | 
					                        <Path Data="M2,2 H14 V14 H2 Z" Fill="{Binding $parent[Button].Foreground}" />
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                    <StackPanel Grid.Column="2" Name="ProgressPanel" Orientation="Vertical">
 | 
				
			||||||
 | 
					                        <StackPanel.Transitions>
 | 
				
			||||||
 | 
					                            <Transitions>
 | 
				
			||||||
 | 
					                                <DoubleTransition Property="Width" Duration="0:0:0.3" Easing="SineEaseInOut"/>
 | 
				
			||||||
 | 
					                                <DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
 | 
				
			||||||
 | 
					                            </Transitions>
 | 
				
			||||||
 | 
					                        </StackPanel.Transitions>
 | 
				
			||||||
 | 
					                        <ProgressBar Name="ProgressPanelBar" Value="{TemplateBinding Progress}" HorizontalAlignment="Left" CornerRadius="2"/>
 | 
				
			||||||
 | 
					                        <StackPanel Orientation="Horizontal">
 | 
				
			||||||
 | 
					                            <Label FontSize="{TemplateBinding FontSize}"  Content="{TemplateBinding CurrentDuration}"/>
 | 
				
			||||||
 | 
					                            <Label FontSize="{TemplateBinding FontSize}"  Content="/"/>
 | 
				
			||||||
 | 
					                            <Label FontSize="{TemplateBinding FontSize}"  Content="{TemplateBinding TotalDuration}"/>
 | 
				
			||||||
 | 
					                        </StackPanel>
 | 
				
			||||||
 | 
					                    </StackPanel>
 | 
				
			||||||
 | 
					                </Grid>
 | 
				
			||||||
 | 
					            </ControlTemplate>
 | 
				
			||||||
 | 
					        </Setter>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    </ControlTheme>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					</ResourceDictionary>
 | 
				
			||||||
							
								
								
									
										107
									
								
								src/Plpext/Plpext.UI/Controls/AudioPlayerControl.axaml.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/Plpext/Plpext.UI/Controls/AudioPlayerControl.axaml.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,107 @@
 | 
				
			||||||
 | 
					using Avalonia;
 | 
				
			||||||
 | 
					using Avalonia.Controls;
 | 
				
			||||||
 | 
					using Avalonia.Controls.Primitives;
 | 
				
			||||||
 | 
					using System.Windows.Input;
 | 
				
			||||||
 | 
					using Avalonia.Data;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class AudioPlayerControl : TemplatedControl
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<PlaybackState> PlaybackStateProperty =
 | 
				
			||||||
 | 
					        AvaloniaProperty.Register<AudioPlayerControl, PlaybackState>(nameof(PlaybackState), defaultValue: PlaybackState.Unknown, inherits: false, defaultBindingMode: BindingMode.OneWay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PlaybackState PlaybackState
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get {return GetValue(PlaybackStateProperty);}
 | 
				
			||||||
 | 
					        set {SetValue(PlaybackStateProperty, value);}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<bool> IsPlayingProperty =
 | 
				
			||||||
 | 
					        AvaloniaProperty.Register<AudioPlayerControl, bool>(nameof(IsPlaying), false, false, Avalonia.Data.BindingMode.TwoWay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool IsPlaying
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get { return GetValue(IsPlayingProperty); }
 | 
				
			||||||
 | 
					        set { SetValue(IsPlayingProperty, value); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<string> CurrentDurationProperty = AvaloniaProperty.Register<AudioPlayerControl, string>
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        name: nameof(CurrentDuration),
 | 
				
			||||||
 | 
					        defaultValue: "0:55",
 | 
				
			||||||
 | 
					        inherits: false,
 | 
				
			||||||
 | 
					        defaultBindingMode: Avalonia.Data.BindingMode.TwoWay,
 | 
				
			||||||
 | 
					        validate: (x) => x != "0"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string CurrentDuration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get { return GetValue(CurrentDurationProperty); }
 | 
				
			||||||
 | 
					        set { SetValue(CurrentDurationProperty, value); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<string> TotalDurationProperty = AvaloniaProperty.Register<AudioPlayerControl, string>
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        name: nameof(TotalDuration),
 | 
				
			||||||
 | 
					        defaultValue: "1:07",
 | 
				
			||||||
 | 
					        inherits: false,
 | 
				
			||||||
 | 
					        defaultBindingMode: Avalonia.Data.BindingMode.TwoWay,
 | 
				
			||||||
 | 
					        validate: (x) => x != "0"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string TotalDuration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get { return GetValue(TotalDurationProperty); }
 | 
				
			||||||
 | 
					        set { SetValue(TotalDurationProperty, value); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<double> ProgressProperty = AvaloniaProperty.Register<AudioPlayerControl, double>
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        name: nameof(Progress),
 | 
				
			||||||
 | 
					        defaultValue: 0,
 | 
				
			||||||
 | 
					        inherits: false,
 | 
				
			||||||
 | 
					        defaultBindingMode: Avalonia.Data.BindingMode.TwoWay,
 | 
				
			||||||
 | 
					        validate: (x) => x >= 0
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public double Progress
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get { return GetValue(ProgressProperty); }
 | 
				
			||||||
 | 
					        set { SetValue(ProgressProperty, value); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<ICommand?> PlayCommandProperty =
 | 
				
			||||||
 | 
					            AvaloniaProperty.Register<Button, ICommand?>(nameof(PlayCommand));
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<object?> PlayCommandParameterProperty =
 | 
				
			||||||
 | 
					AvaloniaProperty.Register<Button, object?>(nameof(PlayCommandParameter));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ICommand? PlayCommand
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get => GetValue(PlayCommandProperty);
 | 
				
			||||||
 | 
					        set => SetValue(PlayCommandProperty, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public object? PlayCommandParameter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get => GetValue(PlayCommandParameterProperty);
 | 
				
			||||||
 | 
					        set => SetValue(PlayCommandParameterProperty, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<ICommand?> StopCommandProperty =
 | 
				
			||||||
 | 
					        AvaloniaProperty.Register<Button, ICommand?>(nameof(StopCommand));
 | 
				
			||||||
 | 
					    public static readonly StyledProperty<object?> StopCommandParameterProperty =
 | 
				
			||||||
 | 
					        AvaloniaProperty.Register<Button, object?>(nameof(StopCommandParameter));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ICommand? StopCommand
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get => GetValue(StopCommandProperty);
 | 
				
			||||||
 | 
					        set => SetValue(StopCommandProperty, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public object? StopCommandParameter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        get => GetValue(StopCommandParameterProperty);
 | 
				
			||||||
 | 
					        set => SetValue(StopCommandParameterProperty, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using Avalonia.Data.Converters;
 | 
				
			||||||
 | 
					using Avalonia.Media;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Controls.Converters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PlaybackStateToPathConverter : IValueConverter
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static readonly PlaybackStateToPathConverter Instance = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (value is PlaybackState state)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return state == PlaybackState.Playing
 | 
				
			||||||
 | 
					                ? Geometry.Parse("M4,2 H7 V14 H4 Z M11,2 H14 V14 H11 Z")
 | 
				
			||||||
 | 
					                : Geometry.Parse("M5,2 L5,14 L15,8 Z");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Geometry.Parse("M4,2 L4,14 L14,8 Z");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        throw new NotImplementedException();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										63
									
								
								src/Plpext/Plpext.UI/DependencyInjection/Container.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/Plpext/Plpext.UI/DependencyInjection/Container.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,63 @@
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Hosting;
 | 
				
			||||||
 | 
					using Plpext.Core.FileStorage;
 | 
				
			||||||
 | 
					using Plpext.Core.Interfaces;
 | 
				
			||||||
 | 
					using Plpext.Core.MP3Parser;
 | 
				
			||||||
 | 
					using Plpext.Core.PackExtractor;
 | 
				
			||||||
 | 
					using Plpext.UI.ViewModels;
 | 
				
			||||||
 | 
					using Serilog;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Plpext.Core.AudioConverter;
 | 
				
			||||||
 | 
					using Plpext.Core.AudioPlayer;
 | 
				
			||||||
 | 
					using Plpext.UI.Services;
 | 
				
			||||||
 | 
					using Plpext.UI.Services.Converter;
 | 
				
			||||||
 | 
					using Plpext.UI.Services.FileLoader;
 | 
				
			||||||
 | 
					using Plpext.UI.Services.PlatformStorage;
 | 
				
			||||||
 | 
					using Plpext.UI.Views;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.DependencyInjection
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class Container
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private static IServiceProvider? _container;
 | 
				
			||||||
 | 
					        public static IServiceProvider Services
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _container ?? Register();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static IServiceProvider Register()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var hostBuilder = Host
 | 
				
			||||||
 | 
					                .CreateDefaultBuilder()
 | 
				
			||||||
 | 
					                .UseSerilog((context, loggerConfiguration) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    loggerConfiguration.WriteTo.Debug();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .ConfigureServices((context, services) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    services.AddSingleton<MainWindow>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<MainWindowViewModel>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    services.AddTransient<AudioPlayerViewModel>();
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    services.AddSingleton<AudioContext>();
 | 
				
			||||||
 | 
					                    services.AddScoped<IAudioPlayer, AudioPlayer>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IAudioConverter, MP3AudioConverter>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IMP3Parser, MP3Parser>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IPackExtractor, CorePackExtractor>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IFileStorage, DiskFileStorage>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IPlatformStorageService, PlatformStorageService>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IFileLoaderService, FileLoaderService>();
 | 
				
			||||||
 | 
					                    services.AddSingleton<IConvertService, FileConvertService>();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					            hostBuilder.Start();
 | 
				
			||||||
 | 
					            _container = hostBuilder.Services;
 | 
				
			||||||
 | 
					            return _container;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										75
									
								
								src/Plpext/Plpext.UI/Plpext.UI.csproj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/Plpext/Plpext.UI/Plpext.UI.csproj
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,75 @@
 | 
				
			||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <OutputType>WinExe</OutputType>
 | 
				
			||||||
 | 
					    <TargetFramework>net8.0</TargetFramework>
 | 
				
			||||||
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
 | 
					    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
 | 
				
			||||||
 | 
					    <ApplicationManifest>app.manifest</ApplicationManifest>
 | 
				
			||||||
 | 
					    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
 | 
				
			||||||
 | 
					    <Platforms>x86;x64</Platforms>
 | 
				
			||||||
 | 
					    <AssemblyName>Plpext</AssemblyName>
 | 
				
			||||||
 | 
					    <SelfContained>true</SelfContained>
 | 
				
			||||||
 | 
					    <PublishReadyToRun>true</PublishReadyToRun>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <Folder Include="Models\" />
 | 
				
			||||||
 | 
					    <AvaloniaResource Include="Assets\**" />
 | 
				
			||||||
 | 
					    <AvaloniaResource Remove="ViewModels\Mocks\**" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <PackageReference Include="Avalonia" Version="11.2.2" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.2" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Avalonia.Desktop" Version="11.2.2" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.2" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.2" />
 | 
				
			||||||
 | 
					    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
 | 
				
			||||||
 | 
					    <PackageReference Include="Avalonia.Diagnostics" Version="11.2.2">
 | 
				
			||||||
 | 
					      <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
 | 
				
			||||||
 | 
					      <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
 | 
				
			||||||
 | 
					    </PackageReference>
 | 
				
			||||||
 | 
					    <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0-preview3" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Serilog" Version="4.1.0" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Velopack" Version="0.0.942" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\..\Plpext.Core\Plpext.Core.csproj" />
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\..\Plpext.Core.Windows\Plpext.Core.Windows.csproj" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <Compile Remove="ViewModels\Mocks\MockAudioPlayerViewModel.cs" />
 | 
				
			||||||
 | 
					    <Compile Remove="ViewModels\Mocks\**" />
 | 
				
			||||||
 | 
					    <Compile Remove="ViewLocator.cs" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <AvaloniaXaml Remove="ViewModels\Mocks\**" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <EmbeddedResource Remove="ViewModels\Mocks\**" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <AdditionalFiles Remove="ViewModels\Mocks\**" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <None Remove="ViewModels\Mocks\**" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Target Name="CopyOpenALToPublish" AfterTargets="Publish">
 | 
				
			||||||
 | 
					  <Copy SourceFiles="$(MSBuildThisFileDirectory)..\..\Plpext.Core.Windows\bin\x64\Release\net8.0\OpenAL32.dll" 
 | 
				
			||||||
 | 
					        DestinationFolder="$(PublishDir)" />
 | 
				
			||||||
 | 
					  <Message Text="OpenAL32.dll copied to publish directory: $(PublishDir)" Importance="high" />
 | 
				
			||||||
 | 
					</Target>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/Plpext/Plpext.UI/Program.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Plpext/Plpext.UI/Program.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Avalonia;
 | 
				
			||||||
 | 
					using Velopack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    internal sealed class Program
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Initialization code. Don't use any Avalonia, third-party APIs or any
 | 
				
			||||||
 | 
					        // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
 | 
				
			||||||
 | 
					        // yet and stuff might break.
 | 
				
			||||||
 | 
					        [STAThread]
 | 
				
			||||||
 | 
					        public static void Main(string[] args)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            VelopackApp.Build().Run();
 | 
				
			||||||
 | 
					            BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Avalonia configuration, don't remove; also used by visual designer.
 | 
				
			||||||
 | 
					        public static AppBuilder BuildAvaloniaApp()
 | 
				
			||||||
 | 
					            => AppBuilder.Configure<App>()
 | 
				
			||||||
 | 
					                .UsePlatformDetect()
 | 
				
			||||||
 | 
					                .WithInterFont()
 | 
				
			||||||
 | 
					                .LogToTrace();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/Plpext/Plpext.UI/Resources/Colors.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Plpext/Plpext.UI/Resources/Colors.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					<ResourceDictionary xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
				
			||||||
 | 
					    <Color x:Key="PrimaryBackground">#020615</Color>
 | 
				
			||||||
 | 
					    <Color x:Key="PrimaryMiddle">#2D3C81</Color>
 | 
				
			||||||
 | 
					    <Color x:Key="PrimaryForeground">#99A6E5</Color>
 | 
				
			||||||
 | 
					    <Color x:Key="SecondaryDarkest">#1F0400</Color>
 | 
				
			||||||
 | 
					    <Color x:Key="SecondaryDark">#661409</Color>
 | 
				
			||||||
 | 
					    <Color x:Key="SecondaryMiddle">#BD4131</Color>
 | 
				
			||||||
 | 
					    <Color x:Key="SecondaryLightest">#FFAA9F</Color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource PrimaryBackground}" x:Key="PrimaryBackgroundBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource PrimaryMiddle}" x:Key="PrimaryMiddleBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource PrimaryForeground}" x:Key="PrimaryForegroundBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource SecondaryDarkest}" x:Key="SecondaryDarkestBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource SecondaryDark}" x:Key="SecondaryDarkBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource SecondaryMiddle}" x:Key="SecondaryMiddleBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					    <SolidColorBrush Color="{StaticResource SecondaryLightest}" x:Key="SecondaryLightestBrush"></SolidColorBrush>
 | 
				
			||||||
 | 
					</ResourceDictionary>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Plpext.Core.Interfaces;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Services.Converter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class FileConvertService : IConvertService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly IMP3Parser _mp3Parser;
 | 
				
			||||||
 | 
					    private readonly IFileStorage _fileStorage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public FileConvertService(IMP3Parser mp3Parser, IFileStorage fileStorage)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _mp3Parser = mp3Parser;
 | 
				
			||||||
 | 
					        _fileStorage = fileStorage;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task ConvertFilesAsync(IEnumerable<AudioFile> files, string outputFolder)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var mp3Files = new List<MP3File>();
 | 
				
			||||||
 | 
					        foreach (var file in files)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            mp3Files.Add(await _mp3Parser.ParseIntoMP3(file.MP3Data, default));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        await _fileStorage.SaveFilesAsync(mp3Files, outputFolder);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Plpext.Core.AudioPlayer;
 | 
				
			||||||
 | 
					using Plpext.Core.Interfaces;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					using Plpext.UI.ViewModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Services.FileLoader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class FileLoaderService : IFileLoaderService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly IPackExtractor _packExtractor;
 | 
				
			||||||
 | 
					    private readonly IAudioConverter _audioConverter;
 | 
				
			||||||
 | 
					    private IEnumerable<ReadOnlyMemory<byte>> _currentFile = null!;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public FileLoaderService(IPackExtractor packExtractor, IAudioConverter audioConverter)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _packExtractor = packExtractor;
 | 
				
			||||||
 | 
					        _audioConverter = audioConverter;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async Task<int> GetFileCountAsync(string filePath)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _currentFile = await _packExtractor.GetFileListAsync(filePath, default);
 | 
				
			||||||
 | 
					        return _currentFile?.Count() ?? 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async IAsyncEnumerable<AudioPlayerViewModel> LoadFilesAsync()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        foreach (var file in _currentFile)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var audioFile = await _audioConverter.ConvertAudioAsync(file, default);
 | 
				
			||||||
 | 
					            yield return new AudioPlayerViewModel(new AudioPlayer(), audioFile);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/Plpext/Plpext.UI/Services/IConvertService.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Plpext/Plpext.UI/Services/IConvertService.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface IConvertService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public Task ConvertFilesAsync(IEnumerable<AudioFile> files, string outputFolder);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/Plpext/Plpext.UI/Services/IFileLoaderService.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Plpext/Plpext.UI/Services/IFileLoaderService.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Plpext.UI.ViewModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface IFileLoaderService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Task<int> GetFileCountAsync(string filePath);
 | 
				
			||||||
 | 
					    IAsyncEnumerable<AudioPlayerViewModel> LoadFilesAsync();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/Plpext/Plpext.UI/Services/IPlatformStorageService.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Plpext/Plpext.UI/Services/IPlatformStorageService.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface IPlatformStorageService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Task<string> GetOriginFilePath();
 | 
				
			||||||
 | 
					    Task<string> GetTargetFolderPath();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					using Avalonia;
 | 
				
			||||||
 | 
					using Avalonia.Controls.ApplicationLifetimes;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Avalonia.Platform.Storage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Services.PlatformStorage
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class PlatformStorageService : IPlatformStorageService
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public async Task<string> GetOriginFilePath()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop ||
 | 
				
			||||||
 | 
					                desktop.MainWindow?.StorageProvider is not { } provider)
 | 
				
			||||||
 | 
					                return string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var filePath = await provider.OpenFilePickerAsync(new FilePickerOpenOptions()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                AllowMultiple = false,
 | 
				
			||||||
 | 
					                Title = "Select Plus Library Pack",
 | 
				
			||||||
 | 
					                FileTypeFilter = [new ("Plus Library Pack"){Patterns = ["*.plp"]}],
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return filePath.Any() ? filePath[0].Path.AbsolutePath : string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public async Task<string> GetTargetFolderPath()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop ||
 | 
				
			||||||
 | 
					                desktop.MainWindow?.StorageProvider is not { } provider)
 | 
				
			||||||
 | 
					                return string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var tentativeFolder = await provider.TryGetWellKnownFolderAsync(Avalonia.Platform.Storage.WellKnownFolder.Music);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var outputPath = await provider.OpenFolderPickerAsync(new Avalonia.Platform.Storage.FolderPickerOpenOptions()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                AllowMultiple = false,
 | 
				
			||||||
 | 
					                Title = "Select output folder",
 | 
				
			||||||
 | 
					                SuggestedStartLocation = tentativeFolder
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return outputPath.Any() ? outputPath[0].Path.AbsolutePath : string.Empty;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								src/Plpext/Plpext.UI/Styles/AudioPlayerControl.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/Plpext/Plpext.UI/Styles/AudioPlayerControl.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,86 @@
 | 
				
			||||||
 | 
					<Styles xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
				
			||||||
 | 
					        xmlns:c="clr-namespace:Plpext.UI"
 | 
				
			||||||
 | 
					        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
				
			||||||
 | 
					        xmlns:vm="clr-namespace:Plpext.UI.ViewModels">
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					        <Border Padding="20" Background="RoyalBlue" BorderBrush="Black" BorderThickness="1">
 | 
				
			||||||
 | 
					            <StackPanel>
 | 
				
			||||||
 | 
					                <Border Background="Navy" BorderBrush="White" BorderThickness="1">
 | 
				
			||||||
 | 
					                <c:AudioPlayerControl
 | 
				
			||||||
 | 
					                    x:DataType="vm:AudioPlayerViewModel"
 | 
				
			||||||
 | 
					                    IsPlaying="{Binding IsPlaying}"
 | 
				
			||||||
 | 
					                    PlayCommand="{Binding PlayCommand}"
 | 
				
			||||||
 | 
					                    StopCommand="{Binding StopCommand}"
 | 
				
			||||||
 | 
					                    PlaybackState="{Binding PlaybackState}"
 | 
				
			||||||
 | 
					                TotalDuration="0:22"
 | 
				
			||||||
 | 
					                CurrentDuration="0:15"
 | 
				
			||||||
 | 
					                Progress="44.8"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <c:AudioPlayerControl.DataContext>
 | 
				
			||||||
 | 
					                        <vm:AudioPlayerViewModel/>
 | 
				
			||||||
 | 
					                    </c:AudioPlayerControl.DataContext>
 | 
				
			||||||
 | 
					                </c:AudioPlayerControl>
 | 
				
			||||||
 | 
					                    </Border>
 | 
				
			||||||
 | 
					                <c:AudioPlayerControl
 | 
				
			||||||
 | 
					                    x:DataType="vm:AudioPlayerViewModel"
 | 
				
			||||||
 | 
					                    IsPlaying="True"
 | 
				
			||||||
 | 
					                    PlayCommand="{Binding PlayCommand}"
 | 
				
			||||||
 | 
					                    TotalDuration="22"
 | 
				
			||||||
 | 
					                    CurrentDuration="15"
 | 
				
			||||||
 | 
					                    Progress="44.8"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <c:AudioPlayerControl.DataContext>
 | 
				
			||||||
 | 
					                        <vm:AudioPlayerViewModel/>
 | 
				
			||||||
 | 
					                    </c:AudioPlayerControl.DataContext>
 | 
				
			||||||
 | 
					                </c:AudioPlayerControl>
 | 
				
			||||||
 | 
					            </StackPanel>
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl">
 | 
				
			||||||
 | 
					        <Setter Property="FontSize" Value="14"/>
 | 
				
			||||||
 | 
					        <Setter Property="Margin" Value="0,0,0,0"/>
 | 
				
			||||||
 | 
					        <Setter Property="Width" Value="176"></Setter>
 | 
				
			||||||
 | 
					        <Setter Property="Height" Value="36"></Setter>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl Button">
 | 
				
			||||||
 | 
					        <Setter Property="Foreground" Value="{StaticResource SecondaryLightest}"/>
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource SecondaryDarkest}"/>
 | 
				
			||||||
 | 
					        <Setter Property="BorderThickness" Value="2"/>
 | 
				
			||||||
 | 
					        <Setter Property="BorderBrush" Value="{StaticResource SecondaryDark}"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl Button:pointerover">
 | 
				
			||||||
 | 
					        <Setter Property="Foreground" Value="{StaticResource SecondaryMiddle}"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl Button:pointerover /template/ ContentPresenter">
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource SecondaryDark}"/>
 | 
				
			||||||
 | 
					        <Setter Property="BorderThickness" Value="2"/>
 | 
				
			||||||
 | 
					        <Setter Property="BorderBrush" Value="{StaticResource SecondaryDarkest}"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl[IsPlaying=False] /template/ Grid#ButtonGrid">
 | 
				
			||||||
 | 
					        <Setter Property="Margin" Value="142,0,-32,0"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl[IsPlaying=True] /template/ Grid#ButtonGrid">
 | 
				
			||||||
 | 
					        <Setter Property="Margin" Value="0,0,0,0"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl[IsPlaying=False] /template/ StackPanel#ProgressPanel">
 | 
				
			||||||
 | 
					        <Setter Property="Width" Value="0"/>
 | 
				
			||||||
 | 
					        <Setter Property="Opacity" Value="0"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl[IsPlaying=True] /template/ StackPanel#ProgressPanel">
 | 
				
			||||||
 | 
					        <Setter Property="UseLayoutRounding" Value="True"/>
 | 
				
			||||||
 | 
					        <Setter Property="Opacity" Value="1"/>
 | 
				
			||||||
 | 
					        <Setter Property="Margin" Value="6,10,0,0"/>
 | 
				
			||||||
 | 
					        <Setter Property="VerticalAlignment" Value="Center"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl[IsPlaying=False] /template/ ProgressBar:horizontal">
 | 
				
			||||||
 | 
					        <Setter Property="MinWidth" Value="0"/>
 | 
				
			||||||
 | 
					        <Setter Property="Width" Value="0"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="c|AudioPlayerControl[IsPlaying=True] /template/ ProgressBar:horizontal">
 | 
				
			||||||
 | 
					        <Setter Property="MinWidth" Value="0"/>
 | 
				
			||||||
 | 
					        <Setter Property="Width" Value="100"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Styles>
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/Plpext/Plpext.UI/Styles/Border.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Plpext/Plpext.UI/Styles/Border.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					<Styles xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					        <Border Padding="20">
 | 
				
			||||||
 | 
					            <!-- Add Controls for Previewer Here -->
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <Style Selector="Border.Section">
 | 
				
			||||||
 | 
					        <Setter Property="BorderBrush" Value="{StaticResource PrimaryForeground}"/>
 | 
				
			||||||
 | 
					        <Setter Property="BorderThickness" Value="1"/>
 | 
				
			||||||
 | 
					        <Setter Property="BoxShadow" Value="0 0 1 1 Black"/>
 | 
				
			||||||
 | 
					        <Setter Property="CornerRadius" Value="1"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					</Styles>
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/Plpext/Plpext.UI/Styles/Button.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Plpext/Plpext.UI/Styles/Button.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					<Styles xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					        <Border Padding="20">
 | 
				
			||||||
 | 
					            <StackPanel>
 | 
				
			||||||
 | 
					                <Button Classes="Primary" Content="Primary Button" />
 | 
				
			||||||
 | 
					                <Button IsEnabled="False" Classes="Primary" Content="Primary Button" />
 | 
				
			||||||
 | 
					            </StackPanel>            <!-- Add Controls for Previewer Here -->
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <Style Selector="Button.Primary">
 | 
				
			||||||
 | 
					        <Setter Property="BorderThickness" Value="1"/>
 | 
				
			||||||
 | 
					        <Setter Property="BorderBrush" Value="{StaticResource SecondaryDarkestBrush}"/>
 | 
				
			||||||
 | 
					        <Setter Property="Padding" Value="8 12 8 12"/>
 | 
				
			||||||
 | 
					        <Setter Property="CornerRadius" Value="4"/>
 | 
				
			||||||
 | 
					        <Setter Property="Margin" Value="2"/>
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource SecondaryDark}"/>
 | 
				
			||||||
 | 
					        <Setter Property="Foreground" Value="{StaticResource SecondaryLightest}"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="Button.Primary:disabled /template/ ContentPresenter">
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource SecondaryDarkest}"/>
 | 
				
			||||||
 | 
					        <Setter Property="Foreground" Value="Gray"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					    <Style Selector="Button.Primary:pointerover /template/ ContentPresenter">
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource SecondaryLightest}"/>
 | 
				
			||||||
 | 
					        <Setter Property="Foreground" Value="{StaticResource SecondaryDarkest}"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Styles>
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/Plpext/Plpext.UI/Styles/ProgressBar.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Plpext/Plpext.UI/Styles/ProgressBar.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					<Styles xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					        <Border Padding="20">
 | 
				
			||||||
 | 
					            <ProgressBar IsIndeterminate="True"></ProgressBar>
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Style Selector="ProgressBar">
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource SecondaryDark}"/>
 | 
				
			||||||
 | 
					        <Setter Property="Foreground" Value="{StaticResource SecondaryLightest}"/>
 | 
				
			||||||
 | 
					        <Setter Property="CornerRadius" Value="0"/>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					</Styles>
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/Plpext/Plpext.UI/Styles/TextBox.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Plpext/Plpext.UI/Styles/TextBox.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					<Styles xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					        <Border Padding="20">
 | 
				
			||||||
 | 
					            <!-- Add Controls for Previewer Here -->
 | 
				
			||||||
 | 
					            <TextBox Classes="Primary"></TextBox>
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <Style Selector="TextBox.Primary">
 | 
				
			||||||
 | 
					        <Setter Property="Height" Value="32"/>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Styles>
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/Plpext/Plpext.UI/Styles/Window.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Plpext/Plpext.UI/Styles/Window.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					<Styles xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
				
			||||||
 | 
					    <Design.PreviewWith>
 | 
				
			||||||
 | 
					    </Design.PreviewWith>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <Style Selector="Window">
 | 
				
			||||||
 | 
					        <Setter Property="Background" Value="{StaticResource PrimaryBackground}"></Setter>
 | 
				
			||||||
 | 
					    </Style>
 | 
				
			||||||
 | 
					</Styles>
 | 
				
			||||||
							
								
								
									
										120
									
								
								src/Plpext/Plpext.UI/ViewModels/AudioPlayerViewModel.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/Plpext/Plpext.UI/ViewModels/AudioPlayerViewModel.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,120 @@
 | 
				
			||||||
 | 
					using CommunityToolkit.Mvvm.ComponentModel;
 | 
				
			||||||
 | 
					using Plpext.Core.AudioPlayer;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.ComponentModel;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using CommunityToolkit.Mvvm.Input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.ViewModels
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public partial class AudioPlayerViewModel : ViewModelBase, IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly AudioPlayer _audioPlayer = null!;
 | 
				
			||||||
 | 
					        private readonly AudioFile _audioFile = null!;
 | 
				
			||||||
 | 
					        private bool _firstExecution = true;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public AudioFile AudioFile
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get { return _audioFile; }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AudioPlayerViewModel()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AudioPlayerViewModel(AudioPlayer audioPlayer, AudioFile audioFile)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _audioPlayer = audioPlayer;
 | 
				
			||||||
 | 
					            _audioFile = audioFile;
 | 
				
			||||||
 | 
					            CurrentDuration = "0:00";
 | 
				
			||||||
 | 
					            Name = _audioFile.Name;
 | 
				
			||||||
 | 
					            TotalDuration = $"{_audioFile.Duration:m\\:ss}";
 | 
				
			||||||
 | 
					            audioPlayer.OnProgressUpdated += OnProgressUpdated;
 | 
				
			||||||
 | 
					            audioPlayer.OnPlaybackStopped += OnPlaybackStopped;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnPlaybackStopped(object? sender, PlaybackStoppedEventArgs e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            IsPlaying = false;
 | 
				
			||||||
 | 
					            PlaybackState = PlaybackState.Stopped;
 | 
				
			||||||
 | 
					            CurrentDuration = "0:00";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnProgressUpdated(object? sender, PlaybackProgress e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Progress = e.ProgressPercentage;
 | 
				
			||||||
 | 
					            CurrentDuration = $"{e.CurrentPosition:m\\:ss}";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ObservableProperty]
 | 
				
			||||||
 | 
					        private bool _isSelected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ObservableProperty]
 | 
				
			||||||
 | 
					        private bool _isPlaying;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ObservableProperty]
 | 
				
			||||||
 | 
					        private string _totalDuration = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ObservableProperty]
 | 
				
			||||||
 | 
					        private string _currentDuration = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ObservableProperty]
 | 
				
			||||||
 | 
					        private string _name = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ObservableProperty] private PlaybackState _playbackState = PlaybackState.Stopped;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        [ObservableProperty]
 | 
				
			||||||
 | 
					        private double _progress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [RelayCommand]
 | 
				
			||||||
 | 
					        private async Task Play()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (IsPlaying)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (PlaybackState == PlaybackState.Playing)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _audioPlayer.Pause();
 | 
				
			||||||
 | 
					                    PlaybackState = PlaybackState.Paused;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (PlaybackState == PlaybackState.Paused)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _audioPlayer.Resume();
 | 
				
			||||||
 | 
					                    PlaybackState = PlaybackState.Playing;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            IsPlaying = true;
 | 
				
			||||||
 | 
					            if (_firstExecution)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _firstExecution = false;
 | 
				
			||||||
 | 
					                await _audioPlayer.InitAudioPlayerAsync(_audioFile, true, default);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                await _audioPlayer.Start();
 | 
				
			||||||
 | 
					            PlaybackState = PlaybackState.Playing;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [RelayCommand]
 | 
				
			||||||
 | 
					        private Task Stop()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            IsPlaying = false;
 | 
				
			||||||
 | 
					            _audioPlayer.Stop();
 | 
				
			||||||
 | 
					            PlaybackState = PlaybackState.Stopped;
 | 
				
			||||||
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _audioPlayer.OnPlaybackStopped -= OnPlaybackStopped;
 | 
				
			||||||
 | 
					            _audioPlayer.OnProgressUpdated -= OnProgressUpdated;
 | 
				
			||||||
 | 
					            _audioPlayer.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										129
									
								
								src/Plpext/Plpext.UI/ViewModels/MainWindowViewModel.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/Plpext/Plpext.UI/ViewModels/MainWindowViewModel.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					using CommunityToolkit.Mvvm.ComponentModel;
 | 
				
			||||||
 | 
					using CommunityToolkit.Mvvm.Input;
 | 
				
			||||||
 | 
					using Plpext.UI.Services;
 | 
				
			||||||
 | 
					using System.Collections.ObjectModel;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Avalonia.Threading;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.ViewModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public partial class MainWindowViewModel : ViewModelBase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly IPlatformStorageService _platformStorageService = null!;
 | 
				
			||||||
 | 
					    private readonly IFileLoaderService _fileLoaderService = null!;
 | 
				
			||||||
 | 
					    private readonly IConvertService _convertService = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MainWindowViewModel()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MainWindowViewModel(IPlatformStorageService platformStorageService, IFileLoaderService fileLoaderService,
 | 
				
			||||||
 | 
					        IConvertService convertService)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _fileLoaderService = fileLoaderService;
 | 
				
			||||||
 | 
					        _platformStorageService = platformStorageService;
 | 
				
			||||||
 | 
					        _convertService = convertService;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(LoadFileCommand))]
 | 
				
			||||||
 | 
					    private string _originPath = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ConvertAllFilesCommand),nameof(ConvertSelectedFilesCommand))]
 | 
				
			||||||
 | 
					    private string _targetPath = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] private int _totalFilesToExtract;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] private int _filesReady;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] private string _progressBarText = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] private double _progressBarValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] private bool _isProgressBarIndeterminate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] private string _progressBarDetails = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ConvertAllFilesCommand), nameof(ConvertSelectedFilesCommand), nameof(LoadFileCommand))]
 | 
				
			||||||
 | 
					    private bool _showProgressBar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ConvertAllFilesCommand),nameof(ConvertSelectedFilesCommand))]
 | 
				
			||||||
 | 
					    private ObservableCollection<AudioPlayerViewModel> _audioFiles = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [RelayCommand(CanExecute = nameof(CanLoadFile))]
 | 
				
			||||||
 | 
					    private async Task LoadFile()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        TotalFilesToExtract = await _fileLoaderService.GetFileCountAsync(OriginPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Dispatcher.UIThread.InvokeAsync(() =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ProgressBarText = "Loading files...";
 | 
				
			||||||
 | 
					            ProgressBarValue = 0 / (double)TotalFilesToExtract * 100;
 | 
				
			||||||
 | 
					            ShowProgressBar = true;
 | 
				
			||||||
 | 
					            IsProgressBarIndeterminate = false;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await foreach (var item in _fileLoaderService.LoadFilesAsync())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await Dispatcher.UIThread.InvokeAsync(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                AudioFiles.Add(item);
 | 
				
			||||||
 | 
					                FilesReady += 1;
 | 
				
			||||||
 | 
					                ProgressBarValue = FilesReady / (double)TotalFilesToExtract * 100;
 | 
				
			||||||
 | 
					                ProgressBarDetails = $"{FilesReady} of {TotalFilesToExtract} ({ProgressBarValue:00.0}%)";
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Dispatcher.UIThread.InvokeAsync(() => ShowProgressBar = false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private bool CanLoadFile() => !string.IsNullOrEmpty(OriginPath) && !ShowProgressBar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [RelayCommand]
 | 
				
			||||||
 | 
					    private async Task SelectOriginPath()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        OriginPath = await _platformStorageService.GetOriginFilePath();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [RelayCommand]
 | 
				
			||||||
 | 
					    private async Task SelectTargetPath()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        TargetPath = await _platformStorageService.GetTargetFolderPath();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [RelayCommand(CanExecute = nameof(CanConvertAllFiles))]
 | 
				
			||||||
 | 
					    private async Task ConvertAllFiles()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        await Dispatcher.UIThread.InvokeAsync(() =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ProgressBarText = $"Converting {TotalFilesToExtract} files...";
 | 
				
			||||||
 | 
					            IsProgressBarIndeterminate = true;
 | 
				
			||||||
 | 
					            ShowProgressBar = true;
 | 
				
			||||||
 | 
					            ProgressBarDetails = string.Empty;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        await _convertService.ConvertFilesAsync(AudioFiles.Select(x => x.AudioFile), TargetPath);
 | 
				
			||||||
 | 
					        await Dispatcher.UIThread.InvokeAsync(() => ShowProgressBar = false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private bool CanConvertAllFiles() => !string.IsNullOrEmpty(TargetPath) && AudioFiles.Any() && !ShowProgressBar;
 | 
				
			||||||
 | 
					    private bool CanConvertSelectFiles() => CanConvertAllFiles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [RelayCommand(CanExecute = nameof(CanConvertSelectFiles))]
 | 
				
			||||||
 | 
					    private async Task ConvertSelectedFiles()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var filesToConvert = AudioFiles.Where(a => a.IsSelected).Select(x => x.AudioFile).ToArray();
 | 
				
			||||||
 | 
					        if (filesToConvert.Length == 0) return;
 | 
				
			||||||
 | 
					        await Dispatcher.UIThread.InvokeAsync(() =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ProgressBarText = $"Converting {filesToConvert.Length} files...";
 | 
				
			||||||
 | 
					            IsProgressBarIndeterminate = true;
 | 
				
			||||||
 | 
					            ShowProgressBar = true;
 | 
				
			||||||
 | 
					            ProgressBarDetails = string.Empty;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        await _convertService.ConvertFilesAsync(filesToConvert, TargetPath);
 | 
				
			||||||
 | 
					        await Dispatcher.UIThread.InvokeAsync(() => ShowProgressBar = false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,105 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.ComponentModel;
 | 
				
			||||||
 | 
					using System.Runtime.CompilerServices;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Windows.Input;
 | 
				
			||||||
 | 
					using Plpext.Core.AudioPlayer;
 | 
				
			||||||
 | 
					using Plpext.Core.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.ViewModels.Mocks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class MockAudioPlayerViewModel
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private static readonly AudioPlayer _audioPlayer = new AudioPlayer();
 | 
				
			||||||
 | 
					        private static readonly AudioFile _audioFile = new AudioFile()
 | 
				
			||||||
 | 
					        { Name = "Dummy", Duration = TimeSpan.FromSeconds(14), Format = AudioFormat.Mono16, Frequency = 44100 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static MockAudioPlayerViewModel()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Instance = new MockAudioPlayerViewModelInstance();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static MockAudioPlayerViewModelInstance Instance { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public class MockAudioPlayerViewModelInstance : INotifyPropertyChanged
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public MockAudioPlayerViewModelInstance()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PlayCommand = new RelayCommand(async () => await Play());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private bool _isPlaying;
 | 
				
			||||||
 | 
					            public bool IsPlaying
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                get => _isPlaying;
 | 
				
			||||||
 | 
					                set => SetProperty(ref _isPlaying, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private string _totalDuration;
 | 
				
			||||||
 | 
					            public string TotalDuration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                get => _totalDuration;
 | 
				
			||||||
 | 
					                set => SetProperty(ref _totalDuration, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private string _currentDuration;
 | 
				
			||||||
 | 
					            public string CurrentDuration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                get => _currentDuration;
 | 
				
			||||||
 | 
					                set => SetProperty(ref _currentDuration, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private string _name;
 | 
				
			||||||
 | 
					            public string Name
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                get => _name;
 | 
				
			||||||
 | 
					                set => SetProperty(ref _name, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public ICommand PlayCommand { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private async Task Play()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                IsPlaying = !IsPlaying;
 | 
				
			||||||
 | 
					                // Implement play logic here
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public event PropertyChangedEventHandler PropertyChanged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (Equals(field, value)) return false;
 | 
				
			||||||
 | 
					                field = value;
 | 
				
			||||||
 | 
					                OnPropertyChanged(propertyName);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class RelayCommand : ICommand
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly Func<Task> _execute;
 | 
				
			||||||
 | 
					        private readonly Func<bool> _canExecute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RelayCommand(Func<Task> execute, Func<bool> canExecute = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
 | 
				
			||||||
 | 
					            _canExecute = canExecute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async void Execute(object parameter) => await _execute();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public event EventHandler CanExecuteChanged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void RaiseCanExecuteChanged()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/Plpext/Plpext.UI/ViewModels/ViewModelBase.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Plpext/Plpext.UI/ViewModels/ViewModelBase.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					using CommunityToolkit.Mvvm.ComponentModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.ViewModels
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ViewModelBase : ObservableObject
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										187
									
								
								src/Plpext/Plpext.UI/Views/MainWindow.axaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/Plpext/Plpext.UI/Views/MainWindow.axaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,187 @@
 | 
				
			||||||
 | 
					<Window
 | 
				
			||||||
 | 
					    x:Class="Plpext.UI.Views.MainWindow"
 | 
				
			||||||
 | 
					    xmlns="https://github.com/avaloniaui"
 | 
				
			||||||
 | 
					    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
				
			||||||
 | 
					    xmlns:c="using:Plpext.UI"
 | 
				
			||||||
 | 
					    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
				
			||||||
 | 
					    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 | 
				
			||||||
 | 
					    xmlns:vm="using:Plpext.UI.ViewModels"
 | 
				
			||||||
 | 
					    Title="Plpext"
 | 
				
			||||||
 | 
					    d:DesignHeight="450"
 | 
				
			||||||
 | 
					    d:DesignWidth="800"
 | 
				
			||||||
 | 
					    Width="800"
 | 
				
			||||||
 | 
					    Height="450"
 | 
				
			||||||
 | 
					    x:DataType="vm:MainWindowViewModel"
 | 
				
			||||||
 | 
					    Icon="/Assets/plpext.png"
 | 
				
			||||||
 | 
					    mc:Ignorable="d">
 | 
				
			||||||
 | 
					    <Window.Styles>
 | 
				
			||||||
 | 
					        <StyleInclude Source="/Styles/Button.axaml" />
 | 
				
			||||||
 | 
					        <StyleInclude Source="/Styles/AudioPlayerControl.axaml" />
 | 
				
			||||||
 | 
					        <StyleInclude Source="/Styles/Window.axaml" />
 | 
				
			||||||
 | 
					        <StyleInclude Source="/Styles/Border.axaml" />
 | 
				
			||||||
 | 
					        <StyleInclude Source="/Styles/TextBox.axaml" />
 | 
				
			||||||
 | 
					        <StyleInclude Source="/Styles/ProgressBar.axaml" />
 | 
				
			||||||
 | 
					    </Window.Styles>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Design.DataContext>
 | 
				
			||||||
 | 
					        <!--
 | 
				
			||||||
 | 
					            This only sets the DataContext for the previewer in an IDE,
 | 
				
			||||||
 | 
					            to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs)
 | 
				
			||||||
 | 
					        -->
 | 
				
			||||||
 | 
					        <vm:MainWindowViewModel />
 | 
				
			||||||
 | 
					    </Design.DataContext>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Grid
 | 
				
			||||||
 | 
					        Margin="16, 8, 16, 8"
 | 
				
			||||||
 | 
					        ColumnDefinitions="300,32,*"
 | 
				
			||||||
 | 
					        RowDefinitions="Auto,Auto,*">
 | 
				
			||||||
 | 
					        <Border Grid.Row="0"
 | 
				
			||||||
 | 
					                Grid.Column="0" Classes="Section">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <StackPanel Orientation="Vertical">
 | 
				
			||||||
 | 
					                <Grid
 | 
				
			||||||
 | 
					                    Margin="8"
 | 
				
			||||||
 | 
					                    ColumnDefinitions="*"
 | 
				
			||||||
 | 
					                    RowDefinitions="*, Auto, Auto">
 | 
				
			||||||
 | 
					                    <Label Grid.Row="0" Content="Select your .plp pack file:" />
 | 
				
			||||||
 | 
					                    <StackPanel Height="36"
 | 
				
			||||||
 | 
					                                Grid.Row="1"
 | 
				
			||||||
 | 
					                                Orientation="Horizontal"
 | 
				
			||||||
 | 
					                                Spacing="2">
 | 
				
			||||||
 | 
					                        <TextBox
 | 
				
			||||||
 | 
					                            Width="248"
 | 
				
			||||||
 | 
					                            HorizontalContentAlignment="Stretch"
 | 
				
			||||||
 | 
					                            VerticalContentAlignment="Center"
 | 
				
			||||||
 | 
					                            Classes="Primary"
 | 
				
			||||||
 | 
					                            Text="{Binding OriginPath}" />
 | 
				
			||||||
 | 
					                        <Button Classes="Primary" Command="{Binding SelectOriginPathCommand}">
 | 
				
			||||||
 | 
					                            <TextBlock Height="24">...</TextBlock>
 | 
				
			||||||
 | 
					                        </Button>
 | 
				
			||||||
 | 
					                    </StackPanel>
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                        Height="38"
 | 
				
			||||||
 | 
					                        Grid.Row="2"
 | 
				
			||||||
 | 
					                        HorizontalAlignment="Right"
 | 
				
			||||||
 | 
					                        Margin="0 4 0 0"
 | 
				
			||||||
 | 
					                        Classes="Primary"
 | 
				
			||||||
 | 
					                        Command="{Binding LoadFileCommand}">
 | 
				
			||||||
 | 
					                        <TextBlock VerticalAlignment="Center">Load</TextBlock>
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                </Grid>
 | 
				
			||||||
 | 
					                <Grid
 | 
				
			||||||
 | 
					                    Margin="8"
 | 
				
			||||||
 | 
					                    ColumnDefinitions="*, *"
 | 
				
			||||||
 | 
					                    RowDefinitions="*, Auto, Auto">
 | 
				
			||||||
 | 
					                    <Label
 | 
				
			||||||
 | 
					                        Grid.Row="0"
 | 
				
			||||||
 | 
					                        Grid.Column="0"
 | 
				
			||||||
 | 
					                        Grid.ColumnSpan="2"
 | 
				
			||||||
 | 
					                        Content="Select output folder:" />
 | 
				
			||||||
 | 
					                    <StackPanel
 | 
				
			||||||
 | 
					                        Height="36"
 | 
				
			||||||
 | 
					                        Grid.Row="1"
 | 
				
			||||||
 | 
					                        Grid.Column="0"
 | 
				
			||||||
 | 
					                        Grid.ColumnSpan="2"
 | 
				
			||||||
 | 
					                        Orientation="Horizontal"
 | 
				
			||||||
 | 
					                        Spacing="2">
 | 
				
			||||||
 | 
					                        <TextBox
 | 
				
			||||||
 | 
					                            Width="248"
 | 
				
			||||||
 | 
					                            VerticalContentAlignment="Center"
 | 
				
			||||||
 | 
					                            Classes="Primary"
 | 
				
			||||||
 | 
					                            Text="{Binding TargetPath}" />
 | 
				
			||||||
 | 
					                        <Button Classes="Primary" Command="{Binding SelectTargetPathCommand}">
 | 
				
			||||||
 | 
					                            <TextBlock Height="24">...</TextBlock>
 | 
				
			||||||
 | 
					                        </Button>
 | 
				
			||||||
 | 
					                    </StackPanel>
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                        HorizontalAlignment="Left"
 | 
				
			||||||
 | 
					                        Height="38"
 | 
				
			||||||
 | 
					                        Grid.Row="2"
 | 
				
			||||||
 | 
					                        Grid.Column="0"
 | 
				
			||||||
 | 
					                        Classes="Primary"
 | 
				
			||||||
 | 
					                        Command="{Binding ConvertAllFilesCommand}">
 | 
				
			||||||
 | 
					                        <TextBlock  VerticalAlignment="Center">Extract All</TextBlock>
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                        Height="38"
 | 
				
			||||||
 | 
					                        HorizontalAlignment="Right"
 | 
				
			||||||
 | 
					                        Grid.Row="2"
 | 
				
			||||||
 | 
					                        Grid.Column="1"
 | 
				
			||||||
 | 
					                        Classes="Primary"
 | 
				
			||||||
 | 
					                        Command="{Binding ConvertSelectedFilesCommand}">
 | 
				
			||||||
 | 
					                        <TextBlock VerticalAlignment="Center">Extract Selected</TextBlock>
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                </Grid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </StackPanel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </Border>
 | 
				
			||||||
 | 
					        <Grid Grid.Row="2" Grid.Column="0">
 | 
				
			||||||
 | 
					            <StackPanel Orientation="Vertical" VerticalAlignment="Center" Height="60" IsVisible="{Binding ShowProgressBar}">
 | 
				
			||||||
 | 
					            <Label Content="{Binding ProgressBarText}"/>
 | 
				
			||||||
 | 
					                <Border Classes="Section">
 | 
				
			||||||
 | 
					                    <ProgressBar Width="298" Orientation="Horizontal" Height="50" IsIndeterminate="{Binding IsProgressBarIndeterminate}" Value="{Binding ProgressBarValue}" />
 | 
				
			||||||
 | 
					                </Border>
 | 
				
			||||||
 | 
					            <Label Content="{Binding ProgressBarDetails}" HorizontalAlignment="Right"/>
 | 
				
			||||||
 | 
					            </StackPanel>
 | 
				
			||||||
 | 
					        </Grid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Grid
 | 
				
			||||||
 | 
					            Grid.Row="0"
 | 
				
			||||||
 | 
					            Grid.RowSpan="3"
 | 
				
			||||||
 | 
					            Grid.Column="2">
 | 
				
			||||||
 | 
					            <StackPanel Orientation="Vertical">
 | 
				
			||||||
 | 
					                <StackPanel Orientation="Horizontal">
 | 
				
			||||||
 | 
					                    <Label Content="Audio Files" />
 | 
				
			||||||
 | 
					                </StackPanel>
 | 
				
			||||||
 | 
					                <DataGrid
 | 
				
			||||||
 | 
					                    MaxHeight="410"
 | 
				
			||||||
 | 
					                    CanUserSortColumns="False"
 | 
				
			||||||
 | 
					                    CanUserResizeColumns="False"
 | 
				
			||||||
 | 
					                    CanUserReorderColumns="False"
 | 
				
			||||||
 | 
					                    AreRowDetailsFrozen="True"
 | 
				
			||||||
 | 
					                    HeadersVisibility="None"
 | 
				
			||||||
 | 
					                    AutoGenerateColumns="False"
 | 
				
			||||||
 | 
					                    VerticalScrollBarVisibility="Visible"
 | 
				
			||||||
 | 
					                    IsReadOnly="False"
 | 
				
			||||||
 | 
					                    SelectionMode="Extended"
 | 
				
			||||||
 | 
					                    ItemsSource="{Binding AudioFiles}">
 | 
				
			||||||
 | 
					                    <DataGrid.Columns>
 | 
				
			||||||
 | 
					                        <DataGridTemplateColumn Width="36">
 | 
				
			||||||
 | 
					                            <DataGridTemplateColumn.CellTemplate>
 | 
				
			||||||
 | 
					                                <DataTemplate>
 | 
				
			||||||
 | 
					                                    <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
 | 
				
			||||||
 | 
					                                              HorizontalAlignment="Center"
 | 
				
			||||||
 | 
					                                              VerticalAlignment="Center" />
 | 
				
			||||||
 | 
					                                </DataTemplate>
 | 
				
			||||||
 | 
					                            </DataGridTemplateColumn.CellTemplate>
 | 
				
			||||||
 | 
					                        </DataGridTemplateColumn>
 | 
				
			||||||
 | 
					                        <DataGridTemplateColumn Width="*">
 | 
				
			||||||
 | 
					                            <DataGridTemplateColumn.CellTemplate>
 | 
				
			||||||
 | 
					                                <DataTemplate DataType="vm:AudioPlayerViewModel">
 | 
				
			||||||
 | 
					                                    <Label VerticalAlignment="Center" Content="{Binding Name}" />
 | 
				
			||||||
 | 
					                                </DataTemplate>
 | 
				
			||||||
 | 
					                            </DataGridTemplateColumn.CellTemplate>
 | 
				
			||||||
 | 
					                        </DataGridTemplateColumn>
 | 
				
			||||||
 | 
					                        <DataGridTemplateColumn Width="200" MinWidth="200">
 | 
				
			||||||
 | 
					                            <DataGridTemplateColumn.CellTemplate>
 | 
				
			||||||
 | 
					                                <DataTemplate DataType="vm:AudioPlayerViewModel">
 | 
				
			||||||
 | 
					                                    <c:AudioPlayerControl
 | 
				
			||||||
 | 
					                                        Margin="0 0 16 0"
 | 
				
			||||||
 | 
					                                        CurrentDuration="{Binding CurrentDuration}"
 | 
				
			||||||
 | 
					                                        TotalDuration="{Binding TotalDuration}"
 | 
				
			||||||
 | 
					                                        PlayCommand="{Binding PlayCommand}"
 | 
				
			||||||
 | 
					                                        StopCommand="{Binding StopCommand}"
 | 
				
			||||||
 | 
					                                        IsPlaying="{Binding IsPlaying}"
 | 
				
			||||||
 | 
					                                        PlaybackState="{Binding PlaybackState}"
 | 
				
			||||||
 | 
					                                        Progress="{Binding Progress}" />
 | 
				
			||||||
 | 
					                                </DataTemplate>
 | 
				
			||||||
 | 
					                            </DataGridTemplateColumn.CellTemplate>
 | 
				
			||||||
 | 
					                        </DataGridTemplateColumn>
 | 
				
			||||||
 | 
					                    </DataGrid.Columns>
 | 
				
			||||||
 | 
					                </DataGrid>
 | 
				
			||||||
 | 
					            </StackPanel>
 | 
				
			||||||
 | 
					        </Grid>
 | 
				
			||||||
 | 
					    </Grid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Window>
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/Plpext/Plpext.UI/Views/MainWindow.axaml.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Plpext/Plpext.UI/Views/MainWindow.axaml.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					using Avalonia.Controls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Plpext.UI.Views
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public partial class MainWindow : Window
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public MainWindow()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            InitializeComponent();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/Plpext/Plpext.UI/app.manifest
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Plpext/Plpext.UI/app.manifest
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
 | 
				
			||||||
 | 
					  <!-- This manifest is used on Windows only.
 | 
				
			||||||
 | 
					       Don't remove it as it might cause problems with window transparency and embedded controls.
 | 
				
			||||||
 | 
					       For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
 | 
				
			||||||
 | 
					  <assemblyIdentity version="1.0.0.0" name="Plpext.UI.Desktop"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
 | 
				
			||||||
 | 
					    <application>
 | 
				
			||||||
 | 
					      <!-- A list of the Windows versions that this application has been tested on
 | 
				
			||||||
 | 
					           and is designed to work with. Uncomment the appropriate elements
 | 
				
			||||||
 | 
					           and Windows will automatically select the most compatible environment. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <!-- Windows 10 -->
 | 
				
			||||||
 | 
					      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
 | 
				
			||||||
 | 
					    </application>
 | 
				
			||||||
 | 
					  </compatibility>
 | 
				
			||||||
 | 
					</assembly>
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/Plpext/Plpext.UI/velopack.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/Plpext/Plpext.UI/velopack.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "version": "1.0.0",
 | 
				
			||||||
 | 
					    "executable": "Plpext.exe",
 | 
				
			||||||
 | 
					    "iconFile": "Assets/plpext.png",
 | 
				
			||||||
 | 
					    "splashImage": "Assets/plpext.png"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
							
								
								
									
										62
									
								
								src/Plpext/Plpext.sln
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/Plpext/Plpext.sln
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Microsoft Visual Studio Solution File, Format Version 12.00
 | 
				
			||||||
 | 
					# Visual Studio Version 17
 | 
				
			||||||
 | 
					VisualStudioVersion = 17.12.35521.163
 | 
				
			||||||
 | 
					MinimumVisualStudioVersion = 10.0.40219.1
 | 
				
			||||||
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plpext.UI", "Plpext.UI\Plpext.UI.csproj", "{CEC79B8A-12B4-4649-B859-08051B00FA96}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plpext.Core", "..\Plpext.Core\Plpext.Core.csproj", "{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plpext.Core.Windows", "..\Plpext.Core.Windows\Plpext.Core.Windows.csproj", "{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
 | 
					Global
 | 
				
			||||||
 | 
						GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
				
			||||||
 | 
							Debug|Any CPU = Debug|Any CPU
 | 
				
			||||||
 | 
							Debug|x64 = Debug|x64
 | 
				
			||||||
 | 
							Debug|x86 = Debug|x86
 | 
				
			||||||
 | 
							Release|Any CPU = Release|Any CPU
 | 
				
			||||||
 | 
							Release|x64 = Release|x64
 | 
				
			||||||
 | 
							Release|x86 = Release|x86
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Debug|x64.ActiveCfg = Debug|x64
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Debug|x64.Build.0 = Debug|x64
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Debug|x86.ActiveCfg = Debug|x86
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Debug|x86.Build.0 = Debug|x86
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Release|x64.ActiveCfg = Release|x64
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Release|x64.Build.0 = Release|x64
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Release|x86.ActiveCfg = Release|x86
 | 
				
			||||||
 | 
							{CEC79B8A-12B4-4649-B859-08051B00FA96}.Release|x86.Build.0 = Release|x86
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Debug|x64.ActiveCfg = Debug|x64
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Debug|x64.Build.0 = Debug|x64
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Debug|x86.ActiveCfg = Debug|x86
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Debug|x86.Build.0 = Debug|x86
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Release|x64.ActiveCfg = Release|x64
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Release|x64.Build.0 = Release|x64
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Release|x86.ActiveCfg = Release|x86
 | 
				
			||||||
 | 
							{2A0B3CBC-C79F-4B19-93AE-BCEEE44BDAD2}.Release|x86.Build.0 = Release|x86
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Debug|x64.ActiveCfg = Debug|x64
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Debug|x64.Build.0 = Debug|x64
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Debug|x86.ActiveCfg = Debug|x86
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Debug|x86.Build.0 = Debug|x86
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Release|x64.ActiveCfg = Release|x64
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Release|x64.Build.0 = Release|x64
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Release|x86.ActiveCfg = Release|x86
 | 
				
			||||||
 | 
							{CEF95E1F-8E07-4B32-A5D9-EE980AF5FBC4}.Release|x86.Build.0 = Release|x86
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
						GlobalSection(SolutionProperties) = preSolution
 | 
				
			||||||
 | 
							HideSolutionNode = FALSE
 | 
				
			||||||
 | 
						EndGlobalSection
 | 
				
			||||||
 | 
					EndGlobal
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue