From 38972262a38ab2cde1530bb3de3c4c3e93100652 Mon Sep 17 00:00:00 2001 From: Smaltin Date: Sat, 11 Nov 2023 21:21:22 -0800 Subject: [PATCH] Buttons and Album Artist fallback (#86) * Adds fallback for the Album Artist field if the "Artist" field isn't filled * Merge button changes Also adds tidal and foobar2000 support Co-Authored-By: jojo2357 <66704796+jojo2357@users.noreply.github.com> * Fixes non-urlencoded strings from breaking lastfm Also adds buttons to the configuration file * Adds buttons to local, and groundwork for MM5 * Update DiscordPresenceConfig.ini Adds amazon and MM5 discord applications * Enables foobar2000 support. Do note that it doesn't have full support. Album title is not transmitted, resulting in lack of album art Closes #69 * Update README.MD And unimportant changes * Spacing issue --------- Co-authored-by: jojo2357 <66704796+jojo2357@users.noreply.github.com> --- DiscordPresenceConfig.ini | 18 ++ MDRP/Program.cs | 182 +++++++++++++++----- MDRP/bin/Release/MDRP.exe | Bin 430592 -> 433664 bytes MDRP/util/ExternalAPI/ExternalArtManager.cs | 2 +- MDRP/util/JsonResponse.cs | 7 +- README.MD | 17 +- languages/italian-it.lang | 2 +- 7 files changed, 179 insertions(+), 49 deletions(-) diff --git a/DiscordPresenceConfig.ini b/DiscordPresenceConfig.ini index 631a148..72617ba 100644 --- a/DiscordPresenceConfig.ini +++ b/DiscordPresenceConfig.ini @@ -5,6 +5,10 @@ microsoft.media.player=true=807774172574253056 wmplayer=true=807774172574253056 wavelink=true=927328178618376212 tidal=true=922625678271197215 +amazon music=true=808571949227049000 +mediamonkeyengine=true=1096929771511873587 +foobar2000=true=1009193842211299428 + verbose=false debug missing player=false #spotify-style-rp: @@ -38,3 +42,17 @@ large music.ui asset=https://cdn.discordapp.com/app-assets/801209905020272681/80 # Windows Media Player small microsoft.media.player asset=https://cdn.discordapp.com/app-assets/801209905020272681/801224923547959376.png large microsoft.media.player asset=https://cdn.discordapp.com/app-assets/801209905020272681/801224923547959376.png + +# Buttons + +# view artist on Last.fm +//button text=View ${artist} on Last.fm +//button url=https://last.fm/music/${artist} + +# view track on Last.fm +//button text=View Last.fm info +//button url=https://www.last.fm/music/${artist}/_/${title} + +# rickroll your friends +//button text=Mystery Button! +//button url=https://www.youtube.com/watch?v=dQw4w9WgXcQ \ No newline at end of file diff --git a/MDRP/Program.cs b/MDRP/Program.cs index b7ceae1..946acfc 100644 --- a/MDRP/Program.cs +++ b/MDRP/Program.cs @@ -13,8 +13,6 @@ using CSCore.CoreAudioAPI; using DiscordRPC; using DiscordRPC.Message; -using IWshRuntimeLibrary; -using Microsoft.Toolkit.Uwp.Notifications; using Newtonsoft.Json.Linq; using File = System.IO.File; @@ -23,7 +21,7 @@ namespace MDRP internal partial class Program { public static LangHelper langHelper = new LangHelper(); - public const string Version = "1.7.2"; + public const string Version = "1.7.3"; public const string Github = "https://github.com/jojo2357/Music-Discord-Rich-Presence"; public static readonly string Title = langHelper.get(LocalizableStrings.MDRP_FULL); private const int titleLength = 64; @@ -45,8 +43,11 @@ internal partial class Program { "musicbee", new DiscordRpcClient("820837854385012766", autoEvents: false) }, { "spotify", new DiscordRpcClient("802222525110812725", autoEvents: false) }, { "tidal", new DiscordRpcClient("922625678271197215", autoEvents: false) }, + { "tidalplayer", new DiscordRpcClient("922625678271197215", autoEvents: false) }, { "wavelink", new DiscordRpcClient("927328178618376212", autoEvents: false) }, { "amazon music", new DiscordRpcClient("807774172574253056", autoEvents: false) }, + { "mediamonkeyengine", new DiscordRpcClient("1096929771511873587", autoEvents: false) }, + { "foobar2000", new DiscordRpcClient("1009193842211299428", autoEvents: false) }, { "", new DiscordRpcClient("821398156905283585", autoEvents: false) } }; @@ -81,15 +82,18 @@ internal partial class Program { "spotify", ConsoleColor.DarkGreen }, { "musicbee", ConsoleColor.Yellow }, { "tidal", ConsoleColor.Gray }, + { "tidalplayer", ConsoleColor.Gray }, { "wavelink", ConsoleColor.DarkBlue }, { "amazon music", ConsoleColor.Gray }, + { "foobar2000", ConsoleColor.DarkCyan}, + {"mediamonkeyengine", ConsoleColor.Yellow} }; private static string _presenceDetails = string.Empty; private static readonly string[] ValidPlayers = { - "music.ui", "spotify", "musicbee", "microsoft.media.player", "wmplayer", "tidal", "wavelink", "amazon music" + "music.ui", "spotify", "musicbee", "microsoft.media.player", "wmplayer", "tidal", "tidalplayer", "wavelink", "amazon music", "foobar2000", "mediamonkeyengine" }; private static readonly string[] RequiresPipeline = { "musicbee" }; @@ -104,8 +108,11 @@ internal partial class Program { "wmplayer", "Windows Media Player" }, { "music.ui", "Groove Music Player" }, { "tidal", "Tidal Music" }, + { "tidalplayer", "Tidal Music" }, { "wavelink", "Wave Link" }, - { "amazon music", "Amazon Music" } + { "amazon music", "Amazon Music" }, + { "foobar2000", "FooBar2000"}, + { "mediamonkeyengine", "MediaMonkey5"} }; private static readonly Dictionary BigAssets = new Dictionary @@ -119,7 +126,10 @@ internal partial class Program }, { "spotify", "https://cdn.discordapp.com/app-assets/802222525110812725/802222954821582869.png" }, { "tidal", "https://cdn.discordapp.com/app-assets/922625678271197215/978018192393920562.png" }, + { "tidalplayer", "https://cdn.discordapp.com/app-assets/922625678271197215/978018192393920562.png" }, { "wavelink", "https://cdn.discordapp.com/app-assets/927328178618376212/927329727180574760.png" }, + { "foobar2000", "https://cdn.discordapp.com/app-assets/1009193842211299428/1009196080853950464.png" }, + {"mediamonkeyengine", "https://cdn.discordapp.com/app-assets/1096929771511873587/1096930933325713519.png"} }; //might just combine these later @@ -134,7 +144,10 @@ internal partial class Program }, { "spotify", "https://cdn.discordapp.com/app-assets/802222525110812725/802222992683827200.png" }, { "tidal", "https://cdn.discordapp.com/app-assets/922625678271197215/978018192637198406.png" }, + { "tidalplayer", "https://cdn.discordapp.com/app-assets/922625678271197215/978018192637198406.png" }, { "wavelink", "https://cdn.discordapp.com/app-assets/927328178618376212/927329727218319410.png" }, + { "foobar2000", "https://cdn.discordapp.com/app-assets/1009193842211299428/1009196080950423563.png" }, + {"mediamonkeyengine", "https://cdn.discordapp.com/app-assets/1096929771511873587/1096930933325713519.png"} }; private static readonly Dictionary Whatpeoplecallthisplayer = new Dictionary @@ -145,8 +158,11 @@ internal partial class Program { "wmplayer", "Windows Media Player" }, { "spotify", "Spotify" }, { "tidal", "Tidal Music" }, + { "tidalplayer", "Tidal Music" }, { "wavelink", "Wave Link" }, - { "amazon music", "Amazon Music" } + { "amazon music", "Amazon Music" }, + { "foobar2000", "FooBar2000"}, + {"mediamonkeyengine", "MediaMonkey5"} }; private static readonly Dictionary InverseWhatpeoplecallthisplayer = @@ -158,7 +174,9 @@ internal partial class Program { "spotify", "spotify" }, { "Tidal Music", "tidal" }, { "Wave Link", "wavelink" }, - { "Amazon Music", "anazon music" } + { "Amazon Music", "anazon music" }, + { "FooBar2000", "foobar2000"}, + {"MediaMonkey5", "mediamonkeyengine"} }; private static readonly string defaultPlayer = "groove"; @@ -208,6 +226,8 @@ internal partial class Program private static string lineData = "", currentTitle = ""; private static bool foundFirst = false, foundSecond = false; + private static string buttonText = "Click Me!", buttonURL = "https://archive.org/donate/"; + private static bool foundButtonText = false, foundButtonURL = false; private static bool foundImageRemotely = false; private static readonly Regex _smallAssetRex = @@ -216,6 +236,31 @@ internal partial class Program private static readonly Regex _largeAssetReg = new Regex("(?<=^large\\s?)\\b[\\w\\\\.]+\\b(?=\\s?asset)", RegexOptions.IgnoreCase); + private static String GetArtist(JsonResponse lastMessage) + { + if (lastMessage != null) + { + if (lastMessage.Artist != "" || lastMessage.AlbumArtist != "") + { + if (lastMessage.Artist != "") + { + return lastMessage.Artist; + } + return lastMessage.AlbumArtist; + } + } + else if (_currentTrack.Artist != "" || _currentTrack.AlbumArtist != "") + { + if (_currentTrack.Artist != "") + { + return _currentTrack.Artist; + } + + return _currentTrack.AlbumArtist; + } + return langHelper[LocalizableStrings.UNKNOWN_ARTIST]; + } + public static async Task HandleIncomingConnections() { // While a user hasn't visited the `shutdown` url, keep on handling requests @@ -523,7 +568,7 @@ private static void HandleLocalRequests() } }); InvokeActiveClient(); - SetConsole(_lastTrack.Title, _lastTrack.Artist, _lastTrack.AlbumTitle, + SetConsole(_lastTrack.Title, GetArtist(null), _lastTrack.AlbumTitle, currentAlbum); } else if @@ -552,16 +597,12 @@ private static void HandleLocalRequests() string newDetailsWithTitle = Functions.CapLength( lineData.Split('\n')[0] .Replace("${artist}", - (_currentTrack.Artist == "" - ? langHelper[LocalizableStrings.UNKNOWN_ARTIST] - : _currentTrack.Artist)).Replace("${title}", _currentTrack.Title) + (GetArtist(null))).Replace("${title}", _currentTrack.Title) .Replace("${album}", _currentTrack.AlbumTitle), titleLength); string newStateWithArtist = Functions.CapLength( lineData.Split('\n')[1] .Replace("${artist}", - (_currentTrack.Artist == "" - ? langHelper[LocalizableStrings.UNKNOWN_ARTIST] - : _currentTrack.Artist)).Replace("${title}", _currentTrack.Title) + (GetArtist(null))).Replace("${title}", _currentTrack.Title) .Replace("${album}", _currentTrack.AlbumTitle), artistLength); if (activeClient.CurrentPresence == null || activeClient.CurrentPresence.Details != newDetailsWithTitle || @@ -601,7 +642,7 @@ private static void HandleLocalRequests() } } - activeClient.SetPresence(new RichPresence + RichPresence richPresence = new RichPresence { Details = newDetailsWithTitle, State = newStateWithArtist, @@ -612,8 +653,20 @@ private static void HandleLocalRequests() SmallImageKey = GetSmallImageKey(), SmallImageText = GetSmallImageText() } - }); - SetConsole(_currentTrack.Title, _currentTrack.Artist, _currentTrack.AlbumTitle, + }; + + if (foundButtonText || foundButtonURL) + { + richPresence.Buttons = new []{new Button + { + Label = PrepareFormatStringCappedLocal(GetArtist(null), _currentTrack.AlbumTitle, _currentTrack.Title, buttonText, 32), + Url = PrepareEscapedFormatStringLocal(GetArtist(null), _currentTrack.AlbumTitle, _currentTrack.Title, buttonURL) + }}; + } + + activeClient.SetPresence(richPresence); + + SetConsole(_currentTrack.Title, GetArtist(null), _currentTrack.AlbumTitle, currentAlbum); InvokeActiveClient(); } @@ -734,23 +787,10 @@ private static void HandleRemoteRequests() currentTitle = lastMessage.Title; WrongArtistFlag = HasNameNotQuite(new Album(lastMessage.Album.Name), _playerName); - - activeClient.SetPresence(new RichPresence + RichPresence richPresence = new RichPresence { - Details = Functions.CapLength( - lineData.Split('\n')[0] - .Replace("${artist}", - (lastMessage.Artist == "" - ? langHelper[LocalizableStrings.UNKNOWN_ARTIST] - : lastMessage.Artist)).Replace("${title}", lastMessage.Title) - .Replace("${album}", lastMessage.Album.Name), titleLength), - State = Functions.CapLength( - lineData.Split('\n')[1] - .Replace("${artist}", - (lastMessage.Artist == "" - ? langHelper[LocalizableStrings.UNKNOWN_ARTIST] - : lastMessage.Artist)).Replace("${title}", lastMessage.Title) - .Replace("${album}", lastMessage.Album.Name), artistLength), + Details = PrepareFormatStringCapped(lastMessage, lineData.Split('\n')[0], titleLength), + State = PrepareFormatStringCapped(lastMessage, lineData.Split('\n')[1], titleLength), Timestamps = _isPlaying ? new Timestamps @@ -765,8 +805,21 @@ private static void HandleRemoteRequests() LargeImageText = Functions.GetLargeImageText(lastMessage.Album.Name), SmallImageKey = GetSmallImageKey(), SmallImageText = GetSmallImageText() - } - }); + }, + }; + + if (foundButtonText || foundButtonURL) + { + richPresence.Buttons = new []{new Button + { + Label = PrepareFormatStringCapped(lastMessage, buttonText, 32), + Url = PrepareEscapedFormatString(lastMessage, buttonURL) + }}; + } + + activeClient.SetPresence(richPresence); + + InvokeActiveClient(); if (ScreamAtUser && !presenceIsRich && !NotifiedAlbums.Contains(currentAlbum) && @@ -793,12 +846,53 @@ private static void HandleRemoteRequests() } } - SetConsole(lastMessage.Title, lastMessage.Artist, lastMessage.Album.Name, + SetConsole(lastMessage.Title, GetArtist(lastMessage), lastMessage.Album.Name, lastMessage.Album); if (!_isPlaying) Timer.Restart(); } } + private static string PrepareFormatStringCappedLocal(string artist, string album, string title, string instring, + int capLength) + { + return Functions.CapLength( + PrepareFormatStringLocal(artist, album, title, instring), capLength); + } + + private static string PrepareFormatStringCapped(JsonResponse lastMessage, string instring, int capLength) + { + return Functions.CapLength( + PrepareFormatString(lastMessage, instring), capLength); + } + + private static string PrepareFormatStringLocal(string artist, string album, string title, string instring) + { + return instring.Replace("${artist}", + (artist)).Replace("${title}", title) + .Replace("${album}", album); + } + + private static string PrepareFormatString(JsonResponse lastMessage, string instring) + { + return instring.Replace("${artist}", + (GetArtist(lastMessage))).Replace("${title}", lastMessage.Title) + .Replace("${album}", lastMessage.Album.Name); + } + + private static string PrepareEscapedFormatStringLocal(string artist, string album, string title, string instring) + { + return instring.Replace("${artist}", + WebUtility.UrlEncode(Uri.EscapeUriString(artist))).Replace("${title}", WebUtility.UrlEncode(Uri.EscapeUriString(title))) + .Replace("${album}", WebUtility.UrlEncode(Uri.EscapeUriString(album))); + } + + private static string PrepareEscapedFormatString(JsonResponse lastMessage, string instring) + { + return instring.Replace("${artist}", + WebUtility.UrlEncode(Uri.EscapeUriString(GetArtist(lastMessage)))).Replace("${title}", WebUtility.UrlEncode(Uri.EscapeUriString(lastMessage.Title))) + .Replace("${album}", WebUtility.UrlEncode(Uri.EscapeUriString(lastMessage.Album.Name))); + } + private static string GetSmallImageText() { if (_isPlaying) @@ -862,7 +956,7 @@ private static string GetLargeImageKey(Album activeAlbum, string activeTitle) } } - return BigAssets[_playerName]; + return BigAssets.ContainsKey(_playerName) ? BigAssets[_playerName] : BigAssets[defaultPlayer]; } private static void UnsetAllPresences() @@ -1205,12 +1299,22 @@ private static void LoadSettings() else if (!foundFirst && firstPortion == "first line") { foundFirst = true; - lineData = String.Join("=", explodedLine.Skip(1)) + lineData; + lineData = String.Join("=", explodedLine.Skip(1).ToArray()) + lineData; } else if (!foundSecond && firstPortion == "second line") { foundSecond = true; - lineData = lineData + "\n" + String.Join("=", explodedLine.Skip(1)); + lineData = lineData + "\n" + String.Join("=", explodedLine.Skip(1).ToArray()); + } + else if (!foundButtonText && firstPortion == "button text") + { + foundButtonText = true; + buttonText = String.Join("=", explodedLine.Skip(1).ToArray()); + } + else if (!foundButtonURL && firstPortion == "button url") + { + foundButtonURL = true; + buttonURL = String.Join("=", explodedLine.Skip(1).ToArray()); } else if (firstPortion == "get remote artwork") { diff --git a/MDRP/bin/Release/MDRP.exe b/MDRP/bin/Release/MDRP.exe index 6b0c977102357bce971ba02bbdad2c6621084260..35b484c2cfcb2830f1839c46535dca3530c1c6fa 100644 GIT binary patch delta 30614 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 P*r>icc*CMU{9XCKLG2hr diff --git a/MDRP/util/ExternalAPI/ExternalArtManager.cs b/MDRP/util/ExternalAPI/ExternalArtManager.cs index c78e819..94be9ef 100644 --- a/MDRP/util/ExternalAPI/ExternalArtManager.cs +++ b/MDRP/util/ExternalAPI/ExternalArtManager.cs @@ -76,7 +76,7 @@ private async Task ExternalAlbumLookup(Album album, string backupTitle) string text = ""; using (StreamReader reader = new StreamReader(result.GetResponseStream(), Encoding.UTF8)) { - text = reader.ReadToEnd(); + text = await reader.ReadToEndAsync(); } result.Close(); diff --git a/MDRP/util/JsonResponse.cs b/MDRP/util/JsonResponse.cs index 3c6b73c..e78a853 100644 --- a/MDRP/util/JsonResponse.cs +++ b/MDRP/util/JsonResponse.cs @@ -10,6 +10,7 @@ public class JsonResponse public JsonResponse(JObject jObject) { Artist = GetJasonField(jObject, "artist"); + AlbumArtist = GetJasonField(jObject, "album_artist"); if (jObject["album"] != null && jObject["album"].ToString() != string.Empty) Album = new Album(jObject["album"].ToString(), jObject["artist"].ToString()); else @@ -30,6 +31,7 @@ private static string GetJasonField(JObject jObject, string fieldName) } public string Artist { get; } + public string AlbumArtist { get; } public Album Album { get; } public string Title { get; } public string TimeStamp { get; } @@ -57,7 +59,8 @@ public string getReasonInvalid() else { if (!ValidPlayers.Contains(Player)) - return "invalid player name. expected one of \"" + string.Join("\", \"", ValidPlayers) + "\" got " + Player + " instead"; + return "invalid player name. expected one of \"" + string.Join("\", \"", ValidPlayers) + + "\" got " + Player + " instead"; else if (!EnabledClients[Player]) return "user has disabled this player"; else @@ -67,7 +70,7 @@ public string getReasonInvalid() public override string ToString() { - return Action + " " + Title + " by " + Artist + " on " + Album.Name + " ending " + TimeStamp + + return Action + " " + Title + " by " + Artist + " (or " + AlbumArtist + ") on " + Album.Name + " ending " + TimeStamp + " from " + Player; } diff --git a/README.MD b/README.MD index 5efc3f6..29cb2a8 100644 --- a/README.MD +++ b/README.MD @@ -9,12 +9,17 @@ This program has been designed to work with many music players in the windows en - Spotify - Music Bee ([Special Plugin](https://github.com/jojo2357/MDRP-MusicBee-Bridge) Required, but has bonus features) - Wavelink +- Media Monkey +- foobar2000 - Tidal Player\* +- Amazon Music\*\* There may be more to come, time will tell. \* Note for Tidal, may not work for some users. Has been successfully tested on windows 10 and 11, on the free version on computers that MDRP works with either Groove or WMP. Paid versions of Tidal may experience issues, and older versions of Tidal will not work because they changed the exe name. +\*\* Very unreliable due to Amazon Music not reporting its media to WMC properly, so there is nothing that MDRP can to + ### If you have a version below 1.7, please refer to the [old readme](https://github.com/jojo2357/Music-Discord-Rich-Presence/blob/1.6.4/README.MD) or update your entire MDRP installation from scratch ## Features @@ -30,7 +35,7 @@ There may be more to come, time will tell. 1. Unzip the zip file 2. Navigate to the unzipped contents 3. Run `Music_DRP_Launcher.bat`. Your antivirus may flag it as potentially dangerous, so optionally run a scan first, or live on the edge and select "Run Anyway" (you may need to google how to do so) -4. Select the option you would like, and play some tunes. It is recommended that you do *not* run hidden on win 11 or until you are confortable using MDRP +4. Select the option you would like, and play some tunes. It is recommended that you do *not* run hidden on win 11 or until you are comfortable using MDRP 5. Profit # Extended Setup @@ -70,7 +75,7 @@ To change any MDRP settings, they will all be located in your `DiscordPresenceCo Data in this file is in `key=value` pairs so any line that does not have a `=` will be ignored. ### Changing default background -In the ini config file, there is the option to change the default background on a per-player setting. The schema is `large asset=url/application asset`. For example, to change the default beckground of MusicBee to the album cover of "My Head is an Animal", you would put: +In the ini config file, there is the option to change the default background on a per-player setting. The schema is `large asset=url/application asset`. For example, to change the default background of MusicBee to the album cover of "My Head is an Animal", you would put: `large musicbee asset=https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/11/50/a7/1150a7a6-ad88-1985-2869-7ae3d66391d5/12UMGIM10062.rgb.jpg/512x512bb.jpg` @@ -94,7 +99,7 @@ The `verbose` setting enables desktop notifications about the following: - Depreciated settings/key style ### Changing Rich Presence Format -If you would like, you can change how your Rich Presence is shown in Discord. You are limited to two lines (by discord, not MDRP) and these can be changed in the ini. The two examples will render the song King And Lionheard by Of Monsters And Men on the ablum My Head is An Animal as +If you would like, you can change how your Rich Presence is shown in Discord. You are limited to two lines (by discord, not MDRP) and these can be changed in the ini. The two examples will render the song King And Lionheart by Of Monsters And Men on the album My Head is An Animal as Spotify-style: line 1: `King And Lionheart by Of Monsters And Men` @@ -107,11 +112,11 @@ line 2: `Artist: Of Monsters and Men` The tooltip on the large image will always have the album name, and the tooltip on the small icon will have one of `Listening to ` or `paused` (when appropriate). ### Automatic Album Art Settings -Automatic Album Art or Remote Artowork as it is sometimes referred to, is the process by which MDRP will look on the internet for the album art that corresponds to your currently playing media. **If MDRP gets the album art wrong, please comment on [this discussion](https://github.com/jojo2357/Music-Discord-Rich-Presence/discussions/59) +Automatic Album Art or Remote Artwork as it is sometimes referred to, is the process by which MDRP will look on the internet for the album art that corresponds to your currently playing media. **If MDRP gets the album art wrong, please comment on [this discussion](https://github.com/jojo2357/Music-Discord-Rich-Presence/discussions/59) | Setting | When It applies | Behavior when true | Behavior when false | Recommendation | |---------------------------|-----------------|--------------------|---------------------|----------| -| `get remote artwork` | The current album is unkeyed for the current Media Player |MDRP will attempt to get the artowork for the currently playing media online and display it on the default application for the current Media Player | MDRP will simply show the default background on the default application | Set this to true if you haven't keyed you albums and would like to have arts in your rich Presence | +| `get remote artwork` | The current album is unkeyed for the current Media Player |MDRP will attempt to get the artwork for the currently playing media online and display it on the default application for the current Media Player | MDRP will simply show the default background on the default application | Set this to true if you haven't keyed you albums and would like to have arts in your rich Presence | | `remote needs exact match` | `get remote artwork` is true, The current album is unkeyed, and MDRP could not find an identical match either because there were too many results or the artist name was slightly off | MDRP will search again with more restrictive terms, and choose an exact match, or the only match, or the best non perfect artist match | ditto ^ | Set to false if your arts are frequently incorrect, true if your arts are frequently not found | | `create cache file` | `get remote artwork` is true, and MDRP had to search for an artwork | after a successful search, MDRP will key this album to a file in your clientdata folder. This key file will apply to all players, and will use the default app for those players | The cache will not be saved, and lost on restart | Not a lot of reasons to set this to false, MDRP is basically keying all your music for you, and if MDRP gets it wrong, you can correct it yourself | | `translate from japanese` | `get remote artwork` is true | Searches to ITunes will be done with the language of the search set to Japanese | Searches assume english, and will only directly latinize searches | Set this to true if you have a lot of Japanese music, especially if the searches don't find it often. Searching in JP will provide subtle corrections like reordering first and last names | @@ -135,5 +140,5 @@ All translations are courtesy of the community. If you would like to contribute Icon is thanks to [Ghoelian](https://github.com/Ghoelian) so thanks for that! -### Licence +### License Since I have added more features that allow the user to do more and more, I must add that while licensed under an MIT license, I am not responsible for any damages caused by the use or abuse of the tools that I have provided. Use for good, not evil. diff --git a/languages/italian-it.lang b/languages/italian-it.lang index 2dfc6e1..27544b4 100644 --- a/languages/italian-it.lang +++ b/languages/italian-it.lang @@ -14,7 +14,7 @@ UNKEYED==Album non salvato :( UNKNOWN_ARTIST==Artista sconosciuto CONSOLE_NAME==Music Discord Rich Presence NO_VALID_MEDIA==Rilevato volume in qualcosa ma non visualizzato perché non supportato oppure è disabilitato attualmente. -REQUIRE_PIPELINE==Rilevato il volume in {0} , ma non sono stati ricevuti dati da esso. Potrebbe essere necessario aggiornare il lettore, installare un plug-in o semplicemente mettere in pausa e riprendere la musica. Vedi di più su +REQUIRE_PIPELINE==Rilevato il volume in {0}, ma non sono stati ricevuti dati da esso. Potrebbe essere necessario aggiornare il lettore, installare un plug-in o semplicemente mettere in pausa e riprendere la musica. Vedi di più su AND==e FAILED_TO_GET_INFO==Impossibile ottenere informazioni sulla traccia UNKNOWN_ALBUM==Album ignoto