Files
spotify-honorific/SpotifyHonorific/Updaters/Updater.cs
2025-06-07 02:03:54 +03:00

286 lines
9.1 KiB
C#

using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using Newtonsoft.Json;
using Scriban;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
namespace SpotifyHonorific.Updaters;
public class Updater : IDisposable
{
private Config Config { get; init; }
private IFramework Framework { get; init; }
private IDalamudPluginInterface PluginInterface { get; init; }
private IPluginLog PluginLog { get; init; }
private ICallGateSubscriber<int, string, object> SetCharacterTitleSubscriber { get; init; }
private ICallGateSubscriber<int, object> ClearCharacterTitleSubscriber { get; init; }
private Action? UpdateTitle { get; set; }
private string? UpdatedTitleJson { get; set; }
private UpdaterContext UpdaterContext { get; init; } = new();
private Timer MediaCheckTimer { get; init; }
private MediaActivity? CurrentMediaActivity { get; set; }
public Updater(Config config, IFramework framwork, IDalamudPluginInterface pluginInterface, IPluginLog pluginLog)
{
Config = config;
Framework = framwork;
PluginInterface = pluginInterface;
PluginLog = pluginLog;
SetCharacterTitleSubscriber = PluginInterface.GetIpcSubscriber<int, string, object>("Honorific.SetCharacterTitle");
ClearCharacterTitleSubscriber = PluginInterface.GetIpcSubscriber<int, object>("Honorific.ClearCharacterTitle");
MediaCheckTimer = new Timer(2000);
MediaCheckTimer.Elapsed += CheckMediaActivity;
MediaCheckTimer.AutoReset = true;
if (Config.Enabled)
{
Start();
}
Framework.Update += OnFrameworkUpdate;
}
public void Dispose()
{
Framework.Update -= OnFrameworkUpdate;
MediaCheckTimer?.Stop();
MediaCheckTimer?.Dispose();
Framework.RunOnFrameworkThread(() =>
{
ClearCharacterTitleSubscriber.InvokeAction(0);
});
}
public Task Enable(bool value)
{
return value ? Start() : Stop();
}
public Task Restart()
{
return Stop().ContinueWith(t => Start());
}
public Task Start()
{
if (Config.Enabled)
{
MediaCheckTimer.Start();
PluginLog.Info("Media activity monitoring started");
}
return Task.CompletedTask;
}
public Task Stop()
{
MediaCheckTimer.Stop();
Framework.RunOnFrameworkThread(() =>
{
ClearCharacterTitleSubscriber.InvokeAction(0);
});
PluginLog.Info("Media activity monitoring stopped");
return Task.CompletedTask;
}
public string State()
{
return MediaCheckTimer.Enabled ? "Running" : "Stopped";
}
private void CheckMediaActivity(object sender, ElapsedEventArgs e)
{
try
{
var mediaActivity = GetCurrentMediaActivity();
if (!MediaActivitiesEqual(CurrentMediaActivity, mediaActivity))
{
CurrentMediaActivity = mediaActivity;
MediaActivityUpdated(mediaActivity);
}
}
catch (Exception ex)
{
PluginLog.Error($"Error checking media activity: {ex.Message}");
}
}
private MediaActivity? GetCurrentMediaActivity()
{
var spotifyTrack = GetSpotifyTrack();
if (!string.IsNullOrEmpty(spotifyTrack))
{
string artist = "";
string songName = spotifyTrack;
var parts = spotifyTrack.Split(new[] { " - " }, 2, StringSplitOptions.None);
if (parts.Length == 2)
{
artist = parts[0];
songName = parts[1];
}
return new MediaActivity
{
Name = "Spotify (V1)",
SongName = songName,
Artist = artist,
Type = ActivityType.Listening
};
}
return null;
}
private string? GetSpotifyTrack()
{
try
{
var spotifyProcesses = Process.GetProcessesByName("Spotify");
foreach (var process in spotifyProcesses)
{
if (!string.IsNullOrEmpty(process.MainWindowTitle) &&
process.MainWindowTitle != "Spotify" &&
process.MainWindowTitle != "Spotify Premium" &&
process.MainWindowTitle != "Spotify Free")
{
return process.MainWindowTitle;
}
}
}
catch (Exception ex)
{
PluginLog.Debug($"Error getting Spotify track: {ex.Message}");
}
return null;
}
private bool MediaActivitiesEqual(MediaActivity? a, MediaActivity? b)
{
if (a == null && b == null) return true;
if (a == null || b == null) return false;
return a.Name == b.Name && a.SongName == b.SongName && a.Type == b.Type;
}
private void MediaActivityUpdated(MediaActivity? mediaActivity)
{
PluginLog.Verbose($"MediaActivityUpdated: {JsonConvert.SerializeObject(mediaActivity, Formatting.Indented)}");
if (mediaActivity != null)
{
foreach (var activityConfig in Config.ActivityConfigs.Where(c => c.Enabled).OrderByDescending(c => c.Priority))
{
var matchesType =
(mediaActivity.Type == ActivityType.Listening);
PluginLog.Verbose($"Checking activity config '{activityConfig.Name}' for match: {matchesType}");
if (matchesType)
{
var matchFilter = true;
if (!activityConfig.FilterTemplate.IsNullOrWhitespace())
{
var filterTemplate = Template.Parse(activityConfig.FilterTemplate);
var filter = filterTemplate.Render(new { Activity = mediaActivity, Context = UpdaterContext }, member => member.Name);
if (bool.TryParse(filter, out var parsedFilter))
{
matchFilter = parsedFilter;
}
else
{
PluginLog.Error($"Unable to parse filter '{filter}' as boolean, skipping result");
}
}
if (matchFilter)
{
UpdaterContext.SecsElapsed = 0;
UpdateTitle = () =>
{
if (Config.Enabled && activityConfig.Enabled)
{
var titleTemplate = Template.Parse(activityConfig.TitleTemplate);
var title = titleTemplate.Render(new { Activity = mediaActivity, Context = UpdaterContext }, member => member.Name);
var data = new Dictionary<string, object>() {
{"Title", title},
{"IsPrefix", activityConfig.IsPrefix},
{"Color", activityConfig.Color!},
{"Glow", activityConfig.Glow!}
};
var serializedData = JsonConvert.SerializeObject(data, Formatting.Indented);
if (serializedData != UpdatedTitleJson)
{
PluginLog.Verbose($"Call Honorific SetCharacterTitle IPC with:\n{serializedData}");
SetCharacterTitleSubscriber.InvokeAction(0, serializedData);
UpdatedTitleJson = serializedData;
}
}
else
{
ClearTitle();
}
};
return;
}
}
}
}
if (UpdateTitle != null || UpdatedTitleJson != null)
{
ClearTitle();
}
}
private void OnFrameworkUpdate(IFramework framework)
{
if (Config.Enabled && UpdateTitle != null)
{
UpdateTitle.Invoke();
UpdaterContext.SecsElapsed += framework.UpdateDelta.TotalSeconds;
}
}
private void ClearTitle()
{
PluginLog.Verbose("Call Honorific ClearCharacterTitle IPC");
Framework.RunOnFrameworkThread(() =>
{
ClearCharacterTitleSubscriber.InvokeAction(0);
});
UpdaterContext.SecsElapsed = 0;
UpdateTitle = null;
UpdatedTitleJson = null;
}
}
public class MediaActivity
{
public string Name { get; set; } = "";
public string SongName { get; set; } = "";
public string Artist { get; set; } = "";
public ActivityType Type { get; set; }
}
public enum ActivityType
{
Playing,
Streaming,
Listening,
Watching,
Custom,
Competing
}