using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Events; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Implementations.Events.Consumers { /// /// Creates an activity log entry whenever a task is completed. /// public class TaskCompletedLogger : IEventConsumer { private readonly ILocalizationManager _localizationManager; private readonly IActivityManager _activityManager; /// /// Initializes a new instance of the class. /// /// The localization manager. /// The activity manager. public TaskCompletedLogger(ILocalizationManager localizationManager, IActivityManager activityManager) { _localizationManager = localizationManager; _activityManager = activityManager; } /// public async Task OnEvent(TaskCompletionEventArgs e) { var result = e.Result; var task = e.Task; if (task.ScheduledTask is IConfigurableScheduledTask activityTask && !activityTask.IsLogged) { return; } var time = result.EndTimeUtc - result.StartTimeUtc; var runningTime = string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("LabelRunningTimeValue"), ToUserFriendlyString(time)); if (result.Status == TaskCompletionStatus.Failed) { var vals = new List(); if (!string.IsNullOrEmpty(e.Result.ErrorMessage)) { vals.Add(e.Result.ErrorMessage); } if (!string.IsNullOrEmpty(e.Result.LongErrorMessage)) { vals.Add(e.Result.LongErrorMessage); } await _activityManager.CreateAsync(new ActivityLog( string.Format(CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), NotificationType.TaskFailed.ToString(), Guid.Empty) { LogSeverity = LogLevel.Error, Overview = string.Join(Environment.NewLine, vals), ShortOverview = runningTime }).ConfigureAwait(false); } } private static string ToUserFriendlyString(TimeSpan span) { const int DaysInYear = 365; const int DaysInMonth = 30; // Get each non-zero value from TimeSpan component var values = new List(); // Number of years int days = span.Days; if (days >= DaysInYear) { int years = days / DaysInYear; values.Add(CreateValueString(years, "year")); days %= DaysInYear; } // Number of months if (days >= DaysInMonth) { int months = days / DaysInMonth; values.Add(CreateValueString(months, "month")); days = days % DaysInMonth; } // Number of days if (days >= 1) { values.Add(CreateValueString(days, "day")); } // Number of hours if (span.Hours >= 1) { values.Add(CreateValueString(span.Hours, "hour")); } // Number of minutes if (span.Minutes >= 1) { values.Add(CreateValueString(span.Minutes, "minute")); } // Number of seconds (include when 0 if no other components included) if (span.Seconds >= 1 || values.Count == 0) { values.Add(CreateValueString(span.Seconds, "second")); } // Combine values into string var builder = new StringBuilder(); for (int i = 0; i < values.Count; i++) { if (builder.Length > 0) { builder.Append(i == values.Count - 1 ? " and " : ", "); } builder.Append(values[i]); } // Return result return builder.ToString(); } /// /// Constructs a string description of a time-span value. /// /// The value of this item. /// The name of this item (singular form). private static string CreateValueString(int value, string description) { return string.Format( CultureInfo.InvariantCulture, "{0:#,##0} {1}", value, value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description)); } } }