listenbrainz-discord-rpc/Program.cs

248 lines
10 KiB
C#

using DiscordRPC.Logging;
using DiscordRPC;
using MetaBrainz.ListenBrainz;
using MetaBrainz.MusicBrainz;
using IniParser;
using IniParser.Model;
using Topshelf;
using System.Web;
using MetaBrainz.MusicBrainz.Interfaces.Entities;
using MetaBrainz.MusicBrainz.Interfaces.Searches;
public class LbzRpcService
{
DiscordRpcClient? client = null;
ListenBrainz? listenBrainz = null;
readonly Thread t;
bool running = false;
public LbzRpcService()
{
t = new Thread(MainThread);
}
public void Start()
{
running = true;
t.Start();
}
public void Stop()
{
running = false;
client?.Dispose();
listenBrainz?.Dispose();
}
public void MainThread()
{
FileIniDataParser config = new FileIniDataParser();
if (!File.Exists("config.ini"))
{
throw new ApplicationException("Configuration file not found. Please create a config.ini file with your tokens and user information.");
}
IniData data = config.ReadFile("config.ini");
listenBrainz = new ListenBrainz();
client = new DiscordRpcClient(data["general"]["discord_token"]);
void InitializeDiscordRpc()
{
client.Logger = new ConsoleLogger() { Level = LogLevel.Warning };
client.SkipIdenticalPresence = true;
client.OnReady += (sender, e) =>
{
Console.WriteLine("Received Ready from user {0}", e.User.Username);
};
client.Initialize();
}
bool connected = false;
client.OnConnectionEstablished += (sender, e) =>
{
Console.WriteLine("Connected to Discord");
connected = true;
};
InitializeDiscordRpc();
string? lastRecording = null;
List<Button> buttons = new List<Button>();
string albumArtUrl = "";
while (running)
{
if (!connected)
{
// Wait for Discord client to connect
Thread.Sleep(2000);
continue;
}
var username = data["general"]["listenbrainz_username"];
var ignoredPlayers = data["general"]["ignored_sources"].Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var listen = listenBrainz.GetPlayingNow(username);
if (listen != null)
{
//check ignored players
if (listen.Track?.Info?.AdditionalInfo?.MediaPlayer != null && ignoredPlayers.Any(player => player.ToLower() == listen.Track.Info.AdditionalInfo.MediaPlayer.ToLower()))
{
Console.WriteLine("Ignoring player {0}", listen.Track.Info.AdditionalInfo.MediaPlayer);
client.ClearPresence();
}
else
{
var listenData = listen.Track?.Info;
if (listenData != null)
{
if (listenData.AdditionalInfo.OriginUrl != null && (listenData.AdditionalInfo.OriginUrl?.Host == "music.youtube.com" || listenData.AdditionalInfo.OriginUrl?.Host == "www.youtube.com") && lastRecording != GetYoutubeId(listenData.AdditionalInfo.OriginUrl))
{
lastRecording = GetYoutubeId(listenData.AdditionalInfo.OriginUrl);
Console.WriteLine("Searching for release {0} on MusicBrainz", listenData.Release);
Query q = new("LBZ-DRPC", new Version(1, 0, 0));
ISearchResult<IRelease>? release = null;
if (listenData.Release != null || listenData.Artist != null)
{
release = q.FindReleases(listenData.Release ?? listenData.Artist, 1).Results?.FirstOrDefault();
if (release?.Item.CoverArtArchive == null || !release.Item.CoverArtArchive.Front)
{
if (release?.Item.Aliases != null)
foreach (IAlias alias in release.Item.Aliases)
{
if (alias.Primary == true)
{
var r = q.FindReleases(alias.Name, 1).Results?.FirstOrDefault();
if (release != null && release.Item.CoverArtArchive?.Front == true)
{
release = r;
}
}
};
}
}
if (release != null)
{
albumArtUrl = "https://coverartarchive.org/release/" + release.Item.Id + "/front-250.jpg";
//albumArtUrl = "https://coverartarchive.org/release/" + release?.Item.Id + "/front-250.jpg";
}
else
{
var youtubeId = GetYoutubeId(listenData.AdditionalInfo.OriginUrl);
albumArtUrl = "https://img.youtube.com/vi/" + youtubeId + "/0.jpg";
}
}
else if (lastRecording != listenData.AdditionalInfo?.RecordingId.ToString()) // MusicBrainz ID available
{
Console.WriteLine("Listening to {0} by {1}", listenData.Name, listenData.Artist);
lastRecording = listenData.AdditionalInfo?.RecordingId.ToString();
buttons = new List<Button>();
if (listenData.AdditionalInfo?.RecordingId != null)
{
buttons.Add(new Button() { Label = "Listen on MusicBrainz", Url = $"https://listenbrainz.org/player/?recording_mbids={listenData.AdditionalInfo?.RecordingId}" });
}
buttons.Add(new Button() { Label = $"{username} on ListenBrainz", Url = $"https://listenbrainz.org/user/{username}" });
if (listenData.AdditionalInfo?.ReleaseId != null)
{
albumArtUrl = "https://coverartarchive.org/release/" + listenData.AdditionalInfo?.ReleaseId + "/front-250.jpg";
}
else if (listenData.Release != null)
{
Console.WriteLine("Searching for release {0} on MusicBrainz", listenData.Release);
Query q = new("LBZ-DRPC", new Version(1, 0, 0));
var release = q.FindReleases(listenData.Release, 1).Results?.FirstOrDefault();
if (release != null)
{
albumArtUrl = "https://coverartarchive.org/release/" + release?.Item.Id + "/front-250.jpg";
}
}
Console.WriteLine("Album art URL: " + albumArtUrl);
}
client.SetPresence(new RichPresence()
{
Details = $"{listenData.Name}",
State = (listenData.Release != null && listenData.Release != listenData.Name) ? $"{listenData.Artist} ({listenData.Release})" : listenData.Artist,
Buttons = buttons.ToArray(),
Assets = new Assets()
{
SmallImageKey = "play",
LargeImageKey = albumArtUrl,
LargeImageText = $"Listening to {listenData.Name} by {listenData.Artist}{(listenData.AdditionalInfo?.MediaPlayer != null ? " (via " + listenData.AdditionalInfo.MediaPlayer + ")" : "")}"
}
});
}
else
{
Console.WriteLine("Not listening to anything");
client.ClearPresence();
}
}
}
else
{
Console.WriteLine("Not listening to anything");
client.ClearPresence();
}
var interval = int.Parse(data["general"]["poll_interval"]);
while (interval > 0 && running)
{
Thread.Sleep(1000);
interval--;
}
}
}
private string? GetYoutubeId(Uri originUrl)
{
var query = HttpUtility.ParseQueryString(originUrl.Query);
if (query["v"] != null)
{
return query["v"];
}
return null;
}
}
internal static class ConfigureService
{
internal static void Configure()
{
HostFactory.Run(configure =>
{
configure.Service<LbzRpcService>(service =>
{
service.ConstructUsing(s => new LbzRpcService());
service.WhenStarted(s => s.Start());
service.WhenStopped(s => s.Stop());
});
//Setup Account that window service use to run.
configure.RunAsLocalSystem();
configure.SetServiceName("ListenBrainzDiscordRpc");
configure.SetDisplayName("ListenBrainz Discord Rich Presence");
configure.SetDescription("Updates your Discord Rich Presence with your currently playing track on ListenBrainz.");
});
}
}
public class Program
{
public static void Main(string[] args)
{
ConfigureService.Configure();
}
}