| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Globalization; |
| | | 4 | | using System.Text; |
| | | 5 | | using System.Threading.Tasks; |
| | | 6 | | using Jellyfin.Database.Implementations.Entities; |
| | | 7 | | using MediaBrowser.Controller.Events; |
| | | 8 | | using MediaBrowser.Model.Activity; |
| | | 9 | | using MediaBrowser.Model.Globalization; |
| | | 10 | | using MediaBrowser.Model.Notifications; |
| | | 11 | | using MediaBrowser.Model.Tasks; |
| | | 12 | | using Microsoft.Extensions.Logging; |
| | | 13 | | |
| | | 14 | | namespace Jellyfin.Server.Implementations.Events.Consumers.System |
| | | 15 | | { |
| | | 16 | | /// <summary> |
| | | 17 | | /// Creates an activity log entry whenever a task is completed. |
| | | 18 | | /// </summary> |
| | | 19 | | public class TaskCompletedLogger : IEventConsumer<TaskCompletionEventArgs> |
| | | 20 | | { |
| | | 21 | | private readonly ILocalizationManager _localizationManager; |
| | | 22 | | private readonly IActivityManager _activityManager; |
| | | 23 | | |
| | | 24 | | /// <summary> |
| | | 25 | | /// Initializes a new instance of the <see cref="TaskCompletedLogger"/> class. |
| | | 26 | | /// </summary> |
| | | 27 | | /// <param name="localizationManager">The localization manager.</param> |
| | | 28 | | /// <param name="activityManager">The activity manager.</param> |
| | | 29 | | public TaskCompletedLogger(ILocalizationManager localizationManager, IActivityManager activityManager) |
| | | 30 | | { |
| | 0 | 31 | | _localizationManager = localizationManager; |
| | 0 | 32 | | _activityManager = activityManager; |
| | 0 | 33 | | } |
| | | 34 | | |
| | | 35 | | /// <inheritdoc /> |
| | | 36 | | public async Task OnEvent(TaskCompletionEventArgs eventArgs) |
| | | 37 | | { |
| | | 38 | | var result = eventArgs.Result; |
| | | 39 | | var task = eventArgs.Task; |
| | | 40 | | |
| | | 41 | | if (task.ScheduledTask is IConfigurableScheduledTask activityTask |
| | | 42 | | && !activityTask.IsLogged) |
| | | 43 | | { |
| | | 44 | | return; |
| | | 45 | | } |
| | | 46 | | |
| | | 47 | | var time = result.EndTimeUtc - result.StartTimeUtc; |
| | | 48 | | var runningTime = string.Format( |
| | | 49 | | CultureInfo.InvariantCulture, |
| | | 50 | | _localizationManager.GetLocalizedString("LabelRunningTimeValue"), |
| | | 51 | | ToUserFriendlyString(time)); |
| | | 52 | | |
| | | 53 | | if (result.Status == TaskCompletionStatus.Failed) |
| | | 54 | | { |
| | | 55 | | var vals = new List<string>(); |
| | | 56 | | |
| | | 57 | | if (!string.IsNullOrEmpty(eventArgs.Result.ErrorMessage)) |
| | | 58 | | { |
| | | 59 | | vals.Add(eventArgs.Result.ErrorMessage); |
| | | 60 | | } |
| | | 61 | | |
| | | 62 | | if (!string.IsNullOrEmpty(eventArgs.Result.LongErrorMessage)) |
| | | 63 | | { |
| | | 64 | | vals.Add(eventArgs.Result.LongErrorMessage); |
| | | 65 | | } |
| | | 66 | | |
| | | 67 | | await _activityManager.CreateAsync(new ActivityLog( |
| | | 68 | | string.Format(CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("ScheduledTaskFa |
| | | 69 | | NotificationType.TaskFailed.ToString(), |
| | | 70 | | Guid.Empty) |
| | | 71 | | { |
| | | 72 | | LogSeverity = LogLevel.Error, |
| | | 73 | | Overview = string.Join(Environment.NewLine, vals), |
| | | 74 | | ShortOverview = runningTime |
| | | 75 | | }).ConfigureAwait(false); |
| | | 76 | | } |
| | | 77 | | } |
| | | 78 | | |
| | | 79 | | private static string ToUserFriendlyString(TimeSpan span) |
| | | 80 | | { |
| | | 81 | | const int DaysInYear = 365; |
| | | 82 | | const int DaysInMonth = 30; |
| | | 83 | | |
| | | 84 | | // Get each non-zero value from TimeSpan component |
| | 0 | 85 | | var values = new List<string>(); |
| | | 86 | | |
| | | 87 | | // Number of years |
| | 0 | 88 | | int days = span.Days; |
| | 0 | 89 | | if (days >= DaysInYear) |
| | | 90 | | { |
| | 0 | 91 | | int years = days / DaysInYear; |
| | 0 | 92 | | values.Add(CreateValueString(years, "year")); |
| | 0 | 93 | | days %= DaysInYear; |
| | | 94 | | } |
| | | 95 | | |
| | | 96 | | // Number of months |
| | 0 | 97 | | if (days >= DaysInMonth) |
| | | 98 | | { |
| | 0 | 99 | | int months = days / DaysInMonth; |
| | 0 | 100 | | values.Add(CreateValueString(months, "month")); |
| | 0 | 101 | | days = days % DaysInMonth; |
| | | 102 | | } |
| | | 103 | | |
| | | 104 | | // Number of days |
| | 0 | 105 | | if (days >= 1) |
| | | 106 | | { |
| | 0 | 107 | | values.Add(CreateValueString(days, "day")); |
| | | 108 | | } |
| | | 109 | | |
| | | 110 | | // Number of hours |
| | 0 | 111 | | if (span.Hours >= 1) |
| | | 112 | | { |
| | 0 | 113 | | values.Add(CreateValueString(span.Hours, "hour")); |
| | | 114 | | } |
| | | 115 | | |
| | | 116 | | // Number of minutes |
| | 0 | 117 | | if (span.Minutes >= 1) |
| | | 118 | | { |
| | 0 | 119 | | values.Add(CreateValueString(span.Minutes, "minute")); |
| | | 120 | | } |
| | | 121 | | |
| | | 122 | | // Number of seconds (include when 0 if no other components included) |
| | 0 | 123 | | if (span.Seconds >= 1 || values.Count == 0) |
| | | 124 | | { |
| | 0 | 125 | | values.Add(CreateValueString(span.Seconds, "second")); |
| | | 126 | | } |
| | | 127 | | |
| | | 128 | | // Combine values into string |
| | 0 | 129 | | var builder = new StringBuilder(); |
| | 0 | 130 | | for (int i = 0; i < values.Count; i++) |
| | | 131 | | { |
| | 0 | 132 | | if (builder.Length > 0) |
| | | 133 | | { |
| | 0 | 134 | | builder.Append(i == values.Count - 1 ? " and " : ", "); |
| | | 135 | | } |
| | | 136 | | |
| | 0 | 137 | | builder.Append(values[i]); |
| | | 138 | | } |
| | | 139 | | |
| | | 140 | | // Return result |
| | 0 | 141 | | return builder.ToString(); |
| | | 142 | | } |
| | | 143 | | |
| | | 144 | | /// <summary> |
| | | 145 | | /// Constructs a string description of a time-span value. |
| | | 146 | | /// </summary> |
| | | 147 | | /// <param name="value">The value of this item.</param> |
| | | 148 | | /// <param name="description">The name of this item (singular form).</param> |
| | | 149 | | private static string CreateValueString(int value, string description) |
| | | 150 | | { |
| | 0 | 151 | | return string.Format( |
| | 0 | 152 | | CultureInfo.InvariantCulture, |
| | 0 | 153 | | "{0:#,##0} {1}", |
| | 0 | 154 | | value, |
| | 0 | 155 | | value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description)); |
| | | 156 | | } |
| | | 157 | | } |
| | | 158 | | } |