fixes #762 - Marking unwatched doesn't update display

pull/702/head
Luke Pulverenti 10 years ago
parent 59dc591f66
commit 7fa9b14f56

@ -0,0 +1,28 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Branding;
using ServiceStack;
namespace MediaBrowser.Api
{
[Route("/Branding/Configuration", "GET", Summary = "Gets branding configuration")]
public class GetBrandingOptions : IReturn<BrandingOptions>
{
}
public class BrandingService : BaseApiService
{
private readonly IConfigurationManager _config;
public BrandingService(IConfigurationManager config)
{
_config = config;
}
public object Get(GetBrandingOptions request)
{
var result = _config.GetConfiguration<BrandingOptions>("branding");
return ToOptimizedResult(result);
}
}
}

@ -4,6 +4,7 @@ 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.Net;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
@ -27,6 +28,7 @@ namespace MediaBrowser.Api
} }
[Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")] [Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
[Authenticated]
public class GetNamedConfiguration public class GetNamedConfiguration
{ {
[ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -37,11 +39,13 @@ namespace MediaBrowser.Api
/// Class UpdateConfiguration /// Class UpdateConfiguration
/// </summary> /// </summary>
[Route("/System/Configuration", "POST", Summary = "Updates application configuration")] [Route("/System/Configuration", "POST", Summary = "Updates application configuration")]
[Authenticated]
public class UpdateConfiguration : ServerConfiguration, IReturnVoid public class UpdateConfiguration : ServerConfiguration, IReturnVoid
{ {
} }
[Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")] [Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")]
[Authenticated]
public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream
{ {
[ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -51,18 +55,21 @@ namespace MediaBrowser.Api
} }
[Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")] [Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")]
[Authenticated]
public class GetDefaultMetadataOptions : IReturn<MetadataOptions> public class GetDefaultMetadataOptions : IReturn<MetadataOptions>
{ {
} }
[Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")] [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")]
[Authenticated]
public class GetMetadataPlugins : IReturn<List<MetadataPluginSummary>> public class GetMetadataPlugins : IReturn<List<MetadataPluginSummary>>
{ {
} }
[Route("/System/Configuration/VideoImageExtraction", "POST", Summary = "Updates image extraction for all types")] [Route("/System/Configuration/VideoImageExtraction", "POST", Summary = "Updates image extraction for all types")]
[Authenticated]
public class UpdateVideoImageExtraction : IReturnVoid public class UpdateVideoImageExtraction : IReturnVoid
{ {
public bool Enabled { get; set; } public bool Enabled { get; set; }

@ -3,6 +3,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
@ -26,6 +27,7 @@ namespace MediaBrowser.Api.Images
/// </summary> /// </summary>
[Route("/Items/{Id}/Images", "GET")] [Route("/Items/{Id}/Images", "GET")]
[Api(Description = "Gets information about an item's images")] [Api(Description = "Gets information about an item's images")]
[Authenticated]
public class GetItemImageInfos : IReturn<List<ImageInfo>> public class GetItemImageInfos : IReturn<List<ImageInfo>>
{ {
/// <summary> /// <summary>
@ -56,6 +58,7 @@ namespace MediaBrowser.Api.Images
/// </summary> /// </summary>
[Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST")] [Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST")]
[Api(Description = "Updates the index for an item image")] [Api(Description = "Updates the index for an item image")]
[Authenticated]
public class UpdateItemImageIndex : IReturnVoid public class UpdateItemImageIndex : IReturnVoid
{ {
/// <summary> /// <summary>
@ -137,6 +140,7 @@ namespace MediaBrowser.Api.Images
[Route("/Items/{Id}/Images/{Type}", "DELETE")] [Route("/Items/{Id}/Images/{Type}", "DELETE")]
[Route("/Items/{Id}/Images/{Type}/{Index}", "DELETE")] [Route("/Items/{Id}/Images/{Type}/{Index}", "DELETE")]
[Api(Description = "Deletes an item image")] [Api(Description = "Deletes an item image")]
[Authenticated]
public class DeleteItemImage : DeleteImageRequest, IReturnVoid public class DeleteItemImage : DeleteImageRequest, IReturnVoid
{ {
/// <summary> /// <summary>
@ -153,6 +157,7 @@ namespace MediaBrowser.Api.Images
[Route("/Users/{Id}/Images/{Type}", "DELETE")] [Route("/Users/{Id}/Images/{Type}", "DELETE")]
[Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")] [Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")]
[Api(Description = "Deletes a user image")] [Api(Description = "Deletes a user image")]
[Authenticated]
public class DeleteUserImage : DeleteImageRequest, IReturnVoid public class DeleteUserImage : DeleteImageRequest, IReturnVoid
{ {
/// <summary> /// <summary>
@ -169,6 +174,7 @@ namespace MediaBrowser.Api.Images
[Route("/Users/{Id}/Images/{Type}", "POST")] [Route("/Users/{Id}/Images/{Type}", "POST")]
[Route("/Users/{Id}/Images/{Type}/{Index}", "POST")] [Route("/Users/{Id}/Images/{Type}/{Index}", "POST")]
[Api(Description = "Posts a user image")] [Api(Description = "Posts a user image")]
[Authenticated]
public class PostUserImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid public class PostUserImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
{ {
/// <summary> /// <summary>
@ -191,6 +197,7 @@ namespace MediaBrowser.Api.Images
[Route("/Items/{Id}/Images/{Type}", "POST")] [Route("/Items/{Id}/Images/{Type}", "POST")]
[Route("/Items/{Id}/Images/{Type}/{Index}", "POST")] [Route("/Items/{Id}/Images/{Type}/{Index}", "POST")]
[Api(Description = "Posts an item image")] [Api(Description = "Posts an item image")]
[Authenticated]
public class PostItemImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid public class PostItemImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
{ {
/// <summary> /// <summary>

@ -13,10 +13,16 @@ namespace MediaBrowser.Api
{ {
public class BaseRefreshRequest : IReturnVoid public class BaseRefreshRequest : IReturnVoid
{ {
[ApiMember(Name = "Forced", Description = "Indicates if a normal or forced refresh should occur.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "MetadataRefreshMode", Description = "Specifies the metadata refresh mode", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool Forced { get; set; } public MetadataRefreshMode MetadataRefreshMode { get; set; }
[ApiMember(Name = "ReplaceAllImages", Description = "Determines if images should be replaced during the refresh.", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "ImageRefreshMode", Description = "Specifies the image refresh mode", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public ImageRefreshMode ImageRefreshMode { get; set; }
[ApiMember(Name = "ReplaceAllMetadata", Description = "Determines if metadata should be replaced. Only applicable if mode is FullRefresh", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool ReplaceAllMetadata { get; set; }
[ApiMember(Name = "ReplaceAllImages", Description = "Determines if images should be replaced. Only applicable if mode is FullRefresh", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool ReplaceAllImages { get; set; } public bool ReplaceAllImages { get; set; }
} }
@ -93,7 +99,7 @@ namespace MediaBrowser.Api
private async Task RefreshItem(RefreshItem request, BaseItem item) private async Task RefreshItem(RefreshItem request, BaseItem item)
{ {
var options = GetRefreshOptions(request); var options = GetRefreshOptions(request);
try try
{ {
await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false); await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false);
@ -148,10 +154,10 @@ namespace MediaBrowser.Api
{ {
return new MetadataRefreshOptions return new MetadataRefreshOptions
{ {
MetadataRefreshMode = MetadataRefreshMode.FullRefresh, MetadataRefreshMode = request.MetadataRefreshMode,
ImageRefreshMode = ImageRefreshMode.FullRefresh, ImageRefreshMode = request.ImageRefreshMode,
ReplaceAllMetadata = request.Forced, ReplaceAllImages = request.ReplaceAllImages,
ReplaceAllImages = request.ReplaceAllImages ReplaceAllMetadata = request.ReplaceAllMetadata
}; };
} }
} }

@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.Audio;
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.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -226,6 +227,7 @@ namespace MediaBrowser.Api.Library
/// <summary> /// <summary>
/// Class LibraryService /// Class LibraryService
/// </summary> /// </summary>
[Authenticated]
public class LibraryService : BaseApiService public class LibraryService : BaseApiService
{ {
/// <summary> /// <summary>

@ -65,11 +65,12 @@
<Compile Include="..\SharedVersion.cs"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>
<Compile Include="BrandingService.cs" />
<Compile Include="ChannelService.cs" /> <Compile Include="ChannelService.cs" />
<Compile Include="Dlna\DlnaServerService.cs" /> <Compile Include="Dlna\DlnaServerService.cs" />
<Compile Include="Dlna\DlnaService.cs" /> <Compile Include="Dlna\DlnaService.cs" />
<Compile Include="Library\ChapterService.cs" /> <Compile Include="Library\ChapterService.cs" />
<Compile Include="Library\SubtitleService.cs" /> <Compile Include="Subtitles\SubtitleService.cs" />
<Compile Include="Movies\CollectionService.cs" /> <Compile Include="Movies\CollectionService.cs" />
<Compile Include="Music\AlbumsService.cs" /> <Compile Include="Music\AlbumsService.cs" />
<Compile Include="AppThemeService.cs" /> <Compile Include="AppThemeService.cs" />
@ -139,7 +140,6 @@
<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\LogFileWebSocketListener.cs" />
<Compile Include="WebSocket\SessionInfoWebSocketListener.cs" /> <Compile Include="WebSocket\SessionInfoWebSocketListener.cs" />
<Compile Include="WebSocket\SystemInfoWebSocketListener.cs" /> <Compile Include="WebSocket\SystemInfoWebSocketListener.cs" />
</ItemGroup> </ItemGroup>

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Notifications;
using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Notifications;
using ServiceStack; using ServiceStack;
@ -82,6 +83,7 @@ namespace MediaBrowser.Api
public string Ids { get; set; } public string Ids { get; set; }
} }
[Authenticated]
public class NotificationsService : BaseApiService public class NotificationsService : BaseApiService
{ {
private readonly INotificationsRepository _notificationsRepo; private readonly INotificationsRepository _notificationsRepo;

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using ServiceStack; using ServiceStack;
@ -14,6 +15,7 @@ namespace MediaBrowser.Api
/// Class GetSessions /// Class GetSessions
/// </summary> /// </summary>
[Route("/Sessions", "GET", Summary = "Gets a list of sessions")] [Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
[Authenticated]
public class GetSessions : IReturn<List<SessionInfoDto>> public class GetSessions : IReturn<List<SessionInfoDto>>
{ {
[ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
@ -27,6 +29,7 @@ namespace MediaBrowser.Api
/// Class DisplayContent /// Class DisplayContent
/// </summary> /// </summary>
[Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")] [Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")]
[Authenticated]
public class DisplayContent : IReturnVoid public class DisplayContent : IReturnVoid
{ {
/// <summary> /// <summary>
@ -59,6 +62,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Playing", "POST", Summary = "Instructs a session to play an item")] [Route("/Sessions/{Id}/Playing", "POST", Summary = "Instructs a session to play an item")]
[Authenticated]
public class Play : IReturnVoid public class Play : IReturnVoid
{ {
/// <summary> /// <summary>
@ -91,6 +95,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Playing/{Command}", "POST", Summary = "Issues a playstate command to a client")] [Route("/Sessions/{Id}/Playing/{Command}", "POST", Summary = "Issues a playstate command to a client")]
[Authenticated]
public class SendPlaystateCommand : IReturnVoid public class SendPlaystateCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -115,6 +120,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/System/{Command}", "POST", Summary = "Issues a system command to a client")] [Route("/Sessions/{Id}/System/{Command}", "POST", Summary = "Issues a system command to a client")]
[Authenticated]
public class SendSystemCommand : IReturnVoid public class SendSystemCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -133,6 +139,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Command/{Command}", "POST", Summary = "Issues a system command to a client")] [Route("/Sessions/{Id}/Command/{Command}", "POST", Summary = "Issues a system command to a client")]
[Authenticated]
public class SendGeneralCommand : IReturnVoid public class SendGeneralCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -151,6 +158,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Command", "POST", Summary = "Issues a system command to a client")] [Route("/Sessions/{Id}/Command", "POST", Summary = "Issues a system command to a client")]
[Authenticated]
public class SendFullGeneralCommand : GeneralCommand, IReturnVoid public class SendFullGeneralCommand : GeneralCommand, IReturnVoid
{ {
/// <summary> /// <summary>
@ -162,6 +170,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")] [Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")]
[Authenticated]
public class SendMessageCommand : IReturnVoid public class SendMessageCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -182,6 +191,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Users/{UserId}", "POST", Summary = "Adds an additional user to a session")] [Route("/Sessions/{Id}/Users/{UserId}", "POST", Summary = "Adds an additional user to a session")]
[Authenticated]
public class AddUserToSession : IReturnVoid public class AddUserToSession : IReturnVoid
{ {
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@ -192,6 +202,7 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/{Id}/Users/{UserId}", "DELETE", Summary = "Removes an additional user from a session")] [Route("/Sessions/{Id}/Users/{UserId}", "DELETE", Summary = "Removes an additional user from a session")]
[Authenticated]
public class RemoveUserFromSession : IReturnVoid public class RemoveUserFromSession : IReturnVoid
{ {
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@ -202,7 +213,6 @@ namespace MediaBrowser.Api
} }
[Route("/Sessions/Capabilities", "POST", Summary = "Updates capabilities for a device")] [Route("/Sessions/Capabilities", "POST", Summary = "Updates capabilities for a device")]
[Route("/Sessions/{Id}/Capabilities", "POST", Summary = "Updates capabilities for a device")]
public class PostCapabilities : IReturnVoid public class PostCapabilities : IReturnVoid
{ {
/// <summary> /// <summary>

@ -1,4 +1,6 @@
using MediaBrowser.Controller.Entities; using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
@ -9,37 +11,13 @@ using MediaBrowser.Model.Providers;
using ServiceStack; using ServiceStack;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Api.Library namespace MediaBrowser.Api.Subtitles
{ {
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format (vtt).")]
public class GetSubtitle
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string MediaSourceId { get; set; }
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
public int Index { get; set; }
[ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Format { get; set; }
[ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public long StartPositionTicks { get; set; }
}
[Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")] [Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")]
[Authenticated]
public class DeleteSubtitle public class DeleteSubtitle
{ {
/// <summary> /// <summary>
@ -54,6 +32,7 @@ namespace MediaBrowser.Api.Library
} }
[Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")] [Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")]
[Authenticated]
public class SearchRemoteSubtitles : IReturn<List<RemoteSubtitleInfo>> public class SearchRemoteSubtitles : IReturn<List<RemoteSubtitleInfo>>
{ {
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -64,6 +43,7 @@ namespace MediaBrowser.Api.Library
} }
[Route("/Items/{Id}/RemoteSearch/Subtitles/Providers", "GET")] [Route("/Items/{Id}/RemoteSearch/Subtitles/Providers", "GET")]
[Authenticated]
public class GetSubtitleProviders : IReturn<List<SubtitleProviderInfo>> public class GetSubtitleProviders : IReturn<List<SubtitleProviderInfo>>
{ {
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -71,6 +51,7 @@ namespace MediaBrowser.Api.Library
} }
[Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")] [Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")]
[Authenticated]
public class DownloadRemoteSubtitles : IReturnVoid public class DownloadRemoteSubtitles : IReturnVoid
{ {
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@ -81,13 +62,36 @@ namespace MediaBrowser.Api.Library
} }
[Route("/Providers/Subtitles/Subtitles/{Id}", "GET")] [Route("/Providers/Subtitles/Subtitles/{Id}", "GET")]
[Authenticated]
public class GetRemoteSubtitles : IReturnVoid public class GetRemoteSubtitles : IReturnVoid
{ {
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; } public string Id { get; set; }
} }
[Authenticated] [Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format (vtt).")]
public class GetSubtitle
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string MediaSourceId { get; set; }
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
public int Index { get; set; }
[ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Format { get; set; }
[ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public long StartPositionTicks { get; set; }
}
public class SubtitleService : BaseApiService public class SubtitleService : BaseApiService
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
@ -101,14 +105,6 @@ namespace MediaBrowser.Api.Library
_subtitleEncoder = subtitleEncoder; _subtitleEncoder = subtitleEncoder;
} }
public object Get(SearchRemoteSubtitles request)
{
var video = (Video)_libraryManager.GetItemById(request.Id);
var response = _subtitleManager.SearchSubtitles(video, request.Language, CancellationToken.None).Result;
return ToOptimizedResult(response);
}
public object Get(GetSubtitle request) public object Get(GetSubtitle request)
{ {
if (string.IsNullOrEmpty(request.Format)) if (string.IsNullOrEmpty(request.Format))
@ -131,14 +127,23 @@ namespace MediaBrowser.Api.Library
private async Task<Stream> GetSubtitles(GetSubtitle request) private async Task<Stream> GetSubtitles(GetSubtitle request)
{ {
return await _subtitleEncoder.GetSubtitles(request.Id, return await _subtitleEncoder.GetSubtitles(request.Id,
request.MediaSourceId, request.MediaSourceId,
request.Index, request.Index,
request.Format, request.Format,
request.StartPositionTicks, request.StartPositionTicks,
CancellationToken.None).ConfigureAwait(false); CancellationToken.None).ConfigureAwait(false);
} }
public object Get(SearchRemoteSubtitles request)
{
var video = (Video)_libraryManager.GetItemById(request.Id);
var response = _subtitleManager.SearchSubtitles(video, request.Language, CancellationToken.None).Result;
return ToOptimizedResult(response);
}
public void Delete(DeleteSubtitle request) public void Delete(DeleteSubtitle request)
{ {
var task = _subtitleManager.DeleteSubtitles(request.Id, request.Index); var task = _subtitleManager.DeleteSubtitles(request.Id, request.Index);

@ -1,6 +1,12 @@
using MediaBrowser.Controller; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using ServiceStack; using ServiceStack;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Api namespace MediaBrowser.Api
@ -18,15 +24,30 @@ namespace MediaBrowser.Api
/// Class RestartApplication /// Class RestartApplication
/// </summary> /// </summary>
[Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")] [Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")]
[Authenticated]
public class RestartApplication public class RestartApplication
{ {
} }
[Route("/System/Shutdown", "POST", Summary = "Shuts down the application")] [Route("/System/Shutdown", "POST", Summary = "Shuts down the application")]
[Authenticated]
public class ShutdownApplication public class ShutdownApplication
{ {
} }
[Route("/System/Logs", "GET", Summary = "Gets a list of available server log files")]
[Authenticated]
public class GetServerLogs : IReturn<List<LogFile>>
{
}
[Route("/System/Logs/Log", "GET", Summary = "Gets a log file")]
public class GetLogFile
{
[ApiMember(Name = "Name", Description = "The log file name.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Name { get; set; }
}
/// <summary> /// <summary>
/// Class SystemInfoService /// Class SystemInfoService
/// </summary> /// </summary>
@ -36,16 +57,59 @@ namespace MediaBrowser.Api
/// The _app host /// The _app host
/// </summary> /// </summary>
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SystemService" /> class. /// Initializes a new instance of the <see cref="SystemService" /> class.
/// </summary> /// </summary>
/// <param name="appHost">The app host.</param> /// <param name="appHost">The app host.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception> /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
public SystemService(IServerApplicationHost appHost) public SystemService(IServerApplicationHost appHost, IApplicationPaths appPaths, IFileSystem fileSystem)
{ {
_appHost = appHost; _appHost = appHost;
_appPaths = appPaths;
_fileSystem = fileSystem;
}
public object Get(GetServerLogs request)
{
List<FileInfo> files;
try
{
files = new DirectoryInfo(_appPaths.LogDirectoryPath)
.EnumerateFiles("*", SearchOption.AllDirectories)
.Where(i => string.Equals(i.Extension, ".txt", System.StringComparison.OrdinalIgnoreCase))
.ToList();
}
catch (DirectoryNotFoundException)
{
files = new List<FileInfo>();
}
var result = files.Select(i => new LogFile
{
DateCreated = _fileSystem.GetCreationTimeUtc(i),
DateModified = _fileSystem.GetLastWriteTimeUtc(i),
Name = i.Name,
Size = i.Length
}).OrderByDescending(i => i.DateModified)
.ThenByDescending(i => i.DateCreated)
.ThenBy(i => i.Name)
.ToList();
return ToOptimizedResult(result);
}
public object Get(GetLogFile request)
{
var file = new DirectoryInfo(_appPaths.LogDirectoryPath)
.EnumerateFiles("*", SearchOption.AllDirectories)
.First(i => string.Equals(i.Name, request.Name, System.StringComparison.OrdinalIgnoreCase));
return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
} }
/// <summary> /// <summary>

@ -717,9 +717,7 @@ namespace MediaBrowser.Api.UserLibrary
await _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None).ConfigureAwait(false); await _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None).ConfigureAwait(false);
data = _userDataRepository.GetUserData(user.Id, key); return _userDataRepository.GetUserDataDto(item, user);
return _dtoService.GetUserItemDataDto(data);
} }
/// <summary> /// <summary>
@ -766,9 +764,7 @@ namespace MediaBrowser.Api.UserLibrary
await _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None).ConfigureAwait(false); await _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None).ConfigureAwait(false);
data = _userDataRepository.GetUserData(user.Id, key); return _userDataRepository.GetUserDataDto(item, user);
return _dtoService.GetUserItemDataDto(data);
} }
/// <summary> /// <summary>
@ -936,7 +932,7 @@ namespace MediaBrowser.Api.UserLibrary
await item.MarkUnplayed(user, _userDataRepository).ConfigureAwait(false); await item.MarkUnplayed(user, _userDataRepository).ConfigureAwait(false);
} }
return _dtoService.GetUserItemDataDto(_userDataRepository.GetUserData(user.Id, item.GetUserDataKey())); return _userDataRepository.GetUserDataDto(item, user);
} }
} }
} }

@ -51,6 +51,7 @@ namespace MediaBrowser.Api
/// Class DeleteUser /// Class DeleteUser
/// </summary> /// </summary>
[Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")] [Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")]
[Authenticated]
public class DeleteUser : IReturnVoid public class DeleteUser : IReturnVoid
{ {
/// <summary> /// <summary>
@ -107,6 +108,7 @@ namespace MediaBrowser.Api
/// Class UpdateUserPassword /// Class UpdateUserPassword
/// </summary> /// </summary>
[Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")] [Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")]
[Authenticated]
public class UpdateUserPassword : IReturnVoid public class UpdateUserPassword : IReturnVoid
{ {
/// <summary> /// <summary>
@ -138,6 +140,7 @@ namespace MediaBrowser.Api
/// Class UpdateUser /// Class UpdateUser
/// </summary> /// </summary>
[Route("/Users/{Id}", "POST", Summary = "Updates a user")] [Route("/Users/{Id}", "POST", Summary = "Updates a user")]
[Authenticated]
public class UpdateUser : UserDto, IReturnVoid public class UpdateUser : UserDto, IReturnVoid
{ {
} }
@ -146,6 +149,7 @@ namespace MediaBrowser.Api
/// Class CreateUser /// Class CreateUser
/// </summary> /// </summary>
[Route("/Users", "POST", Summary = "Creates a user")] [Route("/Users", "POST", Summary = "Creates a user")]
[Authenticated]
public class CreateUser : UserDto, IReturn<UserDto> public class CreateUser : UserDto, IReturn<UserDto>
{ {
} }

@ -1,149 +0,0 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.WebSocket
{
/// <summary>
/// Class ScheduledTasksWebSocketListener
/// </summary>
public class LogFileWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<string>, LogFileWebSocketState>
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
protected override string Name
{
get { return "LogFile"; }
}
/// <summary>
/// The _kernel
/// </summary>
private readonly ILogManager _logManager;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="LogFileWebSocketListener" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="logManager">The log manager.</param>
public LogFileWebSocketListener(ILogger logger, ILogManager logManager, IFileSystem fileSystem)
: base(logger)
{
_logManager = logManager;
_fileSystem = fileSystem;
_logManager.LoggerLoaded += kernel_LoggerLoaded;
}
/// <summary>
/// Gets the data to send.
/// </summary>
/// <param name="state">The state.</param>
/// <returns>IEnumerable{System.String}.</returns>
protected override async Task<IEnumerable<string>> GetDataToSend(LogFileWebSocketState state)
{
if (!string.Equals(_logManager.LogFilePath, state.LastLogFilePath))
{
state.LastLogFilePath = _logManager.LogFilePath;
state.StartLine = 0;
}
var lines = await GetLogLines(state.LastLogFilePath, state.StartLine, _fileSystem).ConfigureAwait(false);
state.StartLine += lines.Count;
return lines;
}
/// <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 override void Dispose(bool dispose)
{
if (dispose)
{
_logManager.LoggerLoaded -= kernel_LoggerLoaded;
}
base.Dispose(dispose);
}
/// <summary>
/// Handles the LoggerLoaded event of the kernel control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
void kernel_LoggerLoaded(object sender, EventArgs e)
{
// Reset the startline for each connection whenever the logger reloads
lock (ActiveConnections)
{
foreach (var connection in ActiveConnections)
{
connection.Item4.StartLine = 0;
}
}
}
/// <summary>
/// Gets the log lines.
/// </summary>
/// <param name="logFilePath">The log file path.</param>
/// <param name="startLine">The start line.</param>
/// <returns>Task{IEnumerable{System.String}}.</returns>
internal static async Task<List<string>> GetLogLines(string logFilePath, int startLine, IFileSystem fileSystem)
{
var lines = new List<string>();
using (var fs = fileSystem.GetFileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
{
using (var reader = new StreamReader(fs))
{
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line.IndexOf("Info -", StringComparison.OrdinalIgnoreCase) != -1 ||
line.IndexOf("Warn -", StringComparison.OrdinalIgnoreCase) != -1 ||
line.IndexOf("Error -", StringComparison.OrdinalIgnoreCase) != -1)
{
lines.Add(line);
}
}
}
}
if (startLine > 0)
{
lines = lines.Skip(startLine).ToList();
}
return lines;
}
}
/// <summary>
/// Class LogFileWebSocketState
/// </summary>
public class LogFileWebSocketState : WebSocketListenerState
{
/// <summary>
/// Gets or sets the last log file path.
/// </summary>
/// <value>The last log file path.</value>
public string LastLogFilePath { get; set; }
/// <summary>
/// Gets or sets the start line.
/// </summary>
/// <value>The start line.</value>
public int StartLine { get; set; }
}
}

@ -199,6 +199,10 @@ namespace MediaBrowser.Common.Net
{ {
return "application/x-javascript"; return "application/x-javascript";
} }
if (ext.Equals(".map", StringComparison.OrdinalIgnoreCase))
{
return "application/x-javascript";
}
if (ext.Equals(".woff", StringComparison.OrdinalIgnoreCase)) if (ext.Equals(".woff", StringComparison.OrdinalIgnoreCase))
{ {

@ -25,13 +25,6 @@ namespace MediaBrowser.Controller.Dto
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetDtoId(BaseItem item); string GetDtoId(BaseItem item);
/// <summary>
/// Gets the user item data dto.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>UserItemDataDto.</returns>
UserItemDataDto GetUserItemDataDto(UserItemData data);
/// <summary> /// <summary>
/// Attaches the primary image aspect ratio. /// Attaches the primary image aspect ratio.
/// </summary> /// </summary>

@ -6,6 +6,7 @@ using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library; using MediaBrowser.Model.Library;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -1571,5 +1572,19 @@ namespace MediaBrowser.Controller.Entities
return path; return path;
} }
public virtual void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user)
{
if (RunTimeTicks.HasValue)
{
double pct = RunTimeTicks.Value;
if (pct > 0)
{
pct = userData.PlaybackPositionTicks / pct;
dto.PlayedPercentage = 100 * pct;
}
}
}
} }
} }

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MoreLinq; using MoreLinq;
using System; using System;
@ -769,6 +770,11 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
/// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentNullException"></exception>
public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
return GetChildren(user, includeLinkedChildren, false);
}
internal IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren, bool includeHidden)
{ {
if (user == null) if (user == null)
{ {
@ -780,7 +786,7 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>(); var list = new List<BaseItem>();
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false); var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, includeHidden, false);
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
} }
@ -796,9 +802,10 @@ namespace MediaBrowser.Controller.Entities
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <param name="list">The list.</param> /// <param name="list">The list.</param>
/// <param name="includeHidden">if set to <c>true</c> [include hidden].</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool recursive) private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool includeHidden, bool recursive)
{ {
var hasLinkedChildren = false; var hasLinkedChildren = false;
@ -806,7 +813,7 @@ namespace MediaBrowser.Controller.Entities
{ {
if (child.IsVisible(user)) if (child.IsVisible(user))
{ {
if (!child.IsHiddenFromUser(user)) if (includeHidden || !child.IsHiddenFromUser(user))
{ {
list.Add(child); list.Add(child);
} }
@ -815,7 +822,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var folder = (Folder)child; var folder = (Folder)child;
if (folder.AddChildrenToList(user, includeLinkedChildren, list, true)) if (folder.AddChildrenToList(user, includeLinkedChildren, list, includeHidden, true))
{ {
hasLinkedChildren = true; hasLinkedChildren = true;
} }
@ -855,7 +862,7 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>(); var list = new List<BaseItem>();
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true); var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, true);
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
} }
@ -1069,5 +1076,68 @@ namespace MediaBrowser.Controller.Entities
return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual) return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.All(i => i.IsUnplayed(user)); .All(i => i.IsUnplayed(user));
} }
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user)
{
var recursiveItemCount = 0;
var unplayed = 0;
double totalPercentPlayed = 0;
IEnumerable<BaseItem> children;
var folder = this;
var season = folder as Season;
if (season != null)
{
children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual);
}
else
{
children = folder.GetRecursiveChildren(user)
.Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual);
}
// Loop through each recursive child
foreach (var child in children)
{
recursiveItemCount++;
var isUnplayed = true;
var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey());
// Incrememt totalPercentPlayed
if (itemUserData != null)
{
if (itemUserData.Played)
{
totalPercentPlayed += 100;
isUnplayed = false;
}
else if (itemUserData.PlaybackPositionTicks > 0 && child.RunTimeTicks.HasValue && child.RunTimeTicks.Value > 0)
{
double itemPercent = itemUserData.PlaybackPositionTicks;
itemPercent /= child.RunTimeTicks.Value;
totalPercentPlayed += itemPercent;
}
}
if (isUnplayed)
{
unplayed++;
}
}
dto.UnplayedItemCount = unplayed;
if (recursiveItemCount > 0)
{
dto.PlayedPercentage = totalPercentPlayed / recursiveItemCount;
dto.Played = dto.PlayedPercentage.Value >= 100;
}
}
} }
} }

@ -1,4 +1,6 @@
 using MediaBrowser.Model.Dto;
using System;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
/// <summary> /// <summary>
@ -6,10 +8,24 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public interface IHasUserData public interface IHasUserData
{ {
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
Guid Id { get; set; }
/// <summary> /// <summary>
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetUserDataKey(); string GetUserDataKey();
/// <summary>
/// Fills the user data dto values.
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="userData">The user data.</param>
/// <param name="user">The user.</param>
void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user);
} }
} }

@ -91,7 +91,7 @@ namespace MediaBrowser.Controller.Entities.TV
{ {
get get
{ {
return FindParent<Season>(); return Season;
} }
} }

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -51,5 +52,10 @@ namespace MediaBrowser.Controller.Entities
LibraryManager.RegisterItem(item); LibraryManager.RegisterItem(item);
} }
} }
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user)
{
// Nothing meaninful here and will only waste resources
}
} }
} }

@ -44,7 +44,7 @@ namespace MediaBrowser.Controller.Entities
var excludeFolderIds = user.Configuration.ExcludeFoldersFromGrouping.Select(i => new Guid(i)).ToList(); var excludeFolderIds = user.Configuration.ExcludeFoldersFromGrouping.Select(i => new Guid(i)).ToList();
return user.RootFolder return user.RootFolder
.GetChildren(user, true) .GetChildren(user, true, true)
.OfType<Folder>() .OfType<Folder>()
.Where(i => !excludeFolderIds.Contains(i.Id) && !IsExcludedFromGrouping(i)); .Where(i => !excludeFolderIds.Contains(i.Id) && !IsExcludedFromGrouping(i));
} }

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Threading; using System.Threading;
@ -34,5 +35,13 @@ namespace MediaBrowser.Controller.Library
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns> /// <returns>Task{UserItemData}.</returns>
UserItemData GetUserData(Guid userId, string key); UserItemData GetUserData(Guid userId, string key);
/// <summary>
/// Gets the user data dto.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>UserItemDataDto.</returns>
UserItemDataDto GetUserDataDto(IHasUserData item, User user);
} }
} }

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
public interface ILiveTvRecording : IHasImages, IHasMediaSources public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData
{ {
string ServiceName { get; set; } string ServiceName { get; set; }
@ -20,8 +20,6 @@ namespace MediaBrowser.Controller.LiveTv
string GetClientTypeName(); string GetClientTypeName();
string GetUserDataKey();
bool IsParentalAllowed(User user); bool IsParentalAllowed(User user);
Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken); Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);

@ -1,6 +1,6 @@
using System; using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Providers namespace MediaBrowser.Controller.Providers
{ {
@ -18,6 +18,11 @@ namespace MediaBrowser.Controller.Providers
/// </summary> /// </summary>
[Obsolete] [Obsolete]
public bool ForceSave { get; set; } public bool ForceSave { get; set; }
public MetadataRefreshOptions()
{
MetadataRefreshMode = MetadataRefreshMode.Default;
}
} }
public class ImageRefreshOptions public class ImageRefreshOptions
@ -38,48 +43,54 @@ namespace MediaBrowser.Controller.Providers
public bool IsReplacingImage(ImageType type) public bool IsReplacingImage(ImageType type)
{ {
return ReplaceAllImages || ReplaceImages.Contains(type); return ImageRefreshMode == ImageRefreshMode.FullRefresh &&
(ReplaceAllImages || ReplaceImages.Contains(type));
} }
} }
public enum MetadataRefreshMode public enum MetadataRefreshMode
{ {
/// <summary> /// <summary>
/// Providers will be executed based on default rules /// The none
/// </summary> /// </summary>
EnsureMetadata = 0, None = 0,
/// <summary> /// <summary>
/// No providers will be executed /// The validation only
/// </summary> /// </summary>
None = 1, ValidationOnly = 1,
/// <summary> /// <summary>
/// All providers will be executed to search for new metadata /// Providers will be executed based on default rules
/// </summary> /// </summary>
FullRefresh = 2, Default = 2,
/// <summary> /// <summary>
/// The validation only /// All providers will be executed to search for new metadata
/// </summary> /// </summary>
ValidationOnly = 3 FullRefresh = 3
} }
public enum ImageRefreshMode public enum ImageRefreshMode
{ {
/// <summary>
/// The none
/// </summary>
None = 0,
/// <summary> /// <summary>
/// The default /// The default
/// </summary> /// </summary>
Default = 0, Default = 1,
/// <summary> /// <summary>
/// Existing images will be validated /// Existing images will be validated
/// </summary> /// </summary>
ValidationOnly = 1, ValidationOnly = 2,
/// <summary> /// <summary>
/// All providers will be executed to search for new metadata /// All providers will be executed to search for new metadata
/// </summary> /// </summary>
FullRefresh = 2 FullRefresh = 3
} }
} }

@ -219,7 +219,13 @@ namespace MediaBrowser.Controller.Session
/// <param name="deviceName">Name of the device.</param> /// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param> /// <param name="remoteEndPoint">The remote end point.</param>
/// <returns>Task{SessionInfo}.</returns> /// <returns>Task{SessionInfo}.</returns>
Task<AuthenticationResult> AuthenticateNewSession(string username, string password, string clientType, string appVersion, string deviceId, string deviceName, string remoteEndPoint); Task<AuthenticationResult> AuthenticateNewSession(string username,
string password,
string clientType,
string appVersion,
string deviceId,
string deviceName,
string remoteEndPoint);
/// <summary> /// <summary>
/// Reports the capabilities. /// Reports the capabilities.

@ -77,6 +77,9 @@
<Compile Include="..\MediaBrowser.Model\ApiClient\SessionUpdatesEventArgs.cs"> <Compile Include="..\MediaBrowser.Model\ApiClient\SessionUpdatesEventArgs.cs">
<Link>ApiClient\SessionUpdatesEventArgs.cs</Link> <Link>ApiClient\SessionUpdatesEventArgs.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Branding\BrandingOptions.cs">
<Link>Branding\BrandingOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Channels\ChannelFeatures.cs"> <Compile Include="..\MediaBrowser.Model\Channels\ChannelFeatures.cs">
<Link>Channels\ChannelFeatures.cs</Link> <Link>Channels\ChannelFeatures.cs</Link>
</Compile> </Compile>
@ -815,6 +818,9 @@
<Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs"> <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
<Link>Session\UserDataChangeInfo.cs</Link> <Link>Session\UserDataChangeInfo.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\System\LogFile.cs">
<Link>System\LogFile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\System\SystemInfo.cs"> <Compile Include="..\MediaBrowser.Model\System\SystemInfo.cs">
<Link>System\SystemInfo.cs</Link> <Link>System\SystemInfo.cs</Link>
</Compile> </Compile>

@ -64,6 +64,9 @@
<Compile Include="..\MediaBrowser.Model\ApiClient\SessionUpdatesEventArgs.cs"> <Compile Include="..\MediaBrowser.Model\ApiClient\SessionUpdatesEventArgs.cs">
<Link>ApiClient\SessionUpdatesEventArgs.cs</Link> <Link>ApiClient\SessionUpdatesEventArgs.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Branding\BrandingOptions.cs">
<Link>Branding\BrandingOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Channels\ChannelFeatures.cs"> <Compile Include="..\MediaBrowser.Model\Channels\ChannelFeatures.cs">
<Link>Channels\ChannelFeatures.cs</Link> <Link>Channels\ChannelFeatures.cs</Link>
</Compile> </Compile>
@ -796,6 +799,9 @@
<Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs"> <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
<Link>Session\UserDataChangeInfo.cs</Link> <Link>Session\UserDataChangeInfo.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\System\LogFile.cs">
<Link>System\LogFile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\System\SystemInfo.cs"> <Compile Include="..\MediaBrowser.Model\System\SystemInfo.cs">
<Link>System\SystemInfo.cs</Link> <Link>System\SystemInfo.cs</Link>
</Compile> </Compile>

@ -0,0 +1,12 @@

namespace MediaBrowser.Model.Branding
{
public class BrandingOptions
{
/// <summary>
/// Gets or sets the login disclaimer.
/// </summary>
/// <value>The login disclaimer.</value>
public string LoginDisclaimer { get; set; }
}
}

@ -158,9 +158,6 @@ namespace MediaBrowser.Model.Configuration
public bool EnableTmdbUpdates { get; set; } public bool EnableTmdbUpdates { get; set; }
public bool EnableFanArtUpdates { get; set; } public bool EnableFanArtUpdates { get; set; }
public bool RequireMobileManualLogin { get; set; }
public bool RequireNonMobileManualLogin { get; set; }
/// <summary> /// <summary>
/// Gets or sets the image saving convention. /// Gets or sets the image saving convention.
/// </summary> /// </summary>

@ -10,12 +10,15 @@ namespace MediaBrowser.Model.Configuration
public bool SaveImagePathsInNfo { get; set; } public bool SaveImagePathsInNfo { get; set; }
public bool EnablePathSubstitution { get; set; } public bool EnablePathSubstitution { get; set; }
public bool EnableExtraThumbsDuplication { get; set; }
public XbmcMetadataOptions() public XbmcMetadataOptions()
{ {
ReleaseDateFormat = "yyyy-MM-dd"; ReleaseDateFormat = "yyyy-MM-dd";
SaveImagePathsInNfo = true; SaveImagePathsInNfo = true;
EnablePathSubstitution = true; EnablePathSubstitution = true;
EnableExtraThumbsDuplication = true;
} }
} }
} }

@ -1,6 +1,6 @@
using System; using MediaBrowser.Model.Extensions;
using System;
using System.ComponentModel; using System.ComponentModel;
using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dto namespace MediaBrowser.Model.Dto
{ {
@ -15,6 +15,18 @@ namespace MediaBrowser.Model.Dto
/// <value>The rating.</value> /// <value>The rating.</value>
public double? Rating { get; set; } public double? Rating { get; set; }
/// <summary>
/// Gets or sets the played percentage.
/// </summary>
/// <value>The played percentage.</value>
public double? PlayedPercentage { get; set; }
/// <summary>
/// Gets or sets the unplayed item count.
/// </summary>
/// <value>The unplayed item count.</value>
public int? UnplayedItemCount { get; set; }
/// <summary> /// <summary>
/// Gets or sets the playback position ticks. /// Gets or sets the playback position ticks.
/// </summary> /// </summary>

@ -65,6 +65,7 @@
<Compile Include="ApiClient\IServerEvents.cs" /> <Compile Include="ApiClient\IServerEvents.cs" />
<Compile Include="ApiClient\GeneralCommandEventArgs.cs" /> <Compile Include="ApiClient\GeneralCommandEventArgs.cs" />
<Compile Include="ApiClient\SessionUpdatesEventArgs.cs" /> <Compile Include="ApiClient\SessionUpdatesEventArgs.cs" />
<Compile Include="Branding\BrandingOptions.cs" />
<Compile Include="Channels\ChannelFeatures.cs" /> <Compile Include="Channels\ChannelFeatures.cs" />
<Compile Include="Channels\ChannelInfo.cs" /> <Compile Include="Channels\ChannelInfo.cs" />
<Compile Include="Channels\ChannelItemQuery.cs" /> <Compile Include="Channels\ChannelItemQuery.cs" />
@ -294,6 +295,7 @@
<Compile Include="Session\SessionInfoDto.cs" /> <Compile Include="Session\SessionInfoDto.cs" />
<Compile Include="Session\SessionUserInfo.cs" /> <Compile Include="Session\SessionUserInfo.cs" />
<Compile Include="Session\UserDataChangeInfo.cs" /> <Compile Include="Session\UserDataChangeInfo.cs" />
<Compile Include="System\LogFile.cs" />
<Compile Include="Themes\AppTheme.cs" /> <Compile Include="Themes\AppTheme.cs" />
<Compile Include="Themes\AppThemeInfo.cs" /> <Compile Include="Themes\AppThemeInfo.cs" />
<Compile Include="Themes\ThemeImage.cs" /> <Compile Include="Themes\ThemeImage.cs" />

@ -1,5 +1,4 @@
using MediaBrowser.Model.Configuration; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace MediaBrowser.Model.Notifications namespace MediaBrowser.Model.Notifications

@ -1,32 +1,20 @@
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Session namespace MediaBrowser.Model.Session
{ {
[DebuggerDisplay("Client = {Client}, Username = {UserName}")] [DebuggerDisplay("Client = {Client}, Username = {UserName}")]
public class SessionInfoDto : IHasPropertyChangedEvent public class SessionInfoDto : IHasPropertyChangedEvent
{ {
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary> /// <summary>
/// Gets or sets the supported commands. /// Gets or sets the supported commands.
/// </summary> /// </summary>
/// <value>The supported commands.</value> /// <value>The supported commands.</value>
public List<string> SupportedCommands { get; set; } public List<string> SupportedCommands { get; set; }
/// <summary>
/// Gets or sets the remote end point.
/// </summary>
/// <value>The remote end point.</value>
public string RemoteEndPoint { get; set; }
/// <summary> /// <summary>
/// Gets or sets the queueable media types. /// Gets or sets the queueable media types.
@ -99,18 +87,6 @@ namespace MediaBrowser.Model.Session
/// </summary> /// </summary>
/// <value>The name of the device.</value> /// <value>The name of the device.</value>
public string DeviceName { get; set; } public string DeviceName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is paused.
/// </summary>
/// <value><c>true</c> if this instance is paused; otherwise, <c>false</c>.</value>
public bool IsPaused { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is muted.
/// </summary>
/// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value>
public bool IsMuted { get; set; }
/// <summary> /// <summary>
/// Gets or sets the now playing item. /// Gets or sets the now playing item.
@ -118,12 +94,6 @@ namespace MediaBrowser.Model.Session
/// <value>The now playing item.</value> /// <value>The now playing item.</value>
public BaseItemInfo NowPlayingItem { get; set; } public BaseItemInfo NowPlayingItem { get; set; }
/// <summary>
/// Gets or sets the now playing position ticks.
/// </summary>
/// <value>The now playing position ticks.</value>
public long? NowPlayingPositionTicks { get; set; }
/// <summary> /// <summary>
/// Gets or sets the device id. /// Gets or sets the device id.
/// </summary> /// </summary>

@ -0,0 +1,31 @@
using System;
namespace MediaBrowser.Model.System
{
public class LogFile
{
/// <summary>
/// Gets or sets the date created.
/// </summary>
/// <value>The date created.</value>
public DateTime DateCreated { get; set; }
/// <summary>
/// Gets or sets the date modified.
/// </summary>
/// <value>The date modified.</value>
public DateTime DateModified { get; set; }
/// <summary>
/// Gets or sets the size.
/// </summary>
/// <value>The size.</value>
public long Size { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Common.IO; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -441,11 +442,16 @@ namespace MediaBrowser.Providers.Manager
var extraFanartFilename = GetBackdropSaveFilename(item.GetImages(ImageType.Backdrop), "fanart", "fanart", outputIndex); var extraFanartFilename = GetBackdropSaveFilename(item.GetImages(ImageType.Backdrop), "fanart", "fanart", outputIndex);
return new[] var list = new List<string>
{ {
Path.Combine(item.ContainingFolderPath, "extrafanart", extraFanartFilename + extension), Path.Combine(item.ContainingFolderPath, "extrafanart", extraFanartFilename + extension)
Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension) };
};
if (EnableExtraThumbsDuplication)
{
list.Add(Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension));
}
return list.ToArray();
} }
if (type == ImageType.Primary) if (type == ImageType.Primary)
@ -528,6 +534,16 @@ namespace MediaBrowser.Providers.Manager
return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) }; return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) };
} }
private bool EnableExtraThumbsDuplication
{
get
{
var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
return config.EnableExtraThumbsDuplication;
}
}
/// <summary> /// <summary>
/// Gets the save path for item in mixed folder. /// Gets the save path for item in mixed folder.
/// </summary> /// </summary>

@ -234,7 +234,7 @@ namespace MediaBrowser.Providers.MediaInfo
await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false);
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
options.MetadataRefreshMode == MetadataRefreshMode.EnsureMetadata) options.MetadataRefreshMode == MetadataRefreshMode.Default)
{ {
try try
{ {
@ -460,7 +460,7 @@ namespace MediaBrowser.Providers.MediaInfo
var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, options.DirectoryService, false).ToList(); var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, options.DirectoryService, false).ToList();
var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.EnsureMetadata || var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default ||
options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
if (enableSubtitleDownloading && (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles && if (enableSubtitleDownloading && (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&

@ -0,0 +1,21 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Branding;
using System.Collections.Generic;
namespace MediaBrowser.Server.Implementations.Branding
{
public class BrandingConfigurationFactory : IConfigurationFactory
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
return new[]
{
new ConfigurationStore
{
ConfigurationType = typeof(BrandingOptions),
Key = "branding"
}
};
}
}
}

@ -212,6 +212,12 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
if (item.IsFolder) if (item.IsFolder)
{ {
var userData = _userDataRepository.GetUserData(user.Id, item.GetUserDataKey());
// Skip the user data manager because we've already looped through the recursive tree and don't want to do it twice
// TODO: Improve in future
dto.UserData = GetUserItemDataDto(userData);
var folder = (Folder)item; var folder = (Folder)item;
dto.ChildCount = GetChildCount(folder, user); dto.ChildCount = GetChildCount(folder, user);
@ -220,15 +226,15 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
SetSpecialCounts(folder, user, dto, fields); SetSpecialCounts(folder, user, dto, fields);
} }
}
var userData = _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()); dto.UserData.Played = dto.PlayedPercentage.HasValue && dto.PlayedPercentage.Value >= 100;
dto.UserData.PlayedPercentage = dto.PlayedPercentage;
dto.UserData = GetUserItemDataDto(userData); dto.UserData.UnplayedItemCount = dto.RecursiveUnplayedItemCount;
}
if (item.IsFolder) else
{ {
dto.UserData.Played = dto.PlayedPercentage.HasValue && dto.PlayedPercentage.Value >= 100; dto.UserData = _userDataRepository.GetUserDataDto(item, user);
} }
dto.PlayAccess = item.GetPlayAccess(user); dto.PlayAccess = item.GetPlayAccess(user);
@ -1110,16 +1116,17 @@ namespace MediaBrowser.Server.Implementations.Dto
if (episode != null) if (episode != null)
{ {
series = item.FindParent<Series>(); series = episode.Series;
dto.SeriesId = GetDtoId(series); if (series != null)
dto.SeriesName = series.Name; {
dto.AirTime = series.AirTime; dto.SeriesId = GetDtoId(series);
dto.SeriesStudio = series.Studios.FirstOrDefault(); dto.SeriesName = series.Name;
dto.AirTime = series.AirTime;
dto.SeriesThumbImageTag = GetImageCacheTag(series, ImageType.Thumb); dto.SeriesStudio = series.Studios.FirstOrDefault();
dto.SeriesThumbImageTag = GetImageCacheTag(series, ImageType.Thumb);
dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary); dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary);
}
} }
// Add SeasonInfo // Add SeasonInfo
@ -1127,14 +1134,17 @@ namespace MediaBrowser.Server.Implementations.Dto
if (season != null) if (season != null)
{ {
series = item.FindParent<Series>(); series = season.Series;
dto.SeriesId = GetDtoId(series); if (series != null)
dto.SeriesName = series.Name; {
dto.AirTime = series.AirTime; dto.SeriesId = GetDtoId(series);
dto.SeriesStudio = series.Studios.FirstOrDefault(); dto.SeriesName = series.Name;
dto.AirTime = series.AirTime;
dto.SeriesStudio = series.Studios.FirstOrDefault();
dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary); dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary);
}
} }
var game = item as Game; var game = item as Game;

@ -1,10 +1,11 @@
using MediaBrowser.Controller.Dto; 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.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using MoreLinq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -17,21 +18,21 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{ {
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IDtoService _dtoService;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly IUserManager _userManager;
private readonly object _syncLock = new object(); private readonly object _syncLock = new object();
private Timer UpdateTimer { get; set; } private Timer UpdateTimer { get; set; }
private const int UpdateDuration = 500; private const int UpdateDuration = 500;
private readonly Dictionary<Guid, List<string>> _changedKeys = new Dictionary<Guid, List<string>>(); private readonly Dictionary<Guid, List<IHasUserData>> _changedItems = new Dictionary<Guid, List<IHasUserData>>();
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IDtoService dtoService, ILogger logger) public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager)
{ {
_userDataManager = userDataManager; _userDataManager = userDataManager;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_dtoService = dtoService;
_logger = logger; _logger = logger;
_userManager = userManager;
} }
public void Run() public void Run()
@ -58,15 +59,28 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
UpdateTimer.Change(UpdateDuration, Timeout.Infinite); UpdateTimer.Change(UpdateDuration, Timeout.Infinite);
} }
List<string> keys; List<IHasUserData> keys;
if (!_changedKeys.TryGetValue(e.UserId, out keys)) if (!_changedItems.TryGetValue(e.UserId, out keys))
{ {
keys = new List<string>(); keys = new List<IHasUserData>();
_changedKeys[e.UserId] = keys; _changedItems[e.UserId] = keys;
} }
keys.Add(e.Key); keys.Add(e.Item);
var baseItem = e.Item as BaseItem;
// Go up one level for indicators
if (baseItem != null)
{
var parent = baseItem.Parent;
if (parent != null)
{
keys.Add(parent);
}
}
} }
} }
@ -75,8 +89,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
lock (_syncLock) lock (_syncLock)
{ {
// Remove dupes in case some were saved multiple times // Remove dupes in case some were saved multiple times
var changes = _changedKeys.ToList(); var changes = _changedItems.ToList();
_changedKeys.Clear(); _changedItems.Clear();
SendNotifications(changes, CancellationToken.None); SendNotifications(changes, CancellationToken.None);
@ -88,7 +102,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
} }
} }
private async Task SendNotifications(IEnumerable<KeyValuePair<Guid, List<string>>> changes, CancellationToken cancellationToken) private async Task SendNotifications(IEnumerable<KeyValuePair<Guid, List<IHasUserData>>> changes, CancellationToken cancellationToken)
{ {
foreach (var pair in changes) foreach (var pair in changes)
{ {
@ -99,8 +113,11 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
if (userSessions.Count > 0) if (userSessions.Count > 0)
{ {
var user = _userManager.GetUserById(userId);
var dtoList = pair.Value var dtoList = pair.Value
.Select(i => _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(userId, i))) .DistinctBy(i => i.Id)
.Select(i => _userDataManager.GetUserDataDto(i, user))
.ToList(); .ToList();
var info = new UserDataChangeInfo var info = new UserDataChangeInfo

@ -363,19 +363,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{ {
try try
{ {
var errorResponse = new ErrorResponse
{
ResponseStatus = new ResponseStatus
{
ErrorCode = ex.GetType().GetOperationName(),
Message = ex.Message,
StackTrace = ex.StackTrace,
}
};
var operationName = context.Request.GetOperationName(); var operationName = context.Request.GetOperationName();
var httpReq = GetRequest(context, operationName); var httpReq = GetRequest(context, operationName);
var httpRes = httpReq.Response; var httpRes = httpReq.Response;
if (httpRes.IsClosed)
{
return;
}
var contentType = httpReq.ResponseContentType; var contentType = httpReq.ResponseContentType;
var serializer = HostContext.ContentTypes.GetResponseSerializer(contentType); var serializer = HostContext.ContentTypes.GetResponseSerializer(contentType);
@ -398,6 +394,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer
httpRes.ContentType = contentType; httpRes.ContentType = contentType;
var errorResponse = new ErrorResponse
{
ResponseStatus = new ResponseStatus
{
ErrorCode = ex.GetType().GetOperationName(),
Message = ex.Message,
StackTrace = ex.StackTrace,
}
};
serializer(httpReq, errorResponse, httpRes); serializer(httpReq, errorResponse, httpRes);
httpRes.Close(); httpRes.Close();

@ -36,6 +36,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
auth.TryGetValue("Version", out version); auth.TryGetValue("Version", out version);
} }
var token = httpReq.Headers["X-MediaBrowser-Token"];
if (string.IsNullOrWhiteSpace(token))
{
token = httpReq.QueryString["api_key"];
}
return new AuthorizationInfo return new AuthorizationInfo
{ {
Client = client, Client = client,
@ -43,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
DeviceId = deviceId, DeviceId = deviceId,
UserId = userId, UserId = userId,
Version = version, Version = version,
Token = httpReq.Headers["X-AUTH-TOKEN"] Token = token
}; };
} }

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
@ -125,5 +126,41 @@ namespace MediaBrowser.Server.Implementations.Library
{ {
return userId + key; return userId + key;
} }
public UserItemDataDto GetUserDataDto(IHasUserData item, User user)
{
var userData = GetUserData(user.Id, item.GetUserDataKey());
var dto = GetUserItemDataDto(userData);
item.FillUserDataDtoValues(dto, userData, user);
return dto;
}
/// <summary>
/// Converts a UserItemData to a DTOUserItemData
/// </summary>
/// <param name="data">The data.</param>
/// <returns>DtoUserItemData.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
private UserItemDataDto GetUserItemDataDto(UserItemData data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
return new UserItemDataDto
{
IsFavorite = data.IsFavorite,
Likes = data.Likes,
PlaybackPositionTicks = data.PlaybackPositionTicks,
PlayCount = data.PlayCount,
Rating = data.Rating,
Played = data.Played,
LastPlayedDate = data.LastPlayedDate,
Key = data.Key
};
}
} }
} }

@ -4,7 +4,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -23,15 +22,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly IItemRepository _itemRepo;
public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger, IItemRepository itemRepo) public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger)
{ {
_dtoService = dtoService; _dtoService = dtoService;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_logger = logger; _logger = logger;
_itemRepo = itemRepo;
} }
public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel) public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel)
@ -249,7 +246,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (user != null) if (user != null)
{ {
dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, recording.GetUserDataKey())); dto.UserData = _userDataManager.GetUserDataDto(recording, user);
dto.PlayAccess = recording.GetPlayAccess(user); dto.PlayAccess = recording.GetPlayAccess(user);
} }
@ -322,7 +319,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (user != null) if (user != null)
{ {
dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey())); dto.UserData = _userDataManager.GetUserDataDto(info, user);
dto.PlayAccess = info.GetPlayAccess(user); dto.PlayAccess = info.GetPlayAccess(user);
} }
@ -401,7 +398,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (user != null) if (user != null)
{ {
dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, item.GetUserDataKey())); dto.UserData = _userDataManager.GetUserDataDto(item, user);
dto.PlayAccess = item.GetPlayAccess(user); dto.PlayAccess = item.GetPlayAccess(user);
} }

@ -40,7 +40,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly IJsonSerializer _json;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
@ -58,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1);
public LiveTvManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, IJsonSerializer json, ILocalizationManager localization) public LiveTvManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization)
{ {
_config = config; _config = config;
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -67,12 +66,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_userManager = userManager; _userManager = userManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_taskManager = taskManager; _taskManager = taskManager;
_json = json;
_localization = localization; _localization = localization;
_dtoService = dtoService; _dtoService = dtoService;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo); _tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger);
} }
/// <summary> /// <summary>

@ -258,11 +258,11 @@
"LabelCachePath": "Cache path:", "LabelCachePath": "Cache path:",
"LabelCachePathHelp": "This folder contains server cache files, such as images.", "LabelCachePathHelp": "This folder contains server cache files, such as images.",
"LabelImagesByNamePath": "Images by name path:", "LabelImagesByNamePath": "Images by name path:",
"LabelImagesByNamePathHelp": "This folder contains actor, artist, genre and studio images.", "LabelImagesByNamePathHelp": "This folder contains downloaded actor, artist, genre and studio images.",
"LabelMetadataPath": "Metadata path:", "LabelMetadataPath": "Metadata path:",
"LabelMetadataPathHelp": "This location contains downloaded artwork and metadata that is not configured to be stored in media folders.", "LabelMetadataPathHelp": "This location contains downloaded artwork and metadata that is not configured to be stored in media folders.",
"LabelTranscodingTempPath": "Transcoding temporary path:", "LabelTranscodingTempPath": "Transcoding temporary path:",
"LabelTranscodingTempPathHelp": "This folder contains working files used by the transcoder.", "LabelTranscodingTempPathHelp": "This folder contains working files used by the transcoder. Specify a custom path, or leave empty to use the default within the server's data folder.",
"TabBasics": "Basics", "TabBasics": "Basics",
"TabTV": "TV", "TabTV": "TV",
"TabGames": "Games", "TabGames": "Games",
@ -284,7 +284,7 @@
"ButtonAutoScroll": "Auto-scroll", "ButtonAutoScroll": "Auto-scroll",
"LabelImageSavingConvention": "Image saving convention:", "LabelImageSavingConvention": "Image saving convention:",
"LabelImageSavingConventionHelp": "Media Browser recognizes images from most major media applications. Choosing your downloading convention is useful if you also use other products.", "LabelImageSavingConventionHelp": "Media Browser recognizes images from most major media applications. Choosing your downloading convention is useful if you also use other products.",
"OptionImageSavingCompatible": "Compatible - Media Browser/Plex/Xbmc", "OptionImageSavingCompatible": "Compatible - Media Browser/Xbmc/Plex",
"OptionImageSavingStandard": "Standard - MB2", "OptionImageSavingStandard": "Standard - MB2",
"ButtonSignIn": "Sign In", "ButtonSignIn": "Sign In",
"TitleSignIn": "Sign In", "TitleSignIn": "Sign In",
@ -849,5 +849,14 @@
"LabelXbmcMetadataEnablePathSubstitutionHelp2": "See path substitution.", "LabelXbmcMetadataEnablePathSubstitutionHelp2": "See path substitution.",
"LabelGroupChannelsIntoViews": "Display the following channels directly within my views:", "LabelGroupChannelsIntoViews": "Display the following channels directly within my views:",
"LabelGroupChannelsIntoViewsHelp": "If enabled, these channels will be displayed directly alongside other views. If disabled, they'll be displayed within a separate Channels view.", "LabelGroupChannelsIntoViewsHelp": "If enabled, these channels will be displayed directly alongside other views. If disabled, they'll be displayed within a separate Channels view.",
"LabelDisplayCollectionsView": "Display a Collections view to show movie collections" "LabelDisplayCollectionsView": "Display a Collections view to show movie collections",
"LabelXbmcMetadataEnableExtraThumbs": "Copy extrafanart into extrathumbs",
"LabelXbmcMetadataEnableExtraThumbsHelp": "When downloading images they can be saved into both extrafanart and extrathumbs for maximum Xbmc skin compatibility.",
"TabServices": "Services",
"TabLogs": "Logs",
"HeaderServerLogFiles": "Server log files:",
"TabBranding": "Branding",
"HeaderBrandingHelp": "Customize the appearance of Media Browser to fit the needs of your group or organization.",
"LabelLoginDisclaimer": "Login disclaimer:",
"LabelLoginDisclaimerHelp": "This will be displayed at the bottom of the login page."
} }

@ -101,6 +101,7 @@
<Compile Include="..\SharedVersion.cs"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>
<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" />
<Compile Include="Channels\ChannelImageProvider.cs" /> <Compile Include="Channels\ChannelImageProvider.cs" />

@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Notifications;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Notifications;
using System; using System;
@ -93,7 +92,9 @@ namespace MediaBrowser.Server.Implementations.Notifications
if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType)) if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType))
{ {
return _userManager.Users.Where(i => _config.Configuration.NotificationOptions.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Configuration)) var config = GetConfiguration();
return _userManager.Users.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Configuration))
.Select(i => i.Id.ToString("N")); .Select(i => i.Id.ToString("N"));
} }

@ -1,4 +1,6 @@
using MediaBrowser.Common.Events; using System.Security.Cryptography;
using System.Text;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -1185,6 +1187,24 @@ namespace MediaBrowser.Server.Implementations.Session
}; };
} }
private bool IsLocal(string remoteEndpoint)
{
if (string.IsNullOrWhiteSpace(remoteEndpoint))
{
throw new ArgumentNullException("remoteEndpoint");
}
// Private address space:
// http://en.wikipedia.org/wiki/Private_network
return remoteEndpoint.IndexOf("localhost", StringComparison.OrdinalIgnoreCase) != -1 ||
remoteEndpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
remoteEndpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) ||
remoteEndpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) ||
remoteEndpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
remoteEndpoint.StartsWith("::", StringComparison.OrdinalIgnoreCase);
}
/// <summary> /// <summary>
/// Reports the capabilities. /// Reports the capabilities.
/// </summary> /// </summary>
@ -1283,15 +1303,10 @@ namespace MediaBrowser.Server.Implementations.Session
DeviceName = session.DeviceName, DeviceName = session.DeviceName,
Id = session.Id, Id = session.Id,
LastActivityDate = session.LastActivityDate, LastActivityDate = session.LastActivityDate,
NowPlayingPositionTicks = session.PlayState.PositionTicks,
IsPaused = session.PlayState.IsPaused,
IsMuted = session.PlayState.IsMuted,
NowViewingItem = session.NowViewingItem, NowViewingItem = session.NowViewingItem,
ApplicationVersion = session.ApplicationVersion, ApplicationVersion = session.ApplicationVersion,
CanSeek = session.PlayState.CanSeek,
QueueableMediaTypes = session.QueueableMediaTypes, QueueableMediaTypes = session.QueueableMediaTypes,
PlayableMediaTypes = session.PlayableMediaTypes, PlayableMediaTypes = session.PlayableMediaTypes,
RemoteEndPoint = session.RemoteEndPoint,
AdditionalUsers = session.AdditionalUsers, AdditionalUsers = session.AdditionalUsers,
SupportedCommands = session.SupportedCommands, SupportedCommands = session.SupportedCommands,
UserName = session.UserName, UserName = session.UserName,

@ -26,13 +26,36 @@ namespace MediaBrowser.Server.Implementations.Sorting
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private DateTime GetValue(BaseItem x) private DateTime GetValue(BaseItem x)
{ {
var series = (x as Series) ?? x.FindParent<Series>(); var series = x as Series;
DateTime result; if (series == null)
if (series != null && DateTime.TryParse(series.AirTime, out result))
{ {
return result; var season = x as Season;
}
if (season != null)
{
series = season.Series;
}
else
{
var episode = x as Episode;
if (episode != null)
{
series = episode.Series;
}
}
}
if (series != null)
{
DateTime result;
if (DateTime.TryParse(series.AirTime, out result))
{
return result;
}
}
return DateTime.MinValue; return DateTime.MinValue;
} }

@ -654,7 +654,7 @@ namespace MediaBrowser.ServerApplication
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor); var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);
RegisterSingleInstance<ICollectionManager>(collectionManager); RegisterSingleInstance<ICollectionManager>(collectionManager);
LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, JsonSerializer, LocalizationManager); LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager);
RegisterSingleInstance(LiveTvManager); RegisterSingleInstance(LiveTvManager);
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, FileSystemManager, UserManager, ChannelManager, LiveTvManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, FileSystemManager, UserManager, ChannelManager, LiveTvManager);

@ -86,18 +86,6 @@ namespace MediaBrowser.ServerApplication.Native
appHost.WebApplicationName + "/swagger-ui/index.html", logger); appHost.WebApplicationName + "/swagger-ui/index.html", logger);
} }
/// <summary>
/// Opens the standard API documentation.
/// </summary>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="appHost">The app host.</param>
/// <param name="logger">The logger.</param>
public static void OpenStandardApiDocumentation(IServerConfigurationManager configurationManager, IServerApplicationHost appHost, ILogger logger)
{
OpenUrl("http://localhost:" + configurationManager.Configuration.HttpServerPortNumber + "/" +
appHost.WebApplicationName + "/metadata", logger);
}
/// <summary> /// <summary>
/// Opens the URL. /// Opens the URL.
/// </summary> /// </summary>

@ -29,7 +29,6 @@ namespace MediaBrowser.ServerApplication
private System.Windows.Forms.ToolStripMenuItem cmdLogWindow; private System.Windows.Forms.ToolStripMenuItem cmdLogWindow;
private System.Windows.Forms.ToolStripMenuItem cmdCommunity; private System.Windows.Forms.ToolStripMenuItem cmdCommunity;
private System.Windows.Forms.ToolStripMenuItem cmdApiDocs; private System.Windows.Forms.ToolStripMenuItem cmdApiDocs;
private System.Windows.Forms.ToolStripMenuItem cmdStandardDocs;
private System.Windows.Forms.ToolStripMenuItem cmdSwagger; private System.Windows.Forms.ToolStripMenuItem cmdSwagger;
private System.Windows.Forms.ToolStripMenuItem cmdGtihub; private System.Windows.Forms.ToolStripMenuItem cmdGtihub;
@ -90,7 +89,6 @@ namespace MediaBrowser.ServerApplication
cmdConfigure = new System.Windows.Forms.ToolStripMenuItem(); cmdConfigure = new System.Windows.Forms.ToolStripMenuItem();
cmdBrowse = new System.Windows.Forms.ToolStripMenuItem(); cmdBrowse = new System.Windows.Forms.ToolStripMenuItem();
cmdApiDocs = new System.Windows.Forms.ToolStripMenuItem(); cmdApiDocs = new System.Windows.Forms.ToolStripMenuItem();
cmdStandardDocs = new System.Windows.Forms.ToolStripMenuItem();
cmdSwagger = new System.Windows.Forms.ToolStripMenuItem(); cmdSwagger = new System.Windows.Forms.ToolStripMenuItem();
cmdGtihub = new System.Windows.Forms.ToolStripMenuItem(); cmdGtihub = new System.Windows.Forms.ToolStripMenuItem();
@ -169,17 +167,11 @@ namespace MediaBrowser.ServerApplication
// cmdApiDocs // cmdApiDocs
// //
cmdApiDocs.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { cmdApiDocs.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
cmdStandardDocs,
cmdSwagger, cmdSwagger,
cmdGtihub}); cmdGtihub});
cmdApiDocs.Name = "cmdApiDocs"; cmdApiDocs.Name = "cmdApiDocs";
cmdApiDocs.Size = new System.Drawing.Size(208, 22); cmdApiDocs.Size = new System.Drawing.Size(208, 22);
// //
// cmdStandardDocs
//
cmdStandardDocs.Name = "cmdStandardDocs";
cmdStandardDocs.Size = new System.Drawing.Size(136, 22);
//
// cmdSwagger // cmdSwagger
// //
cmdSwagger.Name = "cmdSwagger"; cmdSwagger.Name = "cmdSwagger";
@ -199,7 +191,6 @@ namespace MediaBrowser.ServerApplication
cmdLibraryExplorer.Click += cmdLibraryExplorer_Click; cmdLibraryExplorer.Click += cmdLibraryExplorer_Click;
cmdSwagger.Click += cmdSwagger_Click; cmdSwagger.Click += cmdSwagger_Click;
cmdStandardDocs.Click += cmdStandardDocs_Click;
cmdGtihub.Click += cmdGtihub_Click; cmdGtihub.Click += cmdGtihub_Click;
LoadLogWindow(null, EventArgs.Empty); LoadLogWindow(null, EventArgs.Empty);
@ -224,7 +215,6 @@ namespace MediaBrowser.ServerApplication
cmdCommunity.Text = _localization.GetLocalizedString("LabelVisitCommunity"); cmdCommunity.Text = _localization.GetLocalizedString("LabelVisitCommunity");
cmdGtihub.Text = _localization.GetLocalizedString("LabelGithubWiki"); cmdGtihub.Text = _localization.GetLocalizedString("LabelGithubWiki");
cmdSwagger.Text = _localization.GetLocalizedString("LabelSwagger"); cmdSwagger.Text = _localization.GetLocalizedString("LabelSwagger");
cmdStandardDocs.Text = _localization.GetLocalizedString("LabelStandard");
cmdApiDocs.Text = _localization.GetLocalizedString("LabelViewApiDocumentation"); cmdApiDocs.Text = _localization.GetLocalizedString("LabelViewApiDocumentation");
cmdBrowse.Text = _localization.GetLocalizedString("LabelBrowseLibrary"); cmdBrowse.Text = _localization.GetLocalizedString("LabelBrowseLibrary");
cmdConfigure.Text = _localization.GetLocalizedString("LabelConfigureMediaBrowser"); cmdConfigure.Text = _localization.GetLocalizedString("LabelConfigureMediaBrowser");
@ -346,11 +336,6 @@ namespace MediaBrowser.ServerApplication
BrowserLauncher.OpenGithub(_logger); BrowserLauncher.OpenGithub(_logger);
} }
void cmdStandardDocs_Click(object sender, EventArgs e)
{
BrowserLauncher.OpenStandardApiDocumentation(_configurationManager, _appHost, _logger);
}
void cmdSwagger_Click(object sender, EventArgs e) void cmdSwagger_Click(object sender, EventArgs e)
{ {
BrowserLauncher.OpenSwagger(_configurationManager, _appHost, _logger); BrowserLauncher.OpenSwagger(_configurationManager, _appHost, _logger);

@ -521,6 +521,7 @@ namespace MediaBrowser.WebDashboard.Api
"mediacontroller.js", "mediacontroller.js",
"chromecast.js", "chromecast.js",
"backdrops.js", "backdrops.js",
"branding.js",
"mediaplayer.js", "mediaplayer.js",
"mediaplayer-video.js", "mediaplayer-video.js",
@ -529,7 +530,6 @@ namespace MediaBrowser.WebDashboard.Api
"ratingdialog.js", "ratingdialog.js",
"aboutpage.js", "aboutpage.js",
"allusersettings.js",
"alphapicker.js", "alphapicker.js",
"addpluginpage.js", "addpluginpage.js",
"advancedconfigurationpage.js", "advancedconfigurationpage.js",
@ -537,7 +537,6 @@ namespace MediaBrowser.WebDashboard.Api
"advancedserversettings.js", "advancedserversettings.js",
"metadataadvanced.js", "metadataadvanced.js",
"appsplayback.js", "appsplayback.js",
"appsweather.js",
"autoorganizetv.js", "autoorganizetv.js",
"autoorganizelog.js", "autoorganizelog.js",
"channels.js", "channels.js",

@ -98,6 +98,9 @@
<Content Include="dashboard-ui\appsplayback.html"> <Content Include="dashboard-ui\appsplayback.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\branding.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\channelitems.html"> <Content Include="dashboard-ui\channelitems.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -392,9 +395,6 @@
<Content Include="dashboard-ui\musicalbumartists.html"> <Content Include="dashboard-ui\musicalbumartists.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\allusersettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\collections.html"> <Content Include="dashboard-ui\collections.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -617,6 +617,9 @@
<Content Include="dashboard-ui\scripts\backdrops.js"> <Content Include="dashboard-ui\scripts\backdrops.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\branding.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\channelitems.js"> <Content Include="dashboard-ui\scripts\channelitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -1499,9 +1502,6 @@
<Content Include="dashboard-ui\musicvideos.html"> <Content Include="dashboard-ui\musicvideos.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\allusersettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\alphapicker.js"> <Content Include="dashboard-ui\scripts\alphapicker.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -1748,11 +1748,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="dashboard-ui\appsweather.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="dashboard-ui\useredit.html"> <Content Include="dashboard-ui\useredit.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -1814,11 +1809,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="dashboard-ui\scripts\appsweather.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="dashboard-ui\scripts\pluginspage.js"> <Content Include="dashboard-ui\scripts\pluginspage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

Loading…
Cancel
Save