248 lines
10 KiB
C#
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();
|
|
}
|
|
} |