diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.DiscordMediaRP/.idea/.gitignore b/.idea/.idea.DiscordMediaRP/.idea/.gitignore new file mode 100644 index 0000000..fa9279f --- /dev/null +++ b/.idea/.idea.DiscordMediaRP/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/.idea.DiscordMediaRP.iml +/modules.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.DiscordMediaRP/.idea/encodings.xml b/.idea/.idea.DiscordMediaRP/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.DiscordMediaRP/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.DiscordMediaRP/.idea/indexLayout.xml b/.idea/.idea.DiscordMediaRP/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.DiscordMediaRP/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.DiscordMediaRP/.idea/vcs.xml b/.idea/.idea.DiscordMediaRP/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.DiscordMediaRP/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DiscordMediaRP.sln b/DiscordMediaRP.sln new file mode 100644 index 0000000..36912b0 --- /dev/null +++ b/DiscordMediaRP.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscordMediaRP", "DiscordMediaRP\DiscordMediaRP.csproj", "{8556A589-F4B3-4889-9AAC-4D9201CF1504}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8556A589-F4B3-4889-9AAC-4D9201CF1504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8556A589-F4B3-4889-9AAC-4D9201CF1504}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8556A589-F4B3-4889-9AAC-4D9201CF1504}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8556A589-F4B3-4889-9AAC-4D9201CF1504}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/DiscordMediaRP/DisLogger.cs b/DiscordMediaRP/DisLogger.cs new file mode 100644 index 0000000..d3c201b --- /dev/null +++ b/DiscordMediaRP/DisLogger.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; + +namespace DiscordMediaRP; + +public class DisLogger : DiscordRPC.Logging.ILogger +{ + //WHY TF DID YOU CREATE YOUR OWN LOGGER-TYPE YOU ********* + private ILogger? _logger; + + public DisLogger(ILogger? logger) + { + this._logger = logger; + } + + public void Trace(string message, params object[] args) + { + this._logger?.Log(LogLevel.Trace, message, args); + } + + public void Info(string message, params object[] args) + { + this._logger?.Log(LogLevel.Information, message, args); + } + + public void Warning(string message, params object[] args) + { + this._logger?.Log(LogLevel.Warning, message, args); + } + + public void Error(string message, params object[] args) + { + this._logger?.Log(LogLevel.Error, message, args); + } + + public DiscordRPC.Logging.LogLevel Level { get; set; } +} \ No newline at end of file diff --git a/DiscordMediaRP/DisMediaRP.cs b/DiscordMediaRP/DisMediaRP.cs new file mode 100644 index 0000000..6a5c5ec --- /dev/null +++ b/DiscordMediaRP/DisMediaRP.cs @@ -0,0 +1,149 @@ +using System.Reflection; +using Windows.Media; +using Windows.Media.Control; +using DiscordRPC; +using GlaxLogger; +using Microsoft.Extensions.Logging; +using WindowsMediaController; +using ILogger = Microsoft.Extensions.Logging.ILogger; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace DiscordMediaRP; + +//https://discord.com/developers/docs/rich-presence/how-to +//https://github.com/Lachee/discord-rpc-csharp?tab=readme-ov-file +//https://github.com/DubyaDude/WindowsMediaController +public class DisMediaRP : IDisposable +{ + private readonly ILogger? _logger; + private readonly MediaManager _mediaManager = new(); + private readonly DiscordRpcClient _discordRpcClient; + private readonly RichPresence _currentStatus = new(); + private bool _running = true; + + public DisMediaRP(string applicationId, LogLevel? logLevel) : this(applicationId, new Logger(logLevel ?? LogLevel.Information)) + { + } + + public DisMediaRP(string applicationId, ILogger? logger) + { + this._logger = logger; + this._discordRpcClient = new DiscordRpcClient(applicationId, logger: new DisLogger(this._logger)); + this._discordRpcClient.Initialize(); + this._discordRpcClient.OnError += (sender, args) => + { + this._logger?.LogError("Discord RPC encountered an error:\n{args}", args); + this.Dispose(); + }; + + this._mediaManager.Start(); + this._mediaManager.OnAnyMediaPropertyChanged += MediaPropertyChanged; + this._mediaManager.OnAnyPlaybackStateChanged += PlaybackStateChanged; + this._mediaManager.OnAnyTimelinePropertyChanged += TimelinePropertyChanged; + + + if (this._mediaManager.GetFocusedSession() is not null) + { + this.MediaPropertyChanged(this._mediaManager.GetFocusedSession(), this._mediaManager.GetFocusedSession().ControlSession.TryGetMediaPropertiesAsync().GetResults()); + this.PlaybackStateChanged(this._mediaManager.GetFocusedSession(), this._mediaManager.GetFocusedSession().ControlSession.GetPlaybackInfo()); + this.TimelinePropertyChanged(this._mediaManager.GetFocusedSession(), this._mediaManager.GetFocusedSession().ControlSession.GetTimelineProperties()); + } + + while(_running) + Thread.Sleep(50); + } + + private void MediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties) + { + this._logger?.LogDebug(ObjectToString(mediaSession)); + this._logger?.LogDebug(ObjectToString(mediaProperties)); + if (mediaSession != this._mediaManager.GetFocusedSession()) + return; + + + string details = $"{mediaProperties.Title}"; + if (mediaProperties.Artist.Length > 0) + details += $" - {mediaProperties.Artist}"; + if (mediaProperties.AlbumTitle.Length > 0) + details += $" - Album: {mediaProperties.AlbumTitle}"; + this._currentStatus.Details = details; + + this._discordRpcClient.SetPresence(this._currentStatus); + } + + private void PlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo) + { + this._logger?.LogDebug(ObjectToString(mediaSession)); + this._logger?.LogDebug(ObjectToString(playbackInfo)); + if (mediaSession != this._mediaManager.GetFocusedSession()) + return; + + + string? playbackState = playbackInfo.PlaybackStatus switch + { + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Paused => "\u23f8", + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => "\u25b6", + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Stopped => "\u23f9", + _ => null + }; + + string? repeatMode = playbackInfo.AutoRepeatMode switch + { + MediaPlaybackAutoRepeatMode.Track => "\ud83d\udd02", + MediaPlaybackAutoRepeatMode.List => "\ud83d\udd01", + _ => null + }; + + string? shuffle = (playbackInfo.IsShuffleActive ?? false) ? "\ud83d\udd00" : null; + + this._currentStatus.State = string.Join(' ', playbackState, repeatMode, shuffle, mediaSession.Id); + + this._discordRpcClient.SetPresence(this._currentStatus); + } + + private void TimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties) + { + this._logger?.LogDebug(ObjectToString(mediaSession)); + this._logger?.LogDebug(ObjectToString(timelineProperties)); + if (mediaSession != this._mediaManager.GetFocusedSession()) + return; + + if (timelineProperties.LastUpdatedTime < DateTimeOffset.UnixEpoch) + return; + + this._currentStatus.Timestamps = new Timestamps(DateTime.Now.Subtract(timelineProperties.Position), + DateTime.Now.Add(timelineProperties.EndTime - timelineProperties.Position)); + + this._discordRpcClient.SetPresence(this._currentStatus); + } + + public void Dispose() + { + _mediaManager.Dispose(); + _discordRpcClient.Dispose(); + _running = false; + } + + private string? ObjectToString(object? obj) + { + if (obj is null) + return null; + string? ns = obj.GetType().Namespace; + if (ns == "System.Collections.Generic") + { + IReadOnlyCollection i = (IReadOnlyCollection) obj; + return string.Join(", ", i); + } + if (ns is null || (ns != "Windows.Media.Control" && ns != "WindowsMediaController")) + return obj.ToString(); + + FieldInfo[] fieldInfos = obj.GetType().GetFields(); + PropertyInfo[] propertyInfos = obj.GetType().GetProperties(); + return + $"{obj.GetType().FullName}\n" + + $"Fields:\n\t" + + $"{string.Join("\n\t", fieldInfos.Select(f => $"{f.Name} {f.GetValue(obj)}"))}\n" + + $"Properties:\n\t" + + $"{string.Join("\n\t", propertyInfos.Select(p => $"{p.Name} {ObjectToString(p.GetValue(obj))}"))}"; + } +} \ No newline at end of file diff --git a/DiscordMediaRP/DiscordMediaRP.csproj b/DiscordMediaRP/DiscordMediaRP.csproj new file mode 100644 index 0000000..23f2207 --- /dev/null +++ b/DiscordMediaRP/DiscordMediaRP.csproj @@ -0,0 +1,16 @@ + + + + Exe + net7.0-windows10.0.22000.0 + enable + enable + + + + + + + + + diff --git a/DiscordMediaRP/Program.cs b/DiscordMediaRP/Program.cs new file mode 100644 index 0000000..0b3a1ba --- /dev/null +++ b/DiscordMediaRP/Program.cs @@ -0,0 +1,12 @@ +// See https://aka.ms/new-console-template for more information + +using DiscordMediaRP; +using Microsoft.Extensions.Logging; + +int loglevelIndex = Array.IndexOf(args, "-l") + 1; +LogLevel? level = loglevelIndex >= 0 && loglevelIndex < args.Length ? Enum.Parse(args[loglevelIndex]) : null; +int discordKeyIndex = Array.IndexOf(args, "-d") + 1; +if (discordKeyIndex < 1 || discordKeyIndex >= args.Length) + throw new IndexOutOfRangeException("No Discord ApplicationKey provided"); +string discordKey = args[discordKeyIndex]; +DisMediaRP _ = new (discordKey, level); \ No newline at end of file diff --git a/DiscordMediaRP/media/cat.png b/DiscordMediaRP/media/cat.png new file mode 100644 index 0000000..e3805d8 Binary files /dev/null and b/DiscordMediaRP/media/cat.png differ diff --git a/DiscordMediaRP/media/cat.webp.txt b/DiscordMediaRP/media/cat.webp.txt new file mode 100644 index 0000000..61079e2 --- /dev/null +++ b/DiscordMediaRP/media/cat.webp.txt @@ -0,0 +1 @@ +https://www.rawpixel.com/image/6728007/png-sticker-public-domain \ No newline at end of file