mirror of https://github.com/jellyfin/jellyfin.git
add subtitle management page
This commit is contained in:
parent
26aa47eefd
commit
c8e4889ac7
|
@ -7,7 +7,6 @@ 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.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Controller.Subtitles;
|
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Providers;
|
using MediaBrowser.Model.Providers;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
|
@ -32,16 +31,6 @@ namespace MediaBrowser.Api
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")]
|
|
||||||
public class SearchRemoteSubtitles : IReturn<List<RemoteSubtitleInfo>>
|
|
||||||
{
|
|
||||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
[ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
|
||||||
public string Language { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("/Items/RemoteSearch/Movie", "POST")]
|
[Route("/Items/RemoteSearch/Movie", "POST")]
|
||||||
[Api(Description = "Gets external id infos for an item")]
|
[Api(Description = "Gets external id infos for an item")]
|
||||||
public class GetMovieRemoteSearchResults : RemoteSearchQuery<MovieInfo>, IReturn<List<RemoteSearchResult>>
|
public class GetMovieRemoteSearchResults : RemoteSearchQuery<MovieInfo>, IReturn<List<RemoteSearchResult>>
|
||||||
|
@ -121,24 +110,13 @@ namespace MediaBrowser.Api
|
||||||
private readonly IServerApplicationPaths _appPaths;
|
private readonly IServerApplicationPaths _appPaths;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ISubtitleManager _subtitleManager;
|
|
||||||
|
|
||||||
public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager, ISubtitleManager subtitleManager)
|
public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
_providerManager = providerManager;
|
_providerManager = providerManager;
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_subtitleManager = subtitleManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(GetExternalIdInfos request)
|
public object Get(GetExternalIdInfos request)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Controller.Channels;
|
||||||
using MediaBrowser.Controller.Channels;
|
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
@ -35,21 +34,6 @@ namespace MediaBrowser.Api.Library
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Videos/{Id}/Subtitles/{Index}", "GET")]
|
|
||||||
[Api(Description = "Gets an external subtitle file")]
|
|
||||||
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 = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
|
|
||||||
public int Index { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GetCriticReviews
|
/// Class GetCriticReviews
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -305,25 +289,6 @@ namespace MediaBrowser.Api.Library
|
||||||
return ToStaticFileResult(item.Path);
|
return ToStaticFileResult(item.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetSubtitle request)
|
|
||||||
{
|
|
||||||
var subtitleStream = _itemRepo.GetMediaStreams(new MediaStreamQuery
|
|
||||||
{
|
|
||||||
|
|
||||||
Index = request.Index,
|
|
||||||
ItemId = new Guid(request.Id),
|
|
||||||
Type = MediaStreamType.Subtitle
|
|
||||||
|
|
||||||
}).FirstOrDefault();
|
|
||||||
|
|
||||||
if (subtitleStream == null)
|
|
||||||
{
|
|
||||||
throw new ResourceNotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ToStaticFileResult(subtitleStream.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the specified request.
|
/// Gets the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Controller.Subtitles;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
using ServiceStack;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Api.Library
|
||||||
|
{
|
||||||
|
[Route("/Videos/{Id}/Subtitles/{Index}", "GET", Summary = "Gets an external subtitle file")]
|
||||||
|
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 = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
|
||||||
|
public int Index { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")]
|
||||||
|
public class DeleteSubtitle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the id.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The id.</value>
|
||||||
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "DELETE")]
|
||||||
|
public int Index { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")]
|
||||||
|
public class SearchRemoteSubtitles : IReturn<List<RemoteSubtitleInfo>>
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||||
|
public string Language { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Items/{Id}/RemoteSearch/Subtitles/Providers", "GET")]
|
||||||
|
public class GetSubtitleProviders : IReturn<List<SubtitleProviderInfo>>
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")]
|
||||||
|
public class DownloadRemoteSubtitles : IReturnVoid
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "SubtitleId", Description = "SubtitleId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
|
public string SubtitleId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Providers/Subtitles/Subtitles/{Id}", "GET")]
|
||||||
|
public class GetRemoteSubtitles : IReturnVoid
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SubtitleService : BaseApiService
|
||||||
|
{
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly ISubtitleManager _subtitleManager;
|
||||||
|
private readonly IItemRepository _itemRepo;
|
||||||
|
|
||||||
|
public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, IItemRepository itemRepo)
|
||||||
|
{
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
_subtitleManager = subtitleManager;
|
||||||
|
_itemRepo = itemRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var subtitleStream = _itemRepo.GetMediaStreams(new MediaStreamQuery
|
||||||
|
{
|
||||||
|
|
||||||
|
Index = request.Index,
|
||||||
|
ItemId = new Guid(request.Id),
|
||||||
|
Type = MediaStreamType.Subtitle
|
||||||
|
|
||||||
|
}).FirstOrDefault();
|
||||||
|
|
||||||
|
if (subtitleStream == null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToStaticFileResult(subtitleStream.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(DeleteSubtitle request)
|
||||||
|
{
|
||||||
|
var task = _subtitleManager.DeleteSubtitles(request.Id, request.Index);
|
||||||
|
|
||||||
|
Task.WaitAll(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Get(GetSubtitleProviders request)
|
||||||
|
{
|
||||||
|
var result = _subtitleManager.GetProviders(request.Id);
|
||||||
|
|
||||||
|
return ToOptimizedResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Get(GetRemoteSubtitles request)
|
||||||
|
{
|
||||||
|
var result = _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).Result;
|
||||||
|
|
||||||
|
return ResultFactory.GetResult(result.Stream, MimeTypes.GetMimeType("file." + result.Format));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Post(DownloadRemoteSubtitles request)
|
||||||
|
{
|
||||||
|
var video = (Video)_libraryManager.GetItemById(request.Id);
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
await video.RefreshMetadata(new MetadataRefreshOptions(), CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("Error downloading subtitles", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,6 +68,7 @@
|
||||||
<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\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" />
|
||||||
|
|
|
@ -34,17 +34,22 @@ namespace MediaBrowser.Controller.Providers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Providers will be executed based on default rules
|
/// Providers will be executed based on default rules
|
||||||
/// </summary>
|
/// </summary>
|
||||||
EnsureMetadata,
|
EnsureMetadata = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No providers will be executed
|
/// No providers will be executed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
None,
|
None = 1,
|
||||||
|
|
||||||
/// <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
|
FullRefresh = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The validation only
|
||||||
|
/// </summary>
|
||||||
|
ValidationOnly = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ImageRefreshMode
|
public enum ImageRefreshMode
|
||||||
|
@ -52,16 +57,16 @@ namespace MediaBrowser.Controller.Providers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default
|
/// The default
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Default,
|
Default = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Existing images will be validated
|
/// Existing images will be validated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ValidationOnly,
|
ValidationOnly = 1,
|
||||||
|
|
||||||
/// <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
|
FullRefresh = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,33 @@ namespace MediaBrowser.Controller.Subtitles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="video">The video.</param>
|
/// <param name="video">The video.</param>
|
||||||
/// <param name="subtitleId">The subtitle identifier.</param>
|
/// <param name="subtitleId">The subtitle identifier.</param>
|
||||||
/// <param name="providerName">Name of the provider.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task DownloadSubtitles(Video video,
|
Task DownloadSubtitles(Video video,
|
||||||
string subtitleId,
|
string subtitleId,
|
||||||
string providerName,
|
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the remote subtitles.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The identifier.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task{SubtitleResponse}.</returns>
|
||||||
|
Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the subtitles.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item identifier.</param>
|
||||||
|
/// <param name="index">The index.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
Task DeleteSubtitles(string itemId, int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the providers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item identifier.</param>
|
||||||
|
/// <returns>IEnumerable{SubtitleProviderInfo}.</returns>
|
||||||
|
IEnumerable<SubtitleProviderInfo> GetProviders(string itemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace MediaBrowser.Controller.Subtitles
|
||||||
{
|
{
|
||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
public string Format { get; set; }
|
public string Format { get; set; }
|
||||||
|
public bool IsForced { get; set; }
|
||||||
public Stream Stream { get; set; }
|
public Stream Stream { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,8 +21,11 @@ namespace MediaBrowser.Controller.Subtitles
|
||||||
public long? RuntimeTicks { get; set; }
|
public long? RuntimeTicks { get; set; }
|
||||||
public Dictionary<string, string> ProviderIds { get; set; }
|
public Dictionary<string, string> ProviderIds { get; set; }
|
||||||
|
|
||||||
|
public bool SearchAllProviders { get; set; }
|
||||||
|
|
||||||
public SubtitleSearchRequest()
|
public SubtitleSearchRequest()
|
||||||
{
|
{
|
||||||
|
SearchAllProviders = true;
|
||||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Entities
|
namespace MediaBrowser.Model.Entities
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,4 +16,10 @@ namespace MediaBrowser.Model.Providers
|
||||||
public int? DownloadCount { get; set; }
|
public int? DownloadCount { get; set; }
|
||||||
public bool? IsHashMatch { get; set; }
|
public bool? IsHashMatch { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SubtitleProviderInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager);
|
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager);
|
||||||
|
|
||||||
return prober.ProbeVideo(item, directoryService, cancellationToken);
|
return prober.ProbeVideo(item, directoryService, true, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken)
|
public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken)
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
_subtitleManager = subtitleManager;
|
_subtitleManager = subtitleManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ItemUpdateType> ProbeVideo<T>(T item, IDirectoryService directoryService, CancellationToken cancellationToken)
|
public async Task<ItemUpdateType> ProbeVideo<T>(T item, IDirectoryService directoryService, bool enableSubtitleDownloading, CancellationToken cancellationToken)
|
||||||
where T : Video
|
where T : Video
|
||||||
{
|
{
|
||||||
var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false);
|
var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false);
|
||||||
|
@ -105,7 +105,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, directoryService).ConfigureAwait(false);
|
await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, directoryService, enableSubtitleDownloading).ConfigureAwait(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -160,7 +160,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, BlurayDiscInfo blurayInfo, IDirectoryService directoryService)
|
protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, BlurayDiscInfo blurayInfo, IDirectoryService directoryService, bool enableSubtitleDownloading)
|
||||||
{
|
{
|
||||||
var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data);
|
var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data);
|
||||||
var mediaStreams = mediaInfo.MediaStreams;
|
var mediaStreams = mediaInfo.MediaStreams;
|
||||||
|
@ -208,7 +208,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
|
FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
await AddExternalSubtitles(video, mediaStreams, directoryService, cancellationToken).ConfigureAwait(false);
|
await AddExternalSubtitles(video, mediaStreams, directoryService, enableSubtitleDownloading, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
FetchWtvInfo(video, data);
|
FetchWtvInfo(video, data);
|
||||||
|
|
||||||
|
@ -416,13 +416,17 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="video">The video.</param>
|
/// <param name="video">The video.</param>
|
||||||
/// <param name="currentStreams">The current streams.</param>
|
/// <param name="currentStreams">The current streams.</param>
|
||||||
private async Task AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService, CancellationToken cancellationToken)
|
/// <param name="directoryService">The directory service.</param>
|
||||||
|
/// <param name="enableSubtitleDownloading">if set to <c>true</c> [enable subtitle downloading].</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
private async Task AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService, bool enableSubtitleDownloading, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var subtitleResolver = new SubtitleResolver(_localization);
|
var subtitleResolver = new SubtitleResolver(_localization);
|
||||||
|
|
||||||
var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, directoryService, false).ToList();
|
var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, directoryService, false).ToList();
|
||||||
|
|
||||||
if ((_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
|
if (enableSubtitleDownloading && (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
|
||||||
video is Episode) ||
|
video is Episode) ||
|
||||||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
|
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
|
||||||
video is Movie))
|
video is Movie))
|
||||||
|
|
|
@ -124,7 +124,10 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
Name = video.Name,
|
Name = video.Name,
|
||||||
ParentIndexNumber = video.ParentIndexNumber,
|
ParentIndexNumber = video.ParentIndexNumber,
|
||||||
ProductionYear = video.ProductionYear,
|
ProductionYear = video.ProductionYear,
|
||||||
ProviderIds = video.ProviderIds
|
ProviderIds = video.ProviderIds,
|
||||||
|
|
||||||
|
// Stop as soon as we find something
|
||||||
|
SearchAllProviders = false
|
||||||
};
|
};
|
||||||
|
|
||||||
var episode = video as Episode;
|
var episode = video as Episode;
|
||||||
|
@ -143,7 +146,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
await _subtitleManager.DownloadSubtitles(video, result.Id, result.ProviderName, cancellationToken)
|
await _subtitleManager.DownloadSubtitles(video, result.Id, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.Extensions;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Controller.Subtitles;
|
using MediaBrowser.Controller.Subtitles;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
@ -23,12 +25,16 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILibraryMonitor _monitor;
|
private readonly ILibraryMonitor _monitor;
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IItemRepository _itemRepo;
|
||||||
|
|
||||||
public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor)
|
public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IItemRepository itemRepo)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_monitor = monitor;
|
_monitor = monitor;
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
_itemRepo = itemRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
|
public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
|
||||||
|
@ -38,15 +44,45 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
|
|
||||||
public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
|
public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
var contentType = request.ContentType;
|
||||||
var providers = _subtitleProviders
|
var providers = _subtitleProviders
|
||||||
.Where(i => i.SupportedMediaTypes.Contains(request.ContentType))
|
.Where(i => i.SupportedMediaTypes.Contains(contentType))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// If not searching all, search one at a time until something is found
|
||||||
|
if (!request.SearchAllProviders)
|
||||||
|
{
|
||||||
|
foreach (var provider in providers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var searchResults = await provider.Search(request, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var list = searchResults.ToList();
|
||||||
|
|
||||||
|
if (list.Count > 0)
|
||||||
|
{
|
||||||
|
Normalize(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error downloading subtitles from {0}", ex, provider.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new List<RemoteSubtitleInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
var tasks = providers.Select(async i =>
|
var tasks = providers.Select(async i =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await i.Search(request, cancellationToken).ConfigureAwait(false);
|
var searchResults = await i.Search(request, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var list = searchResults.ToList();
|
||||||
|
Normalize(list);
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -62,17 +98,21 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
|
|
||||||
public async Task DownloadSubtitles(Video video,
|
public async Task DownloadSubtitles(Video video,
|
||||||
string subtitleId,
|
string subtitleId,
|
||||||
string providerName,
|
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var provider = _subtitleProviders.First(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase));
|
var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var response = await provider.GetSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
using (var stream = response.Stream)
|
using (var stream = response.Stream)
|
||||||
{
|
{
|
||||||
var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
|
var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
|
||||||
Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower() + "." + response.Format.ToLower());
|
Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
|
||||||
|
|
||||||
|
if (response.IsForced)
|
||||||
|
{
|
||||||
|
savePath += ".forced";
|
||||||
|
}
|
||||||
|
|
||||||
|
savePath += "." + response.Format.ToLower();
|
||||||
|
|
||||||
_logger.Info("Saving subtitles to {0}", savePath);
|
_logger.Info("Saving subtitles to {0}", savePath);
|
||||||
|
|
||||||
|
@ -139,5 +179,93 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
|
|
||||||
return SearchSubtitles(request, cancellationToken);
|
return SearchSubtitles(request, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Normalize(IEnumerable<RemoteSubtitleInfo> subtitles)
|
||||||
|
{
|
||||||
|
foreach (var sub in subtitles)
|
||||||
|
{
|
||||||
|
sub.Id = GetProviderId(sub.ProviderName) + "_" + sub.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetProviderId(string name)
|
||||||
|
{
|
||||||
|
return name.ToLower().GetMD5().ToString("N");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISubtitleProvider GetProvider(string id)
|
||||||
|
{
|
||||||
|
return _subtitleProviders.First(i => string.Equals(id, GetProviderId(i.Name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteSubtitles(string itemId, int index)
|
||||||
|
{
|
||||||
|
var stream = _itemRepo.GetMediaStreams(new MediaStreamQuery
|
||||||
|
{
|
||||||
|
Index = index,
|
||||||
|
ItemId = new Guid(itemId),
|
||||||
|
Type = MediaStreamType.Subtitle
|
||||||
|
|
||||||
|
}).First();
|
||||||
|
|
||||||
|
var path = stream.Path;
|
||||||
|
_monitor.ReportFileSystemChangeBeginning(path);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_monitor.ReportFileSystemChangeComplete(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _libraryManager.GetItemById(itemId).RefreshMetadata(new MetadataRefreshOptions
|
||||||
|
{
|
||||||
|
ImageRefreshMode = ImageRefreshMode.ValidationOnly,
|
||||||
|
MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
|
||||||
|
|
||||||
|
}, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var parts = id.Split(new[] { '_' }, 2);
|
||||||
|
|
||||||
|
var provider = GetProvider(parts.First());
|
||||||
|
id = parts.Last();
|
||||||
|
|
||||||
|
return provider.GetSubtitles(id, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<SubtitleProviderInfo> GetProviders(string itemId)
|
||||||
|
{
|
||||||
|
var video = _libraryManager.GetItemById(itemId) as Video;
|
||||||
|
VideoContentType mediaType;
|
||||||
|
|
||||||
|
if (video is Episode)
|
||||||
|
{
|
||||||
|
mediaType = VideoContentType.Episode;
|
||||||
|
}
|
||||||
|
else if (video is Movie)
|
||||||
|
{
|
||||||
|
mediaType = VideoContentType.Movie;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// These are the only supported types
|
||||||
|
return new List<SubtitleProviderInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var providers = _subtitleProviders
|
||||||
|
.Where(i => i.SupportedMediaTypes.Contains(mediaType))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return providers.Select(i => new SubtitleProviderInfo
|
||||||
|
{
|
||||||
|
Name = i.Name,
|
||||||
|
Id = GetProviderId(i.Name)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The library update duration
|
/// The library update duration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int LibraryUpdateDuration = 20000;
|
private const int LibraryUpdateDuration = 5000;
|
||||||
|
|
||||||
public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger)
|
public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger)
|
||||||
{
|
{
|
||||||
|
|
|
@ -759,5 +759,7 @@
|
||||||
"LabelEpisodeNumber": "Episode number",
|
"LabelEpisodeNumber": "Episode number",
|
||||||
"LabelEndingEpisodeNumber": "Ending episode number",
|
"LabelEndingEpisodeNumber": "Ending episode number",
|
||||||
"HeaderTypeText": "Enter Text",
|
"HeaderTypeText": "Enter Text",
|
||||||
"LabelTypeText": "Text"
|
"LabelTypeText": "Text",
|
||||||
|
"HeaderSearchForSubtitles": "Search for Subtitles",
|
||||||
|
"MessageNoSubtitleSearchResultsFound": "No search results founds."
|
||||||
}
|
}
|
|
@ -138,7 +138,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
if (controller == null)
|
if (controller == null)
|
||||||
{
|
{
|
||||||
controller = new WebSocketController(session, _appHost);
|
controller = new WebSocketController(session, _appHost, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.Sockets.Add(message.Connection);
|
controller.Sockets.Add(message.Connection);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using MediaBrowser.Model.System;
|
using MediaBrowser.Model.System;
|
||||||
|
@ -19,11 +20,13 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
public List<IWebSocketConnection> Sockets { get; private set; }
|
public List<IWebSocketConnection> Sockets { get; private set; }
|
||||||
|
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public WebSocketController(SessionInfo session, IServerApplicationHost appHost)
|
public WebSocketController(SessionInfo session, IServerApplicationHost appHost, ILogger logger)
|
||||||
{
|
{
|
||||||
Session = session;
|
Session = session;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
|
_logger = logger;
|
||||||
Sockets = new List<IWebSocketConnection>();
|
Sockets = new List<IWebSocketConnection>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,11 +38,17 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IWebSocketConnection> GetActiveSockets()
|
||||||
|
{
|
||||||
|
return Sockets
|
||||||
|
.OrderByDescending(i => i.LastActivityDate)
|
||||||
|
.Where(i => i.State == WebSocketState.Open);
|
||||||
|
}
|
||||||
|
|
||||||
private IWebSocketConnection GetActiveSocket()
|
private IWebSocketConnection GetActiveSocket()
|
||||||
{
|
{
|
||||||
var socket = Sockets
|
var socket = GetActiveSockets()
|
||||||
.OrderByDescending(i => i.LastActivityDate)
|
.FirstOrDefault();
|
||||||
.FirstOrDefault(i => i.State == WebSocketState.Open);
|
|
||||||
|
|
||||||
if (socket == null)
|
if (socket == null)
|
||||||
{
|
{
|
||||||
|
@ -51,9 +60,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
|
public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessage(new WebSocketMessage<PlayRequest>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<PlayRequest>
|
|
||||||
{
|
{
|
||||||
MessageType = "Play",
|
MessageType = "Play",
|
||||||
Data = command
|
Data = command
|
||||||
|
@ -63,9 +70,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
|
public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessage(new WebSocketMessage<PlaystateRequest>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<PlaystateRequest>
|
|
||||||
{
|
{
|
||||||
MessageType = "Playstate",
|
MessageType = "Playstate",
|
||||||
Data = command
|
Data = command
|
||||||
|
@ -75,9 +80,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
|
public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<LibraryUpdateInfo>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<LibraryUpdateInfo>
|
|
||||||
{
|
{
|
||||||
MessageType = "LibraryChanged",
|
MessageType = "LibraryChanged",
|
||||||
Data = info
|
Data = info
|
||||||
|
@ -92,9 +95,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
|
public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<SystemInfo>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<SystemInfo>
|
|
||||||
{
|
{
|
||||||
MessageType = "RestartRequired",
|
MessageType = "RestartRequired",
|
||||||
Data = _appHost.GetSystemInfo()
|
Data = _appHost.GetSystemInfo()
|
||||||
|
@ -111,9 +112,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
|
public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<UserDataChangeInfo>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<UserDataChangeInfo>
|
|
||||||
{
|
{
|
||||||
MessageType = "UserDataChanged",
|
MessageType = "UserDataChanged",
|
||||||
Data = info
|
Data = info
|
||||||
|
@ -128,9 +127,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public Task SendServerShutdownNotification(CancellationToken cancellationToken)
|
public Task SendServerShutdownNotification(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<string>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<string>
|
|
||||||
{
|
{
|
||||||
MessageType = "ServerShuttingDown",
|
MessageType = "ServerShuttingDown",
|
||||||
Data = string.Empty
|
Data = string.Empty
|
||||||
|
@ -145,9 +142,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public Task SendServerRestartNotification(CancellationToken cancellationToken)
|
public Task SendServerRestartNotification(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<string>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<string>
|
|
||||||
{
|
{
|
||||||
MessageType = "ServerRestarting",
|
MessageType = "ServerRestarting",
|
||||||
Data = string.Empty
|
Data = string.Empty
|
||||||
|
@ -157,9 +152,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
|
public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessage(new WebSocketMessage<GeneralCommand>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<GeneralCommand>
|
|
||||||
{
|
{
|
||||||
MessageType = "GeneralCommand",
|
MessageType = "GeneralCommand",
|
||||||
Data = command
|
Data = command
|
||||||
|
@ -169,9 +162,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
|
public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<SessionInfoDto>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<SessionInfoDto>
|
|
||||||
{
|
{
|
||||||
MessageType = "SessionEnded",
|
MessageType = "SessionEnded",
|
||||||
Data = sessionInfo
|
Data = sessionInfo
|
||||||
|
@ -181,9 +172,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
|
public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<SessionInfoDto>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<SessionInfoDto>
|
|
||||||
{
|
{
|
||||||
MessageType = "PlaybackStart",
|
MessageType = "PlaybackStart",
|
||||||
Data = sessionInfo
|
Data = sessionInfo
|
||||||
|
@ -193,14 +182,37 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
|
public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSocket();
|
return SendMessages(new WebSocketMessage<SessionInfoDto>
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<SessionInfoDto>
|
|
||||||
{
|
{
|
||||||
MessageType = "PlaybackStopped",
|
MessageType = "PlaybackStopped",
|
||||||
Data = sessionInfo
|
Data = sessionInfo
|
||||||
|
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task SendMessage<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var socket = GetActiveSocket();
|
||||||
|
|
||||||
|
return socket.SendAsync(message, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task SendMessages<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var tasks = GetActiveSockets().Select(i => Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await i.SendAsync(message, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error sending web socket message", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, cancellationToken));
|
||||||
|
|
||||||
|
return Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -537,7 +537,7 @@ namespace MediaBrowser.ServerApplication
|
||||||
|
|
||||||
RegisterSingleInstance<IEncryptionManager>(new EncryptionManager());
|
RegisterSingleInstance<IEncryptionManager>(new EncryptionManager());
|
||||||
|
|
||||||
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor);
|
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, ItemRepository);
|
||||||
RegisterSingleInstance(SubtitleManager);
|
RegisterSingleInstance(SubtitleManager);
|
||||||
|
|
||||||
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
||||||
|
|
|
@ -550,6 +550,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
"editcollectionitems.js",
|
"editcollectionitems.js",
|
||||||
"edititemmetadata.js",
|
"edititemmetadata.js",
|
||||||
"edititemimages.js",
|
"edititemimages.js",
|
||||||
|
"edititemsubtitles.js",
|
||||||
"encodingsettings.js",
|
"encodingsettings.js",
|
||||||
"gamesrecommendedpage.js",
|
"gamesrecommendedpage.js",
|
||||||
"gamesystemspage.js",
|
"gamesystemspage.js",
|
||||||
|
|
|
@ -307,6 +307,9 @@
|
||||||
<Content Include="dashboard-ui\editcollectionitems.html">
|
<Content Include="dashboard-ui\editcollectionitems.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\edititemsubtitles.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\encodingsettings.html">
|
<Content Include="dashboard-ui\encodingsettings.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -616,6 +619,9 @@
|
||||||
<Content Include="dashboard-ui\scripts\editcollectionitems.js">
|
<Content Include="dashboard-ui\scripts\editcollectionitems.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\scripts\edititemsubtitles.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\encodingsettings.js">
|
<Content Include="dashboard-ui\scripts\encodingsettings.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
Loading…
Reference in New Issue