2024-06-02 18:42:01 +03:00
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 ;
2024-06-02 18:45:16 +03:00
readonly Thread t ;
2024-06-02 18:42:01 +03:00
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 ( ) ;
}
}