diff --git a/JellyfinPlugin/Configuration/PluginConfiguration.cs b/JellyfinPlugin/Configuration/PluginConfiguration.cs
index 2fafcea..8b5b65b 100644
--- a/JellyfinPlugin/Configuration/PluginConfiguration.cs
+++ b/JellyfinPlugin/Configuration/PluginConfiguration.cs
@@ -12,5 +12,8 @@ namespace JellyfinPlugin.Configuration
// Tryb odtwarzania: "Server" (FFmpeg wrapper), "Client" (CSS injection w Web UI), "Auto" (Automatyczny: CSS dla Web, FFmpeg dla aplikacji natywnych)
public string PlaybackMode { get; set; } = "Auto";
+
+ // Maksymalna liczba równoległych wątków skanowania czarnych pasów (1 - 16, domyślnie 4)
+ public int MaxParallelScans { get; set; } = 4;
}
}
diff --git a/JellyfinPlugin/Configuration/configPage.html b/JellyfinPlugin/Configuration/configPage.html
index f500045..1f48195 100644
--- a/JellyfinPlugin/Configuration/configPage.html
+++ b/JellyfinPlugin/Configuration/configPage.html
@@ -33,6 +33,12 @@
Choose how the plugin should remove black bars. Auto mode uses lightweight CSS clipping for Web browser DirectPlay, and falls back to FFmpeg transcoding for native apps or unsupported video formats.
+
+
Save
@@ -56,6 +62,7 @@
var mode = config.PlaybackMode || "Auto";
if (mode === "Both") mode = "Auto";
$('#selectPlaybackMode', page).val(mode);
+ $('#txtMaxParallelScans', page).val(config.MaxParallelScans || 4);
Dashboard.hideLoadingMsg();
});
});
@@ -69,6 +76,7 @@
config.IsEnabled = $('#chkEnablePlugin', form).prop('checked');
config.CropDetectLimit = parseFloat($('#txtCropLimit', form).val());
config.PlaybackMode = $('#selectPlaybackMode', form).val();
+ config.MaxParallelScans = parseInt($('#txtMaxParallelScans', form).val(), 10) || 4;
ApiClient.updatePluginConfiguration(BlackBarRemoverConfig.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
diff --git a/JellyfinPlugin/Tasks/LibraryForceRescanTask.cs b/JellyfinPlugin/Tasks/LibraryForceRescanTask.cs
index d0aa37d..e886073 100644
--- a/JellyfinPlugin/Tasks/LibraryForceRescanTask.cs
+++ b/JellyfinPlugin/Tasks/LibraryForceRescanTask.cs
@@ -42,15 +42,21 @@ namespace JellyfinPlugin.Tasks
.ToList();
int total = items.Count;
+ if (total == 0) return;
+
int processed = 0;
+ int maxParallel = Math.Clamp(config.MaxParallelScans, 1, 16);
- foreach (var item in items)
+ var parallelOptions = new ParallelOptions
{
- cancellationToken.ThrowIfCancellationRequested();
-
- // Wymuszamy skanowanie, więc nie sprawdzamy czy ma już BlackBarCrop
+ MaxDegreeOfParallelism = maxParallel,
+ CancellationToken = cancellationToken
+ };
+ _logger.LogInformation("Starting Force Rescan of {Total} items with {MaxParallel} parallel workers.", total, maxParallel);
+ await Parallel.ForEachAsync(items, parallelOptions, async (item, ct) =>
+ {
var path = item.Path;
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
@@ -65,7 +71,7 @@ namespace JellyfinPlugin.Tasks
item.ProviderIds = new Dictionary();
}
item.ProviderIds["BlackBarCrop"] = cropData;
- await _libraryManager.UpdateItemAsync(item, item, ItemUpdateType.MetadataEdit, cancellationToken);
+ await _libraryManager.UpdateItemAsync(item, item, ItemUpdateType.MetadataEdit, ct);
_logger.LogInformation("Found black bars {Crop} in {Path}", cropData, path);
}
}
@@ -75,9 +81,9 @@ namespace JellyfinPlugin.Tasks
}
}
- processed++;
- progress.Report((processed / (double)total) * 100);
- }
+ int current = Interlocked.Increment(ref processed);
+ progress.Report((current / (double)total) * 100);
+ });
}
public IEnumerable GetDefaultTriggers()
diff --git a/JellyfinPlugin/Tasks/LibraryRetroactiveScanTask.cs b/JellyfinPlugin/Tasks/LibraryRetroactiveScanTask.cs
index 4b19445..0804bdf 100644
--- a/JellyfinPlugin/Tasks/LibraryRetroactiveScanTask.cs
+++ b/JellyfinPlugin/Tasks/LibraryRetroactiveScanTask.cs
@@ -42,19 +42,28 @@ namespace JellyfinPlugin.Tasks
.ToList();
int total = items.Count;
+ if (total == 0) return;
+
int processed = 0;
+ int maxParallel = Math.Clamp(config.MaxParallelScans, 1, 16);
- foreach (var item in items)
+ var parallelOptions = new ParallelOptions
{
- cancellationToken.ThrowIfCancellationRequested();
+ MaxDegreeOfParallelism = maxParallel,
+ CancellationToken = cancellationToken
+ };
+ _logger.LogInformation("Starting Retroactive Scan of {Total} items with {MaxParallel} parallel workers.", total, maxParallel);
+
+ await Parallel.ForEachAsync(items, parallelOptions, async (item, ct) =>
+ {
if (item.ProviderIds != null &&
item.ProviderIds.TryGetValue("BlackBarCrop", out string? existingCrop) &&
!string.IsNullOrEmpty(existingCrop))
{
- processed++;
- progress.Report((processed / (double)total) * 100);
- continue;
+ int currentSkipped = Interlocked.Increment(ref processed);
+ progress.Report((currentSkipped / (double)total) * 100);
+ return;
}
var path = item.Path;
@@ -71,7 +80,7 @@ namespace JellyfinPlugin.Tasks
item.ProviderIds = new Dictionary();
}
item.ProviderIds["BlackBarCrop"] = cropData;
- await _libraryManager.UpdateItemAsync(item, item, ItemUpdateType.MetadataEdit, cancellationToken);
+ await _libraryManager.UpdateItemAsync(item, item, ItemUpdateType.MetadataEdit, ct);
_logger.LogInformation("Found black bars {Crop} in {Path}", cropData, path);
}
}
@@ -81,9 +90,9 @@ namespace JellyfinPlugin.Tasks
}
}
- processed++;
- progress.Report((processed / (double)total) * 100);
- }
+ int currentDone = Interlocked.Increment(ref processed);
+ progress.Report((currentDone / (double)total) * 100);
+ });
}
public IEnumerable GetDefaultTriggers()
diff --git a/README.md b/README.md
index 9b1981f..b7e3ea5 100644
--- a/README.md
+++ b/README.md
@@ -63,11 +63,12 @@ This plugin has been verified and tested on:
1. Go to **Dashboard** -> **Plugins** -> **Black Bar Remover**.
2. Set your **Crop Detect Limit** (Default is `0.094`). If your web-dl rips have noisy black bars that are not being cropped, increase this value to `0.15` or `0.20`.
-3. Select your preferred **Playback Mode**:
+3. Set **Max Parallel Scans** (Default is `4`, Range `1 - 16`). Controls how many video files are scanned concurrently during library tasks to maximize multi-core CPU utilization.
+4. Select your preferred **Playback Mode**:
+ - `Auto`: Lightweight CSS clipping for Web browser DirectPlay; FFmpeg transcoding for native apps.
- `Client`: Uses CSS injection in the web player.
- `Server`: Requires setting up the FFmpeg wrapper.
- - `Both`: Attempts both methods depending on the client.
-4. **Important for existing libraries**: The plugin automatically scans *new* files when they are added to Jellyfin. For your existing library, go to **Dashboard -> Scheduled Tasks** and run the **"Scan Missing Black Bars (Black Bar Remover)"** task manually. It will scan all existing movies and episodes in the background.
+5. **Important for existing libraries**: The plugin automatically scans *new* files when they are added to Jellyfin. For your existing library, go to **Dashboard -> Scheduled Tasks** and run the **"Scan Missing Black Bars (Black Bar Remover)"** task manually. It will scan all existing movies and episodes in the background.
### Activating the Server-Side Wrapper (Automatic)