diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 48105e5..2fb5f20 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -27,7 +27,7 @@ jobs: - name: Setup .Net uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Add Jellyfin GitHub Nuget registry to sources run: | @@ -63,7 +63,7 @@ jobs: uses: DarkFighterLuke/jellyfin-plugin-repository-manager@7a96768accc155ac7b597351b5033532f3c48173 id: jprm with: - dotnet-target: net8.0 + dotnet-target: net9.0 version: ${VERSION} update-manifest: "true" 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 94ca9df..3ee7cdd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 1.3.7.0 - 1.3.7.0 - 1.3.7.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 e7acccc..ba1ac2c 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 or sets the preferred numbering scheme for episodes (index number) in Jellyfin. diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html b/Jellyfin.Plugin.TubeArchivistMetadata/Configuration/configPage.html index ac93f4d..8ce0f50 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,10 +87,34 @@
These are the (comma separated) Jellyfin usernames to synchronize data from TubeArchivist
+
+ +
+
+
+ +
+ +
+ +
+
+
+

Tasks intervals

+
-
@@ -90,6 +129,20 @@
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.
+
+ + +
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. @@ -37,7 +41,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. @@ -65,7 +69,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. @@ -75,7 +79,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. @@ -98,5 +102,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 4bf47b7..f947445 100644 --- a/README.md +++ b/README.md @@ -27,18 +27,23 @@ 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) -1. Go to `Dashboard -> Plugins` and select the `Repositories` tab +1. Go to `Dashboard -> Plugins` and click on the `Manage Repositories` button 2. Add a new repository with the following details: - Repository name: `TubeArchivistMetadata` - Repository URL: `https://github.com/tubearchivist/tubearchivist-jf-plugin/raw/master/manifest.json` - ![Add repository](https://github.com/tubearchivist/tubearchivist-jf-plugin/assets/31162436/b0216b21-79c4-445b-8cf1-fbf6b138dee0) + ![Add repository](https://github.com/user-attachments/assets/337ba921-bc97-47ea-815c-c664cf7661a2) -3. Go to the `Catalog` tab -4. Find `TubeArchivistMetadata` in the `Metadata` section and install it -![Find plugin](https://github.com/tubearchivist/tubearchivist-jf-plugin/assets/31162436/a30a14c2-33cd-44c1-b96a-406662726e5e) +3. Go back to the catalog +4. Find `TubeArchivistMetadata` and install it +![Find plugin](https://github.com/user-attachments/assets/41f7315b-27c6-47dd-958f-21a232c30013) 5. Restart Jellyfin to apply the changes ### From ZIP in GitHub releases @@ -103,7 +108,7 @@ The options correlate with: ## Build -1. To build this plugin you will need [.Net 8.x](https://dotnet.microsoft.com/download/dotnet/8.0). +1. To build this plugin you will need [.Net 9.x](https://dotnet.microsoft.com/download/dotnet/9.0). 2. Build plugin with following command ``` diff --git a/build.yaml b/build.yaml index 572b782..f97a326 100644 --- a/build.yaml +++ b/build.yaml @@ -2,9 +2,9 @@ name: "TubeArchivistMetadata" guid: "dc97d0c6-28b0-4242-afb4-5833ae1b3715" imageUrl: https://raw.githubusercontent.com/tubearchivist/tubearchivist-jf-plugin/master/images/logo.png -version: "1.3.7.0" -targetAbi: "10.10.0.0" -framework: "net8.0" +version: "1.4.1.0" +targetAbi: "10.11.0.0" +framework: "net9.0" overview: "Metadata for your TubeArchivist library on Jellyfin" description: > TubeArchivistMetadata is a plugin that automatically manages @@ -14,4 +14,4 @@ owner: "DarkFighterLuke" artifacts: - "Jellyfin.Plugin.TubeArchivistMetadata.dll" changelog: > - Fix JF->TA Sync + Introduce bidirectional playlists synchronization diff --git a/manifest.json b/manifest.json index 31cc5fa..b9a3dbb 100644 --- a/manifest.json +++ b/manifest.json @@ -8,6 +8,22 @@ "overview": "Metadata for your TubeArchivist library on Jellyfin", "imageUrl": "https://raw.githubusercontent.com/tubearchivist/tubearchivist-jf-plugin/master/images/logo.png", "versions": [ + { + "version": "1.4.1.0", + "changelog": "Introduce bidirectional playlists synchronization\n", + "targetAbi": "10.11.0.0", + "sourceUrl": "https://github.com/tubearchivist/tubearchivist-jf-plugin/releases/download/v1.4.1/tubearchivistmetadata_1.4.1.0.zip", + "checksum": "2dc0b9140472a4f783f093a7ee425493", + "timestamp": "2025-10-22T07:00:27Z" + }, + { + "version": "1.4.0.0", + "changelog": "Introduce bidirectional playlists synchronization\n", + "targetAbi": "10.11.0.0", + "sourceUrl": "https://github.com/tubearchivist/tubearchivist-jf-plugin/releases/download/v1.4.0/tubearchivistmetadata_1.4.0.0.zip", + "checksum": "4b81ab60022b532a64bb5eeb7be67dcc", + "timestamp": "2025-10-22T06:57:53Z" + }, { "version": "1.3.7.0", "changelog": "Fix JF->TA Sync\n",