From 6be4d2b495a0d298886be873ae89fdc6a5a324ae Mon Sep 17 00:00:00 2001 From: CrawLeyYou <60201017+CrawLeyYou@users.noreply.github.com> Date: Tue, 26 Aug 2025 04:40:37 +0300 Subject: [PATCH 1/5] [WIP] Options and change how logger works [WIP] Options menu - Add Small Image type [Fix] Refactoring and Auto logging type selector --- .github/workflows/build-dev.yml | 6 +- AMDiscordRPC/AMDiscordRPC.cs | 12 ++-- AMDiscordRPC/AMDiscordRPC.csproj | 7 ++ AMDiscordRPC/AppleMusic.cs | 4 +- AMDiscordRPC/Covers.cs | 2 + AMDiscordRPC/Discord.cs | 3 +- AMDiscordRPC/Globals.cs | 71 +++++++++++++++---- AMDiscordRPC/UI.cs | 15 ++++ AMDiscordRPC/UIComponents/InputWindow.xaml.cs | 4 +- AMDiscordRPC/UIComponents/OptionsWindow.xaml | 21 ++++++ .../UIComponents/OptionsWindow.xaml.cs | 23 ++++++ AMDiscordRPC/log4netconf.xml | 15 ---- README.md | 1 + 13 files changed, 140 insertions(+), 44 deletions(-) create mode 100644 AMDiscordRPC/UIComponents/OptionsWindow.xaml create mode 100644 AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 37dc5a7..da6a692 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -18,9 +18,9 @@ jobs: - name: Restore NuGet Packages run: nuget restore AMDiscordRPC.sln - name: Build - run: msbuild AMDiscordRPC.sln -property:Configuration=Release -property:platform="x64" + run: msbuild AMDiscordRPC.sln -property:Configuration=Debug -property:platform="x64" - name: Zip - run: powershell Compress-Archive -Path ./AMDiscordRPC/bin/x64/Release -DestinationPath Release.zip + run: powershell Compress-Archive -Path ./AMDiscordRPC/bin/x64/Debug -DestinationPath Dev-Release.zip - name: Release uses: softprops/action-gh-release@v2 with: @@ -31,4 +31,4 @@ jobs: Don't use this version if you want more stable experience. prerelease: true files: | - Release.zip \ No newline at end of file + Dev-Release.zip \ No newline at end of file diff --git a/AMDiscordRPC/AMDiscordRPC.cs b/AMDiscordRPC/AMDiscordRPC.cs index a807bb9..07dabcb 100644 --- a/AMDiscordRPC/AMDiscordRPC.cs +++ b/AMDiscordRPC/AMDiscordRPC.cs @@ -20,11 +20,11 @@ internal class AMDiscordRPC private static string oldAlbumnArtist; static void Main(string[] args) { + ConfigureLogger(); InitRegion(); CreateUI(); - ConfigureLogger(); - InitializeDiscordRPC(); - AttachToAppleMusic(); + InitDiscordRPC(); + AttachToAM(); AMSongDataEvent.SongChanged += async (sender, x) => { log.Info($"Song: {x.SongName} \\ Artist and Album: {x.ArtistandAlbumName}"); @@ -249,7 +249,7 @@ static void AMEvent() client.ClearPresence(); while (!AMAttached) { - AttachToAppleMusic(); + AttachToAM(); Thread.Sleep(1000); } AMEvent(); @@ -262,7 +262,7 @@ static void AMEvent() client.ClearPresence(); while (!AMAttached) { - AttachToAppleMusic(); + AttachToAM(); Thread.Sleep(1000); } AMEvent(); @@ -272,7 +272,7 @@ static void AMEvent() { while (!AMAttached) { - AttachToAppleMusic(); + AttachToAM(); Thread.Sleep(1000); } AMEvent(); diff --git a/AMDiscordRPC/AMDiscordRPC.csproj b/AMDiscordRPC/AMDiscordRPC.csproj index 4d4c56a..53f51a0 100644 --- a/AMDiscordRPC/AMDiscordRPC.csproj +++ b/AMDiscordRPC/AMDiscordRPC.csproj @@ -250,6 +250,9 @@ InputWindow.xaml + + OptionsWindow.xaml + @@ -279,6 +282,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + diff --git a/AMDiscordRPC/AppleMusic.cs b/AMDiscordRPC/AppleMusic.cs index 4eb1dd3..519fa23 100644 --- a/AMDiscordRPC/AppleMusic.cs +++ b/AMDiscordRPC/AppleMusic.cs @@ -6,13 +6,13 @@ namespace AMDiscordRPC { internal class AppleMusic { - public static void AttachToAppleMusic() + public static void AttachToAM() { try { AppleMusicProc = Application.Attach("AppleMusic.exe"); AMAttached = true; - log.Info($"Attached to Process Id: {AppleMusicProc.ProcessId}"); + log.Info($"Attached to PID: {AppleMusicProc.ProcessId}"); } catch (Exception e) { diff --git a/AMDiscordRPC/Covers.cs b/AMDiscordRPC/Covers.cs index 5c69e07..02f867e 100644 --- a/AMDiscordRPC/Covers.cs +++ b/AMDiscordRPC/Covers.cs @@ -60,6 +60,7 @@ private static async Task AsyncFetchiTunes(string album, string public static async Task AsyncAMFetch(string album, string searchStr) { + log.Debug($"https://music.apple.com/{AMRegion.ToLower()}/search?term={searchStr}"); try { HttpResponseMessage AMRequest = await hclient.GetAsync($"https://music.apple.com/{AMRegion.ToLower()}/search?term={searchStr}"); @@ -93,6 +94,7 @@ public static async Task CheckAnimatedCover(string album, string url, Cancellati try { var appleMusicDom = await hclient.GetAsync(url); + log.Debug($"Animated Cover Request: {url}"); if (appleMusicDom.IsSuccessStatusCode) { string DOMasAString = await appleMusicDom.Content.ReadAsStringAsync(); diff --git a/AMDiscordRPC/Discord.cs b/AMDiscordRPC/Discord.cs index 124d731..8912d27 100644 --- a/AMDiscordRPC/Discord.cs +++ b/AMDiscordRPC/Discord.cs @@ -13,10 +13,11 @@ public class Discord private static Thread thread = null; public static CancellationTokenSource animatedCoverCts; - public static void InitializeDiscordRPC() + public static void InitDiscordRPC() { client = new DiscordRpcClient("1308911584164319282"); client.Initialize(); + log.Debug("Discord RPC initialized."); } public static void ChangeTimestamps(DateTime start = new DateTime(), DateTime end = new DateTime()) diff --git a/AMDiscordRPC/Globals.cs b/AMDiscordRPC/Globals.cs index dad6c88..231b826 100644 --- a/AMDiscordRPC/Globals.cs +++ b/AMDiscordRPC/Globals.cs @@ -2,7 +2,12 @@ using DiscordRPC; using DiscordRPC.Helper; using log4net; +using log4net.Appender; using log4net.Config; +using log4net.Core; +using log4net.Filter; +using log4net.Layout; +using log4net.Repository.Hierarchy; using System; using System.Collections.Generic; using System.Data.SQLite; @@ -34,22 +39,9 @@ public static class Globals public static string ffmpegPath; public static S3_Creds S3_Credentials; private static List newMatchesArr; - public enum S3ConnectionStatus - { - Connected, - Disconnected, - Error - } - public enum AudioFormat - { - Lossless, - Dolby_Atmos, - Dolby_Audio, - AAC - } public static S3ConnectionStatus S3Status = S3ConnectionStatus.Disconnected; public static string AMRegion; - + public static SmallImage SelectedSmallImage = SmallImage.LossDolby; public static void ConfigureLogger() { @@ -57,9 +49,36 @@ public static void ConfigureLogger() { XmlConfigurator.Configure(stream); } + + LevelRangeFilter lrf = new LevelRangeFilter + { + LevelMax = Level.Fatal, + LevelMin = Level.Info + }; + #if DEBUG + lrf.LevelMin = Level.Debug; + #endif + lrf.ActivateOptions(); + + PatternLayout pl = new PatternLayout + { + ConversionPattern = "[%date{HH:mm:ss.fff}] %level (%method:%line) - %message%newline" + }; + pl.ActivateOptions(); + + RollingFileAppender rfa = new RollingFileAppender + { + AppendToFile = false, + File = @"logs/latest.log", + Layout = pl, + }; + rfa.AddFilter(lrf); + rfa.ActivateOptions(); + + ((Hierarchy)LogManager.GetRepository()).Root.AddAppender(rfa); } - public static async void InitRegion() + public static void InitRegion() { HttpClientHandler HClientHandlerhandler = new HttpClientHandler(); CookieContainer cookies = new CookieContainer(); @@ -72,6 +91,7 @@ public static async void InitRegion() AMRegion = cookies.GetCookies(new Uri("https://music.apple.com/")).Cast() .Where(cookie => cookie.Name == "geo").ToList()[0].Value; + log.Info($"Region selected as: {AMRegion.ToLower()}"); } catch (Exception e) { @@ -173,6 +193,27 @@ public static async void CheckFFmpeg() else FFmpegDialog(); } + public enum S3ConnectionStatus + { + Connected, + Disconnected, + Error + } + public enum AudioFormat + { + Lossless, + Dolby_Atmos, + Dolby_Audio, + AAC + } + + public enum SmallImage + { + LossDolby, + Artist, + None + } + public class SongData : EventArgs { public string SongName { get; set; } diff --git a/AMDiscordRPC/UI.cs b/AMDiscordRPC/UI.cs index 4af808e..1043fff 100644 --- a/AMDiscordRPC/UI.cs +++ b/AMDiscordRPC/UI.cs @@ -16,6 +16,7 @@ namespace AMDiscordRPC internal class UI { private static InputWindow inputWindow; + private static OptionsWindow optionsWindow; private static Application app; private static Thread mainThread = Thread.CurrentThread; @@ -31,6 +32,7 @@ public static void CreateUI() thread.SetApartmentState(ApartmentState.STA); thread.Start(); + log.Debug("Tray thread started."); } public static void FFmpegDialog() @@ -73,6 +75,7 @@ public class AMDiscordRPCTray private static ContextMenu contextMenu = new ContextMenu(); public static MenuItem notifySongState = new MenuItem(); public MenuItem s3Menu = new MenuItem(); + public MenuItem optionsMenu = new MenuItem(); public AMDiscordRPCTray() { @@ -91,11 +94,23 @@ public AMDiscordRPCTray() }); }); + optionsMenu.Text = "Options"; + optionsMenu.Index = 2; + optionsMenu.Click += new EventHandler((object sender, EventArgs e) => + { + app.Dispatcher.Invoke(() => + { + optionsWindow = new OptionsWindow(); + optionsWindow.Show(); + }); + }); + contextMenu.MenuItems.AddRange( new MenuItem[] { notifySongState, s3Menu, + optionsMenu, new MenuItem("Show Latest Log", (s,e) => { Process.Start("notepad", $"{Path.Combine(Directory.GetCurrentDirectory(), @"logs\latest.log")}"); }), new MenuItem("Exit", (s, e) => { Environment.Exit(0); }) } diff --git a/AMDiscordRPC/UIComponents/InputWindow.xaml.cs b/AMDiscordRPC/UIComponents/InputWindow.xaml.cs index 7ff2261..723d161 100644 --- a/AMDiscordRPC/UIComponents/InputWindow.xaml.cs +++ b/AMDiscordRPC/UIComponents/InputWindow.xaml.cs @@ -72,13 +72,13 @@ private void SetS3Button_Click(object sender, RoutedEventArgs e) } } - private static void PutValues(S3_Creds creds, ShowMode mode = ShowMode.Hide) + private void PutValues(S3_Creds creds, ShowMode mode = ShowMode.Hide) { List Instances = new List { Instance.AccessKeyIDBox, Instance.SecretKeyBox, Instance.EndpointBox, Instance.BucketNameBox, Instance.PublicBucketURLBox }; List Keys = new List() { creds.accessKey, creds.secretKey, creds.serviceURL, creds.bucketName, creds.bucketURL }; foreach (var (item, index) in Instances.Select((v, i) => (v, i))) { - PlaceholderAdorner adorner = Helpers.TextBoxHelper.GetPlaceholderAdorner(item); + PlaceholderAdorner adorner = GetPlaceholderAdorner(item); item.Text = (mode == ShowMode.Show) ? Keys[index] : new string('*', Keys[index].Length); item.IsEnabled = (mode == ShowMode.Show) ? true : false; if (Keys[index].Length > 0) diff --git a/AMDiscordRPC/UIComponents/OptionsWindow.xaml b/AMDiscordRPC/UIComponents/OptionsWindow.xaml new file mode 100644 index 0000000..37fdd44 --- /dev/null +++ b/AMDiscordRPC/UIComponents/OptionsWindow.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs b/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs new file mode 100644 index 0000000..4e4e68c --- /dev/null +++ b/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs @@ -0,0 +1,23 @@ + +using System.Management.Instrumentation; +using System.Windows; + +namespace AMDiscordRPC.UIComponents +{ + /// + /// Interaction logic for OptionsWindow.xaml + /// + public partial class OptionsWindow : Window + { + private static OptionsWindow Instance; + public OptionsWindow() + { + InitializeComponent(); + Instance = this; + Instance.Loaded += (s, e) => + { + + }; + } + } +} diff --git a/AMDiscordRPC/log4netconf.xml b/AMDiscordRPC/log4netconf.xml index 30837d1..8f1ebc9 100644 --- a/AMDiscordRPC/log4netconf.xml +++ b/AMDiscordRPC/log4netconf.xml @@ -4,23 +4,8 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 8e17c67..3b9cb6a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@

AMDiscordRPC

An another Apple Music Discord RPC

+[Support Server](https://discord.gg/5trvjuqgm8) ## Usage [Download](https://github.com/CrawLeyYou/AMDiscordRPC/releases/latest) latest release and make sure you have [.NET Framework 4.7.2](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472). From 7b0eb9b5cbbeb0b956b3c674ee9ad2169e4137d9 Mon Sep 17 00:00:00 2001 From: CrawLeyYou <60201017+CrawLeyYou@users.noreply.github.com> Date: Tue, 9 Sep 2025 20:13:59 +0300 Subject: [PATCH 2/5] Push not finished features and bug fixes --- AMDiscordRPC/AMDiscordRPC.cs | 8 +++----- AMDiscordRPC/Covers.cs | 20 ++++++++++++++----- AMDiscordRPC/Database.cs | 17 +++++++++++----- AMDiscordRPC/Discord.cs | 3 +++ AMDiscordRPC/Globals.cs | 13 ++++++++---- AMDiscordRPC/UIComponents/InputWindow.xaml.cs | 5 ++++- AMDiscordRPC/UIComponents/OptionsWindow.xaml | 2 +- .../UIComponents/OptionsWindow.xaml.cs | 15 +++++++++++++- 8 files changed, 61 insertions(+), 22 deletions(-) diff --git a/AMDiscordRPC/AMDiscordRPC.cs b/AMDiscordRPC/AMDiscordRPC.cs index 07dabcb..0657ea7 100644 --- a/AMDiscordRPC/AMDiscordRPC.cs +++ b/AMDiscordRPC/AMDiscordRPC.cs @@ -45,7 +45,7 @@ static void Main(string[] args) } }; CheckDatabaseIntegrity(); - InitDBCreds(); + ConfigureFromDB(); CheckFFmpeg(); InitS3(); AMEvent(); @@ -144,7 +144,7 @@ static void AMEvent() if (oldValue == 0) oldValue = slider.AsSlider().Value; DateTime currentTime = DateTime.UtcNow; DateTime startTime = currentTime.Subtract(subractThis); - DateTime endTime = currentTime.AddSeconds(slider.AsSlider().Maximum).Subtract(subractThis); + DateTime endTime = startTime.AddSeconds(slider.AsSlider().Maximum); DateTime oldEndTime = DateTime.MinValue; DateTime oldStartTime = DateTime.MinValue; bool isSingle = dashSplit[dashSplit.Length - 1].Contains("Single"); @@ -214,8 +214,6 @@ static void AMEvent() } else format = AudioFormat.AAC; oldValue = 0; - startTime = currentTime.Subtract(subractThis); - endTime = currentTime.AddSeconds(slider.AsSlider().Maximum).Subtract(subractThis); oldStartTime = startTime; oldEndTime = endTime; AMSongDataEvent.ChangeSong(new SongData(currentSong, (isSingle) ? string.Join("-", dashSplit.Take(dashSplit.Length - 1).ToArray()) : string.Join("—", currentArtistAlbum.Split('—').Take(2).ToArray()), currentArtistAlbum.Split('—').Length <= 1, startTime, endTime, format)); @@ -256,7 +254,7 @@ static void AMEvent() } Thread.Sleep(20); } - if (!AMAttached & AppleMusicProc.HasExited != true) + if (!AMAttached && AppleMusicProc.HasExited != true) { log.Info("Something happened which needs to reattach"); client.ClearPresence(); diff --git a/AMDiscordRPC/Covers.cs b/AMDiscordRPC/Covers.cs index 02f867e..4a9e667 100644 --- a/AMDiscordRPC/Covers.cs +++ b/AMDiscordRPC/Covers.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using AngleSharp.Dom; using static AMDiscordRPC.Database; using static AMDiscordRPC.Globals; using static AMDiscordRPC.Playlist; @@ -30,9 +31,10 @@ private static async Task AsyncFetchiTunes(string album, string ( imageRes["results"][0]["artworkUrl100"].ToString(), imageRes["results"][0]["trackViewUrl"].ToString(), - imageRes["results"][0]["collectionName"].ToString() + imageRes["results"][0]["collectionName"].ToString(), + imageRes["results"][0]["artistViewUrl"].ToString() ); - Database.UpdateAlbum(new Database.SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL)); + UpdateAlbum(new SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL, null, null, null, webRes.artistURL)); CoverThread = null; return webRes; } @@ -58,6 +60,11 @@ private static async Task AsyncFetchiTunes(string album, string } } + public static async Task AsyncArtistProfileFetch(string url) + { + return null; + } + public static async Task AsyncAMFetch(string album, string searchStr) { log.Debug($"https://music.apple.com/{AMRegion.ToLower()}/search?term={searchStr}"); @@ -68,12 +75,15 @@ public static async Task AsyncAMFetch(string album, string sear { string DOMasAString = await AMRequest.Content.ReadAsStringAsync(); IHtmlDocument document = parser.ParseDocument(DOMasAString); + WebSongResponse webRes = new WebSongResponse( - document.DocumentElement.QuerySelectorAll("div.top-search-lockup__artwork > div > picture > source")[1].GetAttribute("srcset").Split(' ')[0], - document.DocumentElement.QuerySelector("div.top-search-lockup__action > a").GetAttribute("href") + document.DocumentElement.QuerySelectorAll("div.track-lockup__artwork-wrapper > div > picture > source")[1].GetAttribute("srcset").Split(',')[1].Split(' ')[0], + document.DocumentElement.QuerySelectorAll("div.track-lockup__clamp-wrapper > a")[0].GetAttribute("href"), + null, + document.DocumentElement.QuerySelectorAll("div.track-lockup__clamp-wrapper > span > a")[0].GetAttribute("href") ); CoverThread = null; - Database.UpdateAlbum(new Database.SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL)); + UpdateAlbum(new Database.SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL, null, null, null, webRes.artistURL)); return webRes; } else diff --git a/AMDiscordRPC/Database.cs b/AMDiscordRPC/Database.cs index 2594fb2..684c33e 100644 --- a/AMDiscordRPC/Database.cs +++ b/AMDiscordRPC/Database.cs @@ -11,9 +11,10 @@ internal class Database private static SQLiteConnection sqlite; public static readonly Dictionary sqlMap = new Dictionary() { - {"coverTable", "album TEXT PRIMARY KEY NOT NULL, source TEXT, redirURL TEXT DEFAULT 'https://music.apple.com/home', animated BOOLEAN CHECK (animated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT" }, + {"coverTable", "album TEXT PRIMARY KEY NOT NULL, source TEXT, redirURL TEXT DEFAULT 'https://music.apple.com/home', artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistSource TEXT, animated BOOLEAN CHECK (animated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT" }, {"creds", "S3_accessKey TEXT, S3_secretKey TEXT, S3_serviceURL TEXT, S3_bucketName TEXT, S3_bucketURL TEXT, S3_isSpecificKey BOOLEAN CHECK (S3_isSpecificKey IN (0,1)), FFmpegPath TEXT" }, - {"logs", "timestamp INTEGER, type TEXT, occuredAt TEXT, message TEXT" } + {"logs", "timestamp INTEGER, type TEXT, occuredAt TEXT, message TEXT" }, + {"clientSettings", "smallImage INTEGER"} }; private static void InitDatabase() @@ -57,6 +58,7 @@ private static void CreateDatabase() } } + // Note: source, streamurl, animated, animatedUrl can be stored in external table so we decrease the file size of database private static void CheckForeignKeys() { ExecuteNonQueryCommand("PRAGMA foreign_keys = on"); @@ -119,7 +121,9 @@ public static SQLCoverResponse GetAlbumDataFromSQL(string album) reader.GetString(2), ((!reader.IsDBNull(3)) ? reader.GetBoolean(3) : null), ((!reader.IsDBNull(4)) ? reader.GetString(4) : null), - ((!reader.IsDBNull(5)) ? reader.GetString(5) : null)); + ((!reader.IsDBNull(5)) ? reader.GetString(5) : null), + reader.GetString(6) + ); } } return null; @@ -155,7 +159,7 @@ private static void CheckColumns() // Recovery functionality will be added next release. } } - else if (column == null) + else if (column == null && !item.Value.primaryKey) { ExecuteNonQueryCommand($"ALTER TABLE {table} ADD COLUMN {SQLInfo}"); } @@ -255,8 +259,10 @@ public class SQLCoverResponse public bool? animated { get; set; } public string streamURL { get; set; } public string animatedURL { get; set; } + public string artistRedirURL { get; set; } - public SQLCoverResponse(string album = null, string source = null, string redirURL = null, bool? animated = null, string streamURL = null, string animatedURL = null) + + public SQLCoverResponse(string album = null, string source = null, string redirURL = null, bool? animated = null, string streamURL = null, string animatedURL = null, string artistRedirURL = null) { this.album = album; this.source = source; @@ -264,6 +270,7 @@ public SQLCoverResponse(string album = null, string source = null, string redirU this.animated = animated; this.streamURL = streamURL; this.animatedURL = animatedURL; + this.artistRedirURL = artistRedirURL; } public List GetNotNullKeys() diff --git a/AMDiscordRPC/Discord.cs b/AMDiscordRPC/Discord.cs index 8912d27..715c973 100644 --- a/AMDiscordRPC/Discord.cs +++ b/AMDiscordRPC/Discord.cs @@ -80,6 +80,7 @@ public static void SetPresence(SongData x, WebSongResponse resp) { Type = ActivityType.Listening, Details = ConvertToValidString(x.SongName), + StateUrl = resp.artistURL, StatusDisplay = StatusDisplayType.State, State = (x.IsMV) ? x.ArtistandAlbumName : ConvertToValidString(x.ArtistandAlbumName.Split('—')[0]), Assets = new Assets() @@ -99,6 +100,8 @@ public static void SetPresence(SongData x, WebSongResponse resp) End = x.EndTime, } }; + if (oldData.Assets.LargeImageText.Length == 1) + oldData.Assets.LargeImageText = $"{oldData.Assets.LargeImageText}‍"; // THIS HAS U+200D AT THE END OF STRING TO FIX '"large_text" length must be at least 2 characters long' ERROR client.SetPresence(oldData); if (resp.artworkURL != null && !resp.artworkURL.Contains((S3_Credentials != null) ? (S3_Credentials.GetNullKeys().Count == 0) ? S3_Credentials.bucketURL : "" : "")) { diff --git a/AMDiscordRPC/Globals.cs b/AMDiscordRPC/Globals.cs index 231b826..c4fbd51 100644 --- a/AMDiscordRPC/Globals.cs +++ b/AMDiscordRPC/Globals.cs @@ -121,9 +121,9 @@ public static string ConvertToValidString(string data) return data; } - public static void InitDBCreds() + public static void ConfigureFromDB() { - using (SQLiteDataReader dbResp = Database.ExecuteReaderCommand($"SELECT {string.Join(", ", Regex.Matches(Database.sqlMap["creds"], @"S3_\w+").FilterRepeatMatches())} FROM creds LIMIT 1")) + using (SQLiteDataReader dbResp = ExecuteReaderCommand($"SELECT {string.Join(", ", Regex.Matches(sqlMap["creds"], @"S3_\w+").FilterRepeatMatches())} FROM creds LIMIT 1")) { while (dbResp.Read()) { @@ -136,6 +136,8 @@ public static void InitDBCreds() ((!dbResp.IsDBNull(5)) ? dbResp.GetBoolean(5) : null)); } } + + SelectedSmallImage = (SmallImage)Convert.ToInt32(ExecuteScalarCommand("SELECT smallImage FROM clientSettings")); } private static void StartFFmpegProcess(string filename) @@ -279,12 +281,14 @@ public class WebSongResponse public string artworkURL { get; set; } public string trackURL { get; set; } public string trackName { get; set; } + public string artistURL { get; set; } - public WebSongResponse(string artworkURL = null, string trackURL = null, string trackName = null) + public WebSongResponse(string artworkURL = null, string trackURL = null, string trackName = null, string artistURL = null) { this.artworkURL = artworkURL; this.trackURL = trackURL; this.trackName = trackName; + this.artistURL = artistURL; } public override bool Equals(object obj) @@ -292,7 +296,8 @@ public override bool Equals(object obj) return obj is WebSongResponse other && artworkURL == other.artworkURL && trackURL == other.trackURL && - trackName == other.trackName; + trackName == other.trackName && + artistURL == other.artistURL; } } } diff --git a/AMDiscordRPC/UIComponents/InputWindow.xaml.cs b/AMDiscordRPC/UIComponents/InputWindow.xaml.cs index 723d161..9d57c8a 100644 --- a/AMDiscordRPC/UIComponents/InputWindow.xaml.cs +++ b/AMDiscordRPC/UIComponents/InputWindow.xaml.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; + +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Windows; @@ -79,6 +80,8 @@ private void PutValues(S3_Creds creds, ShowMode mode = ShowMode.Hide) foreach (var (item, index) in Instances.Select((v, i) => (v, i))) { PlaceholderAdorner adorner = GetPlaceholderAdorner(item); + if (Keys[index] == null) + return; item.Text = (mode == ShowMode.Show) ? Keys[index] : new string('*', Keys[index].Length); item.IsEnabled = (mode == ShowMode.Show) ? true : false; if (Keys[index].Length > 0) diff --git a/AMDiscordRPC/UIComponents/OptionsWindow.xaml b/AMDiscordRPC/UIComponents/OptionsWindow.xaml index 37fdd44..8c4a0af 100644 --- a/AMDiscordRPC/UIComponents/OptionsWindow.xaml +++ b/AMDiscordRPC/UIComponents/OptionsWindow.xaml @@ -8,7 +8,7 @@ Icon="../Resources/Logo Black 32.ico" d:DesignHeight="450" d:DesignWidth="800" d:MaxHeight="450" d:MaxWidth="800" d:ResizeMode="NoResize" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" Title="Options"> - + diff --git a/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs b/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs index 4e4e68c..eb3c5de 100644 --- a/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs +++ b/AMDiscordRPC/UIComponents/OptionsWindow.xaml.cs @@ -1,6 +1,10 @@  +using System; using System.Management.Instrumentation; +using System.Text.RegularExpressions; using System.Windows; +using System.Windows.Controls; +using static AMDiscordRPC.Globals; namespace AMDiscordRPC.UIComponents { @@ -16,8 +20,17 @@ public OptionsWindow() Instance = this; Instance.Loaded += (s, e) => { - + smallImage.SelectedIndex = (int)SelectedSmallImage; }; } + + private void SmallImage_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + SelectedSmallImage = (SmallImage)smallImage.SelectedIndex; + if (Database.ExecuteScalarCommand("SELECT smallImage FROM clientSettings") == null) + Database.ExecuteNonQueryCommand($"INSERT INTO clientSettings (smallImage) VALUES ({smallImage.SelectedIndex})"); + else + Database.ExecuteNonQueryCommand($"UPDATE clientSettings SET (smallImage) = ({smallImage.SelectedIndex})"); + } } } From f74a9d19378c76fb8665bae1b6be1f9f0c3d13bd Mon Sep 17 00:00:00 2001 From: CrawLeyYou <60201017+CrawLeyYou@users.noreply.github.com> Date: Sat, 4 Oct 2025 09:57:06 +0300 Subject: [PATCH 3/5] Add new database base and rewrite checks --- AMDiscordRPC/Covers.cs | 6 ++--- AMDiscordRPC/Database.cs | 55 +++++++++++++++++++++++++++++++++------- AMDiscordRPC/Globals.cs | 12 ++++++--- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/AMDiscordRPC/Covers.cs b/AMDiscordRPC/Covers.cs index 4a9e667..0d207e1 100644 --- a/AMDiscordRPC/Covers.cs +++ b/AMDiscordRPC/Covers.cs @@ -1,10 +1,10 @@ -using AngleSharp.Html.Dom; +using AngleSharp.Dom; +using AngleSharp.Html.Dom; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using AngleSharp.Dom; using static AMDiscordRPC.Database; using static AMDiscordRPC.Globals; using static AMDiscordRPC.Playlist; @@ -129,7 +129,7 @@ public static async Task GetCover(string album, string searchSt { try { - log.Debug($"https://music.apple.com/us/search?term={searchStr}"); + log.Debug($"https://music.apple.com/{AMRegion.ToLower()}/search?term={searchStr}"); SQLCoverResponse cover = GetAlbumDataFromSQL(album); if (cover != null) { diff --git a/AMDiscordRPC/Database.cs b/AMDiscordRPC/Database.cs index 684c33e..46a17ac 100644 --- a/AMDiscordRPC/Database.cs +++ b/AMDiscordRPC/Database.cs @@ -1,4 +1,5 @@ -using System; +using Amazon.Runtime.Internal.Transform; +using System; using System.Collections.Generic; using System.Data.SQLite; using System.Linq; @@ -11,7 +12,11 @@ internal class Database private static SQLiteConnection sqlite; public static readonly Dictionary sqlMap = new Dictionary() { - {"coverTable", "album TEXT PRIMARY KEY NOT NULL, source TEXT, redirURL TEXT DEFAULT 'https://music.apple.com/home', artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistSource TEXT, animated BOOLEAN CHECK (animated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT" }, + //{"coverTable", "album TEXT PRIMARY KEY NOT NULL, source TEXT, redirURL TEXT DEFAULT 'https://music.apple.com/home', artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistSource TEXT, animated BOOLEAN CHECK (animated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT" }, + {"coverTableNew", "coverID INTEGER PRIMARY KEY AUTOINCREMENT, staticCoverURL TEXT NOT NULL, isAnimated BOOLEAN CHECK (isAnimated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT"}, + {"artistTable", "artistID INTEGER PRIMARY KEY AUTOINCREMENT, artistName TEXT NOT NULL, artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistProfileSource TEXT"}, + {"albumTable", "albumID INTEGER PRIMARY KEY AUTOINCREMENT, albumName TEXT NOT NULL, albumURL TEXT UNIQUE, isSingle BOOLEAN CHECK (isSingle IN (0,1)), coverID INTEGER, artistID INTEGER, FOREIGN KEY (coverID) REFERENCES coverTableNew(coverID), FOREIGN KEY (artistID) REFERENCES artistTable(artistID)"}, + {"songTable", "songTitle TEXT, songURL TEXT, albumID INTEGER, artistID INTEGER, FOREIGN KEY (albumID) REFERENCES albumTable(albumID), FOREIGN KEY (artistID) REFERENCES artistTable(artistID)"}, {"creds", "S3_accessKey TEXT, S3_secretKey TEXT, S3_serviceURL TEXT, S3_bucketName TEXT, S3_bucketURL TEXT, S3_isSpecificKey BOOLEAN CHECK (S3_isSpecificKey IN (0,1)), FFmpegPath TEXT" }, {"logs", "timestamp INTEGER, type TEXT, occuredAt TEXT, message TEXT" }, {"clientSettings", "smallImage INTEGER"} @@ -39,7 +44,7 @@ public static void CheckDatabaseIntegrity() { try { - //CheckForeignKeys(); we don't have use case for relationships rn so no need to waste resources on this check + CheckForeignKeys(); CheckTables(); CheckColumns(); } @@ -133,12 +138,21 @@ private static void CheckColumns() { foreach (var table in sqlMap.Keys) { - SQLiteDataReader data = ExecuteReaderCommand($"PRAGMA table_info({table})"); + SQLiteDataReader data = ExecuteReaderCommand($"SELECT * FROM sqlite_master"); Dictionary tableData = new Dictionary(); while (data.Read()) { - tableData.Add(data.GetString(1), new ColumnInfo(data.GetString(2), data.GetBoolean(3), (!data.IsDBNull(4)) ? data.GetString(4) : null, data.GetBoolean(5))); + if (data.GetString(0) == "table" && data.GetString(2) == table) + { + string sqlStr = string.Join("(", data.GetString(4).Split(new[] { "CREATE TABLE " }, StringSplitOptions.None)[1].Split('(').Skip(1)).TrimEnd(1); + var temp = ConvertSQLStringToColumnInfo(sqlStr); + foreach (var keyValuePair in temp) + { + //log.Debug($"{keyValuePair.Key}, autoIncrement: {keyValuePair.Value.isAutoIncrementing}, defaultValue: {keyValuePair.Value.defaultValue}, foreignKey: [Key: {keyValuePair.Value.foreignKey?.key}, refColumn: {keyValuePair.Value.foreignKey?.refColumn}, refTable: {keyValuePair.Value.foreignKey?.refTable}], nullCheck: {keyValuePair.Value.nullCheck}, primaryKey: {keyValuePair.Value.primaryKey}, type: {keyValuePair.Value.type}"); + tableData.Add(keyValuePair.Key, keyValuePair.Value); + } + } } foreach (var item in ConvertSQLStringToColumnInfo(sqlMap[table])) @@ -148,7 +162,7 @@ private static void CheckColumns() if (!item.Value.Equals(column) && column != null) { log.Debug($"Corrupted/Outdated column:{SQLInfo.Split(' ')[0]} found."); - if (!item.Value.primaryKey && ((item.Value.nullCheck && item.Value.defaultValue != null) || !item.Value.nullCheck)) + if (!item.Value.primaryKey && ((item.Value.nullCheck && item.Value.defaultValue != null) || !item.Value.nullCheck) && item.Value.foreignKey == null) { ExecuteNonQueryCommand($"ALTER TABLE {table} DROP COLUMN {SQLInfo.Split(' ')[0]}"); ExecuteNonQueryCommand($"ALTER TABLE {table} ADD COLUMN {SQLInfo}"); @@ -174,11 +188,14 @@ private static Dictionary ConvertSQLStringToColumnInfo(strin foreach (var column in columns) { string[] splitStr = column.Split(' '); + if (splitStr[0] == "FOREIGN") continue; columnsMap.Add(splitStr[0], new ColumnInfo( splitStr[1], column.Contains("NOT NULL"), (column.Contains("DEFAULT")) ? column.Split(new[] { "DEFAULT " }, StringSplitOptions.None)[1] : null, //This is not a proper way to do this but it works for now (DEFAULT value must be on the last section of the SQL Command) - column.Contains("PRIMARY KEY") + column.Contains("PRIMARY KEY"), + column.Contains("AUTOINCREMENT"), + (sqlStr.Contains($"FOREIGN KEY ({splitStr[0]})")) ? new ForeignKey(splitStr[0], columns.Where(a => a.Contains($"FOREIGN KEY ({splitStr[0]})")).First().Split(new[] { "REFERENCES " }, StringSplitOptions.None)[1].Split('(')[0], columns.Where(a => a.Contains($"FOREIGN KEY ({splitStr[0]})")).First().Split(new[] { "REFERENCES " }, StringSplitOptions.None)[1].Split('(')[1].Split(')')[0]) : null )); } return columnsMap; @@ -232,13 +249,17 @@ private class ColumnInfo public bool nullCheck { get; set; } public string defaultValue { get; set; } public bool primaryKey { get; set; } + public bool isAutoIncrementing { get; set; } + public ForeignKey foreignKey { get; set; } - public ColumnInfo(string type, bool nullCheck, string defaultValue, bool primaryKey) + public ColumnInfo(string type, bool nullCheck, string defaultValue, bool primaryKey, bool isAutoIncrementing, ForeignKey foreignKey = null) { this.type = type; this.nullCheck = nullCheck; this.defaultValue = defaultValue; this.primaryKey = primaryKey; + this.isAutoIncrementing = isAutoIncrementing; + this.foreignKey = foreignKey; } public override bool Equals(object obj) @@ -247,7 +268,23 @@ public override bool Equals(object obj) type == other.type && nullCheck == other.nullCheck && defaultValue == other.defaultValue && - primaryKey == other.primaryKey; + primaryKey == other.primaryKey && + isAutoIncrementing == other.isAutoIncrementing && + foreignKey == other.foreignKey; + } + } + + public class ForeignKey + { + public string key { get; set; } + public string refTable { get; set; } + public string refColumn { get; set; } + + public ForeignKey(string key, string refTable, string refColumn) + { + this.key = key; + this.refTable = refTable; + this.refColumn = refColumn; } } diff --git a/AMDiscordRPC/Globals.cs b/AMDiscordRPC/Globals.cs index c4fbd51..e0a1bff 100644 --- a/AMDiscordRPC/Globals.cs +++ b/AMDiscordRPC/Globals.cs @@ -55,9 +55,9 @@ public static void ConfigureLogger() LevelMax = Level.Fatal, LevelMin = Level.Info }; - #if DEBUG - lrf.LevelMin = Level.Debug; - #endif +#if DEBUG + lrf.LevelMin = Level.Debug; +#endif lrf.ActivateOptions(); PatternLayout pl = new PatternLayout @@ -296,9 +296,13 @@ public override bool Equals(object obj) return obj is WebSongResponse other && artworkURL == other.artworkURL && trackURL == other.trackURL && - trackName == other.trackName && + trackName == other.trackName && artistURL == other.artistURL; } } + public static String TrimEnd(this String str, int count) + { + return str.Substring(0, str.Length - count); + } } } \ No newline at end of file From 7e85228dc8aee0ed7c7a4a501d34fb7165f0d75f Mon Sep 17 00:00:00 2001 From: CrawLeyYou <60201017+CrawLeyYou@users.noreply.github.com> Date: Sun, 5 Oct 2025 15:30:06 +0300 Subject: [PATCH 4/5] Add FullScreen Tweak This fixes the issue with Apple Music is not going fullscreen on non primary monitors. --- AMDiscordRPC/AMDiscordRPC.cs | 2 +- AMDiscordRPC/Globals.cs | 99 ++++++++++++++++++++++++++++++++++++ AMDiscordRPC/UI.cs | 44 +++++++++++++++- 3 files changed, 143 insertions(+), 2 deletions(-) diff --git a/AMDiscordRPC/AMDiscordRPC.cs b/AMDiscordRPC/AMDiscordRPC.cs index 0657ea7..8305796 100644 --- a/AMDiscordRPC/AMDiscordRPC.cs +++ b/AMDiscordRPC/AMDiscordRPC.cs @@ -88,7 +88,7 @@ static void AMEvent() { for (var i = 0; i < windows.Length; i++) { - if (windows[i].Name == "Apple Music") window = windows[i]; + if (windows[i].Name == "Apple Music" && windows[i].FindFirstChild().Name == "Non Client Input Sink Window") window = windows[i]; } } else if (windows.Length == 1) diff --git a/AMDiscordRPC/Globals.cs b/AMDiscordRPC/Globals.cs index e0a1bff..ee644c5 100644 --- a/AMDiscordRPC/Globals.cs +++ b/AMDiscordRPC/Globals.cs @@ -216,6 +216,105 @@ public enum SmallImage None } + public enum GWLP { + EXSTYLE = -20, + HINSTANCE = -6, + HWNDPARENT = -8, + ID = -12, + STYLE = -16, + USERDATA = -21, + WNDPROC = -4 + } + + public enum WS : long + { + BORDER = 0x00800000L, + CAPTION = 0x00C00000L, + CHILD = 0x40000000L, + CHILDWINDOW = 0x40000000L, + CLIPCHILDREN = 0x02000000L, + CLIPSIBLINGS = 0x04000000L, + DISABLED = 0x08000000L, + DLGFRAME = 0x00400000L, + GROUP = 0x00020000L, + HSCROLL = 0x00100000L, + ICONIC = 0x20000000L, + MAXIMIZE = 0x01000000L, + MAXIMIZEBOX = 0x00010000L, + MINIMIZE = 0x20000000L, + MINIMIZEBOX = 0x00020000L, + OVERLAPPED = 0x00000000L, + OVERLAPPEDWINDOW = (OVERLAPPED | CAPTION | SYSMENU | THICKFRAME | MINIMIZEBOX | MAXIMIZEBOX), + POPUP = 0x80000000L, + POPUPWINDOW = (POPUP | BORDER | SYSMENU), + SIZEBOX = 0x00040000L, + SYSMENU = 0x00080000L, + TABSTOP = 0x00010000L, + THICKFRAME = 0x00040000L, + TILED = 0x00000000L, + TILEDWINDOW = (OVERLAPPED | CAPTION | SYSMENU | THICKFRAME | MINIMIZEBOX | MAXIMIZEBOX), + VISIBLE = 0x10000000L, + VSCROLL = 0x00200000L + } + + public enum WS_EX : long + { + ACCEPTFILES = 0x00000010L, + APPWINDOW = 0x00040000L, + CLIENTEDGE = 0x00000200L, + COMPOSITED = 0x02000000L, + CONTEXTHELP = 0x00000400L, + CONTROLPARENT = 0x00010000L, + DLGMODALFRAME = 0x00000001L, + LAYERED = 0x00080000L, + LAYOUTRTL = 0x00400000L, + LEFT = 0x00000000L, + LEFTSCROLLBAR = 0x00004000L, + LTRREADING = 0x00000000L, + MDICHILD = 0x00000040L, + NOACTIVATE = 0x08000000L, + NOINHERITLAYOUT = 0x00100000L, + NOPARENTNOTIFY = 0x00000004L, + NOREDIRECTIONBITMAP = 0x00200000L, + OVERLAPPEDWINDOW = (WINDOWEDGE | CLIENTEDGE), + PALETTEWINDOW = (WINDOWEDGE | TOOLWINDOW | TOPMOST), + RIGHT = 0x00001000L, + RIGHTSCROLLBAR = 0x00000000L, + RTLREADING = 0x00002000L, + STATICEDGE = 0x00020000L, + TOOLWINDOW = 0x00000080L, + TOPMOST = 0x00000008L, + TRANSPARENT = 0x00000020L, + WINDOWEDGE = 0x00000100L + } + + public enum HWND + { + BOTTOM = 1, + NOTOPMOST = -2, + TOP = 0, + TOPMOST = -1 + } + + public enum SWP + { + ASYNCWINDOWPOS = 0x4000, + DEFERERASE = 0x2000, + DRAWFRAME = 0x0020, + FRAMECHANGED = 0x0020, + HIDEWINDOW = 0x0080, + NOACTIVATE = 0x0010, + NOCOPYBITS = 0x0100, + NOMOVE = 0x0002, + NOOWNERZORDER = 0x0200, + NOREDRAW = 0x0008, + NOREPOSITION = 0x0200, + NOSENDCHANGING = 0x0400, + NOSIZE = 0x0001, + NOZORDER = 0x0004, + SHOWWINDOW = 0x0040 + } + public class SongData : EventArgs { public string SongName { get; set; } diff --git a/AMDiscordRPC/UI.cs b/AMDiscordRPC/UI.cs index 1043fff..2b4a651 100644 --- a/AMDiscordRPC/UI.cs +++ b/AMDiscordRPC/UI.cs @@ -1,8 +1,9 @@ using AMDiscordRPC.UIComponents; +using FlaUI.UIA3; using System; using System.Diagnostics; -using System.Drawing; using System.IO; +using System.Runtime.InteropServices; using System.Threading; using System.Windows; using System.Windows.Forms; @@ -10,6 +11,7 @@ using static AMDiscordRPC.Globals; using Application = System.Windows.Application; using OpenFileDialog = Microsoft.Win32.OpenFileDialog; +using Window = FlaUI.Core.AutomationElements.Window; namespace AMDiscordRPC { @@ -20,6 +22,15 @@ internal class UI private static Application app; private static Thread mainThread = Thread.CurrentThread; + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + private static extern long GetWindowLongPtrA(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern long SetWindowLongPtrA(IntPtr hWnd, int nIndex, long dwNewLong); + public static void CreateUI() { Thread thread = new Thread(() => @@ -69,6 +80,36 @@ public static void FFmpegDialog() thread.Start(); } + public static void FullScreenTweak() + { + using (var automation = new UIA3Automation()) + { + Window lyricScreenWindow = null; + foreach (var window in AppleMusicProc.GetAllTopLevelWindows(automation)) + { + if (window.FindFirstChild().Name != "Non Client Input Sink Window" && window.Name == "Apple Music") lyricScreenWindow = window; + } + + if (lyricScreenWindow != null) + { + IntPtr lyricsScreenHandler = (IntPtr)lyricScreenWindow.Properties.NativeWindowHandle; + Screen lyricsScreenHandlerCurrentMonitor = Screen.FromHandle(lyricsScreenHandler); + long style = GetWindowLongPtrA(lyricsScreenHandler, (int) GWLP.STYLE); + style &= ~((long) WS.CAPTION | (long) WS.THICKFRAME); + SetWindowLongPtrA(lyricsScreenHandler, (int) GWLP.STYLE, style); + + long exStyle = GetWindowLongPtrA(lyricsScreenHandler, (int) GWLP.EXSTYLE); + exStyle &= ~((long) WS_EX.DLGMODALFRAME | (long) WS_EX.CLIENTEDGE | (long) WS_EX.STATICEDGE); + SetWindowLongPtrA(lyricsScreenHandler, (int) GWLP.EXSTYLE, exStyle); + + SetWindowPos(lyricsScreenHandler, (int) HWND.TOPMOST, lyricsScreenHandlerCurrentMonitor.Bounds.Left, + lyricsScreenHandlerCurrentMonitor.Bounds.Top, lyricsScreenHandlerCurrentMonitor.Bounds.Width, + lyricsScreenHandlerCurrentMonitor.Bounds.Height, + (uint) SWP.NOOWNERZORDER | (uint) SWP.FRAMECHANGED | (uint) SWP.SHOWWINDOW); + } + } + } + public class AMDiscordRPCTray { private static NotifyIcon notifyIcon = new NotifyIcon(); @@ -111,6 +152,7 @@ public AMDiscordRPCTray() notifySongState, s3Menu, optionsMenu, + new MenuItem("Fix Fullscreen", (s,e) => FullScreenTweak()), new MenuItem("Show Latest Log", (s,e) => { Process.Start("notepad", $"{Path.Combine(Directory.GetCurrentDirectory(), @"logs\latest.log")}"); }), new MenuItem("Exit", (s, e) => { Environment.Exit(0); }) } From 9e6e9375d56f3e2f003b7153f7da7e794aac5a5b Mon Sep 17 00:00:00 2001 From: CrawLeyYou <60201017+CrawLeyYou@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:55:35 +0300 Subject: [PATCH 5/5] Fix the not registering to database issue on albums with apostrophe also + Add WebView2 (in the near future we will switch to React based UI prob.) + Change how albums gets registered in first place + New data types for new database structure --- AMDiscordRPC/AMDiscordRPC.cs | 5 +- AMDiscordRPC/AMDiscordRPC.csproj | 14 +++ AMDiscordRPC/App.config | 4 + AMDiscordRPC/Covers.cs | 4 +- AMDiscordRPC/Database.cs | 146 +++++++++++++++++++++++++++---- AMDiscordRPC/packages.config | 1 + 6 files changed, 153 insertions(+), 21 deletions(-) diff --git a/AMDiscordRPC/AMDiscordRPC.cs b/AMDiscordRPC/AMDiscordRPC.cs index 8305796..9aa03e6 100644 --- a/AMDiscordRPC/AMDiscordRPC.cs +++ b/AMDiscordRPC/AMDiscordRPC.cs @@ -26,7 +26,7 @@ static void Main(string[] args) InitDiscordRPC(); AttachToAM(); AMSongDataEvent.SongChanged += async (sender, x) => - { + { log.Info($"Song: {x.SongName} \\ Artist and Album: {x.ArtistandAlbumName}"); AMDiscordRPCTray.ChangeSongState($"{x.ArtistandAlbumName.Split('—')[0]} - {x.SongName}"); if (x.ArtistandAlbumName == oldAlbumnArtist && oldData.Assets.LargeImageKey != null) @@ -43,7 +43,7 @@ static void Main(string[] args) SetPresence(x, httpRes); oldAlbumnArtist = x.ArtistandAlbumName; } - }; + }; CheckDatabaseIntegrity(); ConfigureFromDB(); CheckFFmpeg(); @@ -181,7 +181,6 @@ static void AMEvent() } else log.Debug("Continue"); string idontknowwhatshouldinamethisbutitsaboutalbum = (isSingle) ? string.Join("-", dashSplit.Take(dashSplit.Length - 1).ToArray()) : string.Join("—", currentArtistAlbum.Split('—').Take(2).ToArray()); - CheckAndInsertAlbum(idontknowwhatshouldinamethisbutitsaboutalbum.Split('—')[1]); Task t = new Task(async () => { httpRes = await GetCover(idontknowwhatshouldinamethisbutitsaboutalbum.Split('—')[1], Uri.EscapeDataString((isSingle) ? string.Join("-", dashSplit.Take(dashSplit.Length - 1).ToArray()) : string.Join("—", currentArtistAlbum.Split('—').Take(2).ToArray()) + $" {currentSong}")); diff --git a/AMDiscordRPC/AMDiscordRPC.csproj b/AMDiscordRPC/AMDiscordRPC.csproj index 53f51a0..1178e86 100644 --- a/AMDiscordRPC/AMDiscordRPC.csproj +++ b/AMDiscordRPC/AMDiscordRPC.csproj @@ -100,6 +100,9 @@ Resources\Logo Black.ico + + false + ..\packages\AngleSharp.1.3.0\lib\net472\AngleSharp.dll @@ -138,6 +141,15 @@ ..\packages\Microsoft.Bcl.AsyncInterfaces.9.0.7\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + + ..\packages\Microsoft.Web.WebView2.1.0.3595.46\lib\net462\Microsoft.Web.WebView2.Core.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.3595.46\lib\net462\Microsoft.Web.WebView2.WinForms.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.3595.46\lib\net462\Microsoft.Web.WebView2.Wpf.dll + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll @@ -317,8 +329,10 @@ + + \ No newline at end of file diff --git a/AMDiscordRPC/App.config b/AMDiscordRPC/App.config index 2012abb..64a9a5f 100644 --- a/AMDiscordRPC/App.config +++ b/AMDiscordRPC/App.config @@ -49,6 +49,10 @@ + + + + diff --git a/AMDiscordRPC/Covers.cs b/AMDiscordRPC/Covers.cs index 0d207e1..294c511 100644 --- a/AMDiscordRPC/Covers.cs +++ b/AMDiscordRPC/Covers.cs @@ -34,7 +34,7 @@ private static async Task AsyncFetchiTunes(string album, string imageRes["results"][0]["collectionName"].ToString(), imageRes["results"][0]["artistViewUrl"].ToString() ); - UpdateAlbum(new SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL, null, null, null, webRes.artistURL)); + InsertAlbum(new SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL, null, null, null, webRes.artistURL)); CoverThread = null; return webRes; } @@ -83,7 +83,7 @@ public static async Task AsyncAMFetch(string album, string sear document.DocumentElement.QuerySelectorAll("div.track-lockup__clamp-wrapper > span > a")[0].GetAttribute("href") ); CoverThread = null; - UpdateAlbum(new Database.SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL, null, null, null, webRes.artistURL)); + InsertAlbum(new Database.SQLCoverResponse(album, webRes.artworkURL, webRes.trackURL, null, null, null, webRes.artistURL)); return webRes; } else diff --git a/AMDiscordRPC/Database.cs b/AMDiscordRPC/Database.cs index 46a17ac..03ec51a 100644 --- a/AMDiscordRPC/Database.cs +++ b/AMDiscordRPC/Database.cs @@ -1,5 +1,4 @@ -using Amazon.Runtime.Internal.Transform; -using System; +using System; using System.Collections.Generic; using System.Data.SQLite; using System.Linq; @@ -12,12 +11,12 @@ internal class Database private static SQLiteConnection sqlite; public static readonly Dictionary sqlMap = new Dictionary() { - //{"coverTable", "album TEXT PRIMARY KEY NOT NULL, source TEXT, redirURL TEXT DEFAULT 'https://music.apple.com/home', artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistSource TEXT, animated BOOLEAN CHECK (animated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT" }, + {"coverTable", "album TEXT PRIMARY KEY NOT NULL, source TEXT, redirURL TEXT DEFAULT 'https://music.apple.com/home', artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistSource TEXT, animated BOOLEAN CHECK (animated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT" }, // will be replaced {"coverTableNew", "coverID INTEGER PRIMARY KEY AUTOINCREMENT, staticCoverURL TEXT NOT NULL, isAnimated BOOLEAN CHECK (isAnimated IN (0,1)) DEFAULT NULL, streamURL TEXT, animatedURL TEXT"}, {"artistTable", "artistID INTEGER PRIMARY KEY AUTOINCREMENT, artistName TEXT NOT NULL, artistRedirURL TEXT DEFAULT 'https://music.apple.com/home', artistProfileSource TEXT"}, {"albumTable", "albumID INTEGER PRIMARY KEY AUTOINCREMENT, albumName TEXT NOT NULL, albumURL TEXT UNIQUE, isSingle BOOLEAN CHECK (isSingle IN (0,1)), coverID INTEGER, artistID INTEGER, FOREIGN KEY (coverID) REFERENCES coverTableNew(coverID), FOREIGN KEY (artistID) REFERENCES artistTable(artistID)"}, {"songTable", "songTitle TEXT, songURL TEXT, albumID INTEGER, artistID INTEGER, FOREIGN KEY (albumID) REFERENCES albumTable(albumID), FOREIGN KEY (artistID) REFERENCES artistTable(artistID)"}, - {"creds", "S3_accessKey TEXT, S3_secretKey TEXT, S3_serviceURL TEXT, S3_bucketName TEXT, S3_bucketURL TEXT, S3_isSpecificKey BOOLEAN CHECK (S3_isSpecificKey IN (0,1)), FFmpegPath TEXT" }, + {"creds", "S3_accessKey TEXT, S3_secretKey TEXT, S3_serviceURL TEXT, S3_bucketName TEXT, S3_bucketURL TEXT, S3_isSpecificKey BOOLEAN CHECK (S3_isSpecificKey IN (0,1)), FFmpegPath TEXT, LastFMToken TEXT" }, {"logs", "timestamp INTEGER, type TEXT, occuredAt TEXT, message TEXT" }, {"clientSettings", "smallImage INTEGER"} }; @@ -105,18 +104,18 @@ private static void CheckTables() public static void UpdateAlbum(SQLCoverResponse data) { - ExecuteNonQueryCommand($"UPDATE coverTable SET ({string.Join(", ", data.GetNotNullKeys())}) = ({string.Join(", ", data.GetNotNullValues())}) WHERE album = '{data.album}'"); + ExecuteNonQueryCommand($"UPDATE coverTable SET ({string.Join(", ", data.GetNotNullKeys())}) = ({string.Join(", ", data.GetNotNullValues())}) WHERE album = @album", new[] { new SQLiteParameter("@album", data.album)}); } - public static void CheckAndInsertAlbum(string album) + public static void InsertAlbum(SQLCoverResponse data) { - if (ExecuteScalarCommand($"SELECT album from coverTable WHERE album = '{album}'") == null) - ExecuteNonQueryCommand($"INSERT INTO coverTable(album) VALUES ('{album}')"); + if (ExecuteScalarCommand($"SELECT album from coverTable WHERE album = @album", new[] { new SQLiteParameter("@album", data.album) }) == null) + ExecuteNonQueryCommand($@"INSERT INTO coverTable(album, {string.Join(", ", data.GetNotNullKeys())}) VALUES (@album, {string.Join(", ", data.GetNotNullValues())})", new[] {new SQLiteParameter("@album", data.album)}); } public static SQLCoverResponse GetAlbumDataFromSQL(string album) { - using (SQLiteDataReader reader = ExecuteReaderCommand($"SELECT * FROM coverTable WHERE album = '{album}' LIMIT 1")) + using (SQLiteDataReader reader = ExecuteReaderCommand($"SELECT * FROM coverTable WHERE album = @album LIMIT 1", new[] { new SQLiteParameter("@album", album) })) { while (reader.Read()) { @@ -134,6 +133,32 @@ public static SQLCoverResponse GetAlbumDataFromSQL(string album) return null; } + public static SQLSongResponse GetSongFromDB(string song, string album, string artist) + { + string cmd = @" + SELECT + IIF(coverTableNew.isAnimated = 1, coverTableNew.animatedURL, coverTableNew.staticCoverURL) as coverURL, + artistTable.artistRedirURL as artistRedirURL, + artistTable.artistProfileSource as artistProfileSource, + albumTable.albumURL, + songTable.songURL + FROM songTable + INNER JOIN albumTable on albumTable.albumID = songTable.albumID + INNER JOIN artistTable on artistTable.artistID = albumTable.artistID + INNER JOIN coverTableNew on coverTableNew.coverID = albumTable.coverID + WHERE + artistTable.artistName = @artist + AND songTable.songTitle = @song + AND albumTable.albumName = @album; + "; + using (SQLiteDataReader reader = ExecuteReaderCommand(cmd, new[] { new SQLiteParameter("@album", album), new SQLiteParameter("@song", song), new SQLiteParameter("@artist", artist) })) + { + + } + + return null; + } + private static void CheckColumns() { foreach (var table in sqlMap.Keys) @@ -201,11 +226,12 @@ private static Dictionary ConvertSQLStringToColumnInfo(strin return columnsMap; } - public static object ExecuteScalarCommand(string command) + public static object ExecuteScalarCommand(string command, SQLiteParameter[] parameters = null) { try { - SQLiteCommand cmd = new SQLiteCommand($@"{command}", sqlite); + SQLiteCommand cmd = new SQLiteCommand(command, sqlite); + if (parameters != null) cmd.Parameters.AddRange(parameters); return cmd.ExecuteScalar(); } catch (Exception ex) @@ -215,11 +241,12 @@ public static object ExecuteScalarCommand(string command) } } - public static SQLiteDataReader ExecuteReaderCommand(string command) + public static SQLiteDataReader ExecuteReaderCommand(string command, SQLiteParameter[] parameters = null) { try { - SQLiteCommand cmd = new SQLiteCommand($@"{command}", sqlite); + SQLiteCommand cmd = new SQLiteCommand(command, sqlite); + if (parameters != null) cmd.Parameters.AddRange(parameters); return cmd.ExecuteReader(); } catch (Exception ex) @@ -229,11 +256,12 @@ public static SQLiteDataReader ExecuteReaderCommand(string command) } } - public static int ExecuteNonQueryCommand(string command) + public static int ExecuteNonQueryCommand(string command, SQLiteParameter[] parameters = null) { try { - SQLiteCommand cmd = new SQLiteCommand($@"{command}", sqlite); + SQLiteCommand cmd = new SQLiteCommand(command, sqlite); + if (parameters != null) cmd.Parameters.AddRange(parameters); return cmd.ExecuteNonQuery(); } catch (Exception ex) @@ -274,7 +302,7 @@ public override bool Equals(object obj) } } - public class ForeignKey + private class ForeignKey { public string key { get; set; } public string refTable { get; set; } @@ -321,5 +349,91 @@ public List GetNotNullValues() } } + + public class SQLSongResponse + { + public SQLCoverData? cover; + public SQLAlbumData? album; + public SQLArtistData? artist; + public SQLSongData? song; + + public SQLSongResponse(SQLCoverData cover, SQLAlbumData album, SQLArtistData artist, SQLSongData song) + { + this.cover = cover; + this.album = album; + this.artist = artist; + this.song = song; + } + } + + public class SQLCoverData + { + public int id; + public string staticCoverURL; + public bool? isAnimated; + public string? streamURL; + public string? animatedURL; + + public SQLCoverData(int id, string staticCoverURL, bool? isAnimated, string? streamURL, string? animatedURL) + { + this.id = id; + this.staticCoverURL = staticCoverURL; + this.isAnimated = isAnimated; + this.streamURL = streamURL; + this.animatedURL = animatedURL; + } + } + + public class SQLArtistData + { + public int id; + public string artistName; + public string artistRedirURL; + public string? artistProfileSource; + + public SQLArtistData(int id, string artistName, string artistRedirURL, string? artistProfileSource) + { + this.id = id; + this.artistName = artistName; + this.artistRedirURL = artistRedirURL; + this.artistProfileSource = artistProfileSource; + } + } + + public class SQLAlbumData + { + public int id; + public string albumName; + public string albumURL; + public bool isSingle; + public int? coverID; // Foreign Key + public int? artistID; // Foreign Key + + public SQLAlbumData(int id, string albumName, string albumURL, bool isSingle, int coverID, int artistID) + { + this.id = id; + this.albumName = albumName; + this.albumURL = albumURL; + this.isSingle = isSingle; + this.coverID = coverID; + this.artistID = artistID; + } + } + + public class SQLSongData + { + public string songTitle; + public string songURL; + public int? albumID; // Foreign Key + public int? artistID; // Foreign Key + + public SQLSongData(string songTitle, string songURL, int albumID, int artistID) + { + this.songTitle = songTitle; + this.songURL = songURL; + this.albumID = albumID; + this.artistID = artistID; + } + } } } diff --git a/AMDiscordRPC/packages.config b/AMDiscordRPC/packages.config index 5d6c8c1..79da35e 100644 --- a/AMDiscordRPC/packages.config +++ b/AMDiscordRPC/packages.config @@ -11,6 +11,7 @@ +