diff --git a/.vscode/settings.json b/.vscode/settings.json index b4c529b..1d8c9ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,16 @@ { // jellyfinDir : The directory of the cloned jellyfin server project // This needs to be built once before it can be used - "jellyfinDir" : "${workspaceFolder}/../jellyfin/Jellyfin.Server", + "jellyfinDir": "${workspaceFolder}/../jellyfin/Jellyfin.Server", // jellyfinWebDir : The directory of the cloned jellyfin-web project // This needs to be built once before it can be used - "jellyfinWebDir" : "${workspaceFolder}/../jellyfin-web", + "jellyfinWebDir": "${workspaceFolder}/../jellyfin-web", // jellyfinDataDir : the root data directory for a running jellyfin instance // This is where jellyfin stores its configs, plugins, metadata etc // This is platform specific by default, but on Windows defaults to // ${env:LOCALAPPDATA}/jellyfin - "jellyfinDataDir" : "${env:LOCALAPPDATA}/jellyfin", + "jellyfinDataDir": "${env:LOCALAPPDATA}/jellyfin", // The name of the plugin - "pluginName" : "Jellyfin.Plugin.Template", -} + "pluginName": "Jellyfin.Plugin.Template", + "git.ignoreLimitWarning": true, +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index ed755b6..3ee7cdd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 1.4.0.0 - 1.4.0.0 - 1.4.0.0 + 1.4.1.0 + 1.4.1.0 + 1.4.1.0 diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs index ce10eef..21e23b0 100644 --- a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/PluginConfiguration.cs @@ -36,11 +36,17 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration _tubeArchivistUrl = string.Empty; _tubeArchivistApiKey = string.Empty; MaxDescriptionLength = 500; - JFTASync = false; + JFTAProgressSync = false; JFUsernameFrom = string.Empty; - TAJFSync = false; + TAJFProgressSync = false; + JFTAPlaylistsSync = false; + JFTAPlaylistsDelete = false; + TAJFPlaylistsSync = false; + TAJFPlaylistsDelete = false; _jfUsernamesTo = new HashSet(); - TAJFTaskInterval = 1; + TAJFProgressTaskInterval = 60; + JFTAPlaylistsSyncTaskInterval = 60; + TAJFPlaylistsSyncTaskInterval = 60; } /// @@ -100,12 +106,32 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration /// /// Gets or sets a value indicating whether to enable TA->JF playback progress synchronization. /// - public bool TAJFSync { get; set; } + public bool TAJFProgressSync { get; set; } /// /// Gets or sets a value indicating whether to enable JF->TA playback progress synchronization. /// - public bool JFTASync { get; set; } + public bool JFTAProgressSync { get; set; } + + /// + /// Gets or sets a value indicating whether to enable JF->TA playlists synchronization. + /// + public bool JFTAPlaylistsSync { get; set; } + + /// + /// Gets or sets a value indicating whether to delete playlists from TA when not found on JF. + /// + public bool JFTAPlaylistsDelete { get; set; } + + /// + /// Gets or sets a value indicating whether to enable TA->JF playlists synchronization. + /// + public bool TAJFPlaylistsSync { get; set; } + + /// + /// Gets or sets a value indicating whether to delete playlists from JF when not found on TA. + /// + public bool TAJFPlaylistsDelete { get; set; } /// /// Gets or sets the playback progress owner Jellyfin username to synchronize data to TubeArchivist. @@ -146,7 +172,19 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration /// Gets or sets the interval in seconds at which the TubeArchivist to Jellyfin playback progress synchronization task should run. /// It requires Jellyfin server restart to take effect. /// - public int TAJFTaskInterval { get; set; } + public int TAJFProgressTaskInterval { get; set; } + + /// + /// Gets or sets the interval in seconds at which the Jellyfin to TubeArchivist playlists synchronization task should run. + /// It requires Jellyfin server restart to take effect. + /// + public int JFTAPlaylistsSyncTaskInterval { get; set; } + + /// + /// Gets or sets the interval in seconds at which the TubeArchivist to Jellyfin playlists synchronization task should run. + /// It requires Jellyfin server restart to take effect. + /// + public int TAJFPlaylistsSyncTaskInterval { get; set; } /// /// 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 d4e07d5..86f016d 100644 --- a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html +++ b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html @@ -42,28 +42,43 @@ and episodes
-

Playback synchronization

+

Synchronization

-
- +
+

Jellyfin to TubeArchivist

+ from -
This is the Jellyfin username to synchronize data from on + placeholder="Username" /> +
This is the Jellyfin username to synchronize data to TubeArchivist
-
+
+
+ +
+ +
+ +
+
+
+

TubeArchivist to Jellyfin

+
@@ -72,13 +87,52 @@
These are the (comma separated) Jellyfin usernames to synchronize data from TubeArchivist
+
+ +
+
+
+ +
+ +
+ +
+
+
+

Tasks intervals

+
-
+
+ + +
This is the Jellyfin to TubeArchivist playlists + synchronization interval in seconds (Jellyfin restart required).
+
+
+ + +
This is the TubeArchivist to Jellyfin playlists + synchronization interval in seconds (Jellyfin restart required).
+
public static class Utils { + private const string TAPlaylistIdRegex = @"^(.*)\((.*)\)$"; + private const string YTTAPlaylistNameFormatRegex = @"^(.*)\s\-\s(.*)\s\((.*)\)$"; + private const string TAPlaylistNameFormatRegex = @"^(.*)\s\((.*)\)$"; + /// - /// Sanitize the given URL. + /// Sanitizes the given URL. /// /// An URL string. /// The URL string without spaces, doubled slashes and with a trailing slash. @@ -35,7 +39,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities } /// - /// Format episodes and series descriptions replacing newlines with br tags. + /// Formats episodes and series descriptions replacing newlines with br tags. /// /// String to format. /// A string with \n replaced by br tags. @@ -57,7 +61,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities } /// - /// Get video name from file path on the disk. + /// Gets video name from file path on the disk. /// /// File path on disk. /// The video name. @@ -67,7 +71,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities } /// - /// Get channel name from directory path on the disk. + /// Gets channel name from directory path on the disk. /// /// Directory path on disk. /// The channel name. @@ -90,5 +94,35 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities return '/'; // Unix directory separator } } + + /// + /// Gets the TubeArchivist playlist id from Jellyfin playlist name. + /// + /// The Jellyfin playlist name. + /// The TubeArchvist playlist id. + public static string? GetTAPlaylistIdFromName(string playlistName) + { + var regex = new Regex(TAPlaylistIdRegex); + return regex.Match(playlistName).Groups[2].ToString(); + } + + /// + /// Gets the TubeArchivist playlist name from Jellyfin playlist name. + /// + /// The Jellyfin playlist name. + /// The TubeArchivist playlist name. + public static string? GetTAPlaylistNameFromName(string playlistName) + { + var ytRegex = new Regex(YTTAPlaylistNameFormatRegex); + var regex = new Regex(TAPlaylistNameFormatRegex); + + var name = ytRegex.Match(playlistName).Groups[1].ToString(); + if (string.IsNullOrEmpty(name)) + { + name = regex.Match(playlistName).Groups[1].ToString(); + } + + return name; + } } } diff --git a/README.md b/README.md index 7977bd7..30c844c 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,11 @@ The plugin interacts with TubeArchivist APIs to fetch videos and channels metada - Add images for videos (episodes), ie. thumb images - Add images for channels (shows), ie. thumb, tvart and banner images - Organize videos (episodes) by year (seasons) +- Bidirectional playback progress synchronization +- Bidirectional playlists synchronization + +> [!WARNING] +> Enabling synchronization in both directions you can run in race conditions and unexpected results. ## Installation ### From official repository (recommended) diff --git a/build.yaml b/build.yaml index e96ac80..f97a326 100644 --- a/build.yaml +++ b/build.yaml @@ -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.4.0.0" +version: "1.4.1.0" targetAbi: "10.11.0.0" framework: "net9.0" overview: "Metadata for your TubeArchivist library on Jellyfin" @@ -14,4 +14,4 @@ owner: "DarkFighterLuke" artifacts: - "Jellyfin.Plugin.TubeArchivistMetadata.dll" changelog: > - Align compatibility with Jellyfin 10.11.x + Introduce bidirectional playlists synchronization