add activity log feature

pull/702/head
Luke Pulverenti 10 years ago
parent 0f508dab47
commit e84ba17b9f

@ -230,7 +230,7 @@ namespace MediaBrowser.Api
SortOrder = request.SortOrder,
SortBy = (request.SortBy ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
Filters = request.GetFilters().ToArray(),
Fields = request.GetItemFields().ToList()
Fields = request.GetItemFields().ToArray()
}, CancellationToken.None).Result;

@ -40,6 +40,7 @@ namespace MediaBrowser.Api.Images
[Route("/Items/{Id}/Images/{Type}", "GET")]
[Route("/Items/{Id}/Images/{Type}/{Index}", "GET")]
[Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}", "GET")]
[Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}", "HEAD")]
[Api(Description = "Gets an item image")]
public class GetItemImage : ImageRequest
{

@ -118,10 +118,11 @@
<Compile Include="ScheduledTasks\ScheduledTasksWebSocketListener.cs" />
<Compile Include="ApiEntryPoint.cs" />
<Compile Include="SearchService.cs" />
<Compile Include="SessionsService.cs" />
<Compile Include="Session\SessionsService.cs" />
<Compile Include="SimilarItemsHelper.cs" />
<Compile Include="Sync\SyncService.cs" />
<Compile Include="SystemService.cs" />
<Compile Include="System\ActivityLogService.cs" />
<Compile Include="System\SystemService.cs" />
<Compile Include="Movies\TrailersService.cs" />
<Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" />
@ -139,8 +140,8 @@
<Compile Include="UserService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VideosService.cs" />
<Compile Include="WebSocket\SessionInfoWebSocketListener.cs" />
<Compile Include="WebSocket\SystemInfoWebSocketListener.cs" />
<Compile Include="Session\SessionInfoWebSocketListener.cs" />
<Compile Include="System\SystemInfoWebSocketListener.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@ -173,4 +174,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.WebSocket
namespace MediaBrowser.Api.Session
{
/// <summary>
/// Class SessionInfoWebSocketListener

@ -10,7 +10,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api
namespace MediaBrowser.Api.Session
{
/// <summary>
/// Class GetSessions

@ -0,0 +1,44 @@
using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Querying;
using ServiceStack;
namespace MediaBrowser.Api.System
{
[Route("/System/ActivityLog/Entries", "GET", Summary = "Gets activity log entries")]
public class GetActivityLogs : IReturn<QueryResult<ActivityLogEntry>>
{
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
/// <value>The start index.</value>
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? StartIndex { get; set; }
/// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
}
[Authenticated]
public class ActivityLogService : BaseApiService
{
private readonly IActivityManager _activityManager;
public ActivityLogService(IActivityManager activityManager)
{
_activityManager = activityManager;
}
public object Get(GetActivityLogs request)
{
var result = _activityManager.GetActivityLogEntries(request.StartIndex, request.Limit);
return ToOptimizedResult(result);
}
}
}

@ -4,7 +4,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
using System.Threading.Tasks;
namespace MediaBrowser.Api.WebSocket
namespace MediaBrowser.Api.System
{
/// <summary>
/// Class SystemInfoWebSocketListener

@ -4,12 +4,13 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.System;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api
namespace MediaBrowser.Api.System
{
/// <summary>
/// Class GetSystemInfo
@ -73,7 +74,7 @@ namespace MediaBrowser.Api
/// <param name="appHost">The app host.</param>
/// <param name="appPaths">The application paths.</param>
/// <param name="fileSystem">The file system.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
/// <exception cref="ArgumentNullException">jsonSerializer</exception>
public SystemService(IServerApplicationHost appHost, IApplicationPaths appPaths, IFileSystem fileSystem)
{
_appHost = appHost;
@ -89,7 +90,7 @@ namespace MediaBrowser.Api
{
files = new DirectoryInfo(_appPaths.LogDirectoryPath)
.EnumerateFiles("*", SearchOption.AllDirectories)
.Where(i => string.Equals(i.Extension, ".txt", System.StringComparison.OrdinalIgnoreCase))
.Where(i => string.Equals(i.Extension, ".txt", global::System.StringComparison.OrdinalIgnoreCase))
.ToList();
}
catch (DirectoryNotFoundException)
@ -116,7 +117,7 @@ namespace MediaBrowser.Api
{
var file = new DirectoryInfo(_appPaths.LogDirectoryPath)
.EnumerateFiles("*", SearchOption.AllDirectories)
.First(i => string.Equals(i.Name, request.Name, System.StringComparison.OrdinalIgnoreCase));
.First(i => string.Equals(i.Name, request.Name, global::System.StringComparison.OrdinalIgnoreCase));
return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
}

@ -195,7 +195,7 @@ namespace MediaBrowser.Api
var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
var isDashboard = string.Equals(authInfo.Client, "Dashboard", StringComparison.OrdinalIgnoreCase);
if ((Request.IsLocal && isDashboard) ||
if ((Request.IsLocal && isDashboard) ||
!_config.Configuration.IsStartupWizardCompleted)
{
return Get(new GetUsers
@ -327,7 +327,7 @@ namespace MediaBrowser.Api
var revokeTask = _sessionMananger.RevokeUserTokens(user.Id.ToString("N"));
Task.WaitAll(revokeTask);
var task = _userManager.DeleteUser(user);
Task.WaitAll(task);
@ -374,8 +374,17 @@ namespace MediaBrowser.Api
auth.DeviceId = "Unknown device id";
}
var result = _sessionMananger.AuthenticateNewSession(request.Username, request.Password, auth.Client, auth.Version,
auth.DeviceId, auth.Device, Request.RemoteIp, Request.IsLocal).Result;
var result = _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
{
App = auth.Client,
AppVersion = auth.Version,
DeviceId = auth.DeviceId,
DeviceName = auth.Device,
Password = request.Password,
RemoteEndPoint = Request.RemoteIp,
Username = request.Username
}, Request.IsLocal).Result;
return ToOptimizedResult(result);
}
@ -457,8 +466,8 @@ namespace MediaBrowser.Api
Task.WaitAll(revokeTask);
}
var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ?
_userManager.UpdateUser(user) :
var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ?
_userManager.UpdateUser(user) :
_userManager.RenameUser(user, dtoUser.Name);
Task.WaitAll(task);

@ -28,6 +28,11 @@ namespace MediaBrowser.Common.Implementations.Configuration
/// </summary>
public event EventHandler<EventArgs> ConfigurationUpdated;
/// <summary>
/// Occurs when [configuration updating].
/// </summary>
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
/// <summary>
/// Occurs when [named configuration updated].
/// </summary>
@ -217,6 +222,13 @@ namespace MediaBrowser.Common.Implementations.Configuration
throw new ArgumentException("Expected configuration type is " + configurationType.Name);
}
EventHelper.FireEventIfNotNull(NamedConfigurationUpdating, this, new ConfigurationUpdateEventArgs
{
Key = key,
NewConfiguration = configuration
}, Logger);
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
var path = GetConfigurationFile(key);

@ -547,6 +547,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
if (ex != null)
{
result.ErrorMessage = ex.Message;
result.LongErrorMessage = ex.StackTrace;
}
var path = GetHistoryFilePath();

@ -6,6 +6,11 @@ namespace MediaBrowser.Common.Configuration
{
public interface IConfigurationManager
{
/// <summary>
/// Occurs when [configuration updating].
/// </summary>
event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
/// <summary>
/// Occurs when [configuration updated].
/// </summary>

@ -13,4 +13,9 @@
/// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
bool IsEnabled { get; }
}
public interface IScheduledTaskActivityLog
{
bool IsActivityLogged { get; }
}
}

@ -0,0 +1,17 @@
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using System;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Activity
{
public interface IActivityManager
{
event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
Task Create(ActivityLogEntry entry);
QueryResult<ActivityLogEntry> GetActivityLogEntries(int? startIndex, int? limit);
}
}

@ -0,0 +1,13 @@
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Querying;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Activity
{
public interface IActivityRepository
{
Task Create(ActivityLogEntry entry);
QueryResult<ActivityLogEntry> GetActivityLogEntries(int? startIndex, int? limit);
}
}

@ -1,7 +1,5 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Events;
using System;
namespace MediaBrowser.Controller.Configuration
{
@ -10,11 +8,6 @@ namespace MediaBrowser.Controller.Configuration
/// </summary>
public interface IServerConfigurationManager : IConfigurationManager
{
/// <summary>
/// Occurs when [configuration updating].
/// </summary>
event EventHandler<GenericEventArgs<ServerConfiguration>> ConfigurationUpdating;
/// <summary>
/// Gets the application paths.
/// </summary>

@ -102,17 +102,9 @@ namespace MediaBrowser.Controller.Entities.Movies
var totalItems = items.Count;
var percentages = new Dictionary<Guid, double>(totalItems);
var tasks = new List<Task>();
// Refresh songs
foreach (var item in items)
{
if (tasks.Count >= 3)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
}
cancellationToken.ThrowIfCancellationRequested();
var innerProgress = new ActionableProgress<double>();
@ -132,13 +124,9 @@ namespace MediaBrowser.Controller.Entities.Movies
});
// Avoid implicitly captured closure
var taskChild = item;
tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
await RefreshItem(item, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);

@ -31,6 +31,7 @@ namespace MediaBrowser.Controller.Library
event EventHandler<GenericEventArgs<User>> UserCreated;
event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
/// <summary>
/// Updates the configuration.

@ -269,7 +269,7 @@ namespace MediaBrowser.Controller.Library
if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName);
//logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName);
continue;
}

@ -68,6 +68,8 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Activity\IActivityManager.cs" />
<Compile Include="Activity\IActivityRepository.cs" />
<Compile Include="Channels\ChannelFolderItem.cs" />
<Compile Include="Channels\ChannelItemInfo.cs" />
<Compile Include="Channels\ChannelItemResult.cs" />
@ -241,6 +243,7 @@
<Compile Include="Security\AuthenticationInfoQuery.cs" />
<Compile Include="Security\IAuthenticationRepository.cs" />
<Compile Include="Security\IEncryptionManager.cs" />
<Compile Include="Session\AuthenticationRequest.cs" />
<Compile Include="Subtitles\ISubtitleManager.cs" />
<Compile Include="Subtitles\ISubtitleProvider.cs" />
<Compile Include="Providers\ItemIdentifier.cs" />
@ -320,6 +323,7 @@
<Compile Include="Sorting\IUserBaseItemComparer.cs" />
<Compile Include="Providers\BaseItemXmlParser.cs" />
<Compile Include="Sorting\SortExtensions.cs" />
<Compile Include="Subtitles\SubtitleDownloadEventArgs.cs" />
<Compile Include="Subtitles\SubtitleResponse.cs" />
<Compile Include="Subtitles\SubtitleSearchRequest.cs" />
<Compile Include="Sync\ICloudSyncProvider.cs" />
@ -360,4 +364,4 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -16,10 +16,6 @@ namespace MediaBrowser.Controller.Notifications
/// </summary>
event EventHandler<NotificationUpdateEventArgs> NotificationAdded;
/// <summary>
/// Occurs when [notification updated].
/// </summary>
event EventHandler<NotificationUpdateEventArgs> NotificationUpdated;
/// <summary>
/// Occurs when [notifications marked read].
/// </summary>
event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
@ -37,14 +33,6 @@ namespace MediaBrowser.Controller.Notifications
/// <returns>NotificationResult.</returns>
NotificationResult GetNotifications(NotificationQuery query);
/// <summary>
/// Gets the notification.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="userId">The user id.</param>
/// <returns>Notification.</returns>
Notification GetNotification(string id, string userId);
/// <summary>
/// Adds the notification.
/// </summary>

@ -0,0 +1,14 @@

namespace MediaBrowser.Controller.Session
{
public class AuthenticationRequest
{
public string Username { get; set; }
public string Password { get; set; }
public string App { get; set; }
public string AppVersion { get; set; }
public string DeviceId { get; set; }
public string DeviceName { get; set; }
public string RemoteEndPoint { get; set; }
}
}

@ -1,6 +1,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Users;
using System;
@ -46,6 +47,16 @@ namespace MediaBrowser.Controller.Session
/// Occurs when [capabilities changed].
/// </summary>
event EventHandler<SessionEventArgs> CapabilitiesChanged;
/// <summary>
/// Occurs when [authentication failed].
/// </summary>
event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
/// <summary>
/// Occurs when [authentication succeeded].
/// </summary>
event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded;
/// <summary>
/// Gets the sessions.
@ -211,23 +222,10 @@ namespace MediaBrowser.Controller.Session
/// <summary>
/// Authenticates the new session.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
/// <param name="clientType">Type of the client.</param>
/// <param name="appVersion">The application version.</param>
/// <param name="deviceId">The device identifier.</param>
/// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="request">The request.</param>
/// <param name="isLocal">if set to <c>true</c> [is local].</param>
/// <returns>Task{SessionInfo}.</returns>
Task<AuthenticationResult> AuthenticateNewSession(string username,
string password,
string clientType,
string appVersion,
string deviceId,
string deviceName,
string remoteEndPoint,
bool isLocal);
Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request, bool isLocal);
/// <summary>
/// Reports the capabilities.

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -8,6 +9,16 @@ namespace MediaBrowser.Controller.Subtitles
{
public interface ISubtitleManager
{
/// <summary>
/// Occurs when [subtitle download failure].
/// </summary>
event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure;
/// <summary>
/// Occurs when [subtitles downloaded].
/// </summary>
event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded;
/// <summary>
/// Adds the parts.
/// </summary>
@ -31,7 +42,7 @@ namespace MediaBrowser.Controller.Subtitles
/// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns>
Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request,
Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request,
CancellationToken cancellationToken);
/// <summary>
@ -41,8 +52,8 @@ namespace MediaBrowser.Controller.Subtitles
/// <param name="subtitleId">The subtitle identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task DownloadSubtitles(Video video,
string subtitleId,
Task DownloadSubtitles(Video video,
string subtitleId,
CancellationToken cancellationToken);
/// <summary>

@ -0,0 +1,27 @@
using System;
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Subtitles
{
public class SubtitleDownloadEventArgs
{
public BaseItem Item { get; set; }
public string Format { get; set; }
public string Language { get; set; }
public bool IsForced { get; set; }
public string Provider { get; set; }
}
public class SubtitleDownloadFailureEventArgs
{
public BaseItem Item { get; set; }
public string Provider { get; set; }
public Exception Exception { get; set; }
}
}

@ -27,7 +27,7 @@ namespace MediaBrowser.LocalMetadata
var path = file.FullName;
await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
//await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
@ -46,7 +46,7 @@ namespace MediaBrowser.LocalMetadata
}
finally
{
XmlProviderUtils.XmlParsingResourcePool.Release();
//XmlProviderUtils.XmlParsingResourcePool.Release();
}
return result;

@ -83,6 +83,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\MediaBrowser.Model\Activity\ActivityLogEntry.cs">
<Link>Activity\ActivityLogEntry.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\ApiClient\ApiClientExtensions.cs">
<Link>ApiClient\ApiClientExtensions.cs</Link>
</Compile>
@ -173,9 +176,6 @@
<Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs">
<Link>Configuration\ServerConfiguration.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
<Link>Configuration\SubtitleOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
<Link>Configuration\SubtitlePlaybackMode.cs</Link>
</Compile>
@ -728,6 +728,9 @@
<Compile Include="..\MediaBrowser.Model\Providers\RemoteSubtitleInfo.cs">
<Link>Providers\RemoteSubtitleInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Providers\SubtitleOptions.cs">
<Link>Providers\SubtitleOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\AllThemeMediaResult.cs">
<Link>Querying\AllThemeMediaResult.cs</Link>
</Compile>

@ -52,6 +52,9 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\mediabrowser.model\activity\ActivityLogEntry.cs">
<Link>Activity\ActivityLogEntry.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs">
<Link>ApiClient\GeneralCommandEventArgs.cs</Link>
</Compile>
@ -136,9 +139,6 @@
<Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs">
<Link>Configuration\ServerConfiguration.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
<Link>Configuration\SubtitleOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
<Link>Configuration\SubtitlePlaybackMode.cs</Link>
</Compile>
@ -685,6 +685,9 @@
<Compile Include="..\MediaBrowser.Model\Providers\RemoteSubtitleInfo.cs">
<Link>Providers\RemoteSubtitleInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Providers\SubtitleOptions.cs">
<Link>Providers\SubtitleOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\AllThemeMediaResult.cs">
<Link>Querying\AllThemeMediaResult.cs</Link>
</Compile>

@ -0,0 +1,62 @@
using MediaBrowser.Model.Logging;
using System;
namespace MediaBrowser.Model.Activity
{
public class ActivityLogEntry
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the overview.
/// </summary>
/// <value>The overview.</value>
public string Overview { get; set; }
/// <summary>
/// Gets or sets the short overview.
/// </summary>
/// <value>The short overview.</value>
public string ShortOverview { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string Type { get; set; }
/// <summary>
/// Gets or sets the item identifier.
/// </summary>
/// <value>The item identifier.</value>
public string ItemId { get; set; }
/// <summary>
/// Gets or sets the date.
/// </summary>
/// <value>The date.</value>
public DateTime Date { get; set; }
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
/// <summary>
/// Gets or sets the log severity.
/// </summary>
/// <value>The log severity.</value>
public LogSeverity Severity { get; set; }
}
}

@ -623,7 +623,7 @@ namespace MediaBrowser.Model.ApiClient
Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
/// <summary>
/// Instructs antoher client to browse to a library item.
/// Instructs another client to browse to a library item.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="itemId">The id of the item to browse to.</param>

@ -1,6 +1,5 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using System.Collections.Generic;
namespace MediaBrowser.Model.Channels
{
@ -39,13 +38,13 @@ namespace MediaBrowser.Model.Channels
public SortOrder? SortOrder { get; set; }
public string[] SortBy { get; set; }
public ItemFilter[] Filters { get; set; }
public List<ItemFields> Fields { get; set; }
public ItemFields[] Fields { get; set; }
public ChannelItemQuery()
{
Filters = new ItemFilter[] { };
SortBy = new string[] { };
Fields = new List<ItemFields>();
Fields = new ItemFields[] { };
}
}

@ -2,6 +2,7 @@
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Notifications;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Model.Configuration
{
@ -285,8 +286,6 @@ namespace MediaBrowser.Model.Configuration
new MetadataOptions(0, 1280) {ItemType = "Season"}
};
SubtitleOptions = new SubtitleOptions();
}
}
}

@ -13,5 +13,21 @@ namespace MediaBrowser.Model.Events
/// </summary>
/// <value>The argument.</value>
public T Argument { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
/// </summary>
/// <param name="arg">The argument.</param>
public GenericEventArgs(T arg)
{
Argument = arg;
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
/// </summary>
public GenericEventArgs()
{
}
}
}

@ -59,6 +59,7 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Activity\ActivityLogEntry.cs" />
<Compile Include="ApiClient\HttpResponseEventArgs.cs" />
<Compile Include="ApiClient\IApiClient.cs" />
<Compile Include="ApiClient\ApiClientExtensions.cs" />
@ -99,7 +100,7 @@
<Compile Include="Configuration\PathSubstitution.cs" />
<Compile Include="Notifications\SendToUserType.cs" />
<Compile Include="Configuration\ServerConfiguration.cs" />
<Compile Include="Configuration\SubtitleOptions.cs" />
<Compile Include="Providers\SubtitleOptions.cs" />
<Compile Include="Configuration\UnratedItem.cs" />
<Compile Include="Dlna\AudioOptions.cs" />
<Compile Include="Dlna\CodecProfile.cs" />
@ -378,4 +379,4 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -1,4 +1,4 @@
namespace MediaBrowser.Model.Configuration
namespace MediaBrowser.Model.Providers
{
public class SubtitleOptions
{

@ -42,5 +42,11 @@ namespace MediaBrowser.Model.Tasks
/// </summary>
/// <value>The error message.</value>
public string ErrorMessage { get; set; }
/// <summary>
/// Gets or sets the long error message.
/// </summary>
/// <value>The long error message.</value>
public string LongErrorMessage { get; set; }
}
}

@ -152,6 +152,7 @@
<Compile Include="Manager\ProviderUtils.cs" />
<Compile Include="Studios\StudiosImageProvider.cs" />
<Compile Include="Studios\StudioMetadataService.cs" />
<Compile Include="Subtitles\ConfigurationExtension.cs" />
<Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
<Compile Include="Subtitles\SubtitleManager.cs" />
<Compile Include="TV\EpisodeMetadataService.cs" />
@ -213,4 +214,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -13,10 +13,12 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
@ -464,6 +466,11 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
private SubtitleOptions GetOptions()
{
return _config.GetConfiguration<SubtitleOptions>("subtitles");
}
/// <summary>
/// Adds the external subtitles.
/// </summary>
@ -484,9 +491,11 @@ namespace MediaBrowser.Providers.MediaInfo
var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default ||
options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
if (enableSubtitleDownloading && (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
var subtitleOptions = GetOptions();
if (enableSubtitleDownloading && (subtitleOptions.DownloadEpisodeSubtitles &&
video is Episode) ||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
(subtitleOptions.DownloadMovieSubtitles &&
video is Movie))
{
var downloadedLanguages = await new SubtitleDownloader(_logger,
@ -494,9 +503,9 @@ namespace MediaBrowser.Providers.MediaInfo
.DownloadSubtitles(video,
currentStreams,
externalSubtitleStreams,
_config.Configuration.SubtitleOptions.SkipIfGraphicalSubtitlesPresent,
_config.Configuration.SubtitleOptions.SkipIfAudioTrackMatches,
_config.Configuration.SubtitleOptions.DownloadLanguages,
subtitleOptions.SkipIfGraphicalSubtitlesPresent,
subtitleOptions.SkipIfAudioTrackMatches,
subtitleOptions.DownloadLanguages,
cancellationToken).ConfigureAwait(false);
// Rescan

@ -1,10 +1,12 @@
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
@ -12,6 +14,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.MediaInfo
{
@ -45,8 +48,15 @@ namespace MediaBrowser.Providers.MediaInfo
get { return "Library"; }
}
private SubtitleOptions GetOptions()
{
return _config.GetConfiguration<SubtitleOptions>("subtitles");
}
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
var options = GetOptions();
var videos = _libraryManager.RootFolder
.RecursiveChildren
.OfType<Video>()
@ -57,9 +67,9 @@ namespace MediaBrowser.Providers.MediaInfo
return false;
}
return (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
return (options.DownloadEpisodeSubtitles &&
i is Episode) ||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
(options.DownloadMovieSubtitles &&
i is Movie);
})
.ToList();
@ -70,7 +80,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
try
{
await DownloadSubtitles(video, cancellationToken).ConfigureAwait(false);
await DownloadSubtitles(video, options, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -86,11 +96,11 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
private async Task DownloadSubtitles(Video video, CancellationToken cancellationToken)
private async Task DownloadSubtitles(Video video, SubtitleOptions options, CancellationToken cancellationToken)
{
if ((_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
if ((options.DownloadEpisodeSubtitles &&
video is Episode) ||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
(options.DownloadMovieSubtitles &&
video is Movie))
{
var mediaStreams = video.GetMediaSources(false).First().MediaStreams;
@ -103,9 +113,9 @@ namespace MediaBrowser.Providers.MediaInfo
.DownloadSubtitles(video,
currentStreams,
externalSubtitleStreams,
_config.Configuration.SubtitleOptions.SkipIfGraphicalSubtitlesPresent,
_config.Configuration.SubtitleOptions.SkipIfAudioTrackMatches,
_config.Configuration.SubtitleOptions.DownloadLanguages,
options.SkipIfGraphicalSubtitlesPresent,
options.SkipIfAudioTrackMatches,
options.DownloadLanguages,
cancellationToken).ConfigureAwait(false);
// Rescan

@ -0,0 +1,29 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Providers;
using System.Collections.Generic;
namespace MediaBrowser.Providers.Subtitles
{
public static class ConfigurationExtension
{
public static SubtitleOptions GetSubtitleConfiguration(this IConfigurationManager manager)
{
return manager.GetConfiguration<SubtitleOptions>("subtitles");
}
}
public class SubtitleConfigurationFactory : IConfigurationFactory
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
return new List<ConfigurationStore>
{
new ConfigurationStore
{
Key = "subtitles",
ConfigurationType = typeof (SubtitleOptions)
}
};
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Providers;
@ -6,7 +7,6 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using OpenSubtitlesHandler;
@ -45,16 +45,21 @@ namespace MediaBrowser.Providers.Subtitles
_config = config;
_encryption = encryption;
_config.ConfigurationUpdating += _config_ConfigurationUpdating;
_config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
// Reset the count every 24 hours
_dailyTimer = new Timer(state => _dailyDownloadCount = 0, null, TimeSpan.FromHours(24), TimeSpan.FromHours(24));
}
private const string PasswordHashPrefix = "h:";
void _config_ConfigurationUpdating(object sender, GenericEventArgs<ServerConfiguration> e)
void _config_NamedConfigurationUpdating(object sender, ConfigurationUpdateEventArgs e)
{
var options = e.Argument.SubtitleOptions;
if (!string.Equals(e.Key, "subtitles", StringComparison.OrdinalIgnoreCase))
{
return;
}
var options = (SubtitleOptions)e.NewConfiguration;
if (options != null &&
!string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) &&
@ -85,12 +90,19 @@ namespace MediaBrowser.Providers.Subtitles
get { return "Open Subtitles"; }
}
private SubtitleOptions GetOptions()
{
return _config.GetSubtitleConfiguration();
}
public IEnumerable<VideoContentType> SupportedMediaTypes
{
get
{
if (string.IsNullOrWhiteSpace(_config.Configuration.SubtitleOptions.OpenSubtitlesUsername) ||
string.IsNullOrWhiteSpace(_config.Configuration.SubtitleOptions.OpenSubtitlesPasswordHash))
var options = GetOptions();
if (string.IsNullOrWhiteSpace(options.OpenSubtitlesUsername) ||
string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash))
{
return new VideoContentType[] { };
}
@ -101,10 +113,11 @@ namespace MediaBrowser.Providers.Subtitles
public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
{
return GetSubtitlesInternal(id, cancellationToken);
return GetSubtitlesInternal(id, GetOptions(), cancellationToken);
}
private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
SubtitleOptions options,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(id))
@ -113,7 +126,7 @@ namespace MediaBrowser.Providers.Subtitles
}
if (_dailyDownloadCount >= MaxDownloadsPerDay &&
!_config.Configuration.SubtitleOptions.IsOpenSubtitleVipAccount)
!options.IsOpenSubtitleVipAccount)
{
throw new InvalidOperationException("Open Subtitle's daily download limit has been exceeded. Please try again tomorrow.");
}
@ -167,7 +180,7 @@ namespace MediaBrowser.Providers.Subtitles
return;
}
var options = _config.Configuration.SubtitleOptions ?? new SubtitleOptions();
var options = GetOptions();
var user = options.OpenSubtitlesUsername ?? string.Empty;
var password = DecryptPassword(options.OpenSubtitlesPasswordHash);
@ -289,7 +302,7 @@ namespace MediaBrowser.Providers.Subtitles
public void Dispose()
{
_config.ConfigurationUpdating -= _config_ConfigurationUpdating;
_config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating;
if (_dailyTimer != null)
{

@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@ -28,6 +29,9 @@ namespace MediaBrowser.Providers.Subtitles
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
public event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded;
public event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure;
public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IItemRepository itemRepo)
{
_logger = logger;
@ -100,35 +104,63 @@ namespace MediaBrowser.Providers.Subtitles
string subtitleId,
CancellationToken cancellationToken)
{
var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
var parts = subtitleId.Split(new[] { '_' }, 2);
var provider = GetProvider(parts.First());
using (var stream = response.Stream)
try
{
var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
_fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
if (response.IsForced)
using (var stream = response.Stream)
{
savePath += ".forced";
}
var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
_fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
savePath += "." + response.Format.ToLower();
if (response.IsForced)
{
savePath += ".forced";
}
_logger.Info("Saving subtitles to {0}", savePath);
savePath += "." + response.Format.ToLower();
_monitor.ReportFileSystemChangeBeginning(savePath);
_logger.Info("Saving subtitles to {0}", savePath);
try
{
using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
_monitor.ReportFileSystemChangeBeginning(savePath);
try
{
using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
}
EventHelper.FireEventIfNotNull(SubtitlesDownloaded, this, new SubtitleDownloadEventArgs
{
Item = video,
Format = response.Format,
Language = response.Language,
IsForced = response.IsForced,
Provider = provider.Name
}, _logger);
}
finally
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
_monitor.ReportFileSystemChangeComplete(savePath, false);
}
}
finally
}
catch (Exception ex)
{
EventHelper.FireEventIfNotNull(SubtitleDownloadFailure, this, new SubtitleDownloadFailureEventArgs
{
_monitor.ReportFileSystemChangeComplete(savePath, false);
}
Item = video,
Exception = ex,
Provider = provider.Name
}, _logger);
throw;
}
}
@ -267,5 +299,6 @@ namespace MediaBrowser.Providers.Subtitles
Id = GetProviderId(i.Name)
});
}
}
}

@ -0,0 +1,40 @@
using MediaBrowser.Common.Events;
using MediaBrowser.Controller.Activity;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using System;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Activity
{
public class ActivityManager : IActivityManager
{
public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
private readonly IActivityRepository _repo;
private readonly ILogger _logger;
public ActivityManager(ILogger logger, IActivityRepository repo)
{
_logger = logger;
_repo = repo;
}
public async Task Create(ActivityLogEntry entry)
{
entry.Id = Guid.NewGuid().ToString("N");
entry.Date = DateTime.UtcNow;
await _repo.Create(entry).ConfigureAwait(false);
EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs<ActivityLogEntry>(entry), _logger);
}
public QueryResult<ActivityLogEntry> GetActivityLogEntries(int? startIndex, int? limit)
{
return _repo.GetActivityLogEntries(startIndex, limit);
}
}
}

@ -0,0 +1,293 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Activity;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Server.Implementations.Persistence;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Activity
{
public class ActivityRepository : IActivityRepository, IDisposable
{
private IDbConnection _connection;
private readonly ILogger _logger;
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
private readonly IServerApplicationPaths _appPaths;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private IDbCommand _saveActivityCommand;
public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths)
{
_logger = logger;
_appPaths = appPaths;
}
public async Task Initialize()
{
var dbFile = Path.Combine(_appPaths.DataPath, "activitylog.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
string[] queries = {
"create table if not exists ActivityLogEntries (Id GUID PRIMARY KEY, Name TEXT, Overview TEXT, ShortOverview TEXT, Type TEXT, ItemId TEXT, UserId TEXT, DateCreated DATETIME, LogSeverity TEXT)",
"create index if not exists idx_ActivityLogEntries on ActivityLogEntries(Id)",
//pragmas
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
PrepareStatements();
}
private void PrepareStatements()
{
_saveActivityCommand = _connection.CreateCommand();
_saveActivityCommand.CommandText = "replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)";
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Id");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Name");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Overview");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@ShortOverview");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Type");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@ItemId");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@UserId");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@DateCreated");
_saveActivityCommand.Parameters.Add(_saveActivityCommand, "@LogSeverity");
}
private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
public Task Create(ActivityLogEntry entry)
{
return Update(entry);
}
public async Task Update(ActivityLogEntry entry)
{
if (entry == null)
{
throw new ArgumentNullException("entry");
}
await _writeLock.WaitAsync().ConfigureAwait(false);
IDbTransaction transaction = null;
try
{
transaction = _connection.BeginTransaction();
var index = 0;
_saveActivityCommand.GetParameter(index++).Value = new Guid(entry.Id);
_saveActivityCommand.GetParameter(index++).Value = entry.Name;
_saveActivityCommand.GetParameter(index++).Value = entry.Overview;
_saveActivityCommand.GetParameter(index++).Value = entry.ShortOverview;
_saveActivityCommand.GetParameter(index++).Value = entry.Type;
_saveActivityCommand.GetParameter(index++).Value = entry.ItemId;
_saveActivityCommand.GetParameter(index++).Value = entry.UserId;
_saveActivityCommand.GetParameter(index++).Value = entry.Date;
_saveActivityCommand.GetParameter(index++).Value = entry.Severity.ToString();
_saveActivityCommand.Transaction = transaction;
_saveActivityCommand.ExecuteNonQuery();
transaction.Commit();
}
catch (OperationCanceledException)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
catch (Exception e)
{
_logger.ErrorException("Failed to save record:", e);
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
_writeLock.Release();
}
}
public QueryResult<ActivityLogEntry> GetActivityLogEntries(int? startIndex, int? limit)
{
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = BaseActivitySelectText;
var whereClauses = new List<string>();
if (startIndex.HasValue && startIndex.Value > 0)
{
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries ORDER BY DateCreated DESC LIMIT {0})",
startIndex.Value.ToString(_usCulture)));
}
if (whereClauses.Count > 0)
{
cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
}
cmd.CommandText += " ORDER BY DateCreated DESC";
if (limit.HasValue)
{
cmd.CommandText += " LIMIT " + limit.Value.ToString(_usCulture);
}
cmd.CommandText += "; select count (Id) from ActivityLogEntries";
var list = new List<ActivityLogEntry>();
var count = 0;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
{
list.Add(GetEntry(reader));
}
if (reader.NextResult() && reader.Read())
{
count = reader.GetInt32(0);
}
}
return new QueryResult<ActivityLogEntry>()
{
Items = list.ToArray(),
TotalRecordCount = count
};
}
}
private ActivityLogEntry GetEntry(IDataReader reader)
{
var index = 0;
var info = new ActivityLogEntry
{
Id = reader.GetGuid(index).ToString("N")
};
index++;
if (!reader.IsDBNull(index))
{
info.Name = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
info.Overview = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
info.ShortOverview = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
info.Type = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
info.ItemId = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
info.UserId = reader.GetString(index);
}
index++;
info.Date = reader.GetDateTime(index).ToUniversalTime();
index++;
if (!reader.IsDBNull(index))
{
info.Severity = (LogSeverity)Enum.Parse(typeof(LogSeverity), reader.GetString(index), true);
}
return info;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private readonly object _disposeLock = new object();
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
try
{
lock (_disposeLock)
{
if (_connection != null)
{
if (_connection.IsOpen())
{
_connection.Close();
}
_connection.Dispose();
_connection = null;
}
}
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing database", ex);
}
}
}
}
}

@ -0,0 +1,543 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Implementations.Logging;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Updates;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MediaBrowser.Server.Implementations.EntryPoints
{
public class ActivityLogEntryPoint : IServerEntryPoint
{
private readonly IInstallationManager _installationManager;
//private readonly ILogManager _logManager;
private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
private readonly ITaskManager _taskManager;
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
private readonly ILibraryManager _libraryManager;
private readonly ISubtitleManager _subManager;
private readonly IUserManager _userManager;
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
public ActivityLogEntryPoint(ISessionManager sessionManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost)
{
//_logger = _logManager.GetLogger("ActivityLogEntryPoint");
_sessionManager = sessionManager;
_taskManager = taskManager;
_activityManager = activityManager;
_localization = localization;
_installationManager = installationManager;
_libraryManager = libraryManager;
_subManager = subManager;
_userManager = userManager;
_config = config;
//_logManager = logManager;
_appHost = appHost;
}
public void Run()
{
_taskManager.TaskExecuting += _taskManager_TaskExecuting;
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
_installationManager.PluginInstalled += _installationManager_PluginInstalled;
_installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
_installationManager.PluginUpdated += _installationManager_PluginUpdated;
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
_libraryManager.ItemRemoved += _libraryManager_ItemRemoved;
_sessionManager.SessionStarted += _sessionManager_SessionStarted;
_sessionManager.AuthenticationFailed += _sessionManager_AuthenticationFailed;
_sessionManager.AuthenticationSucceeded += _sessionManager_AuthenticationSucceeded;
_sessionManager.SessionEnded += _sessionManager_SessionEnded;
_sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
_sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
_subManager.SubtitlesDownloaded += _subManager_SubtitlesDownloaded;
_subManager.SubtitleDownloadFailure += _subManager_SubtitleDownloadFailure;
_userManager.UserCreated += _userManager_UserCreated;
_userManager.UserPasswordChanged += _userManager_UserPasswordChanged;
_userManager.UserDeleted += _userManager_UserDeleted;
_userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
//_logManager.LoggerLoaded += _logManager_LoggerLoaded;
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
}
void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureForItem"), Notifications.Notifications.GetItemName(e.Item)),
Type = "SubtitleDownloadFailure",
ItemId = e.Item.Id.ToString("N"),
ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider),
Overview = LogHelper.GetLogMessage(e.Exception).ToString()
});
}
void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
{
var item = e.MediaInfo;
if (item == null)
{
//_logger.Warn("PlaybackStopped reported with null media info.");
return;
}
if (e.Users.Count == 0)
{
return;
}
var username = e.Users.First().Name;
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), username, item.Name),
Type = "PlaybackStopped",
ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName)
});
}
void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
{
var item = e.MediaInfo;
if (item == null)
{
//_logger.Warn("PlaybackStart reported with null media info.");
return;
}
if (e.Users.Count == 0)
{
return;
}
var username = e.Users.First().Name;
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), username, item.Name),
Type = "PlaybackStart",
ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName)
});
}
void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
{
string name;
var session = e.SessionInfo;
if (string.IsNullOrWhiteSpace(session.UserName))
{
name = string.Format(_localization.GetLocalizedString("DeviceOfflineWithName"), session.DeviceName);
}
else
{
name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName);
}
CreateLogEntry(new ActivityLogEntry
{
Name = name,
Type = "SessionEnded",
ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint)
});
}
void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationRequest> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), e.Argument.Username),
Type = "AuthenticationSucceeded"
});
}
void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username),
Type = "AuthenticationFailed"
});
}
void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = _localization.GetLocalizedString("MessageApplicationUpdated"),
Type = "ApplicationUpdated",
ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr),
Overview = e.Argument.description
});
}
void _logManager_LoggerLoaded(object sender, EventArgs e)
{
}
void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key),
Type = "NamedConfigurationUpdated"
});
}
void _config_ConfigurationUpdated(object sender, EventArgs e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"),
Type = "ServerConfigurationUpdated"
});
}
void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserConfigurationUpdatedWithName"), e.Argument.Name),
Type = "UserConfigurationUpdated"
});
}
void _userManager_UserDeleted(object sender, GenericEventArgs<User> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name),
Type = "UserDeleted"
});
}
void _userManager_UserPasswordChanged(object sender, GenericEventArgs<User> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
Type = "UserPasswordChanged"
});
}
void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
Type = "UserCreated"
});
}
void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)),
Type = "SubtitlesDownloaded",
ItemId = e.Item.Id.ToString("N"),
ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider)
});
}
void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
{
string name;
var session = e.SessionInfo;
if (string.IsNullOrWhiteSpace(session.UserName))
{
name = string.Format(_localization.GetLocalizedString("DeviceOnlineWithName"), session.DeviceName);
}
else
{
name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName);
}
CreateLogEntry(new ActivityLogEntry
{
Name = name,
Type = "SessionStarted",
ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint)
});
}
void _libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
{
if (e.Item is LiveTvProgram || e.Item is IChannelItem)
{
return;
}
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ItemRemovedWithName"), Notifications.Notifications.GetItemName(e.Item)),
Type = "ItemRemoved"
});
}
void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
{
if (e.Item is LiveTvProgram || e.Item is IChannelItem)
{
return;
}
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ItemAddedWithName"), Notifications.Notifications.GetItemName(e.Item)),
Type = "ItemAdded",
ItemId = e.Item.Id.ToString("N")
});
}
void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
Type = "PluginUpdated",
ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.versionStr),
Overview = e.Argument.Item2.description
});
}
void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
Type = "PluginUninstalled"
});
}
void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
Type = "PluginInstalled",
ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr)
});
}
void _taskManager_TaskExecuting(object sender, GenericEventArgs<IScheduledTaskWorker> e)
{
var task = e.Argument;
var activityTask = task.ScheduledTask as IScheduledTaskActivityLog;
if (activityTask != null && !activityTask.IsActivityLogged)
{
return;
}
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskStartedWithName"), task.Name),
Type = "ScheduledTaskStarted"
});
}
void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
{
var result = e.Result;
var task = e.Task;
var activityTask = task.ScheduledTask as IScheduledTaskActivityLog;
if (activityTask != null && !activityTask.IsActivityLogged)
{
return;
}
var time = result.EndTimeUtc - result.StartTimeUtc;
var runningTime = string.Format(_localization.GetLocalizedString("LabelRunningTimeValue"), ToUserFriendlyString(time));
if (result.Status == TaskCompletionStatus.Cancelled)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskCancelledWithName"), task.Name),
Type = "ScheduledTaskCancelled",
ShortOverview = runningTime
});
}
else if (result.Status == TaskCompletionStatus.Completed)
{
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskCompletedWithName"), task.Name),
Type = "ScheduledTaskCompleted",
ShortOverview = runningTime
});
}
else if (result.Status == TaskCompletionStatus.Failed)
{
var vals = new List<string>();
if (!string.IsNullOrWhiteSpace(e.Result.ErrorMessage))
{
vals.Add(e.Result.ErrorMessage);
}
if (!string.IsNullOrWhiteSpace(e.Result.LongErrorMessage))
{
vals.Add(e.Result.LongErrorMessage);
}
CreateLogEntry(new ActivityLogEntry
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
Type = "ScheduledTaskFailed",
Overview = string.Join(Environment.NewLine, vals.ToArray()),
ShortOverview = runningTime
});
}
}
private async void CreateLogEntry(ActivityLogEntry entry)
{
try
{
await _activityManager.Create(entry).ConfigureAwait(false);
}
catch
{
// Logged at lower levels
}
}
public void Dispose()
{
_taskManager.TaskExecuting -= _taskManager_TaskExecuting;
_taskManager.TaskCompleted -= _taskManager_TaskCompleted;
_installationManager.PluginInstalled -= _installationManager_PluginInstalled;
_installationManager.PluginUninstalled -= _installationManager_PluginUninstalled;
_installationManager.PluginUpdated -= _installationManager_PluginUpdated;
_libraryManager.ItemAdded -= _libraryManager_ItemAdded;
_libraryManager.ItemRemoved -= _libraryManager_ItemRemoved;
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
_sessionManager.AuthenticationFailed -= _sessionManager_AuthenticationFailed;
_sessionManager.AuthenticationSucceeded -= _sessionManager_AuthenticationSucceeded;
_sessionManager.SessionEnded -= _sessionManager_SessionEnded;
_sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
_sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped;
_subManager.SubtitlesDownloaded -= _subManager_SubtitlesDownloaded;
_subManager.SubtitleDownloadFailure -= _subManager_SubtitleDownloadFailure;
_userManager.UserCreated -= _userManager_UserCreated;
_userManager.UserPasswordChanged -= _userManager_UserPasswordChanged;
_userManager.UserDeleted -= _userManager_UserDeleted;
_userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
_config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated;
//_logManager.LoggerLoaded -= _logManager_LoggerLoaded;
_appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
}
/// <summary>
/// Constructs a user-friendly string for this TimeSpan instance.
/// </summary>
public static string ToUserFriendlyString(TimeSpan span)
{
const int DaysInYear = 365;
const int DaysInMonth = 30;
// Get each non-zero value from TimeSpan component
List<string> values = new List<string>();
// Number of years
int days = span.Days;
if (days >= DaysInYear)
{
int years = (days / DaysInYear);
values.Add(CreateValueString(years, "year"));
days = (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
StringBuilder 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();
}
/// <summary>
/// Constructs a string description of a time-span value.
/// </summary>
/// <param name="value">The value of this item</param>
/// <param name="description">The name of this item (singular form)</param>
private static string CreateValueString(int value, string description)
{
return String.Format("{0:#,##0} {1}",
value, (value == 1) ? description : String.Format("{0}s", description));
}
}
}

@ -2,14 +2,12 @@
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
@ -37,7 +35,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
private readonly ITaskManager _taskManager;
private readonly INotificationManager _notificationManager;
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly ISessionManager _sessionManager;
private readonly IServerApplicationHost _appHost;
@ -45,14 +42,13 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
private Timer LibraryUpdateTimer { get; set; }
private readonly object _libraryChangedSyncLock = new object();
public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, IServerConfigurationManager config, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost)
public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost)
{
_installationManager = installationManager;
_userManager = userManager;
_logger = logger;
_taskManager = taskManager;
_notificationManager = notificationManager;
_config = config;
_libraryManager = libraryManager;
_sessionManager = sessionManager;
_appHost = appHost;
@ -317,7 +313,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
}
}
private string GetItemName(BaseItem item)
public static string GetItemName(BaseItem item)
{
var name = item.Name;

@ -23,7 +23,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
public void Run()
{
_notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded;
_notificationsRepo.NotificationUpdated += _notificationsRepo_NotificationUpdated;
_notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead;
}
@ -40,13 +39,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
_serverManager.SendWebSocketMessage("NotificationsMarkedRead", msg);
}
void _notificationsRepo_NotificationUpdated(object sender, NotificationUpdateEventArgs e)
{
var msg = e.Notification.UserId + "|" + e.Notification.Id;
_serverManager.SendWebSocketMessage("NotificationUpdated", msg);
}
void _notificationsRepo_NotificationAdded(object sender, NotificationUpdateEventArgs e)
{
var msg = e.Notification.UserId + "|" + e.Notification.Id;
@ -57,7 +49,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
public void Dispose()
{
_notificationsRepo.NotificationAdded -= _notificationsRepo_NotificationAdded;
_notificationsRepo.NotificationUpdated -= _notificationsRepo_NotificationUpdated;
}
}
}

@ -3,11 +3,13 @@ using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using System;
using System.Threading;
@ -47,6 +49,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly IDtoService _dtoService;
private readonly ISessionManager _sessionManager;
private readonly IActivityManager _activityManager;
/// <summary>
/// Initializes a new instance of the <see cref="ServerEventNotifier" /> class.
@ -58,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// <param name="taskManager">The task manager.</param>
/// <param name="dtoService">The dto service.</param>
/// <param name="sessionManager">The session manager.</param>
public ServerEventNotifier(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, IDtoService dtoService, ISessionManager sessionManager)
public ServerEventNotifier(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, IDtoService dtoService, ISessionManager sessionManager, IActivityManager activityManager)
{
_serverManager = serverManager;
_userManager = userManager;
@ -67,6 +70,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_taskManager = taskManager;
_dtoService = dtoService;
_sessionManager = sessionManager;
_activityManager = activityManager;
}
public void Run()
@ -84,6 +88,13 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
_activityManager.EntryCreated += _activityManager_EntryCreated;
}
void _activityManager_EntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
{
_serverManager.SendWebSocketMessage("ActivityLogEntryCreated", e.Argument);
}
void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask
public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask, IScheduledTaskActivityLog
{
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
@ -77,5 +77,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
get { return GetTvOptions().IsEnabled; }
}
public bool IsActivityLogged
{
get { return false; }
}
}
}

@ -110,7 +110,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
try
{
var webSocketContext = ctx.AcceptWebSocket(null, null);
var webSocketContext = ctx.AcceptWebSocket(null);
if (WebSocketHandler != null)
{

@ -48,6 +48,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
/// <value>The user repository.</value>
private IUserRepository UserRepository { get; set; }
public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
private readonly IXmlSerializer _xmlSerializer;
@ -390,7 +391,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="user">The user.</param>
/// <param name="newPassword">The new password.</param>
/// <returns>Task.</returns>
public Task ChangePassword(User user, string newPassword)
public async Task ChangePassword(User user, string newPassword)
{
if (user == null)
{
@ -399,7 +400,9 @@ namespace MediaBrowser.Server.Implementations.Library
user.Password = string.IsNullOrEmpty(newPassword) ? string.Empty : GetSha1String(newPassword);
return UpdateUser(user);
await UpdateUser(user).ConfigureAwait(false);
EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
}
/// <summary>

@ -699,10 +699,10 @@
"HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
"LabelMaxBitrate": "Max bitrate:",
"LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
"LabelMaxStreamingBitrate": "Max streaming bitrate:",
"LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
"LabelMaxStaticBitrate": "Max sync bitrate:",
"LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
"LabelMaxStreamingBitrate": "Max streaming bitrate:",
"LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
"LabelMaxStaticBitrate": "Max sync bitrate:",
"LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
"OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
"LabelFriendlyName": "Friendly name",
@ -808,8 +808,8 @@
"TabNextUp": "Next Up",
"MessageNoMovieSuggestionsAvailable": "No movie suggestions are currently available. Start watching and rating your movies, and then come back to view your recommendations.",
"MessageNoCollectionsAvailable": "Collections allow you to enjoy personalized groupings of Movies, Series, Albums, Books and Games. Click the New button to start creating Collections.",
"MessageNoPlaylistsAvailable": "Playlists allow you to create lists of content to play consecutively at a time. To add items to playlists, right click or tap and hold, then select Add to Playlist.",
"MessageNoPlaylistItemsAvailable": "This playlist is currently empty.",
"MessageNoPlaylistsAvailable": "Playlists allow you to create lists of content to play consecutively at a time. To add items to playlists, right click or tap and hold, then select Add to Playlist.",
"MessageNoPlaylistItemsAvailable": "This playlist is currently empty.",
"HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client",
"ButtonDismiss": "Dismiss",
"MessageLearnHowToCustomize": "Learn how to customize this page to your own personal tastes. Click your user icon in the top right corner of the screen to view and update your preferences.",
@ -918,52 +918,84 @@
"LabelContext": "Context:",
"OptionContextStreaming": "Streaming",
"OptionContextStatic": "Sync",
"ButtonAddToPlaylist": "Add to playlist",
"TabPlaylists": "Playlists",
"ButtonClose": "Close",
"LabelAllLanguages": "All languages",
"HeaderBrowseOnlineImages": "Browse Online Images",
"LabelSource": "Source:",
"OptionAll": "All",
"LabelImage": "Image:",
"ButtonUpload": "Upload",
"ButtonBrowseImages": "Browse Images",
"HeaderImages": "Images",
"HeaderBackdrops": "Backdrops",
"HeaderScreenshots": "Screenshots",
"HeaderAddUpdateImage": "Add/Update Image",
"LabelDropImageHere": "Drop image here",
"LabelJpgPngOnly": "JPG/PNG only",
"LabelImageType": "Image type:",
"OptionPrimary": "Primary",
"OptionArt": "Art",
"OptionBackdrop": "Backdrop",
"OptionBox": "Box",
"OptionBoxRear": "Box rear",
"OptionDisc": "Disc",
"OptionLogo": "Logo",
"OptionMenu": "Menu",
"OptionScreenshot": "Screenshot",
"OptionLocked": "Locked",
"OptionUnidentified": "Unidentified",
"OptionMissingParentalRating": "Missing parental rating",
"OptionStub": "Stub",
"HeaderEpisodes": "Episodes:",
"OptionSeason0": "Season 0",
"LabelReport": "Report:",
"OptionReportSongs": "Songs",
"OptionReportSeries": "Series",
"OptionReportSeasons": "Seasons",
"OptionReportTrailers": "Trailers",
"OptionReportMusicVideos": "Music videos",
"OptionReportMovies": "Movies",
"OptionReportHomeVideos": "Home videos",
"OptionReportGames": "Games",
"OptionReportEpisodes": "Episodes",
"OptionReportCollections": "Collections",
"OptionReportBooks": "Books",
"OptionReportArtists": "Artists",
"OptionReportAlbums": "Albums",
"ButtonAddToPlaylist": "Add to playlist",
"TabPlaylists": "Playlists",
"ButtonClose": "Close",
"LabelAllLanguages": "All languages",
"HeaderBrowseOnlineImages": "Browse Online Images",
"LabelSource": "Source:",
"OptionAll": "All",
"LabelImage": "Image:",
"ButtonUpload": "Upload",
"ButtonBrowseImages": "Browse Images",
"HeaderImages": "Images",
"HeaderBackdrops": "Backdrops",
"HeaderScreenshots": "Screenshots",
"HeaderAddUpdateImage": "Add/Update Image",
"LabelDropImageHere": "Drop image here",
"LabelJpgPngOnly": "JPG/PNG only",
"LabelImageType": "Image type:",
"OptionPrimary": "Primary",
"OptionArt": "Art",
"OptionBackdrop": "Backdrop",
"OptionBox": "Box",
"OptionBoxRear": "Box rear",
"OptionDisc": "Disc",
"OptionLogo": "Logo",
"OptionMenu": "Menu",
"OptionScreenshot": "Screenshot",
"OptionLocked": "Locked",
"OptionUnidentified": "Unidentified",
"OptionMissingParentalRating": "Missing parental rating",
"OptionStub": "Stub",
"HeaderEpisodes": "Episodes:",
"OptionSeason0": "Season 0",
"LabelReport": "Report:",
"OptionReportSongs": "Songs",
"OptionReportSeries": "Series",
"OptionReportSeasons": "Seasons",
"OptionReportTrailers": "Trailers",
"OptionReportMusicVideos": "Music videos",
"OptionReportMovies": "Movies",
"OptionReportHomeVideos": "Home videos",
"OptionReportGames": "Games",
"OptionReportEpisodes": "Episodes",
"OptionReportCollections": "Collections",
"OptionReportBooks": "Books",
"OptionReportArtists": "Artists",
"OptionReportAlbums": "Albums",
"OptionReportAdultVideos": "Adult videos",
"ButtonMore": "More"
"ButtonMore": "More",
"HeaderActivity": "Activity",
"ScheduledTaskStartedWithName": "{0} started",
"ScheduledTaskCancelledWithName": "{0} was cancelled",
"ScheduledTaskCompletedWithName": "{0} completed",
"ScheduledTaskFailed": "Scheduled task completed",
"PluginInstalledWithName": "{0} was installed",
"PluginUpdatedWithName": "{0} was updated",
"PluginUninstalledWithName": "{0} was uninstalled",
"ScheduledTaskFailedWithName": "{0} failed",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"DeviceOnlineWithName": "{0} is connected",
"UserOnlineFromDevice": "{0} is online from {1}",
"DeviceOfflineWithName": "{0} has disconnected",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"LabelRunningTimeValue": "Running time: {0}",
"LabelIpAddressValue": "Ip address: {0}",
"UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserDeletedWithName": "User {0} has been deleted",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageApplicationUpdated": "Media Browser Server has been updated",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
"UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
"AppDeviceValues": "App: {0}, Device: {1}",
"ProviderValue": "Provider: {0}"
}

@ -101,6 +101,8 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Activity\ActivityManager.cs" />
<Compile Include="Activity\ActivityRepository.cs" />
<Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" />
<Compile Include="Channels\ChannelDownloadScheduledTask.cs" />
@ -117,6 +119,7 @@
<Compile Include="Drawing\PlayedIndicatorDrawer.cs" />
<Compile Include="Drawing\UnplayedCountIndicator.cs" />
<Compile Include="Dto\DtoService.cs" />
<Compile Include="EntryPoints\ActivityLogEntryPoint.cs" />
<Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" />
<Compile Include="EntryPoints\ExternalPortForwarding.cs" />
<Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
@ -492,4 +495,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -206,46 +206,6 @@ namespace MediaBrowser.Server.Implementations.Notifications
return notification;
}
/// <summary>
/// Gets the notification.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="userId">The user id.</param>
/// <returns>Notification.</returns>
/// <exception cref="System.ArgumentNullException">
/// id
/// or
/// userId
/// </exception>
public Notification GetNotification(string id, string userId)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentNullException("userId");
}
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId where Id=@Id And UserId = @UserId";
cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = new Guid(id);
cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(userId);
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
{
if (reader.Read())
{
return GetNotification(reader);
}
}
return null;
}
}
/// <summary>
/// Gets the level.
/// </summary>
@ -289,32 +249,6 @@ namespace MediaBrowser.Server.Implementations.Notifications
}
}
/// <summary>
/// Updates the notification.
/// </summary>
/// <param name="notification">The notification.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public async Task UpdateNotification(Notification notification, CancellationToken cancellationToken)
{
await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false);
if (NotificationUpdated != null)
{
try
{
NotificationUpdated(this, new NotificationUpdateEventArgs
{
Notification = notification
});
}
catch (Exception ex)
{
_logger.ErrorException("Error in NotificationUpdated event handler", ex);
}
}
}
/// <summary>
/// Replaces the notification.
/// </summary>

@ -153,6 +153,8 @@ namespace MediaBrowser.Server.Implementations.ServerManager
/// <param name="result">The result.</param>
private async void ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
{
//_logger.Debug("Websocket message received: {0}", result.MessageType);
var tasks = _webSocketListeners.Select(i => Task.Run(async () =>
{
try

@ -14,11 +14,13 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Users;
using MediaBrowser.Server.Implementations.Security;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -27,7 +29,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Server.Implementations.Security;
namespace MediaBrowser.Server.Implementations.Session
{
@ -76,6 +77,10 @@ namespace MediaBrowser.Server.Implementations.Session
private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections =
new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase);
public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded;
/// <summary>
/// Occurs when [playback start].
/// </summary>
@ -399,6 +404,11 @@ namespace MediaBrowser.Server.Implementations.Session
Id = Guid.NewGuid().ToString("N")
};
sessionInfo.DeviceName = deviceName;
sessionInfo.UserId = userId;
sessionInfo.UserName = username;
sessionInfo.RemoteEndPoint = remoteEndPoint;
OnSessionStarted(sessionInfo);
return sessionInfo;
@ -1191,44 +1201,37 @@ namespace MediaBrowser.Server.Implementations.Session
/// <summary>
/// Authenticates the new session.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
/// <param name="clientType">Type of the client.</param>
/// <param name="appVersion">The application version.</param>
/// <param name="deviceId">The device identifier.</param>
/// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="request">The request.</param>
/// <param name="isLocal">if set to <c>true</c> [is local].</param>
/// <returns>Task{SessionInfo}.</returns>
/// <exception cref="AuthenticationException">Invalid user or password entered.</exception>
/// <exception cref="System.UnauthorizedAccessException">Invalid user or password entered.</exception>
/// <exception cref="UnauthorizedAccessException">Invalid user or password entered.</exception>
public async Task<AuthenticationResult> AuthenticateNewSession(string username,
string password,
string clientType,
string appVersion,
string deviceId,
string deviceName,
string remoteEndPoint,
public async Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request,
bool isLocal)
{
var result = (isLocal && string.Equals(clientType, "Dashboard", StringComparison.OrdinalIgnoreCase)) ||
await _userManager.AuthenticateUser(username, password).ConfigureAwait(false);
var result = (isLocal && string.Equals(request.App, "Dashboard", StringComparison.OrdinalIgnoreCase)) ||
await _userManager.AuthenticateUser(request.Username, request.Password).ConfigureAwait(false);
if (!result)
{
EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
throw new AuthenticationException("Invalid user or password entered.");
}
var user = _userManager.Users
.First(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
.First(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase));
var token = await GetAuthorizationToken(user.Id.ToString("N"), deviceId, clientType, deviceName).ConfigureAwait(false);
var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.DeviceName).ConfigureAwait(false);
var session = await LogSessionActivity(clientType,
appVersion,
deviceId,
deviceName,
remoteEndPoint,
EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
var session = await LogSessionActivity(request.App,
request.AppVersion,
request.DeviceId,
request.DeviceName,
request.RemoteEndPoint,
user)
.ConfigureAwait(false);

@ -1,5 +1,4 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;

@ -15,7 +15,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Sync
{
public class SyncRepository : ISyncRepository
public class SyncRepository : ISyncRepository, IDisposable
{
private IDbConnection _connection;
private readonly ILogger _logger;
@ -422,8 +422,50 @@ namespace MediaBrowser.Server.Implementations.Sync
}
info.TargetId = reader.GetString(5);
return info;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private readonly object _disposeLock = new object();
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
try
{
lock (_disposeLock)
{
if (_connection != null)
{
if (_connection.IsOpen())
{
_connection.Close();
}
_connection.Dispose();
_connection = null;
}
}
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing database", ex);
}
}
}
}
}

@ -9,6 +9,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Collections;
@ -53,6 +54,7 @@ using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.Server.Implementations;
using MediaBrowser.Server.Implementations.Activity;
using MediaBrowser.Server.Implementations.Channels;
using MediaBrowser.Server.Implementations.Collections;
using MediaBrowser.Server.Implementations.Configuration;
@ -343,6 +345,13 @@ namespace MediaBrowser.ServerApplication
saveConfig = true;
}
if (ServerConfigurationManager.Configuration.SubtitleOptions != null)
{
ServerConfigurationManager.SaveConfiguration("subtitles", ServerConfigurationManager.Configuration.SubtitleOptions);
ServerConfigurationManager.Configuration.SubtitleOptions = null;
saveConfig = true;
}
if (saveConfig)
{
ServerConfigurationManager.SaveConfiguration();
@ -641,6 +650,10 @@ namespace MediaBrowser.ServerApplication
MediaEncoder, ChapterManager);
RegisterSingleInstance(EncodingManager);
var activityLogRepo = await GetActivityLogRepository().ConfigureAwait(false);
RegisterSingleInstance(activityLogRepo);
RegisterSingleInstance<IActivityManager>(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo));
var authContext = new AuthorizationContext();
RegisterSingleInstance<IAuthorizationContext>(authContext);
RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
@ -730,6 +743,15 @@ namespace MediaBrowser.ServerApplication
return repo;
}
private async Task<IActivityRepository> GetActivityLogRepository()
{
var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths);
await repo.Initialize().ConfigureAwait(false);
return repo;
}
private async Task<ISyncRepository> GetSyncRepository()
{
var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), ServerConfigurationManager.ApplicationPaths);

Loading…
Cancel
Save