diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..56d0e8a --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DISCORD_APP_ID=your_app_id_here \ No newline at end of file diff --git a/.gitignore b/.gitignore index 370da60..27a3a5b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +*.env + # User-specific files *.rsuser *.suo diff --git a/DiscordPresence.cs b/DiscordPresence.cs new file mode 100644 index 0000000..3c22c3d --- /dev/null +++ b/DiscordPresence.cs @@ -0,0 +1,40 @@ +using DiscordRPC; +using DiscordRPC.Logging; +using System; + +class DiscordPresence +{ + private static DiscordRpcClient client; + + public static void Init(string clientId) + { + client = new DiscordRpcClient(clientId); + client.Logger = new ConsoleLogger() { Level = LogLevel.Warning }; + client.Initialize(); + } + + public static void SetPresence(string image, string texte, string sousTexte) + { + if (client == null) + { + Console.WriteLine("Erreur : Discord RPC non initialisé. Appelle Init(clientId) avant."); + return; + } + + client.SetPresence(new RichPresence() + { + Details = texte, + State = sousTexte, + Assets = new Assets() + { + LargeImageKey = image, + LargeImageText = texte + } + }); + } + + public static void Close() + { + client.Dispose(); + } +} \ No newline at end of file diff --git a/Folder.DotSettings.user b/Folder.DotSettings.user new file mode 100644 index 0000000..e1d286d --- /dev/null +++ b/Folder.DotSettings.user @@ -0,0 +1,6 @@ + + <AssemblyExplorer> + <Assembly Path="/home/kali/RiderProjects/Meio/discord_social_sdk/bin/release/discord_partner_sdk.dll" /> +</AssemblyExplorer> \ No newline at end of file diff --git a/Meio.Api/Api.cs b/Meio.Api/Api.cs new file mode 100644 index 0000000..22dcb45 --- /dev/null +++ b/Meio.Api/Api.cs @@ -0,0 +1,50 @@ +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; +using PrettyLogging.Console; + +namespace Meio.Api; + +public class Api +{ + private static ILoggerFactory? LoggerFactory { get; set; } + + public static ILogger? Logger { get; private set; } + + [ModuleInitializer] + public static void Init() + { + // Create and configure PrettyLogger + LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => + { + builder.ClearProviders(); + builder.AddPrettyConsole(opt => + { + opt.ShowLogLevel = true; + opt.ShowEventId = false; + opt.ShowManagedThreadId = false; + opt.SingleLine = true; + opt.IncludeScopes = true; + opt.ShowTimestamp = true; + opt.LogLevelCase = LogLevelCase.Upper; + opt.CategoryMode = LoggerCategoryMode.Short; + opt.ColorBehavior = LoggerColorBehavior.Enabled; + opt.UseUtcTimestamp = false; + }); + +#if DEBUG + builder.SetMinimumLevel(LogLevel.Trace); +#else + builder.SetMinimumLevel(LogLevel.Information); +#endif + }); + + Logger = LoggerFactory.CreateLogger(); + Logger.LogInformation("Meio API started."); + } + + public static void Main() + { + Init(); + } +} \ No newline at end of file diff --git a/Meio.Api/Meio.Api.csproj b/Meio.Api/Meio.Api.csproj new file mode 100644 index 0000000..cefad2b --- /dev/null +++ b/Meio.Api/Meio.Api.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + + + + + + + + + + + + diff --git a/Meio.app/Services/AudioMetadataService.cs b/Meio.Api/Services/AudioMetadataService.cs similarity index 54% rename from Meio.app/Services/AudioMetadataService.cs rename to Meio.Api/Services/AudioMetadataService.cs index 26cbabe..42e5c3c 100644 --- a/Meio.app/Services/AudioMetadataService.cs +++ b/Meio.Api/Services/AudioMetadataService.cs @@ -1,21 +1,23 @@ +using System.IO; using Microsoft.Extensions.Logging; using TagLib; +using File = TagLib.File; -namespace Meio.app.Services; +namespace Meio.Api.Services; public class MetadataInfo { - public string? Title { get; set; } + public string? Title { get; init; } - public string[]? Artists { get; set; } + public string[]? Artists { get; init; } - public string? Album { get; set; } + public string? Album { get; init; } - public uint Year { get; set; } + public uint Year { get; init; } - public string[]? Genres { get; set; } + public string[]? Genres { get; init; } - public byte[]? AlbumArt { get; set; } + public byte[]? AlbumArt { get; init; } } public static class AudioMetadataService @@ -32,13 +34,13 @@ public static class AudioMetadataService var file = File.Create(filePath); var tag = file.Tag; - App.Logger?.LogDebug("Asked for the metadata of {filePath}.", filePath); - App.Logger?.LogTrace("Title: {tagTitle}", tag.Title); - App.Logger?.LogTrace("Artists: {tagArtists}", string.Join(", ", tag.AlbumArtists)); - App.Logger?.LogTrace("Album: {tagAlbum}", tag.Album); - App.Logger?.LogTrace("Year: {tagYear}", tag.Year); - App.Logger?.LogTrace("Genre: {tagGenres}", string.Join(", ", tag.Genres)); - App.Logger?.LogTrace(tag.Pictures.Length > 0 ? "Got an album art." : "No album art was found."); + Api.Logger?.LogDebug("Current metadata of {filePath}.", filePath); + Api.Logger?.LogTrace("Title: {tagTitle}", tag.Title); + Api.Logger?.LogTrace("Artists: {tagArtists}", string.Join(", ", tag.AlbumArtists)); + Api.Logger?.LogTrace("Album: {tagAlbum}", tag.Album); + Api.Logger?.LogTrace("Year: {tagYear}", tag.Year); + Api.Logger?.LogTrace("Genre: {tagGenres}", string.Join(", ", tag.Genres)); + Api.Logger?.LogTrace(tag.Pictures.Length > 0 ? "Got an album art." : "No album art was found."); return new MetadataInfo { @@ -52,12 +54,17 @@ public static class AudioMetadataService } catch (CorruptFileException corruptFileException) { - App.Logger?.LogError(corruptFileException, "Failed to load metadata. File is corrupted."); + Api.Logger?.LogError(corruptFileException, "Failed to load metadata. File is corrupted."); return null; } catch (UnsupportedFormatException unsupportedFormatException) { - App.Logger?.LogError(unsupportedFormatException, "Failed to load metadata. File format is unsupported."); + Api.Logger?.LogError(unsupportedFormatException, "Failed to load metadata. File format is unsupported."); + return null; + } + catch (FileNotFoundException fileNotFoundException) + { + Api.Logger?.LogError(fileNotFoundException, "Failed to load metadata. File not found."); return null; } } diff --git a/Meio.Api/Services/AudioPlayerService.cs b/Meio.Api/Services/AudioPlayerService.cs new file mode 100644 index 0000000..0735684 --- /dev/null +++ b/Meio.Api/Services/AudioPlayerService.cs @@ -0,0 +1,188 @@ +using System; +using LibVLCSharp.Shared; +using Microsoft.Extensions.Logging; + +// ReSharper disable UnusedMember.Global + +namespace Meio.Api.Services; + +public class AudioPlayerService : IDisposable +{ + // ReSharper disable once InconsistentNaming + private readonly LibVLC _libVLC; + private readonly MediaPlayer _mediaPlayer; + + public AudioPlayerService() + { + try + { + _libVLC = new LibVLC(); + _mediaPlayer = new MediaPlayer(_libVLC); + Api.Logger!.LogDebug("Initialized AudioPlayerService."); + } + catch (VLCException ex) + { + Api.Logger!.LogCritical("An error occured trying to initialize AudioPlayerService. {Exception}", ex.Message); + Api.Logger!.LogInformation( + "!! READ THIS !!\n If you are on a linux host, please make sure you have installed the libvlc library, you will not be able to read any audio otherwise !!!"); + Environment.Exit(-1); + } + } + + /// + /// To be called when done using. + /// + public void Dispose() + { + _mediaPlayer.Stop(); // Make sure it stops first to avoid potential unwanted behaviour. + _mediaPlayer.Dispose(); + Api.Logger!.LogTrace("Disposed media player."); + + _libVLC.Dispose(); + Api.Logger!.LogTrace("Disposed libVLC."); + + GC.SuppressFinalize(this); // Dispose AudioPlayerService. + Api.Logger!.LogDebug("Disposed."); + + Environment.Exit(0); // Exit after this. + } + + /// + /// Starts playing the given audio file. + /// + /// Audio file path. + public Media? Play(string audioFilePath) + { + try + { + if (_mediaPlayer.IsPlaying) + { + Api.Logger!.LogError("An audio file is already being played. Please stop it first."); + return null; + } + + var media = new Media(_libVLC, audioFilePath); + + _mediaPlayer.Play(media); + Api.Logger!.LogInformation("Playing media file {AudioFilePath} .", audioFilePath); + + _mediaPlayer.EndReached += (_, _) => + { + Api.Logger!.LogDebug("Media playback ended."); + // _mediaPlayer.Stop() + }; + + return media; + } + catch (Exception e) + { + Api.Logger?.LogError("An error occured trying to play the audio file. {e}", e.Message); + + return null; + } + } + + /// + /// Starts playing the given audio file. + /// + /// Audio file Uri. + public Media? Play(Uri audioUri) + { + try + { + if (_mediaPlayer.IsPlaying) + { + Api.Logger!.LogError("An audio file is already being played. Please stop it first."); + return null; + } + + var media = new Media(_libVLC, audioUri.AbsolutePath, FromType.FromLocation); + + _mediaPlayer.Play(media); + Api.Logger!.LogInformation("Playing media file from url {AudioFilePath}.", audioUri.AbsolutePath); + + return media; + } + catch (Exception e) + { + Api.Logger?.LogError("An error occured trying to play the audio file from url. {e}", e.Message); + return null; + } + } + + /// + /// Stops the current reading audio file. + /// + public void Stop() + { + if (!_mediaPlayer.IsPlaying) + { + Api.Logger!.LogError("Cannot stop the media player. No media is playing."); + } + + _mediaPlayer.Stop(); + _mediaPlayer.Media?.Dispose(); + Api.Logger!.LogDebug("Stopped media player."); + } + + /// + /// Pauses the current reading audio file. + /// + public void Pause() + { + _mediaPlayer.Pause(); + Api.Logger!.LogDebug("Paused media player."); + } + + /// + /// Changes the volume of the current reading audio file. + /// + /// New volume of the media player. + public void ChangeVolume(int newVolume) + { + try + { + _mediaPlayer.Volume = newVolume; + Api.Logger!.LogTrace("Changed audio volume to {NewAudioVolume}.", newVolume); + } + catch (Exception e) + { + Api.Logger!.LogError("An error occured trying to change audio volume. {Exception}", e.Message); + } + } + + /// + /// Changes the playback speed of the current playing media. + /// + /// New playback speed. + public void ChangePlaybackSpeed(float speed) + { + if (!_mediaPlayer.IsPlaying) + { + Api.Logger!.LogError("Cannot change audio rate, no media is playing."); + } + else + { + _mediaPlayer.SetRate(speed); + Api.Logger!.LogTrace("Changed audio rate to {Speed}.", speed); + } + } + + /// + /// Mute the current reading audio file. + /// + public void Mute() + { + _mediaPlayer.Mute = true; + Api.Logger!.LogDebug("Muted media."); + } + + /// + /// Unmute the current reading audio file. + /// + public void Unmute() + { + _mediaPlayer.Mute = false; + Api.Logger!.LogDebug("Unuted media."); + } +} \ No newline at end of file diff --git a/Meio.app/App.axaml b/Meio.App/App.axaml similarity index 97% rename from Meio.app/App.axaml rename to Meio.App/App.axaml index 7bc5830..fce12ed 100644 --- a/Meio.app/App.axaml +++ b/Meio.App/App.axaml @@ -1,10 +1,10 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/Meio.app/App.axaml.cs b/Meio.App/App.axaml.cs similarity index 96% rename from Meio.app/App.axaml.cs rename to Meio.App/App.axaml.cs index 00e0160..23be316 100644 --- a/Meio.app/App.axaml.cs +++ b/Meio.App/App.axaml.cs @@ -1,58 +1,58 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; -using PrettyLogging.Console; - -namespace Meio.app; - -public class App : Application -{ - private static ILoggerFactory? LoggerFactory { get; set; } - - public static ILogger? Logger { get; private set; } - - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - // Create and configure PrettyLogger - LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => - { - builder.ClearProviders(); - builder.AddPrettyConsole(opt => - { - opt.ShowLogLevel = true; - opt.ShowEventId = false; - opt.ShowManagedThreadId = false; - opt.SingleLine = true; - opt.IncludeScopes = true; - opt.ShowTimestamp = true; - opt.LogLevelCase = LogLevelCase.Upper; - opt.CategoryMode = LoggerCategoryMode.Short; - opt.ColorBehavior = LoggerColorBehavior.Enabled; - opt.UseUtcTimestamp = false; - }); - -#if DEBUG - builder.SetMinimumLevel(LogLevel.Trace); -#else - builder.SetMinimumLevel(LogLevel.Information); -#endif - }); - - Logger = LoggerFactory.CreateLogger(); - Logger.LogInformation("Meio Application started."); - - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow(); - } - - base.OnFrameworkInitializationCompleted(); - } +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; +using PrettyLogging.Console; + +namespace Meio.app; + +public class App : Application +{ + private static ILoggerFactory? LoggerFactory { get; set; } + + public static ILogger? Logger { get; private set; } + + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + // Create and configure PrettyLogger + LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => + { + builder.ClearProviders(); + builder.AddPrettyConsole(opt => + { + opt.ShowLogLevel = true; + opt.ShowEventId = false; + opt.ShowManagedThreadId = false; + opt.SingleLine = true; + opt.IncludeScopes = true; + opt.ShowTimestamp = true; + opt.LogLevelCase = LogLevelCase.Upper; + opt.CategoryMode = LoggerCategoryMode.Short; + opt.ColorBehavior = LoggerColorBehavior.Enabled; + opt.UseUtcTimestamp = false; + }); + +#if DEBUG + builder.SetMinimumLevel(LogLevel.Trace); +#else + builder.SetMinimumLevel(LogLevel.Information); +#endif + }); + + Logger = LoggerFactory.CreateLogger(); + Logger.LogInformation("Meio Application started."); + + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } } \ No newline at end of file diff --git a/Meio.app/MainWindow.axaml b/Meio.App/MainWindow.axaml similarity index 98% rename from Meio.app/MainWindow.axaml rename to Meio.App/MainWindow.axaml index 7f6c48b..e61780c 100644 --- a/Meio.app/MainWindow.axaml +++ b/Meio.App/MainWindow.axaml @@ -1,27 +1,27 @@ - - - - - - - - Currently reading - - - - -