diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/NumberingScheme.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/NumberingScheme.cs
new file mode 100644
index 0000000..c8a99d2
--- /dev/null
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/NumberingScheme.cs
@@ -0,0 +1,17 @@
+namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration;
+
+///
+/// The NumberingScheme.
+///
+public enum NumberingScheme
+{
+ ///
+ /// Default (no numbering).
+ ///
+ Default,
+
+ ///
+ /// YYYYMMDD (e.g. 20250804 for August 4th, 2025).
+ ///
+ YYYYMMDD,
+}
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs
index 21e23b0..ba1ac2c 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs
@@ -186,6 +186,11 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration
///
public int TAJFPlaylistsSyncTaskInterval { get; set; }
+ ///
+ /// Gets or sets the preferred numbering scheme for episodes (index number) in Jellyfin.
+ ///
+ public NumberingScheme EpisodeNumberingScheme { get; set; } = NumberingScheme.Default;
+
///
/// Gets the playback progress owners Jellyfin usernames to synchronize data from TubeArchivist.
///
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html
index 86f016d..ae769f6 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html
@@ -41,6 +41,18 @@
This is the maximum length of the descriptions showed in series
and episodes
+
+
+
+
+
+
+
This is the numbering scheme for episodes in Jellyfin (IndexNumber).
+ Default is the same as no numbering which allows Jellyfin to use the fallback numbering scheme.
+
Synchronization
@@ -160,6 +172,7 @@
document.querySelector('#JFTAPlaylistsSync').checked = config.JFTAPlaylistsSync;
document.querySelector('#TAJFPlaylistsSync').checked = config.TAJFPlaylistsSync;
document.querySelector('#JFUsernameFrom').value = config.JFUsernameFrom;
+ document.querySelector('#EpisodeNumberingScheme').value = config.EpisodeNumberingScheme;
document.querySelector('#TAJFProgressTaskInterval').value = config.TAJFProgressTaskInterval;
document.querySelector('#JFTAPlaylistsSyncTaskInterval').value = config.JFTAPlaylistsSyncTaskInterval;
document.querySelector('#TAJFPlaylistsSyncTaskInterval').value = config.TAJFPlaylistsSyncTaskInterval;
@@ -181,6 +194,7 @@
config.JFTAPlaylistsSync = document.querySelector('#JFTAPlaylistsSync').checked;
config.TAJFPlaylistsSync = document.querySelector('#TAJFPlaylistsSync').checked;
config.JFUsernameFrom = document.querySelector('#JFUsernameFrom').value;
+ config.EpisodeNumberingScheme = document.querySelector('#EpisodeNumberingScheme').value;
config.TAJFProgressTaskInterval = document.querySelector('#TAJFProgressTaskInterval').value;
config.JFTAPlaylistsSyncTaskInterval = document.querySelector('#JFTAPlaylistsSyncTaskInterval').value;
config.TAJFPlaylistsSyncTaskInterval = document.querySelector('#TAJFPlaylistsSyncTaskInterval').value;
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Plugin.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Plugin.cs
index 7e5a89c..7902298 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/Plugin.cs
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Plugin.cs
@@ -162,20 +162,20 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
return;
}
- if (Instance!.Configuration.JFTAProgressSync && eventArgs.Users.Any(u => Instance!.Configuration.JFUsernameFrom.Equals(u.Username, StringComparison.Ordinal)))
+ var topParent = eventArgs.Item.GetTopParent();
+ if (
+ Instance!.Configuration.JFTAProgressSync &&
+ eventArgs.Users.Any(u => Instance!.Configuration.JFUsernameFrom.Equals(u.Username, StringComparison.Ordinal)) &&
+ eventArgs.PlaybackPositionTicks.HasValue &&
+ string.Equals(topParent?.Name, Instance?.Configuration.CollectionTitle, StringComparison.OrdinalIgnoreCase)
+ )
{
- BaseItem? season = LibraryManager.GetItemById(eventArgs.Item.ParentId);
- BaseItem? channel = LibraryManager.GetItemById(season!.ParentId);
- BaseItem? collection = LibraryManager.GetItemById(channel!.ParentId);
- if (collection?.Name.ToLower(CultureInfo.CurrentCulture) == Instance?.Configuration.CollectionTitle.ToLower(CultureInfo.CurrentCulture) && eventArgs.PlaybackPositionTicks != null)
+ long progress = (long)eventArgs.PlaybackPositionTicks / TimeSpan.TicksPerSecond;
+ var videoId = Utils.GetVideoNameFromPath(eventArgs.Item.Path);
+ var statusCode = await TubeArchivistApi.GetInstance().SetProgress(videoId, progress).ConfigureAwait(true);
+ if (statusCode != System.Net.HttpStatusCode.OK)
{
- long progress = (long)eventArgs.PlaybackPositionTicks / TimeSpan.TicksPerSecond;
- var videoId = Utils.GetVideoNameFromPath(eventArgs.Item.Path);
- var statusCode = await TubeArchivistApi.GetInstance().SetProgress(videoId, progress).ConfigureAwait(true);
- if (statusCode != System.Net.HttpStatusCode.OK)
- {
- Logger.LogCritical("{Message}", $"POST /video/{videoId}/progress returned {statusCode} for video {eventArgs.Item.Name} with progress {progress} seconds");
- }
+ Logger.LogCritical("{Message}", $"POST /video/{videoId}/progress returned {statusCode} for video {eventArgs.Item.Name} with progress {progress} seconds");
}
}
}
@@ -188,16 +188,22 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
return;
}
+ var topParent = eventArgs.Item.GetTopParent();
var user = _userManager.GetUserById(eventArgs.UserId);
- if (user == null)
+ if (
+ Configuration.JFTAProgressSync &&
+ user != null &&
+ Configuration.GetJFUsernamesToArray().Contains(user!.Username) &&
+ string.Equals(topParent?.Name, Instance?.Configuration.CollectionTitle, StringComparison.OrdinalIgnoreCase)
+ )
{
Logger.LogError("OnWatchedStatusChange callback called without user id for item {ItemName}", eventArgs.Item.Name);
return;
}
- var userItemData = _userDataManager.GetUserData(user, eventArgs.Item);
if (Configuration.JFTAProgressSync && user != null && Configuration.GetJFUsernamesToArray().Contains(user!.Username))
{
+ var userItemData = _userDataManager.GetUserData(user, eventArgs.Item);
var isPlayed = eventArgs.Item.IsPlayed(user, userItemData);
Logger.LogDebug("User {UserId} changed watched status to {Status} for the item {ItemName}", eventArgs.UserId, isPlayed, eventArgs.Item.Name);
string itemYTId;
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs
index d19acb1..d62e530 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs
@@ -67,6 +67,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Providers
});
result.HasMetadata = true;
result.Item = video.ToEpisode();
+ result.Item.Path = info.Path;
result.Provider = Name;
result.People = peopleInfo;
}
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs
index 661729e..0126395 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs
@@ -153,7 +153,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
foreach (Episode video in videos)
{
var videoYTId = Utils.GetVideoNameFromPath(video.Path);
- _logger.LogDebug("{VideoYtId}", videoYTId);
+ _logger.LogDebug("Current video extracted YouTube id: {VideoYtId}", videoYTId);
HttpStatusCode statusCode;
var userItemData = _userDataManager.GetUserData(user, channel);
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs b/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs
index dec2ec0..22b3f44 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
+using Jellyfin.Plugin.TubeArchivistMetadata.Configuration;
using Jellyfin.Plugin.TubeArchivistMetadata.Utilities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -126,6 +127,11 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist
Overview = Utils.FormatDescription(Description),
SeasonName = Published.Year.ToString(CultureInfo.CurrentCulture),
ParentIndexNumber = Published.Year,
+ IndexNumber = Plugin.Instance?.Configuration?.EpisodeNumberingScheme switch
+ {
+ NumberingScheme.YYYYMMDD => (Published.Year * 10000) + (Published.Month * 100) + Published.Day,
+ _ => null
+ },
SeriesName = Channel.Name,
ProductionYear = Published.Year,
PremiereDate = Published,
diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Utils/Utils.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Utils/Utils.cs
index 8263ff7..d61e626 100644
--- a/Jellyfin.Plugin.TubeArchivistMetadata/Utils/Utils.cs
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Utils/Utils.cs
@@ -1,6 +1,8 @@
+using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
+using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
{
@@ -45,6 +47,11 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
/// A string with \n replaced by br tags.
public static string FormatDescription(string description)
{
+ if (description == null)
+ {
+ return "";
+ }
+
var maxLength = 500;
if (Plugin.Instance != null)
{
@@ -54,9 +61,10 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
if (description.Length > maxLength)
{
description = description.Substring(0, maxLength);
- description = description.Replace("\n", " ", System.StringComparison.CurrentCulture);
}
+ description = description.Replace("\n", " ", System.StringComparison.CurrentCulture);
+
return description;
}
diff --git a/README.md b/README.md
index 7d90674..b42004c 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,16 @@ Keep in mind that Jellyfin lowest accepted period is of 1 minute (60 seconds) an

+## Episode numbering
+
+
There are different ways to number the episodes as they are configured in Jellyfin.
+This changes the number after E in S--E-- (for example S2024E100 for episode number 100 of season 2024).
+
+
+
+The options correlate with:
+- Default - leave the numbering to what Jellyfin does by default (this is what the plugin has always done)
+- YYYYMMDD - numbers the episode by the year, month, day (e.g. 20250804 for a video published on the 4th of August 2025)
## Build