15 Commits

Author SHA1 Message Date
732dbda337 Merge pull request 'chore(main): release 0.0.13' (#14) from release-please--branches--main into main
All checks were successful
Create Release PR / Create Release PR (push) Has been skipped
Build and Publish Plugin / Build Plugin + Update Manifest (release) Successful in 46s
Reviewed-on: #14
2026-03-01 18:20:57 +01:00
Gitea Actions
579331c79f chore(main): release 0.0.13
All checks were successful
Create Release / Publish Release (pull_request) Successful in 7s
2026-03-01 17:20:37 +00:00
063f594753 fix: removed drecks versionen
All checks were successful
Create Release PR / Create Release PR (push) Successful in 9s
2026-03-01 18:20:27 +01:00
b383b5cd81 Merge branch 'main' of https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify
All checks were successful
Create Release PR / Create Release PR (push) Successful in 10s
2026-03-01 18:19:39 +01:00
6c485aa0f7 fix: Bilder und dumme min TImings! 2026-03-01 18:19:37 +01:00
Gitea Actions
5110c999b6 chore: update manifest for v0.0.12
All checks were successful
Create Release PR / Create Release PR (push) Has been skipped
2026-03-01 17:07:16 +00:00
b67dc3aa1a Merge pull request 'chore(main): release 0.0.12' (#13) from release-please--branches--main into main
All checks were successful
Create Release PR / Create Release PR (push) Has been skipped
Build and Publish Plugin / Build Plugin + Update Manifest (release) Successful in 53s
Reviewed-on: #13
2026-03-01 18:06:16 +01:00
Gitea Actions
33a07a19c2 chore(main): release 0.0.12
All checks were successful
Create Release / Publish Release (pull_request) Successful in 7s
2026-03-01 17:06:05 +00:00
25fb75ae0b Merge branch 'main' of https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify
All checks were successful
Create Release PR / Create Release PR (push) Successful in 11s
2026-03-01 18:05:50 +01:00
c9d5fd80ce fix: glaube ich net 2026-03-01 18:05:49 +01:00
Gitea Actions
8a445cc507 chore: update manifest for v0.0.11
All checks were successful
Create Release PR / Create Release PR (push) Has been skipped
2026-03-01 16:47:14 +00:00
f20364b25f Merge pull request 'chore(main): release 0.0.11' (#12) from release-please--branches--main into main
All checks were successful
Build and Publish Plugin / Build Plugin + Update Manifest (release) Successful in 47s
Create Release PR / Create Release PR (push) Has been skipped
Reviewed-on: #12
2026-03-01 17:46:20 +01:00
Gitea Actions
b191779fde chore(main): release 0.0.11
All checks were successful
Create Release / Publish Release (pull_request) Successful in 8s
2026-03-01 16:46:08 +00:00
7425a18241 Merge branch 'main' of https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify
All checks were successful
Create Release PR / Create Release PR (push) Successful in 16s
2026-03-01 17:45:51 +01:00
25ed431d5a fix: claude ist auch für 100€ im Monat noch dumm 2026-03-01 17:45:49 +01:00
10 changed files with 100 additions and 79 deletions

View File

@@ -37,24 +37,24 @@ jobs:
echo "TAG=$TAG" >> "$GITHUB_ENV"
- name: Install JPRM
run: pip install jprm
run: |
pip install jprm
mkdir -p ./artifacts
- name: Build plugin with JPRM
run: |
jprm --verbosity=debug plugin build Jellyfin.Plugin.SmartNotify \
ARTIFACT="$(jprm --verbosity=debug plugin build . \
--dotnet-framework="net9.0" \
--version="${VERSION}.0" \
--output=./artifacts
- name: Prepare artifact
run: |
# Find the ZIP that JPRM created
ARTIFACT=$(ls artifacts/*.zip | head -1)
-v "${VERSION}.0" \
--dotnet-configuration Release \
-o ./artifacts)"
echo "ARTIFACT=$ARTIFACT" >> "$GITHUB_ENV"
cp "$ARTIFACT" "smartnotify_${VERSION}.zip"
# Calculate MD5 checksum
CHECKSUM=$(md5sum "smartnotify_${VERSION}.zip" | cut -d' ' -f1)
echo "CHECKSUM=$CHECKSUM" >> "$GITHUB_ENV"
echo "Artifact: $ARTIFACT"
echo "Checksum: $CHECKSUM"
- name: Upload ZIP to Release

View File

@@ -1,3 +1,3 @@
{
".": "0.0.10"
".": "0.0.13"
}

View File

@@ -1,5 +1,35 @@
# Changelog
## 0.0.13 (2026-03-01)
### Bug Fixes
* fix: removed drecks versionen
* fix: Bilder und dumme min TImings!
### Chores
* chore: update manifest for v0.0.12
## 0.0.12 (2026-03-01)
### Bug Fixes
* fix: glaube ich net
### Chores
* chore: update manifest for v0.0.11
## 0.0.11 (2026-03-01)
### Bug Fixes
* fix: claude ist auch für 100€ im Monat noch dumm
## 0.0.10 (2026-03-01)
### Bug Fixes

View File

@@ -14,7 +14,7 @@ public class PluginConfiguration : BasePluginConfiguration
{
DiscordWebhookUrl = string.Empty;
NotificationDelayMinutes = 5;
GroupingWindowMinutes = 30;
GroupingWindowMinutes = 5;
EnableMovieNotifications = true;
EnableEpisodeNotifications = true;
SuppressUpgrades = true;

View File

@@ -88,7 +88,7 @@
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="txtNotificationDelayMinutes">Verzögerung (Minuten)</label>
<input is="emby-input" type="number" id="txtNotificationDelayMinutes" min="1" max="60" />
<input is="emby-input" type="number" id="txtNotificationDelayMinutes" min="0" max="60" />
<div class="fieldDescription">
Wartezeit bevor eine Benachrichtigung gesendet wird.
Erlaubt Metadaten-Aktualisierung und Erkennung von Ersetzungen.
@@ -97,7 +97,7 @@
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="txtGroupingWindowMinutes">Gruppierungsfenster (Minuten)</label>
<input is="emby-input" type="number" id="txtGroupingWindowMinutes" min="5" max="120" />
<input is="emby-input" type="number" id="txtGroupingWindowMinutes" min="0" max="120" />
<div class="fieldDescription">
Zeitfenster in dem Episoden derselben Serie zusammengefasst werden.
Z.B. bei 30 Minuten: Wenn Episode 1-12 innerhalb von 30 Minuten hinzugefügt werden,
@@ -131,8 +131,8 @@
document.querySelector('#chkEnableEpisodeNotifications').checked = config.EnableEpisodeNotifications;
document.querySelector('#chkEnableMovieNotifications').checked = config.EnableMovieNotifications;
document.querySelector('#chkSuppressUpgrades').checked = config.SuppressUpgrades;
document.querySelector('#txtNotificationDelayMinutes').value = config.NotificationDelayMinutes || 5;
document.querySelector('#txtGroupingWindowMinutes').value = config.GroupingWindowMinutes || 30;
document.querySelector('#txtNotificationDelayMinutes').value = config.NotificationDelayMinutes ?? 0;
document.querySelector('#txtGroupingWindowMinutes').value = config.GroupingWindowMinutes ?? 0;
Dashboard.hideLoadingMsg();
});
});
@@ -149,8 +149,8 @@
config.EnableEpisodeNotifications = document.querySelector('#chkEnableEpisodeNotifications').checked;
config.EnableMovieNotifications = document.querySelector('#chkEnableMovieNotifications').checked;
config.SuppressUpgrades = document.querySelector('#chkSuppressUpgrades').checked;
config.NotificationDelayMinutes = parseInt(document.querySelector('#txtNotificationDelayMinutes').value) || 5;
config.GroupingWindowMinutes = parseInt(document.querySelector('#txtGroupingWindowMinutes').value) || 30;
config.NotificationDelayMinutes = parseInt(document.querySelector('#txtNotificationDelayMinutes').value) || 0;
config.GroupingWindowMinutes = parseInt(document.querySelector('#txtGroupingWindowMinutes').value) || 0;
ApiClient.updatePluginConfiguration(SmartNotifyConfig.pluginUniqueId, config).then(function () {
Dashboard.processPluginConfigurationUpdateResult();
});

View File

@@ -3,8 +3,8 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<RootNamespace>Jellyfin.Plugin.SmartNotify</RootNamespace>
<AssemblyVersion>0.0.10.0</AssemblyVersion>
<FileVersion>0.0.10.0</FileVersion>
<AssemblyVersion>0.0.13.0</AssemblyVersion>
<FileVersion>0.0.13.0</FileVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -14,10 +14,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jellyfin.Controller" Version="10.11.*">
<PackageReference Include="Jellyfin.Controller" Version="10.11.0">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Jellyfin.Model" Version="10.11.*">
<PackageReference Include="Jellyfin.Model" Version="10.11.0">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="LiteDB" Version="5.0.21" />

View File

@@ -8,6 +8,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Timer = System.Timers.Timer;
@@ -185,10 +186,11 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
Overview = item.Overview
};
// Set image URL
if (!string.IsNullOrEmpty(serverUrl))
// Set local image path for attachment-based sending
var imagePath = item.GetImagePath(ImageType.Primary, 0);
if (!string.IsNullOrEmpty(imagePath))
{
notification.ImageUrl = $"{serverUrl}/Items/{item.Id}/Images/Primary";
notification.ImagePath = imagePath;
}
if (item is Episode episode)
@@ -199,10 +201,15 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
notification.EpisodeNumber = episode.IndexNumber;
notification.Year = episode.ProductionYear;
// Use series image if episode doesn't have one
if (!string.IsNullOrEmpty(serverUrl) && episode.SeriesId != Guid.Empty)
// Use series image if episode doesn't have its own
if (episode.SeriesId != Guid.Empty)
{
notification.ImageUrl = $"{serverUrl}/Items/{episode.SeriesId}/Images/Primary";
var series = _libraryManager.GetItemById(episode.SeriesId);
var seriesImage = series?.GetImagePath(ImageType.Primary, 0);
if (!string.IsNullOrEmpty(seriesImage))
{
notification.ImagePath = seriesImage;
}
}
}
else if (item is Movie movie)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
@@ -69,14 +70,11 @@ public class DiscordNotificationService
var title = $"📺 {seriesName}";
var description = $"Neue Episoden hinzugefügt:\n{episodeDescription}";
// Get image from first notification
var imageUrl = first.ImageUrl;
await SendDiscordWebhookAsync(
config,
title,
description,
imageUrl,
first.ImagePath,
BuildExternalLinks(first.ProviderIdsJson),
cancellationToken);
}
@@ -182,7 +180,7 @@ public class DiscordNotificationService
config,
title,
description,
notification.ImageUrl,
notification.ImagePath,
BuildExternalLinks(notification.ProviderIdsJson),
cancellationToken);
}
@@ -236,13 +234,13 @@ public class DiscordNotificationService
}
/// <summary>
/// Sends the actual Discord webhook request.
/// Sends the actual Discord webhook request, with optional image attachment.
/// </summary>
private async Task SendDiscordWebhookAsync(
PluginConfiguration config,
string title,
string description,
string? imageUrl,
string? imagePath,
string externalLinks,
CancellationToken cancellationToken)
{
@@ -253,9 +251,11 @@ public class DiscordNotificationService
["color"] = config.EmbedColor
};
if (!string.IsNullOrEmpty(imageUrl))
// If we have a local image file, reference it as attachment
var hasImage = !string.IsNullOrEmpty(imagePath) && File.Exists(imagePath);
if (hasImage)
{
embed["thumbnail"] = new Dictionary<string, string> { ["url"] = imageUrl };
embed["thumbnail"] = new Dictionary<string, string> { ["url"] = "attachment://poster.jpg" };
}
if (!string.IsNullOrEmpty(externalLinks))
@@ -277,9 +277,27 @@ public class DiscordNotificationService
try
{
using var client = _httpClientFactory.CreateClient();
using var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response;
var response = await client.PostAsync(config.DiscordWebhookUrl, content, cancellationToken);
if (hasImage)
{
// Send as multipart/form-data with image attachment
using var form = new MultipartFormDataContent();
form.Add(new StringContent(json, Encoding.UTF8, "application/json"), "payload_json");
var imageBytes = await File.ReadAllBytesAsync(imagePath!, cancellationToken);
var imageContent = new ByteArrayContent(imageBytes);
imageContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
form.Add(imageContent, "files[0]", "poster.jpg");
response = await client.PostAsync(config.DiscordWebhookUrl, form, cancellationToken);
}
else
{
// Send as plain JSON (no image)
using var content = new StringContent(json, Encoding.UTF8, "application/json");
response = await client.PostAsync(config.DiscordWebhookUrl, content, cancellationToken);
}
if (!response.IsSuccessStatusCode)
{

View File

@@ -139,10 +139,15 @@ public class PendingNotification
public NotificationType Type { get; set; }
/// <summary>
/// Gets or sets the image URL.
/// Gets or sets the image URL (for public servers).
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// Gets or sets the local image file path (for attachment-based sending).
/// </summary>
public string? ImagePath { get; set; }
/// <summary>
/// Gets or sets the provider IDs JSON.
/// </summary>

View File

@@ -8,46 +8,7 @@
"category": "Notifications",
"imageUrl": "",
"versions": [
{
"version": "0.0.9.0",
"changelog": "### Bug Fixes\n\n* fix: angeblich..\n\n### Chores\n\n* chore: update manifest for v0.0.8",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.0.9/smartnotify_0.0.9.zip",
"checksum": "9a1870e82fc3270ca37960a19b289cbd",
"timestamp": "2026-03-01T16:18:31Z"
},
{
"version": "0.0.8.0",
"changelog": "### Bug Fixes\n\n* fix: notsupported again\n\n### Chores\n\n* chore: update manifest for v0.0.7",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.0.8/smartnotify_0.0.8.zip",
"checksum": "21b5826fb120dd0db1ffacc2badb17f7",
"timestamp": "2026-03-01T16:07:10Z"
},
{
"version": "0.0.7.0",
"changelog": "### Bug Fixes\n\n* fix: build lief net!\n* fix: build lief nicht\n* fix: removed Claudes dummes gefriemel\n* fix: notSupported\n\n### Chores\n\n* chore(main): release 0.0.6\n* chore: update manifest for v0.0.5",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.0.7/smartnotify_0.0.7.zip",
"checksum": "6f8ef2061ce35e90c1827a5c8e9a1fe3",
"timestamp": "2026-03-01T15:57:08Z"
},
{
"version": "0.0.5.0",
"changelog": "### Bug Fixes\n\n* fix: remove Jellyfin Trash from Build\n\n### Chores\n\n* chore: loop gefixt\n* chore: update manifest for v0.0.4",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.0.5/smartnotify_0.0.5.zip",
"checksum": "74288c4e9b7b25d113fd1e45cf40d0a2",
"timestamp": "2026-03-01T15:48:20Z"
},
{
"version": "0.0.4.0",
"changelog": "### Bug Fixes\n\n* fix: json fehler\n* fix: Dumme Readme",
"targetAbi": "10.11.0.0",
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.0.4/smartnotify_0.0.4.zip",
"checksum": "d454711f1e6f59de0ab539134a4fd9a7",
"timestamp": "2026-03-01T15:29:46Z"
}
]
}
]