add activity log feature

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

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

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

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

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

@ -10,7 +10,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Api namespace MediaBrowser.Api.Session
{ {
/// <summary> /// <summary>
/// Class GetSessions /// 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 MediaBrowser.Model.System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Api.WebSocket namespace MediaBrowser.Api.System
{ {
/// <summary> /// <summary>
/// Class SystemInfoWebSocketListener /// Class SystemInfoWebSocketListener

@ -4,12 +4,13 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using ServiceStack; using ServiceStack;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Api namespace MediaBrowser.Api.System
{ {
/// <summary> /// <summary>
/// Class GetSystemInfo /// Class GetSystemInfo
@ -73,7 +74,7 @@ namespace MediaBrowser.Api
/// <param name="appHost">The app host.</param> /// <param name="appHost">The app host.</param>
/// <param name="appPaths">The application paths.</param> /// <param name="appPaths">The application paths.</param>
/// <param name="fileSystem">The file system.</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) public SystemService(IServerApplicationHost appHost, IApplicationPaths appPaths, IFileSystem fileSystem)
{ {
_appHost = appHost; _appHost = appHost;
@ -89,7 +90,7 @@ namespace MediaBrowser.Api
{ {
files = new DirectoryInfo(_appPaths.LogDirectoryPath) files = new DirectoryInfo(_appPaths.LogDirectoryPath)
.EnumerateFiles("*", SearchOption.AllDirectories) .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(); .ToList();
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
@ -116,7 +117,7 @@ namespace MediaBrowser.Api
{ {
var file = new DirectoryInfo(_appPaths.LogDirectoryPath) var file = new DirectoryInfo(_appPaths.LogDirectoryPath)
.EnumerateFiles("*", SearchOption.AllDirectories) .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); return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
} }

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

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

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

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

@ -13,4 +13,9 @@
/// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
bool IsEnabled { get; } 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.Common.Configuration;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Events;
using System;
namespace MediaBrowser.Controller.Configuration namespace MediaBrowser.Controller.Configuration
{ {
@ -10,11 +8,6 @@ namespace MediaBrowser.Controller.Configuration
/// </summary> /// </summary>
public interface IServerConfigurationManager : IConfigurationManager public interface IServerConfigurationManager : IConfigurationManager
{ {
/// <summary>
/// Occurs when [configuration updating].
/// </summary>
event EventHandler<GenericEventArgs<ServerConfiguration>> ConfigurationUpdating;
/// <summary> /// <summary>
/// Gets the application paths. /// Gets the application paths.
/// </summary> /// </summary>

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

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

@ -269,7 +269,7 @@ namespace MediaBrowser.Controller.Library
if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) 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; continue;
} }

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

@ -16,10 +16,6 @@ namespace MediaBrowser.Controller.Notifications
/// </summary> /// </summary>
event EventHandler<NotificationUpdateEventArgs> NotificationAdded; event EventHandler<NotificationUpdateEventArgs> NotificationAdded;
/// <summary> /// <summary>
/// Occurs when [notification updated].
/// </summary>
event EventHandler<NotificationUpdateEventArgs> NotificationUpdated;
/// <summary>
/// Occurs when [notifications marked read]. /// Occurs when [notifications marked read].
/// </summary> /// </summary>
event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead; event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
@ -37,14 +33,6 @@ namespace MediaBrowser.Controller.Notifications
/// <returns>NotificationResult.</returns> /// <returns>NotificationResult.</returns>
NotificationResult GetNotifications(NotificationQuery query); 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> /// <summary>
/// Adds the notification. /// Adds the notification.
/// </summary> /// </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.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
using System; using System;
@ -46,6 +47,16 @@ namespace MediaBrowser.Controller.Session
/// Occurs when [capabilities changed]. /// Occurs when [capabilities changed].
/// </summary> /// </summary>
event EventHandler<SessionEventArgs> CapabilitiesChanged; 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> /// <summary>
/// Gets the sessions. /// Gets the sessions.
@ -211,23 +222,10 @@ namespace MediaBrowser.Controller.Session
/// <summary> /// <summary>
/// Authenticates the new session. /// Authenticates the new session.
/// </summary> /// </summary>
/// <param name="username">The username.</param> /// <param name="request">The request.</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="isLocal">if set to <c>true</c> [is local].</param> /// <param name="isLocal">if set to <c>true</c> [is local].</param>
/// <returns>Task{SessionInfo}.</returns> /// <returns>Task{SessionInfo}.</returns>
Task<AuthenticationResult> AuthenticateNewSession(string username, Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request, bool isLocal);
string password,
string clientType,
string appVersion,
string deviceId,
string deviceName,
string remoteEndPoint,
bool isLocal);
/// <summary> /// <summary>
/// Reports the capabilities. /// Reports the capabilities.

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -8,6 +9,16 @@ namespace MediaBrowser.Controller.Subtitles
{ {
public interface ISubtitleManager 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> /// <summary>
/// Adds the parts. /// Adds the parts.
/// </summary> /// </summary>
@ -31,7 +42,7 @@ namespace MediaBrowser.Controller.Subtitles
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns> /// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns>
Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request,
CancellationToken cancellationToken); CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -41,8 +52,8 @@ namespace MediaBrowser.Controller.Subtitles
/// <param name="subtitleId">The subtitle identifier.</param> /// <param name="subtitleId">The subtitle identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task DownloadSubtitles(Video video, Task DownloadSubtitles(Video video,
string subtitleId, string subtitleId,
CancellationToken cancellationToken); CancellationToken cancellationToken);
/// <summary> /// <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; var path = file.FullName;
await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); //await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try try
{ {
@ -46,7 +46,7 @@ namespace MediaBrowser.LocalMetadata
} }
finally finally
{ {
XmlProviderUtils.XmlParsingResourcePool.Release(); //XmlProviderUtils.XmlParsingResourcePool.Release();
} }
return result; return result;

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

@ -52,6 +52,9 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\mediabrowser.model\activity\ActivityLogEntry.cs">
<Link>Activity\ActivityLogEntry.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs"> <Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs">
<Link>ApiClient\GeneralCommandEventArgs.cs</Link> <Link>ApiClient\GeneralCommandEventArgs.cs</Link>
</Compile> </Compile>
@ -136,9 +139,6 @@
<Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs"> <Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs">
<Link>Configuration\ServerConfiguration.cs</Link> <Link>Configuration\ServerConfiguration.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
<Link>Configuration\SubtitleOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs"> <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
<Link>Configuration\SubtitlePlaybackMode.cs</Link> <Link>Configuration\SubtitlePlaybackMode.cs</Link>
</Compile> </Compile>
@ -685,6 +685,9 @@
<Compile Include="..\MediaBrowser.Model\Providers\RemoteSubtitleInfo.cs"> <Compile Include="..\MediaBrowser.Model\Providers\RemoteSubtitleInfo.cs">
<Link>Providers\RemoteSubtitleInfo.cs</Link> <Link>Providers\RemoteSubtitleInfo.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Providers\SubtitleOptions.cs">
<Link>Providers\SubtitleOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\AllThemeMediaResult.cs"> <Compile Include="..\MediaBrowser.Model\Querying\AllThemeMediaResult.cs">
<Link>Querying\AllThemeMediaResult.cs</Link> <Link>Querying\AllThemeMediaResult.cs</Link>
</Compile> </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); Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
/// <summary> /// <summary>
/// Instructs antoher client to browse to a library item. /// Instructs another client to browse to a library item.
/// </summary> /// </summary>
/// <param name="sessionId">The session id.</param> /// <param name="sessionId">The session id.</param>
/// <param name="itemId">The id of the item to browse to.</param> /// <param name="itemId">The id of the item to browse to.</param>

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

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

@ -13,5 +13,21 @@ namespace MediaBrowser.Model.Events
/// </summary> /// </summary>
/// <value>The argument.</value> /// <value>The argument.</value>
public T Argument { get; set; } 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"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>
<Compile Include="Activity\ActivityLogEntry.cs" />
<Compile Include="ApiClient\HttpResponseEventArgs.cs" /> <Compile Include="ApiClient\HttpResponseEventArgs.cs" />
<Compile Include="ApiClient\IApiClient.cs" /> <Compile Include="ApiClient\IApiClient.cs" />
<Compile Include="ApiClient\ApiClientExtensions.cs" /> <Compile Include="ApiClient\ApiClientExtensions.cs" />
@ -99,7 +100,7 @@
<Compile Include="Configuration\PathSubstitution.cs" /> <Compile Include="Configuration\PathSubstitution.cs" />
<Compile Include="Notifications\SendToUserType.cs" /> <Compile Include="Notifications\SendToUserType.cs" />
<Compile Include="Configuration\ServerConfiguration.cs" /> <Compile Include="Configuration\ServerConfiguration.cs" />
<Compile Include="Configuration\SubtitleOptions.cs" /> <Compile Include="Providers\SubtitleOptions.cs" />
<Compile Include="Configuration\UnratedItem.cs" /> <Compile Include="Configuration\UnratedItem.cs" />
<Compile Include="Dlna\AudioOptions.cs" /> <Compile Include="Dlna\AudioOptions.cs" />
<Compile Include="Dlna\CodecProfile.cs" /> <Compile Include="Dlna\CodecProfile.cs" />
@ -378,4 +379,4 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

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

@ -42,5 +42,11 @@ namespace MediaBrowser.Model.Tasks
/// </summary> /// </summary>
/// <value>The error message.</value> /// <value>The error message.</value>
public string ErrorMessage { get; set; } 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="Manager\ProviderUtils.cs" />
<Compile Include="Studios\StudiosImageProvider.cs" /> <Compile Include="Studios\StudiosImageProvider.cs" />
<Compile Include="Studios\StudioMetadataService.cs" /> <Compile Include="Studios\StudioMetadataService.cs" />
<Compile Include="Subtitles\ConfigurationExtension.cs" />
<Compile Include="Subtitles\OpenSubtitleDownloader.cs" /> <Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
<Compile Include="Subtitles\SubtitleManager.cs" /> <Compile Include="Subtitles\SubtitleManager.cs" />
<Compile Include="TV\EpisodeMetadataService.cs" /> <Compile Include="TV\EpisodeMetadataService.cs" />
@ -213,4 +214,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

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

@ -1,10 +1,12 @@
using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
@ -12,6 +14,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.MediaInfo namespace MediaBrowser.Providers.MediaInfo
{ {
@ -45,8 +48,15 @@ namespace MediaBrowser.Providers.MediaInfo
get { return "Library"; } get { return "Library"; }
} }
private SubtitleOptions GetOptions()
{
return _config.GetConfiguration<SubtitleOptions>("subtitles");
}
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{ {
var options = GetOptions();
var videos = _libraryManager.RootFolder var videos = _libraryManager.RootFolder
.RecursiveChildren .RecursiveChildren
.OfType<Video>() .OfType<Video>()
@ -57,9 +67,9 @@ namespace MediaBrowser.Providers.MediaInfo
return false; return false;
} }
return (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles && return (options.DownloadEpisodeSubtitles &&
i is Episode) || i is Episode) ||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles && (options.DownloadMovieSubtitles &&
i is Movie); i is Movie);
}) })
.ToList(); .ToList();
@ -70,7 +80,7 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
try try
{ {
await DownloadSubtitles(video, cancellationToken).ConfigureAwait(false); await DownloadSubtitles(video, options, cancellationToken).ConfigureAwait(false);
} }
catch (Exception ex) 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) || video is Episode) ||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles && (options.DownloadMovieSubtitles &&
video is Movie)) video is Movie))
{ {
var mediaStreams = video.GetMediaSources(false).First().MediaStreams; var mediaStreams = video.GetMediaSources(false).First().MediaStreams;
@ -103,9 +113,9 @@ namespace MediaBrowser.Providers.MediaInfo
.DownloadSubtitles(video, .DownloadSubtitles(video,
currentStreams, currentStreams,
externalSubtitleStreams, externalSubtitleStreams,
_config.Configuration.SubtitleOptions.SkipIfGraphicalSubtitlesPresent, options.SkipIfGraphicalSubtitlesPresent,
_config.Configuration.SubtitleOptions.SkipIfAudioTrackMatches, options.SkipIfAudioTrackMatches,
_config.Configuration.SubtitleOptions.DownloadLanguages, options.DownloadLanguages,
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
// Rescan // 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.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -6,7 +7,6 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using OpenSubtitlesHandler; using OpenSubtitlesHandler;
@ -45,16 +45,21 @@ namespace MediaBrowser.Providers.Subtitles
_config = config; _config = config;
_encryption = encryption; _encryption = encryption;
_config.ConfigurationUpdating += _config_ConfigurationUpdating; _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
// Reset the count every 24 hours // Reset the count every 24 hours
_dailyTimer = new Timer(state => _dailyDownloadCount = 0, null, TimeSpan.FromHours(24), TimeSpan.FromHours(24)); _dailyTimer = new Timer(state => _dailyDownloadCount = 0, null, TimeSpan.FromHours(24), TimeSpan.FromHours(24));
} }
private const string PasswordHashPrefix = "h:"; 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 && if (options != null &&
!string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) && !string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) &&
@ -85,12 +90,19 @@ namespace MediaBrowser.Providers.Subtitles
get { return "Open Subtitles"; } get { return "Open Subtitles"; }
} }
private SubtitleOptions GetOptions()
{
return _config.GetSubtitleConfiguration();
}
public IEnumerable<VideoContentType> SupportedMediaTypes public IEnumerable<VideoContentType> SupportedMediaTypes
{ {
get get
{ {
if (string.IsNullOrWhiteSpace(_config.Configuration.SubtitleOptions.OpenSubtitlesUsername) || var options = GetOptions();
string.IsNullOrWhiteSpace(_config.Configuration.SubtitleOptions.OpenSubtitlesPasswordHash))
if (string.IsNullOrWhiteSpace(options.OpenSubtitlesUsername) ||
string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash))
{ {
return new VideoContentType[] { }; return new VideoContentType[] { };
} }
@ -101,10 +113,11 @@ namespace MediaBrowser.Providers.Subtitles
public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken) public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
{ {
return GetSubtitlesInternal(id, cancellationToken); return GetSubtitlesInternal(id, GetOptions(), cancellationToken);
} }
private async Task<SubtitleResponse> GetSubtitlesInternal(string id, private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
SubtitleOptions options,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(id)) if (string.IsNullOrWhiteSpace(id))
@ -113,7 +126,7 @@ namespace MediaBrowser.Providers.Subtitles
} }
if (_dailyDownloadCount >= MaxDownloadsPerDay && 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."); 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; return;
} }
var options = _config.Configuration.SubtitleOptions ?? new SubtitleOptions(); var options = GetOptions();
var user = options.OpenSubtitlesUsername ?? string.Empty; var user = options.OpenSubtitlesUsername ?? string.Empty;
var password = DecryptPassword(options.OpenSubtitlesPasswordHash); var password = DecryptPassword(options.OpenSubtitlesPasswordHash);
@ -289,7 +302,7 @@ namespace MediaBrowser.Providers.Subtitles
public void Dispose() public void Dispose()
{ {
_config.ConfigurationUpdating -= _config_ConfigurationUpdating; _config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating;
if (_dailyTimer != null) 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.Common.IO;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
@ -28,6 +29,9 @@ namespace MediaBrowser.Providers.Subtitles
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo; 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) public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IItemRepository itemRepo)
{ {
_logger = logger; _logger = logger;
@ -100,35 +104,63 @@ namespace MediaBrowser.Providers.Subtitles
string subtitleId, string subtitleId,
CancellationToken cancellationToken) 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), var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
_fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
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 _monitor.ReportFileSystemChangeBeginning(savePath);
{
using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) 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) 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.ScheduledTasks;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -37,7 +35,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly INotificationManager _notificationManager; private readonly INotificationManager _notificationManager;
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
@ -45,14 +42,13 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
private Timer LibraryUpdateTimer { get; set; } private Timer LibraryUpdateTimer { get; set; }
private readonly object _libraryChangedSyncLock = new object(); 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; _installationManager = installationManager;
_userManager = userManager; _userManager = userManager;
_logger = logger; _logger = logger;
_taskManager = taskManager; _taskManager = taskManager;
_notificationManager = notificationManager; _notificationManager = notificationManager;
_config = config;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_appHost = appHost; _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; var name = item.Name;

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

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

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

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

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

@ -699,10 +699,10 @@
"HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.", "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
"LabelMaxBitrate": "Max bitrate:", "LabelMaxBitrate": "Max bitrate:",
"LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.", "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
"LabelMaxStreamingBitrate": "Max streaming bitrate:", "LabelMaxStreamingBitrate": "Max streaming bitrate:",
"LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.", "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
"LabelMaxStaticBitrate": "Max sync bitrate:", "LabelMaxStaticBitrate": "Max sync bitrate:",
"LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.", "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
"OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests", "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.", "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
"LabelFriendlyName": "Friendly name", "LabelFriendlyName": "Friendly name",
@ -808,8 +808,8 @@
"TabNextUp": "Next Up", "TabNextUp": "Next Up",
"MessageNoMovieSuggestionsAvailable": "No movie suggestions are currently available. Start watching and rating your movies, and then come back to view your recommendations.", "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.", "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.", "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.", "MessageNoPlaylistItemsAvailable": "This playlist is currently empty.",
"HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client", "HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client",
"ButtonDismiss": "Dismiss", "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.", "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:", "LabelContext": "Context:",
"OptionContextStreaming": "Streaming", "OptionContextStreaming": "Streaming",
"OptionContextStatic": "Sync", "OptionContextStatic": "Sync",
"ButtonAddToPlaylist": "Add to playlist", "ButtonAddToPlaylist": "Add to playlist",
"TabPlaylists": "Playlists", "TabPlaylists": "Playlists",
"ButtonClose": "Close", "ButtonClose": "Close",
"LabelAllLanguages": "All languages", "LabelAllLanguages": "All languages",
"HeaderBrowseOnlineImages": "Browse Online Images", "HeaderBrowseOnlineImages": "Browse Online Images",
"LabelSource": "Source:", "LabelSource": "Source:",
"OptionAll": "All", "OptionAll": "All",
"LabelImage": "Image:", "LabelImage": "Image:",
"ButtonUpload": "Upload", "ButtonUpload": "Upload",
"ButtonBrowseImages": "Browse Images", "ButtonBrowseImages": "Browse Images",
"HeaderImages": "Images", "HeaderImages": "Images",
"HeaderBackdrops": "Backdrops", "HeaderBackdrops": "Backdrops",
"HeaderScreenshots": "Screenshots", "HeaderScreenshots": "Screenshots",
"HeaderAddUpdateImage": "Add/Update Image", "HeaderAddUpdateImage": "Add/Update Image",
"LabelDropImageHere": "Drop image here", "LabelDropImageHere": "Drop image here",
"LabelJpgPngOnly": "JPG/PNG only", "LabelJpgPngOnly": "JPG/PNG only",
"LabelImageType": "Image type:", "LabelImageType": "Image type:",
"OptionPrimary": "Primary", "OptionPrimary": "Primary",
"OptionArt": "Art", "OptionArt": "Art",
"OptionBackdrop": "Backdrop", "OptionBackdrop": "Backdrop",
"OptionBox": "Box", "OptionBox": "Box",
"OptionBoxRear": "Box rear", "OptionBoxRear": "Box rear",
"OptionDisc": "Disc", "OptionDisc": "Disc",
"OptionLogo": "Logo", "OptionLogo": "Logo",
"OptionMenu": "Menu", "OptionMenu": "Menu",
"OptionScreenshot": "Screenshot", "OptionScreenshot": "Screenshot",
"OptionLocked": "Locked", "OptionLocked": "Locked",
"OptionUnidentified": "Unidentified", "OptionUnidentified": "Unidentified",
"OptionMissingParentalRating": "Missing parental rating", "OptionMissingParentalRating": "Missing parental rating",
"OptionStub": "Stub", "OptionStub": "Stub",
"HeaderEpisodes": "Episodes:", "HeaderEpisodes": "Episodes:",
"OptionSeason0": "Season 0", "OptionSeason0": "Season 0",
"LabelReport": "Report:", "LabelReport": "Report:",
"OptionReportSongs": "Songs", "OptionReportSongs": "Songs",
"OptionReportSeries": "Series", "OptionReportSeries": "Series",
"OptionReportSeasons": "Seasons", "OptionReportSeasons": "Seasons",
"OptionReportTrailers": "Trailers", "OptionReportTrailers": "Trailers",
"OptionReportMusicVideos": "Music videos", "OptionReportMusicVideos": "Music videos",
"OptionReportMovies": "Movies", "OptionReportMovies": "Movies",
"OptionReportHomeVideos": "Home videos", "OptionReportHomeVideos": "Home videos",
"OptionReportGames": "Games", "OptionReportGames": "Games",
"OptionReportEpisodes": "Episodes", "OptionReportEpisodes": "Episodes",
"OptionReportCollections": "Collections", "OptionReportCollections": "Collections",
"OptionReportBooks": "Books", "OptionReportBooks": "Books",
"OptionReportArtists": "Artists", "OptionReportArtists": "Artists",
"OptionReportAlbums": "Albums", "OptionReportAlbums": "Albums",
"OptionReportAdultVideos": "Adult videos", "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"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>
<Compile Include="Activity\ActivityManager.cs" />
<Compile Include="Activity\ActivityRepository.cs" />
<Compile Include="Branding\BrandingConfigurationFactory.cs" /> <Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" /> <Compile Include="Channels\ChannelConfigurations.cs" />
<Compile Include="Channels\ChannelDownloadScheduledTask.cs" /> <Compile Include="Channels\ChannelDownloadScheduledTask.cs" />
@ -117,6 +119,7 @@
<Compile Include="Drawing\PlayedIndicatorDrawer.cs" /> <Compile Include="Drawing\PlayedIndicatorDrawer.cs" />
<Compile Include="Drawing\UnplayedCountIndicator.cs" /> <Compile Include="Drawing\UnplayedCountIndicator.cs" />
<Compile Include="Dto\DtoService.cs" /> <Compile Include="Dto\DtoService.cs" />
<Compile Include="EntryPoints\ActivityLogEntryPoint.cs" />
<Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" /> <Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" />
<Compile Include="EntryPoints\ExternalPortForwarding.cs" /> <Compile Include="EntryPoints\ExternalPortForwarding.cs" />
<Compile Include="EntryPoints\LibraryChangedNotifier.cs" /> <Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
@ -492,4 +495,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

@ -206,46 +206,6 @@ namespace MediaBrowser.Server.Implementations.Notifications
return notification; 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> /// <summary>
/// Gets the level. /// Gets the level.
/// </summary> /// </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> /// <summary>
/// Replaces the notification. /// Replaces the notification.
/// </summary> /// </summary>

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

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

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

@ -15,7 +15,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Sync namespace MediaBrowser.Server.Implementations.Sync
{ {
public class SyncRepository : ISyncRepository public class SyncRepository : ISyncRepository, IDisposable
{ {
private IDbConnection _connection; private IDbConnection _connection;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -422,8 +422,50 @@ namespace MediaBrowser.Server.Implementations.Sync
} }
info.TargetId = reader.GetString(5); info.TargetId = reader.GetString(5);
return info; 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.Net;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Collections;
@ -53,6 +54,7 @@ using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Subtitles; using MediaBrowser.Providers.Subtitles;
using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations;
using MediaBrowser.Server.Implementations.Activity;
using MediaBrowser.Server.Implementations.Channels; using MediaBrowser.Server.Implementations.Channels;
using MediaBrowser.Server.Implementations.Collections; using MediaBrowser.Server.Implementations.Collections;
using MediaBrowser.Server.Implementations.Configuration; using MediaBrowser.Server.Implementations.Configuration;
@ -343,6 +345,13 @@ namespace MediaBrowser.ServerApplication
saveConfig = true; saveConfig = true;
} }
if (ServerConfigurationManager.Configuration.SubtitleOptions != null)
{
ServerConfigurationManager.SaveConfiguration("subtitles", ServerConfigurationManager.Configuration.SubtitleOptions);
ServerConfigurationManager.Configuration.SubtitleOptions = null;
saveConfig = true;
}
if (saveConfig) if (saveConfig)
{ {
ServerConfigurationManager.SaveConfiguration(); ServerConfigurationManager.SaveConfiguration();
@ -641,6 +650,10 @@ namespace MediaBrowser.ServerApplication
MediaEncoder, ChapterManager); MediaEncoder, ChapterManager);
RegisterSingleInstance(EncodingManager); RegisterSingleInstance(EncodingManager);
var activityLogRepo = await GetActivityLogRepository().ConfigureAwait(false);
RegisterSingleInstance(activityLogRepo);
RegisterSingleInstance<IActivityManager>(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo));
var authContext = new AuthorizationContext(); var authContext = new AuthorizationContext();
RegisterSingleInstance<IAuthorizationContext>(authContext); RegisterSingleInstance<IAuthorizationContext>(authContext);
RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager)); RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
@ -730,6 +743,15 @@ namespace MediaBrowser.ServerApplication
return repo; 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() private async Task<ISyncRepository> GetSyncRepository()
{ {
var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), ServerConfigurationManager.ApplicationPaths); var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), ServerConfigurationManager.ApplicationPaths);

Loading…
Cancel
Save