Compare commits

...

12 Commits

Author SHA1 Message Date
DarkFighterLuke 80a1b6050d Merge pull request #50 from tubearchivist/bugfix/sync-usernames
Update build.yml
2025-05-27 20:34:48 +02:00
DarkFighterLuke a4942e78b6 Update manifest.json
Signed-off-by: DarkFighterLuke <DarkFighterLuke@users.noreply.github.com>
2025-05-26 21:43:57 +00:00
DarkFighterLuke 48070f311e Merge pull request #49 from tubearchivist/bugfix/sync-usernames
JF->TA Sync fix
2025-05-26 23:43:57 +02:00
DarkFighterLuke 96a72ab909 Update build.yml 2025-05-26 23:41:43 +02:00
DarkFighterLuke 9c6aa56db0 Fix JFToTubearchivistProgressSyncTask 2025-05-26 23:38:04 +02:00
DarkFighterLuke 413f45046d Sync progress status in OnWatchedStatusChange when watched is false
modified:   Jellyfin.Plugin.TubeArchivistMetadata/Plugin.cs
2025-05-26 23:37:20 +02:00
DarkFighterLuke 5ad6b2a381 Fix OnPlaybackProgress collection name retrieval 2025-05-26 23:35:42 +02:00
DarkFighterLuke 795047a50e Update README.md 2025-03-13 23:17:08 +01:00
DarkFighterLuke 323a208de2 Update README.md 2025-03-13 23:16:26 +01:00
DarkFighterLuke 55d320c456 Update BUG-REPORT.yml 2025-03-13 23:10:01 +01:00
DarkFighterLuke d5f2f0ae8e Update BUG-REPORT.yml 2025-03-13 23:08:04 +01:00
DarkFighterLuke c3bdfc26ea Update manifest.json
Signed-off-by: DarkFighterLuke <DarkFighterLuke@users.noreply.github.com>
2025-03-10 22:01:17 +00:00
7 changed files with 131 additions and 111 deletions
+4
View File
@@ -15,8 +15,12 @@ body:
options:
- label: I'm running the latest version of tubearchivist-jf-plugin.
required: true
- label: I'm running the latest version of TubeArchivist.
required: true
- label: I have read the [how to open an issue](https://github.com/tubearchivist/tubearchivist/blob/master/CONTRIBUTING.md#how-to-open-an-issue) guide, particularly the [bug report](https://github.com/tubearchivist/tubearchivist/blob/master/CONTRIBUTING.md#bug-report) section.
required: true
- label: I have searched for both closed and open already existing issues about the problem I am reporting.
required: true
- type: input
id: os
+3 -3
View File
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>1.3.4.0</Version>
<AssemblyVersion>1.3.4.0</AssemblyVersion>
<FileVersion>1.3.4.0</FileVersion>
<Version>1.3.7.0</Version>
<AssemblyVersion>1.3.7.0</AssemblyVersion>
<FileVersion>1.3.7.0</FileVersion>
</PropertyGroup>
</Project>
@@ -28,6 +28,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="Plugin"/> class.
@@ -63,6 +64,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
sessionManager.PlaybackProgress += OnPlaybackProgress;
LibraryManager = libraryManager;
_userManager = userManager;
_userDataManager = userDataManager;
userDataManager.UserDataSaved += OnWatchedStatusChange;
var taToJellyfinProgressSyncTask = new TAToJellyfinProgressSyncTask(logger, libraryManager, userManager, userDataManager);
@@ -173,7 +175,8 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
{
if (Instance!.Configuration.JFTASync && eventArgs.Users.Any(u => Instance!.Configuration.JFUsernameFrom.Equals(u.Username, StringComparison.Ordinal)))
{
BaseItem? channel = LibraryManager.GetItemById(eventArgs.Item.ParentId);
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)
{
@@ -224,6 +227,17 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
{
Logger.LogCritical("POST /watched returned {StatusCode} for item {ItemName} ({VideoYTId}) with watched status {IsPlayed}", statusCode, eventArgs.Item.Name, itemYTId, isPlayed);
}
if (eventArgs.Item is Episode)
{
var progress = _userDataManager.GetUserData(user, eventArgs.Item).PlaybackPositionTicks / TimeSpan.TicksPerSecond;
var videoId = Utils.GetVideoNameFromPath(eventArgs.Item.Path);
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");
}
}
}
catch (Exception ex)
{
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
@@ -45,7 +46,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
public string Name => "JFToTubeArchivistProgressSyncTask";
/// <inheritdoc/>
public string Description => "This tasks syncs TubeArchivist playback progresses to Jellyfin";
public string Description => "This tasks syncs Jellyfin playback progresses to TubeArchivist";
/// <inheritdoc/>
public string Category => "TubeArchivistMetadata";
@@ -63,54 +64,52 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
_logger.LogInformation("Starting Jellyfin->TubeArchivist playback progresses synchronization.");
var taApi = TubeArchivistApi.GetInstance();
var videosCount = 0;
foreach (var jfUsername in Plugin.Instance!.Configuration.GetJFUsernamesToArray())
var jfUsername = Plugin.Instance!.Configuration.JFUsernameFrom;
var user = _userManager.GetUserByName(jfUsername);
if (user == null)
{
var user = _userManager.GetUserByName(jfUsername);
if (user == null)
{
_logger.LogInformation("{Message}", $"Jellyfin user with username {jfUsername} not found");
continue;
}
_logger.LogInformation("{Message}", $"Jellyfin user with username {jfUsername} not found");
return;
}
var collectionItem = _libraryManager.GetItemList(new InternalItemsQuery
{
Name = Plugin.Instance?.Configuration.CollectionTitle,
IncludeItemTypes = new[] { BaseItemKind.CollectionFolder }
}).FirstOrDefault();
var collectionItem = _libraryManager.GetItemList(new InternalItemsQuery
{
Name = Plugin.Instance?.Configuration.CollectionTitle,
IncludeItemTypes = new[] { BaseItemKind.CollectionFolder }
}).FirstOrDefault();
if (collectionItem == null)
if (collectionItem == null)
{
var message = $"Collection '{Plugin.Instance?.Configuration.CollectionTitle}' not found.";
_logger.LogCritical("{Message}", message);
}
else
{
var collection = (CollectionFolder)collectionItem;
var channels = collection.GetChildren(user, false, new InternalItemsQuery
{
var message = $"Collection '{Plugin.Instance?.Configuration.CollectionTitle}' not found.";
_logger.LogCritical("{Message}", message);
}
else
IncludeItemTypes = new[] { BaseItemKind.Series }
});
_logger.LogInformation("Analyzing collection {Id} with name {Name}", collectionItem.Id, collectionItem.Name);
_logger.LogDebug("Found {Message} channels", channels.Count);
foreach (Series channel in channels)
{
var collection = (CollectionFolder)collectionItem;
var channels = collection.GetChildren(user, false, new InternalItemsQuery
var channelYTId = Utils.GetChannelNameFromPath(channel.Path);
var years = channel.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Series }
IncludeItemTypes = new[] { BaseItemKind.Season }
});
_logger.LogInformation("Analyzing collection {Id} with name {Name}", collectionItem.Id, collectionItem.Name);
_logger.LogDebug("Found {Message} channels", channels.Count);
_logger.LogDebug("Found {Years} years in channel {ChannelName}", years.Count, channel.Name);
foreach (Series channel in channels)
foreach (Season year in years)
{
var channelYTId = Utils.GetChannelNameFromPath(channel.Path);
var years = channel.GetChildren(user, false, new InternalItemsQuery
var videos = year.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Season }
IncludeItemTypes = new[] { BaseItemKind.Episode }
});
_logger.LogDebug("Found {Years} years in channel {ChannelName}", years.Count, channel.Name);
foreach (Season year in years)
{
var videos = year.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Episode }
});
_logger.LogDebug("Found {Videos} videos in year {YearName} of the channel {ChannelName}", videos.Count, year.Name, channel.Name);
videosCount += videos.Count;
}
_logger.LogDebug("Found {Videos} videos in year {YearName} of the channel {ChannelName}", videos.Count, year.Name, channel.Name);
videosCount += videos.Count;
}
}
}
@@ -118,91 +117,82 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
_logger.LogInformation("Found a total of {VideosCount} videos", videosCount);
var processedVideosCount = 0;
foreach (var jfUsername in Plugin.Instance!.Configuration.GetJFUsernamesToArray())
if (collectionItem == null)
{
var user = _userManager.GetUserByName(jfUsername);
if (user == null)
var message = $"Collection '{Plugin.Instance?.Configuration.CollectionTitle}' not found.";
_logger.LogCritical("{Message}", message);
}
else
{
var collection = (CollectionFolder)collectionItem;
var channels = collection.GetChildren(user, false, new InternalItemsQuery
{
_logger.LogInformation("{Message}", $"Jellyfin user with username {jfUsername} not found");
continue;
}
IncludeItemTypes = new[] { BaseItemKind.Series }
});
var collectionItem = _libraryManager.GetItemList(new InternalItemsQuery
foreach (Series channel in channels)
{
Name = Plugin.Instance?.Configuration.CollectionTitle,
IncludeItemTypes = new[] { BaseItemKind.CollectionFolder }
}).FirstOrDefault();
if (collectionItem == null)
{
var message = $"Collection '{Plugin.Instance?.Configuration.CollectionTitle}' not found.";
_logger.LogCritical("{Message}", message);
}
else
{
var collection = (CollectionFolder)collectionItem;
var channels = collection.GetChildren(user, false, new InternalItemsQuery
var channelYTId = Utils.GetChannelNameFromPath(channel.Path);
var isChannelWatched = false;
var isChannelCheckedForWatched = false;
var years = channel.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Series }
IncludeItemTypes = new[] { BaseItemKind.Season }
});
foreach (Series channel in channels)
foreach (Season year in years)
{
var channelYTId = Utils.GetChannelNameFromPath(channel.Path);
var isChannelWatched = false;
var isChannelCheckedForWatched = false;
var years = channel.GetChildren(user, false, new InternalItemsQuery
var videos = year.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Season }
IncludeItemTypes = new[] { BaseItemKind.Episode }
});
videosCount += videos.Count;
foreach (Season year in years)
foreach (Episode video in videos)
{
var videos = year.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Episode }
});
videosCount += videos.Count;
var videoYTId = Utils.GetVideoNameFromPath(video.Path);
_logger.LogInformation("{VideoYtId}", videoYTId);
HttpStatusCode statusCode;
foreach (Episode video in videos)
if (!isChannelCheckedForWatched && channel.IsPlayed(user))
{
var videoYTId = Utils.GetVideoNameFromPath(video.Path);
var playbackProgress = _userDataManager.GetUserData(user, video).PlaybackPositionTicks / TimeSpan.TicksPerSecond;
var statusCode = await taApi.SetProgress(videoYTId, playbackProgress).ConfigureAwait(true);
var isChannelPlayed = channel.IsPlayed(user);
statusCode = await taApi.SetWatchedStatus(channelYTId, isChannelPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogCritical("{Message}", $"POST /video/{videoYTId}/progress returned {statusCode} for video {video.Name} with progress {progress} seconds");
_logger.LogCritical("{Message}", $"POST /watched returned {statusCode} for channel {channel.Name} ({channelYTId}) with wacthed status {isChannelPlayed}");
}
if (!isChannelCheckedForWatched && channel.IsPlayed(user))
else
{
var isChannelPlayed = channel.IsPlayed(user);
statusCode = await taApi.SetWatchedStatus(channelYTId, isChannelPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogCritical("{Message}", $"POST /watched returned {statusCode} for channel {channel.Name} ({channelYTId}) with wacthed status {isChannelPlayed}");
}
else
{
isChannelWatched = true;
}
isChannelCheckedForWatched = true;
isChannelWatched = true;
}
if (!isChannelWatched)
{
var isVideoPlayed = video.IsPlayed(user);
statusCode = await taApi.SetWatchedStatus(videoYTId, isVideoPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogCritical("{Message}", $"POST /watched returned {statusCode} for video {video.Name} ({videoYTId}) with wacthed status {isVideoPlayed}");
}
}
processedVideosCount++;
progress.Report(processedVideosCount * 100 / videosCount);
isChannelCheckedForWatched = true;
}
if (!isChannelWatched)
{
var isVideoPlayed = video.IsPlayed(user);
statusCode = await taApi.SetWatchedStatus(videoYTId, isVideoPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogCritical("{Message}", $"POST /watched returned {statusCode} for video {video.Name} ({videoYTId}) with wacthed status {isVideoPlayed}");
}
_logger.LogInformation("{Message}", isVideoPlayed);
if (!isVideoPlayed)
{
var playbackProgress = _userDataManager.GetUserData(user, video).PlaybackPositionTicks / TimeSpan.TicksPerSecond;
statusCode = await taApi.SetProgress(videoYTId, playbackProgress).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogCritical("{Message}", $"POST /video/{videoYTId}/progress returned {statusCode} for video {video.Name} with progress {progress} seconds");
}
}
}
processedVideosCount++;
progress.Report(processedVideosCount * 100 / videosCount);
}
}
}
+4
View File
@@ -9,6 +9,10 @@
> Jellyfin release cycle has changed in the past few months and now it is shorter than before. The plugin supports only the latest Jellyfin release, in order to continue using this plugin with all the latest features you will need to upgrade your Jellyfin installation.
> The same rule applies to TubeArchivist: the plugin is only guaranteed to work with the latest TubeArchivist version.
> [!WARNING]
> Jellyfin 10.10 introduced a bug that prevents the plugin to correctly create Season folders by year. The bug has been finally solved on the Jellyfin codebase, but, until the next minor release, a manual build of the Jellyfin branch `release-10.10.z` is required in order to get the fix running.<br>
> This is not a plugin bug, any issue opened about this bug will be immediately closed!
## About
<p>This plugin adds the metadata provider for <a href="https://www.tubearchivist.com/">TubeArchivist</a>, offering improved flexibility and native integration with Jellyfin compared to previous solutions.</p>
+2 -2
View File
@@ -2,7 +2,7 @@
name: "TubeArchivistMetadata"
guid: "dc97d0c6-28b0-4242-afb4-5833ae1b3715"
imageUrl: https://raw.githubusercontent.com/tubearchivist/tubearchivist-jf-plugin/master/images/logo.png
version: "1.3.6.0"
version: "1.3.7.0"
targetAbi: "10.10.0.0"
framework: "net8.0"
overview: "Metadata for your TubeArchivist library on Jellyfin"
@@ -14,4 +14,4 @@ owner: "DarkFighterLuke"
artifacts:
- "Jellyfin.Plugin.TubeArchivistMetadata.dll"
changelog: >
Fix TA auth header
Fix JF->TA Sync
+11 -3
View File
@@ -8,13 +8,21 @@
"overview": "Metadata for your TubeArchivist library on Jellyfin",
"imageUrl": "https://raw.githubusercontent.com/tubearchivist/tubearchivist-jf-plugin/master/images/logo.png",
"versions": [
{
"version": "1.3.7.0",
"changelog": "Fix TA auth header\n",
"targetAbi": "10.10.0.0",
"sourceUrl": "https://github.com/tubearchivist/tubearchivist-jf-plugin/releases/download/v1.3.7/tubearchivistmetadata_1.3.7.0.zip",
"checksum": "a0e4ee866f298df223a0b9b5cece16b0",
"timestamp": "2025-05-26T21:43:54Z"
},
{
"version": "1.3.6.0",
"changelog": "Adapt to the new TA API changes Fix JF crash when setting watched status with TA offline\n",
"changelog": "Fix TA auth header\n",
"targetAbi": "10.10.0.0",
"sourceUrl": "https://github.com/tubearchivist/tubearchivist-jf-plugin/releases/download/v1.3.6/tubearchivistmetadata_1.3.6.0.zip",
"checksum": "4d3ee6b9d85726c9e8050fe7c40d6390",
"timestamp": "2025-03-10T21:55:33Z"
"checksum": "513cde64268867d0047009eb2d986054",
"timestamp": "2025-03-10T22:01:13Z"
},
{
"version": "1.3.5.0",