Added MQTT as a destination (#134)

Co-authored-by: Darick Carpenter <carpenterd@byui.edu>
This commit is contained in:
Darick Carpenter 2022-10-10 07:59:16 -06:00 committed by GitHub
parent 3ae6c098bd
commit 4ec22bd5a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 530 additions and 42 deletions

View File

@ -3,6 +3,7 @@ using Jellyfin.Plugin.Webhook.Destinations.Discord;
using Jellyfin.Plugin.Webhook.Destinations.Generic;
using Jellyfin.Plugin.Webhook.Destinations.GenericForm;
using Jellyfin.Plugin.Webhook.Destinations.Gotify;
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
using Jellyfin.Plugin.Webhook.Destinations.Pushbullet;
using Jellyfin.Plugin.Webhook.Destinations.Pushover;
using Jellyfin.Plugin.Webhook.Destinations.Slack;
@ -30,6 +31,7 @@ namespace Jellyfin.Plugin.Webhook.Configuration
PushoverOptions = Array.Empty<PushoverOption>();
SlackOptions = Array.Empty<SlackOption>();
SmtpOptions = Array.Empty<SmtpOption>();
MqttOptions = Array.Empty<MqttOption>();
}
/// <summary>
@ -76,5 +78,10 @@ namespace Jellyfin.Plugin.Webhook.Configuration
/// Gets or sets the smtp options.
/// </summary>
public SmtpOption[] SmtpOptions { get; set; }
/// <summary>
/// Gets or sets the mqtt options.
/// </summary>
public MqttOption[] MqttOptions { get; set; }
}
}

View File

@ -14,7 +14,7 @@
<form class="webhookConfigurationForm">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtServerUrl" required="required" label="Server Url:"/>
<input is="emby-input" type="text" id="txtServerUrl" required="required" label="Server Url:" />
<span>For linking to content. Include base url.</span>
</div>
<button id="btnAddDiscord" is="emby-button" type="button" class="raised button block">
@ -41,9 +41,12 @@
<button id="btnAddSmtp" is="emby-button" type="button" class="raised button block">
<span>Add SMTP Destination</span>
</button>
<br/>
<button id="btnAddMqtt" is="emby-button" type="button" class="raised button block">
<span>Add MQTT Destination</span>
</button>
<br />
<div id="configurationWrapper"></div>
<br/>
<br />
<button id="saveConfig" is="emby-button" type="submit" class="raised button-submit block">
<span>Save</span>
</button>
@ -248,46 +251,79 @@
</div>
</template>
<template id="template-smtp">
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtSenderAddress" label="Sender Address:"/>
<template id="template-smtp">
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtSenderAddress" label="Sender Address:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtReceiverAddress" label="Receiver Address:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtSmtpServer" label="SMTP Server Address:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" data-name="txtSmtpPort" label="SMTP Port:"/>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkUseCredentials"/>
<span>Use Credentials</span>
</label>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtUsername" label="Username:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtPassword" label="Password:"/>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkUseSsl"/>
<span>Use SSL</span>
</label>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkIsHtml"/>
<span>Is Html Body</span>
</label>
<div class="inputContainer">
<label>Subject Template:</label>
<div>
<textarea data-name="txtSubjectTemplate" style="width: 100%; height: 400px"></textarea>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtReceiverAddress" label="Receiver Address:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtSmtpServer" label="SMTP Server Address:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" data-name="txtSmtpPort" label="SMTP Port:"/>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkUseCredentials"/>
<span>Use Credentials</span>
</label>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtUsername" label="Username:"/>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtPassword" label="Password:"/>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkUseSsl"/>
<span>Use SSL</span>
</label>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkIsHtml"/>
<span>Is Html Body</span>
</label>
<div class="inputContainer">
<label>Subject Template:</label>
<div>
<textarea data-name="txtSubjectTemplate" style="width: 100%; height: 400px"></textarea>
</div>
</div>
</template>
</div>
</template>
<template id="template-pushbullet">
<template id="template-mqtt">
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtMqttServer" label="MQTT Server:" />
</div>
<div class="inputContainer">
<input is="emby-input" type="number" data-name="txtMqttPort" label="MQTT Port (default is 1883):" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtTopic" label="Topic:" />
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkUseCredentials" />
<span>Use Credentials</span>
</label>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtUsername" label="Username:" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtPassword" label="Password:" />
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" data-name="chkUseTls" />
<span>Use TLS</span>
</label>
<div class="selectContainer">
<select is="emby-select" data-name="ddlQosLevel" label="Quality of Service:">
<option value="AtMostOnce">At Most Once</option>
<option value="AtLeastOnce">At Least Once</option>
<option value="ExactlyOnce">Exactly Once</option>
</select>
</div>
</template>
<template id="template-pushbullet">
<div class="inputContainer">
<input is="emby-input" type="text" data-name="txtToken" label="Token:"/>
</div>

View File

@ -573,6 +573,45 @@
return config;
}
},
mqtt: {
btnAdd: document.querySelector("#btnAddMqtt"),
template: document.querySelector("#template-mqtt"),
addConfig: function (config) {
const template = document.createElement("div");
template.dataset.type = "mqtt";
template.appendChild(Webhook.baseConfig.template.cloneNode(true).content);
template.appendChild(Webhook.mqtt.template.cloneNode(true).content);
const baseConfig = Webhook.baseConfig.addConfig(template, "MQTT", config.WebhookName);
Webhook.configurationWrapper.appendChild(baseConfig);
// Load configuration
Webhook.mqtt.setConfig(config, baseConfig);
},
setConfig: function (config, element) {
Webhook.baseConfig.setConfig(config, element);
element.querySelector("[data-name=txtMqttServer]").value = config.MqttServer || "";
element.querySelector("[data-name=txtMqttPort]").value = config.MqttPort || 1883;
element.querySelector("[data-name=txtTopic]").value = Webhook.atou(config.Topic || "");
element.querySelector("[data-name=chkUseCredentials]").checked = config.UseCredentials || false;
element.querySelector("[data-name=txtUsername]").value = config.Username || "";
element.querySelector("[data-name=txtPassword]").value = config.Password || "";
element.querySelector("[data-name=chkUseTls]").checked = config.UseTls || false;
element.querySelector("[data-name=ddlQosLevel]").value = config.QosLevel || "AtMostOnce";
},
getConfig: function (element) {
const config = Webhook.baseConfig.getConfig(element);
config.MqttServer = element.querySelector("[data-name=txtMqttServer]").value || "";
config.MqttPort = element.querySelector("[data-name=txtMqttPort]").value || 1883;
config.Topic = Webhook.utoa(element.querySelector("[data-name=txtTopic]").value || "");
config.UseCredentials = element.querySelector("[data-name=chkUseCredentials]").checked || false;
config.Username = element.querySelector("[data-name=txtUsername]").value || "";
config.Password = element.querySelector("[data-name=txtPassword]").value || "";
config.UseTls = element.querySelector("[data-name=chkUseTls]").checked || false;
config.QosLevel = element.querySelector("[data-name=ddlQosLevel]").value || "";
return config;
}
},
init: async function () {
// Clear any previously loaded configuration.
Webhook.configurationWrapper.innerHTML = "";
@ -586,6 +625,7 @@
Webhook.pushover.btnAdd.addEventListener("click", Webhook.pushover.addConfig);
Webhook.slack.btnAdd.addEventListener("click", Webhook.slack.addConfig);
Webhook.smtp.btnAdd.addEventListener("click", Webhook.smtp.addConfig);
Webhook.mqtt.btnAdd.addEventListener("click", Webhook.mqtt.addConfig);
document.querySelector("#saveConfig").addEventListener("click", Webhook.saveConfig);
await Webhook.userFilter.populate();
@ -650,6 +690,12 @@
config.SmtpOptions.push(Webhook.smtp.getConfig(smtpConfigs[i]));
}
config.MqttOptions = [];
const mqttConfigs = document.querySelectorAll("[data-type=mqtt]");
for (let i = 0; i < mqttConfigs.length; i++) {
config.MqttOptions.push(Webhook.mqtt.getConfig(mqttConfigs[i]));
}
window.ApiClient.updatePluginConfiguration(Webhook.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
},
loadConfig: function () {
@ -688,6 +734,10 @@
for (let i = 0; i < config.SmtpOptions.length; i++) {
Webhook.smtp.addConfig(config.SmtpOptions[i]);
}
for (let i = 0; i < config.MqttOptions.length; i++) {
Webhook.mqtt.addConfig(config.MqttOptions[i]);
}
});
Dashboard.hideLoadingMsg()

View File

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Extensions.ManagedClient;
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
/// <summary>
/// IMqttClients.
/// </summary>
public interface IMqttClients
{
/// <summary>
/// Update Clients with options.
/// </summary>
/// <param name="options">Instance of the <see cref="MqttOption"/>.</param>
/// <returns>Task.</returns>
Task UpdateClients(MqttOption[] options);
/// <summary>
/// Get Managed Mqtt Clients.
/// </summary>
/// <param name="guid">guid of MqttOption.</param>
/// <returns>Instance of the <see cref="IManagedMqttClient"/> interface.</returns>
IManagedMqttClient? GetClient(Guid guid);
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MQTTnet.Extensions.ManagedClient;
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
/// <summary>
/// Client for the <see cref="MqttOption"/>.
/// </summary>
public class MqttClient : BaseClient, IWebhookClient<MqttOption>
{
private readonly ILogger<MqttClient> _logger;
private readonly IMqttClients _mqttClients;
/// <summary>
/// Initializes a new instance of the <see cref="MqttClient"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger{MqttClient}"/> interface.</param>
/// <param name="mqttClients">Instance of the <see cref="IMqttClients"/> interface.</param>
public MqttClient(ILogger<MqttClient> logger, IMqttClients mqttClients)
{
_logger = logger;
_mqttClients = mqttClients;
}
/// <inheritdoc />
public async Task SendAsync(MqttOption option, Dictionary<string, object> data)
{
try
{
if (!SendWebhook(_logger, option, data))
{
return;
}
var body = option.GetMessageBody(data);
var client = _mqttClients.GetClient(option.Guid);
if (client?.IsConnected != true)
{
_logger.LogDebug("MQTT error, not connected {@server}", option.MqttServer);
return;
}
var topic = option.GetCompiledTopicTemplate()(data);
await client.EnqueueAsync(topic, body).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.LogDebug(e, "Error sending MQTT notification");
}
}
}

View File

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
/// <summary>
/// MqttClients.
/// </summary>
public class MqttClients : IMqttClients, IDisposable
{
private readonly ILogger<MqttClients> _logger;
private readonly Dictionary<Guid, IManagedMqttClient> _managedMqttClients = new();
/// <summary>
/// Initializes a new instance of the <see cref="MqttClients"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger{MqttClients}"/> interface.</param>
public MqttClients(ILogger<MqttClients> logger)
{
_logger = logger;
}
/// <inheritdoc />
public async Task UpdateClients(MqttOption[] options)
{
try
{
foreach (var client in _managedMqttClients.Values.Where(c => c.IsConnected))
{
await client.StopAsync().ConfigureAwait(false);
client.ConnectingFailedAsync -= Client_ConnectingFailedAsync;
client.ConnectedAsync -= Client_ConnectedAsync;
client.DisconnectedAsync -= Client_DisconnectedAsync;
}
_managedMqttClients.Clear();
foreach (var option in options)
{
var messageBuilder = new MqttClientOptionsBuilder()
.WithTcpServer(option.MqttServer, option.MqttPort)
.WithWillQualityOfServiceLevel(option.QosLevel)
.WithClientId(option.Guid.ToString());
if (option.UseCredentials && !string.IsNullOrEmpty(option.Username) && !string.IsNullOrEmpty(option.Password))
{
messageBuilder.WithCredentials(option.Username, option.Password);
}
if (option.UseTls)
{
messageBuilder.WithTls();
}
var clientOptions = messageBuilder.Build();
var managedOptions = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
.WithClientOptions(clientOptions)
.Build();
var client = new MqttFactory().CreateManagedMqttClient();
client.ConnectingFailedAsync += Client_ConnectingFailedAsync;
client.ConnectedAsync += Client_ConnectedAsync;
client.DisconnectedAsync += Client_DisconnectedAsync;
_managedMqttClients.Add(option.Guid, client);
await client.StartAsync(managedOptions).ConfigureAwait(false);
}
}
catch (Exception e)
{
_logger.LogWarning(e, "Error adding/starting MQTT Clients");
}
}
private Task Client_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
var message = arg.Reason;
_logger.LogDebug(arg.Exception, "MQTT Client disconnected. Exception: {@message}", message);
return Task.CompletedTask;
}
private Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg)
{
_logger.LogDebug("MQTT Client connected.");
return Task.CompletedTask;
}
private Task Client_ConnectingFailedAsync(ConnectingFailedEventArgs arg)
{
_logger.LogDebug(arg.Exception, "MQTT Client connection failed.");
return Task.CompletedTask;
}
/// <inheritdoc />
public IManagedMqttClient? GetClient(Guid guid)
{
if (_managedMqttClients.TryGetValue(guid, out var client))
{
return client;
}
return null;
}
/// <summary>
/// Dispose Objects.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose.
/// </summary>
/// <param name="disposeAll">somthing.</param>
protected virtual void Dispose(bool disposeAll)
{
foreach (var client in _managedMqttClients.Values)
{
client.Dispose();
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using HandlebarsDotNet;
using Jellyfin.Plugin.Webhook.Helpers;
using MQTTnet.Protocol;
namespace Jellyfin.Plugin.Webhook.Destinations.Mqtt;
/// <summary>
/// Mqtt specific option.
/// </summary>
public class MqttOption : BaseOption
{
private HandlebarsTemplate<object, string>? _compiledTopicTemplate;
/// <summary>
/// Initializes a new instance of the <see cref="MqttOption"/> class.
/// </summary>
public MqttOption()
{
Guid = Guid.NewGuid();
}
/// <summary>
/// Gets the Guid for MqttOption.
/// </summary>
public Guid Guid { get; }
/// <summary>
/// Gets or sets the MqttServer.
/// </summary>
public string MqttServer { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the mqtt port.
/// </summary>
public int MqttPort { get; set; } = 1883;
/// <summary>
/// Gets or sets the BaseTopic.
/// </summary>
public string Topic { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether use credentials.
/// </summary>
public bool UseCredentials { get; set; }
/// <summary>
/// Gets or sets the username.
/// </summary>
public string Username { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the password.
/// </summary>
public string Password { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether to use TLS.
/// </summary>
public bool UseTls { get; set; }
/// <summary>
/// Gets or sets the Quality of service level.
/// </summary>
public MqttQualityOfServiceLevel QosLevel { get; set; }
/// <summary>
/// Gets the compiled handlebars topic template.
/// </summary>
/// <returns>The compiled handlebars subject template.</returns>
public HandlebarsTemplate<object, string> GetCompiledTopicTemplate() => _compiledTopicTemplate ??= Handlebars.Compile(HandlebarsFunctionHelpers.Base64Decode(Topic));
}

View File

@ -15,6 +15,7 @@
<PackageReference Include="MailKit" Version="3.4.1" />
<PackageReference Include="Handlebars.Net" Version="2.1.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.*" />
<PackageReference Include="MQTTnet.Extensions.ManagedClient" Version="4.1.0.247" />
</ItemGroup>
<ItemGroup>

View File

@ -6,6 +6,7 @@ using Jellyfin.Plugin.Webhook.Destinations.Discord;
using Jellyfin.Plugin.Webhook.Destinations.Generic;
using Jellyfin.Plugin.Webhook.Destinations.GenericForm;
using Jellyfin.Plugin.Webhook.Destinations.Gotify;
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
using Jellyfin.Plugin.Webhook.Destinations.Pushbullet;
using Jellyfin.Plugin.Webhook.Destinations.Pushover;
using Jellyfin.Plugin.Webhook.Destinations.Slack;
@ -42,10 +43,14 @@ namespace Jellyfin.Plugin.Webhook
serviceCollection.AddScoped<IWebhookClient<PushoverOption>, PushoverClient>();
serviceCollection.AddScoped<IWebhookClient<SlackOption>, SlackClient>();
serviceCollection.AddScoped<IWebhookClient<SmtpOption>, SmtpClient>();
serviceCollection.AddScoped<IWebhookClient<MqttOption>, MqttClient>();
// Register sender.
serviceCollection.AddScoped<IWebhookSender, WebhookSender>();
// Register MqttClients
serviceCollection.AddSingleton<IMqttClients, MqttClients>();
/*-- Register event consumers. --*/
// Library consumers.
serviceCollection.AddScoped<IEventConsumer<SubtitleDownloadFailureEventArgs>, SubtitleDownloadFailureNotifier>();

View File

@ -0,0 +1,28 @@
{
"DeviceId": "{{DeviceId}}",
"DeviceName": "{{DeviceName}}",
"ClientName": "{{ClientName}}",
"ItemId": "{{ItemId}}",
"RunTime": "{{RunTime}}",
"PlaybackPosition": "{{PlaybackPosition}}",
{{#if_equals NotificationType 'PlaybackStart'}}
"url": "{{ServerUrl}}/web/index.html#!/details?id={{ItemId}}&serverId={{ServerId}}",
"thumbnail": "{{ServerUrl}}/Items/{{ItemId}}/Images/Primary",
"backdrop": "{{ServerUrl}}/Items/{{ItemId}}/Images/Backdrop",
"logo": "{{ServerUrl}}/Items/{{ItemId}}/Images/Logo",
"Name": "{{Name}}",
{{#if_equals ItemType 'Episode'}}
"SeriesName": "{{SeriesName}}",
"SeasonNumber": "{{SeasonNumber}}",
"EpisodeNumber": "{{EpisodeNumber}}"
{{else}}
"Year": {{Year}}
{{/if_equals}}
{{/if_equals}}
{{#if_equals NotificationType 'PlaybackStop'}}
"PlayedToCompletion": "{{PlayedToCompletion}}"
{{/if_equals}}
{{#if_equals NotificationType 'PlaybackProgress'}}
"IsPaused": "{{IsPaused}}"
{{/if_equals}}
}

View File

@ -8,6 +8,7 @@ using Jellyfin.Plugin.Webhook.Destinations.Discord;
using Jellyfin.Plugin.Webhook.Destinations.Generic;
using Jellyfin.Plugin.Webhook.Destinations.GenericForm;
using Jellyfin.Plugin.Webhook.Destinations.Gotify;
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
using Jellyfin.Plugin.Webhook.Destinations.Pushbullet;
using Jellyfin.Plugin.Webhook.Destinations.Pushover;
using Jellyfin.Plugin.Webhook.Destinations.Slack;
@ -31,6 +32,7 @@ namespace Jellyfin.Plugin.Webhook
private readonly IWebhookClient<PushoverOption> _pushoverClient;
private readonly IWebhookClient<SlackOption> _slackClient;
private readonly IWebhookClient<SmtpOption> _smtpClient;
private readonly IWebhookClient<MqttOption> _mqttClient;
/// <summary>
/// Initializes a new instance of the <see cref="WebhookSender"/> class.
@ -44,6 +46,7 @@ namespace Jellyfin.Plugin.Webhook
/// <param name="pushoverClient">Instance of the <see cref="IWebhookClient{PushoverOption}"/>.</param>
/// <param name="slackClient">Instance of the <see cref="IWebhookClient{SlackOption}"/>.</param>
/// <param name="smtpClient">Instance of the <see cref="IWebhookClient{SmtpOption}"/>.</param>
/// <param name="mqttClient">Instance of the <see cref="IWebhookClient{mqttClient}"/>.</param>
public WebhookSender(
ILogger<WebhookSender> logger,
IWebhookClient<DiscordOption> discordClient,
@ -53,7 +56,8 @@ namespace Jellyfin.Plugin.Webhook
IWebhookClient<PushbulletOption> pushbulletClient,
IWebhookClient<PushoverOption> pushoverClient,
IWebhookClient<SlackOption> slackClient,
IWebhookClient<SmtpOption> smtpClient)
IWebhookClient<SmtpOption> smtpClient,
IWebhookClient<MqttOption> mqttClient)
{
_logger = logger;
_discordClient = discordClient;
@ -64,6 +68,7 @@ namespace Jellyfin.Plugin.Webhook
_pushoverClient = pushoverClient;
_slackClient = slackClient;
_smtpClient = smtpClient;
_mqttClient = mqttClient;
}
private static PluginConfiguration Configuration =>
@ -119,6 +124,12 @@ namespace Jellyfin.Plugin.Webhook
await SendNotification(_smtpClient, option, itemData, itemType)
.ConfigureAwait(false);
}
foreach (var option in Configuration.MqttOptions.Where(o => o.NotificationTypes.Contains(notificationType)))
{
await SendNotification(_mqttClient, option, itemData, itemType)
.ConfigureAwait(false);
}
}
private static bool NotifyOnItem<T>(T baseOptions, Type? itemType)

View File

@ -0,0 +1,61 @@
using System;
using System.Threading.Tasks;
using Jellyfin.Plugin.Webhook.Configuration;
using Jellyfin.Plugin.Webhook.Destinations.Mqtt;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Plugins;
namespace Jellyfin.Plugin.Webhook;
/// <summary>
/// WebhookServerEntryPoint.
/// </summary>
public class WebhookServerEntryPoint : IServerEntryPoint
{
private readonly IMqttClients _mqttClients;
/// <summary>
/// Initializes a new instance of the <see cref="WebhookServerEntryPoint"/> class.
/// </summary>
/// <param name="mqttClients">Instance of the <see cref="IMqttClients"/> interface.</param>
public WebhookServerEntryPoint(IMqttClients mqttClients)
{
_mqttClients = mqttClients;
WebhookPlugin.Instance!.ConfigurationChanged += ConfigurationChanged;
}
private static PluginConfiguration Configuration =>
WebhookPlugin.Instance!.Configuration;
private async void ConfigurationChanged(object? sender, BasePluginConfiguration e)
{
await RunAsync().ConfigureAwait(false);
}
/// <summary>
/// Dispose Objects.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose.
/// </summary>
/// <param name="disposeAll">Dispose of objects.</param>
protected virtual void Dispose(bool disposeAll)
{
WebhookPlugin.Instance!.ConfigurationChanged -= ConfigurationChanged;
}
/// <summary>
/// Run this.
/// </summary>
/// <returns>Task.</returns>
public async Task RunAsync()
{
await _mqttClients.UpdateClients(Configuration.MqttOptions).ConfigureAwait(false);
}
}

View File

@ -16,6 +16,8 @@ artifacts:
- "MailKit.dll"
- "MimeKit.dll"
- "BouncyCastle.Crypto.dll"
- "MQTTnet.dll"
- "MQTTnet.Extensions.ManagedClient.dll"
changelog: |-
### Code or Repo Maintenance ###
- Fix scheduled task (#106) @crobibero