119 lines
4.1 KiB
C#
119 lines
4.1 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using MediaBrowser.Controller.Entities;
|
|
using MediaBrowser.Controller.Entities.Movies;
|
|
using MediaBrowser.Controller.Entities.TV;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Controller.Plugins;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
namespace JellyfinPlugin
|
|
{
|
|
public class LibraryMonitor : IHostedService, IDisposable
|
|
{
|
|
private readonly ILibraryManager _libraryManager;
|
|
private readonly ILogger<LibraryMonitor> _logger;
|
|
private readonly SemaphoreSlim _ffmpegSemaphore = new SemaphoreSlim(2, 2);
|
|
|
|
private readonly ConcurrentDictionary<Guid, byte> _processedItems = new ConcurrentDictionary<Guid, byte>();
|
|
|
|
public LibraryMonitor(ILibraryManager libraryManager, ILogger<LibraryMonitor> logger)
|
|
{
|
|
_libraryManager = libraryManager;
|
|
_logger = logger;
|
|
}
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
_libraryManager.ItemAdded += OnItemAddedOrUpdated;
|
|
_libraryManager.ItemUpdated += OnItemAddedOrUpdated;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
private void OnItemAddedOrUpdated(object? sender, ItemChangeEventArgs e)
|
|
{
|
|
if (e.Item is Video video && (video is Movie || video is Episode))
|
|
{
|
|
if (!Plugin.Instance!.Configuration.IsEnabled) return;
|
|
|
|
if (_processedItems.ContainsKey(video.Id)) return;
|
|
|
|
if (video.ProviderIds != null &&
|
|
video.ProviderIds.TryGetValue("BlackBarCrop", out string? existingCrop) &&
|
|
!string.IsNullOrEmpty(existingCrop))
|
|
{
|
|
_processedItems.TryAdd(video.Id, 0);
|
|
return;
|
|
}
|
|
|
|
// Fire and forget, don't block library scan
|
|
_ = Task.Run(() => ProcessVideoAsync(video));
|
|
}
|
|
}
|
|
|
|
private async Task ProcessVideoAsync(Video video)
|
|
{
|
|
try
|
|
{
|
|
if (!_processedItems.TryAdd(video.Id, 0)) return;
|
|
|
|
if (video.ProviderIds != null &&
|
|
video.ProviderIds.TryGetValue("BlackBarCrop", out string? existingCrop) &&
|
|
!string.IsNullOrEmpty(existingCrop))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var path = video.Path;
|
|
if (string.IsNullOrEmpty(path) || !File.Exists(path)) return;
|
|
|
|
await _ffmpegSemaphore.WaitAsync();
|
|
try
|
|
{
|
|
double limit = Plugin.Instance!.Configuration.CropDetectLimit;
|
|
string cropData = await CropHelper.DetectCropAsync(path, limit);
|
|
if (!string.IsNullOrEmpty(cropData))
|
|
{
|
|
if (video.ProviderIds == null)
|
|
{
|
|
video.ProviderIds = new System.Collections.Generic.Dictionary<string, string>();
|
|
}
|
|
video.ProviderIds["BlackBarCrop"] = cropData;
|
|
await _libraryManager.UpdateItemAsync(video, video, ItemUpdateType.MetadataEdit, CancellationToken.None);
|
|
_logger.LogInformation("Saved crop proportions {CropData} for file {Path}", cropData, path);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ffmpegSemaphore.Release();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error processing video: {Path}", video.Path);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_libraryManager.ItemAdded -= OnItemAddedOrUpdated;
|
|
_libraryManager.ItemUpdated -= OnItemAddedOrUpdated;
|
|
_ffmpegSemaphore.Dispose();
|
|
}
|
|
}
|
|
}
|