diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs
new file mode 100644
index 0000000..103c3d5
--- /dev/null
+++ b/Jellyfin.Plugin.TubeArchivistMetadata/Tasks/JFToTubeArchivistProgressSyncTask.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist;
+using Jellyfin.Plugin.TubeArchivistMetadata.Utilities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
+{
+ ///
+ /// Task to sync Jellyfin playback progresses to TubeArchivist.
+ ///
+ public class JFToTubearchivistProgressSyncTask : IScheduledTask
+ {
+ private readonly ILogger _logger;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Logger.
+ /// Library manager.
+ /// User manager.
+ /// User data manager.
+ public JFToTubearchivistProgressSyncTask(ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager)
+ {
+ _logger = logger;
+ _libraryManager = libraryManager;
+ _userManager = userManager;
+ _userDataManager = userDataManager;
+ }
+
+ ///
+ public string Name => "JFToTubearchivistProgressSyncTask";
+
+ ///
+ public string Description => "This tasks syncs TubeArchivist playback progresses to Jellyfin";
+
+ ///
+ public string Category => "TubeArchivistMetadata";
+
+ ///
+ public string Key => "JFToTubearchivistProgressSyncTask";
+
+ ///
+ public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken)
+ {
+ if (Plugin.Instance!.Configuration.TAJFSync)
+ {
+ var start = DateTime.Now;
+ _logger.LogInformation("Starting Jellyfin->TubeArchivist playback progresses synchronization.");
+ var taApi = TubeArchivistApi.GetInstance();
+ foreach (var jfUsername in Plugin.Instance!.Configuration.GetJFUsernamesToArray())
+ {
+ var user = _userManager.GetUserByName(jfUsername);
+ if (user == null)
+ {
+ _logger.LogInformation("{Message}", $"Jellyfin user with username {jfUsername} not found");
+ continue;
+ }
+
+ var collectionItem = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ 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
+ {
+ IncludeItemTypes = new[] { BaseItemKind.Series }
+ });
+ _logger.LogInformation("Analyzing collection {Id} with name {Name}", collectionItem.Id, collectionItem.Name);
+ _logger.LogInformation("Found {Message} channels", channels.Count);
+
+ foreach (Series channel in channels)
+ {
+ var years = channel.GetChildren(user, false, new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { BaseItemKind.Season }
+ });
+ _logger.LogInformation("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.LogInformation("Found {Videos} videos in year {YearName} of the channel {ChannelName}", videos.Count, year.Name, channel.Name);
+
+ foreach (Episode video in videos)
+ {
+ var videoYTId = Utils.GetVideoNameFromPath(video.Path);
+ var playbackProgress = _userDataManager.GetUserData(user.Id, video).PlaybackPositionTicks / TimeSpan.TicksPerSecond;
+ var statusCode = await taApi.SetProgress(videoYTId, playbackProgress).ConfigureAwait(true);
+ if (statusCode != System.Net.HttpStatusCode.OK)
+ {
+ _logger.LogInformation("{Message}", $"POST /video/{videoYTId}/progress returned {statusCode} for video {video.Name} with progress {progress} seconds");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ _logger.LogInformation("Time elapsed: {Time}", DateTime.Now - start);
+ }
+ else
+ {
+ _logger.LogInformation("Jellyfin->TubeArchivist playback synchronization is currently disabled.");
+ }
+ }
+
+ ///
+ public IEnumerable GetDefaultTriggers()
+ {
+ return
+ [
+ new TaskTriggerInfo
+ {
+ Type = TaskTriggerInfo.TriggerStartup,
+ },
+ ];
+ }
+ }
+}