Compare commits
15 Commits
v0.1.1
...
a71ced086a
| Author | SHA1 | Date | |
|---|---|---|---|
| a71ced086a | |||
| 85fd002f4e | |||
|
|
f9aacf2436 | ||
|
|
caee267f8b | ||
| 5a60a6f5b4 | |||
|
|
133c00fab0 | ||
| 0bafe691a0 | |||
| 82b34e288c | |||
|
|
8c77f82d2c | ||
|
|
cb0dfe2c21 | ||
| eaf6ea91e1 | |||
|
|
180a998be1 | ||
| 6fd2638414 | |||
| 0e10e3c089 | |||
|
|
e729c7b8d5 |
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
".": "0.1.1"
|
".": "0.1.3"
|
||||||
}
|
}
|
||||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,5 +1,20 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.3 (2026-04-05)
|
||||||
|
|
||||||
|
## 0.1.3 (2026-04-05)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
Gebe keine Notification wenn der Name der Serie tmdbid oder ähnliches enthält.
|
||||||
|
Dies verhindert das die Notification kommt wenn noch nicht die Metadaten gezogen wurden.
|
||||||
|
## 0.1.2 (2026-04-04)
|
||||||
|
|
||||||
|
## 0.1.2 (2026-04-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Problem behoben das der Serienname vor der Benachrichtigung nicht aktualisiert wurde.
|
||||||
## 0.1.1 (2026-04-03)
|
## 0.1.1 (2026-04-03)
|
||||||
|
|
||||||
## 0.1.1 (2026-04-03)
|
## 0.1.1 (2026-04-03)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<RootNamespace>Jellyfin.Plugin.SmartNotify</RootNamespace>
|
<RootNamespace>Jellyfin.Plugin.SmartNotify</RootNamespace>
|
||||||
<AssemblyVersion>0.1.1.0</AssemblyVersion>
|
<AssemblyVersion>0.1.3.0</AssemblyVersion>
|
||||||
<FileVersion>0.1.1.0</FileVersion>
|
<FileVersion>0.1.3.0</FileVersion>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
@@ -49,11 +50,7 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("SmartNotify background service starting");
|
_logger.LogInformation("SmartNotify background service starting");
|
||||||
|
|
||||||
// Pre-populate DB with all existing library items so they're recognized as "known".
|
// Subscribe to library events (before seeding so we don't miss items added during seed).
|
||||||
// This prevents mass notifications on first run or after DB reset.
|
|
||||||
SeedExistingLibraryItems();
|
|
||||||
|
|
||||||
// Subscribe to library events
|
|
||||||
_libraryManager.ItemAdded += OnItemAdded;
|
_libraryManager.ItemAdded += OnItemAdded;
|
||||||
_libraryManager.ItemRemoved += OnItemRemoved;
|
_libraryManager.ItemRemoved += OnItemRemoved;
|
||||||
|
|
||||||
@@ -63,6 +60,10 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
|
|||||||
_processTimer.AutoReset = true;
|
_processTimer.AutoReset = true;
|
||||||
_processTimer.Start();
|
_processTimer.Start();
|
||||||
|
|
||||||
|
// Pre-populate DB with existing library items in the background
|
||||||
|
// so we don't block Jellyfin startup.
|
||||||
|
_ = Task.Run(() => SeedExistingLibraryItems(), cancellationToken);
|
||||||
|
|
||||||
_logger.LogInformation("SmartNotify is now monitoring library changes");
|
_logger.LogInformation("SmartNotify is now monitoring library changes");
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -318,10 +319,11 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
|
|||||||
|
|
||||||
if (item is Episode episode)
|
if (item is Episode episode)
|
||||||
{
|
{
|
||||||
// episode.SeriesName / SeriesId are often null with Shokofin VFS,
|
|
||||||
// but the Series navigation property usually works
|
|
||||||
var seriesObj = episode.Series;
|
var seriesObj = episode.Series;
|
||||||
notification.SeriesName = episode.SeriesName ?? seriesObj?.Name;
|
var rawSeriesName = episode.SeriesName ?? seriesObj?.Name;
|
||||||
|
// If the name still looks like a Sonarr folder name, leave it null
|
||||||
|
// so the incomplete-episode logic defers until metadata scrape is done.
|
||||||
|
notification.SeriesName = LooksLikeFolderName(rawSeriesName) ? null : rawSeriesName;
|
||||||
notification.SeriesId = (episode.SeriesId != Guid.Empty
|
notification.SeriesId = (episode.SeriesId != Guid.Empty
|
||||||
? episode.SeriesId
|
? episode.SeriesId
|
||||||
: seriesObj?.Id ?? Guid.Empty).ToString();
|
: seriesObj?.Id ?? Guid.Empty).ToString();
|
||||||
@@ -430,12 +432,39 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
|
|||||||
|
|
||||||
var changed = false;
|
var changed = false;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(notification.SeriesName) || notification.SeriesName == "Unknown Series")
|
// Refresh SeriesName from the library. At queue time the name is often
|
||||||
|
// the Sonarr folder name (e.g. "Chained Soldier (2024) [tmdbid-139060]").
|
||||||
|
// After Jellyfin's metadata scrape completes, the name changes to the
|
||||||
|
// correct provider name (e.g. "Demon Slave - The Chained Soldier").
|
||||||
|
// We detect the unscraped folder name by checking for bracket patterns
|
||||||
|
// like [tmdbid-...] or (2024) and clear the SeriesName so the
|
||||||
|
// incomplete-episode logic defers sending until the scrape is done.
|
||||||
{
|
{
|
||||||
// episode.SeriesName is often null (especially with Shokofin VFS),
|
var freshSeriesName = episode.SeriesName ?? episode.Series?.Name;
|
||||||
// but the Series navigation property usually has the correct name
|
if (!string.IsNullOrEmpty(freshSeriesName))
|
||||||
notification.SeriesName = episode.SeriesName ?? episode.Series?.Name;
|
{
|
||||||
changed = true;
|
if (LooksLikeFolderName(freshSeriesName))
|
||||||
|
{
|
||||||
|
// Still the Sonarr folder name — clear so we keep waiting
|
||||||
|
if (!string.IsNullOrEmpty(notification.SeriesName))
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"[DEBUG Refresh] SeriesName '{Name}' looks like a folder name, clearing to defer",
|
||||||
|
freshSeriesName);
|
||||||
|
notification.SeriesName = null;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (freshSeriesName != notification.SeriesName)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"[DEBUG Refresh] SeriesName changed: '{Old}' -> '{New}'",
|
||||||
|
notification.SeriesName,
|
||||||
|
freshSeriesName);
|
||||||
|
notification.SeriesName = freshSeriesName;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(notification.SeriesId) || notification.SeriesId == Guid.Empty.ToString())
|
if (string.IsNullOrEmpty(notification.SeriesId) || notification.SeriesId == Guid.Empty.ToString())
|
||||||
@@ -464,6 +493,17 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always refresh the item name — it may have been a placeholder at queue time
|
||||||
|
if (!string.IsNullOrEmpty(episode.Name) && episode.Name != notification.Name)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"[DEBUG Refresh] Name changed: '{Old}' -> '{New}'",
|
||||||
|
notification.Name,
|
||||||
|
episode.Name);
|
||||||
|
notification.Name = episode.Name;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh image if missing
|
// Refresh image if missing
|
||||||
if (string.IsNullOrEmpty(notification.ImagePath) && episode.SeriesId != Guid.Empty)
|
if (string.IsNullOrEmpty(notification.ImagePath) && episode.SeriesId != Guid.Empty)
|
||||||
{
|
{
|
||||||
@@ -687,6 +727,19 @@ public class SmartNotifyBackgroundService : IHostedService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detects Sonarr-style folder names that haven't been replaced by a metadata scrape yet.
|
||||||
|
/// Matches patterns like "[tmdbid-139060]", "[tvdbid-412656]", "[imdbid-tt1234]".
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex FolderNamePattern = new(
|
||||||
|
@"\[(tmdbid|tvdbid|imdbid)-[^\]]+\]",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private static bool LooksLikeFolderName(string? name)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(name) && FolderNamePattern.IsMatch(name);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,30 @@
|
|||||||
"category": "Notifications",
|
"category": "Notifications",
|
||||||
"imageUrl": "",
|
"imageUrl": "",
|
||||||
"versions": [
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "0.1.3.0",
|
||||||
|
"changelog": "## 0.1.3 (2026-04-05)\n\n### Bug Fixes\n\nGebe keine Notification wenn der Name der Serie tmdbid oder ähnliches enthält.\nDies verhindert das die Notification kommt wenn noch nicht die Metadaten gezogen wurden.",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.1.3/smartnotify_0.1.3.zip",
|
||||||
|
"checksum": "0c8a24dcfed8884f768e5b2aad5f251c",
|
||||||
|
"timestamp": "2026-04-05T16:13:21Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.1.2.0",
|
||||||
|
"changelog": "## 0.1.2 (2026-04-04)\n\n### Bug Fixes\n\n* Problem behoben das der Serienname vor der Benachrichtigung nicht aktualisiert wurde.",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.1.2/smartnotify_0.1.2.zip",
|
||||||
|
"checksum": "56e2b0005f5bc3368c9a7f53b2f806a0",
|
||||||
|
"timestamp": "2026-04-04T09:39:49Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.1.1.0",
|
||||||
|
"changelog": "## 0.1.1 (2026-04-03)\n\n### Bug Fixes\n\n* fix: unknown series\n\n### Chores\n\n* chore: workflow fix\n* chore: removed old versions\n* chore: update manifest for v0.1.0",
|
||||||
|
"targetAbi": "10.11.0.0",
|
||||||
|
"sourceUrl": "https://git.tdpi.dev/TDPI/jellyfin-plugin-smartnotify/releases/download/v0.1.1/smartnotify_0.1.1.zip",
|
||||||
|
"checksum": "459569cbae49ba569291011ac9a54202",
|
||||||
|
"timestamp": "2026-04-03T17:32:41Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.1.0.0",
|
"version": "0.1.0.0",
|
||||||
"changelog": "## 0.1.0 (2026-03-05)\n\n**Features**\n\nIntelligente Discord-Benachrichtigungen\n\nAutomatische Benachrichtigungen bei neuen Filmen und Episoden via Discord Webhook\nSchöne Discord-Embeds mit Thumbnail, Beschreibung und Links zu externen Datenbanken (IMDb, TMDb, AniDB, AniList, TVDB)\n\n**Smarte Episoden-Gruppierung**\n\nEpisoden werden intelligent gebündelt statt einzeln gemeldet — z.B. \"Staffel 1: Episode 1-12\" statt 12 einzelne Nachrichten\nKonfigurierbares Zeitfenster für die Gruppierung\n\n**Qualitäts-Upgrade-Erkennung**\n\nErkennt automatisch ob eine Datei neu ist oder nur ein Qualitäts-Upgrade einer bestehenden Datei\nKeine Spam-Benachrichtigungen mehr beim Ersetzen von Dateien durch bessere Versionen\nStabile Erkennung über Provider-IDs (AniDB, TMDb etc.) — funktioniert auch mit Shokofin/VFS\n\n**Robuste Metadaten-Verarbeitung**\n\nVerzögerte Verarbeitung damit Jellyfin Zeit hat Metadaten zu laden\nDreistufige Validierung: beim Hinzufügen, beim Einreihen und beim Senden\nAutomatische Unterdrückung von reorganisierten Items (Pfad-/Metadata-Änderungen)\n\n**Konfigurierbar**\n\nBenachrichtigungen für Filme und Episoden einzeln aktivierbar\nUpgrade-Unterdrückung optional\nAnpassbare Verzögerung, Gruppierungsfenster, Bot-Name und Embed-Farbe",
|
"changelog": "## 0.1.0 (2026-03-05)\n\n**Features**\n\nIntelligente Discord-Benachrichtigungen\n\nAutomatische Benachrichtigungen bei neuen Filmen und Episoden via Discord Webhook\nSchöne Discord-Embeds mit Thumbnail, Beschreibung und Links zu externen Datenbanken (IMDb, TMDb, AniDB, AniList, TVDB)\n\n**Smarte Episoden-Gruppierung**\n\nEpisoden werden intelligent gebündelt statt einzeln gemeldet — z.B. \"Staffel 1: Episode 1-12\" statt 12 einzelne Nachrichten\nKonfigurierbares Zeitfenster für die Gruppierung\n\n**Qualitäts-Upgrade-Erkennung**\n\nErkennt automatisch ob eine Datei neu ist oder nur ein Qualitäts-Upgrade einer bestehenden Datei\nKeine Spam-Benachrichtigungen mehr beim Ersetzen von Dateien durch bessere Versionen\nStabile Erkennung über Provider-IDs (AniDB, TMDb etc.) — funktioniert auch mit Shokofin/VFS\n\n**Robuste Metadaten-Verarbeitung**\n\nVerzögerte Verarbeitung damit Jellyfin Zeit hat Metadaten zu laden\nDreistufige Validierung: beim Hinzufügen, beim Einreihen und beim Senden\nAutomatische Unterdrückung von reorganisierten Items (Pfad-/Metadata-Änderungen)\n\n**Konfigurierbar**\n\nBenachrichtigungen für Filme und Episoden einzeln aktivierbar\nUpgrade-Unterdrückung optional\nAnpassbare Verzögerung, Gruppierungsfenster, Bot-Name und Embed-Farbe",
|
||||||
|
|||||||
Reference in New Issue
Block a user