mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-26 18:48:21 +01:00
beginning remote subtitle downloading
This commit is contained in:
@@ -108,6 +108,7 @@
|
||||
<Compile Include="MediaInfo\FFProbeHelpers.cs" />
|
||||
<Compile Include="MediaInfo\FFProbeProvider.cs" />
|
||||
<Compile Include="MediaInfo\FFProbeVideoInfo.cs" />
|
||||
<Compile Include="MediaInfo\SubtitleDownloader.cs" />
|
||||
<Compile Include="Movies\MovieDbTrailerProvider.cs" />
|
||||
<Compile Include="Movies\MovieExternalIds.cs" />
|
||||
<Compile Include="Movies\TrailerMetadataService.cs" />
|
||||
@@ -187,6 +188,7 @@
|
||||
<Compile Include="Studios\StudiosImageProvider.cs" />
|
||||
<Compile Include="Studios\StudioMetadataService.cs" />
|
||||
<Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
|
||||
<Compile Include="Subtitles\SubtitleManager.cs" />
|
||||
<Compile Include="TV\EpisodeLocalImageProvider.cs" />
|
||||
<Compile Include="TV\EpisodeMetadataService.cs" />
|
||||
<Compile Include="TV\EpisodeXmlProvider.cs" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
@@ -10,15 +11,16 @@ using MediaBrowser.Controller.Localization;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
@@ -45,6 +47,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
private readonly IJsonSerializer _json;
|
||||
private readonly IEncodingManager _encodingManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ISubtitleManager _subtitleManager;
|
||||
|
||||
public string Name
|
||||
{
|
||||
@@ -96,7 +100,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
return FetchAudioInfo(item, cancellationToken);
|
||||
}
|
||||
|
||||
public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem)
|
||||
public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_isoManager = isoManager;
|
||||
@@ -108,6 +112,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
_json = json;
|
||||
_encodingManager = encodingManager;
|
||||
_fileSystem = fileSystem;
|
||||
_config = config;
|
||||
_subtitleManager = subtitleManager;
|
||||
}
|
||||
|
||||
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
|
||||
@@ -134,7 +140,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
return _cachedTask;
|
||||
}
|
||||
|
||||
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem);
|
||||
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager);
|
||||
|
||||
return prober.ProbeVideo(item, directoryService, cancellationToken);
|
||||
}
|
||||
@@ -165,7 +171,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
|
||||
if (video != null && !video.IsPlaceHolder)
|
||||
{
|
||||
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem);
|
||||
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager);
|
||||
|
||||
return !video.SubtitleFiles.SequenceEqual(prober.GetSubtitleFiles(video, directoryService).Select(i => i.FullName).OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Localization;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
@@ -35,10 +39,12 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
private readonly IJsonSerializer _json;
|
||||
private readonly IEncodingManager _encodingManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ISubtitleManager _subtitleManager;
|
||||
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem)
|
||||
public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_isoManager = isoManager;
|
||||
@@ -50,6 +56,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
_json = json;
|
||||
_encodingManager = encodingManager;
|
||||
_fileSystem = fileSystem;
|
||||
_config = config;
|
||||
_subtitleManager = subtitleManager;
|
||||
}
|
||||
|
||||
public async Task<ItemUpdateType> ProbeVideo<T>(T item, IDirectoryService directoryService, CancellationToken cancellationToken)
|
||||
@@ -118,7 +126,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var idString = item.Id.ToString("N");
|
||||
var cachePath = Path.Combine(_appPaths.CachePath,
|
||||
var cachePath = Path.Combine(_appPaths.CachePath,
|
||||
"ffprobe-video",
|
||||
idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
|
||||
|
||||
@@ -200,7 +208,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
|
||||
}
|
||||
|
||||
AddExternalSubtitles(video, mediaStreams, directoryService);
|
||||
await AddExternalSubtitles(video, mediaStreams, directoryService, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
FetchWtvInfo(video, data);
|
||||
|
||||
@@ -247,7 +255,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
}
|
||||
}
|
||||
|
||||
info.StartPositionTicks = chapter.start/100;
|
||||
info.StartPositionTicks = chapter.start / 100;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -450,11 +458,42 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
/// </summary>
|
||||
/// <param name="video">The video.</param>
|
||||
/// <param name="currentStreams">The current streams.</param>
|
||||
private void AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService)
|
||||
private async Task AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService, CancellationToken cancellationToken)
|
||||
{
|
||||
var externalSubtitleStreams = GetExternalSubtitleStreams(video, currentStreams.Count, directoryService).ToList();
|
||||
|
||||
if ((_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles &&
|
||||
video is Episode) ||
|
||||
(_config.Configuration.SubtitleOptions.DownloadMovieSubtitles &&
|
||||
video is Movie))
|
||||
{
|
||||
var downloadedLanguages = await new SubtitleDownloader(_logger,
|
||||
_subtitleManager)
|
||||
.DownloadSubtitles(video,
|
||||
currentStreams,
|
||||
externalSubtitleStreams,
|
||||
_config.Configuration.SubtitleOptions.RequireExternalSubtitles,
|
||||
_config.Configuration.SubtitleOptions.SubtitleDownloadLanguages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Rescan
|
||||
if (downloadedLanguages.Count > 0)
|
||||
{
|
||||
externalSubtitleStreams = GetExternalSubtitleStreams(video, currentStreams.Count, directoryService).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).OrderBy(i => i).ToList();
|
||||
|
||||
currentStreams.AddRange(externalSubtitleStreams);
|
||||
}
|
||||
|
||||
private IEnumerable<MediaStream> GetExternalSubtitleStreams(Video video,
|
||||
int startIndex,
|
||||
IDirectoryService directoryService)
|
||||
{
|
||||
var files = GetSubtitleFiles(video, directoryService);
|
||||
|
||||
var startIndex = currentStreams.Count;
|
||||
var streams = new List<MediaStream>();
|
||||
|
||||
var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
|
||||
@@ -504,9 +543,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
}
|
||||
}
|
||||
|
||||
video.SubtitleFiles = streams.Select(i => i.Path).OrderBy(i => i).ToList();
|
||||
|
||||
currentStreams.AddRange(streams);
|
||||
return streams;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -627,7 +664,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
var path = mount == null ? item.Path : mount.MountedPath;
|
||||
var dvd = new Dvd(path);
|
||||
|
||||
|
||||
var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault();
|
||||
|
||||
byte? titleNumber = null;
|
||||
|
||||
140
MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
Normal file
140
MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
public class SubtitleDownloader
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISubtitleManager _subtitleManager;
|
||||
|
||||
public SubtitleDownloader(ILogger logger, ISubtitleManager subtitleManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_subtitleManager = subtitleManager;
|
||||
}
|
||||
|
||||
public async Task<List<string>> DownloadSubtitles(Video video,
|
||||
List<MediaStream> internalSubtitleStreams,
|
||||
List<MediaStream> externalSubtitleStreams,
|
||||
bool forceExternal,
|
||||
IEnumerable<string> languages,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (video.LocationType != LocationType.FileSystem ||
|
||||
video.VideoType != VideoType.VideoFile)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
SubtitleMediaType mediaType;
|
||||
|
||||
if (video is Episode)
|
||||
{
|
||||
mediaType = SubtitleMediaType.Episode;
|
||||
}
|
||||
else if (video is Movie)
|
||||
{
|
||||
mediaType = SubtitleMediaType.Movie;
|
||||
}
|
||||
else
|
||||
{
|
||||
// These are the only supported types
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
var downloadedLanguages = new List<string>();
|
||||
|
||||
foreach (var lang in languages)
|
||||
{
|
||||
try
|
||||
{
|
||||
var downloaded = await DownloadSubtitles(video, internalSubtitleStreams, externalSubtitleStreams, forceExternal, lang, mediaType, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (downloaded)
|
||||
{
|
||||
downloadedLanguages.Add(lang);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error downloading subtitles", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return downloadedLanguages;
|
||||
}
|
||||
|
||||
private async Task<bool> DownloadSubtitles(Video video,
|
||||
IEnumerable<MediaStream> internalSubtitleStreams,
|
||||
IEnumerable<MediaStream> externalSubtitleStreams,
|
||||
bool forceExternal,
|
||||
string language,
|
||||
SubtitleMediaType mediaType,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// There's already subtitles for this language
|
||||
if (externalSubtitleStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// There's an internal subtitle stream for this language
|
||||
if (!forceExternal && internalSubtitleStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = new SubtitleSearchRequest
|
||||
{
|
||||
ContentType = mediaType,
|
||||
IndexNumber = video.IndexNumber,
|
||||
Language = language,
|
||||
MediaPath = video.Path,
|
||||
Name = video.Name,
|
||||
ParentIndexNumber = video.ParentIndexNumber,
|
||||
ProductionYear = video.ProductionYear,
|
||||
ProviderIds = video.ProviderIds
|
||||
};
|
||||
|
||||
var episode = video as Episode;
|
||||
|
||||
if (episode != null)
|
||||
{
|
||||
request.IndexNumberEnd = episode.IndexNumberEnd;
|
||||
request.SeriesName = episode.SeriesName;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var searchResults = await _subtitleManager.SearchSubtitles(request, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = searchResults.FirstOrDefault();
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
await _subtitleManager.DownloadSubtitles(video, result.Id, result.ProviderName, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error downloading subtitles", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using OpenSubtitlesHandler;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -20,9 +22,9 @@ namespace MediaBrowser.Providers.Subtitles
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public OpenSubtitleDownloader(ILogger logger, IHttpClient httpClient)
|
||||
public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_logger = logManager.GetLogger(GetType().Name);
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
@@ -36,39 +38,71 @@ namespace MediaBrowser.Providers.Subtitles
|
||||
get { return new[] { SubtitleMediaType.Episode, SubtitleMediaType.Movie }; }
|
||||
}
|
||||
|
||||
public Task<SubtitleResponse> GetSubtitles(SubtitleRequest request, CancellationToken cancellationToken)
|
||||
public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
return GetSubtitlesInternal(request, cancellationToken);
|
||||
return GetSubtitlesInternal(id, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<SubtitleResponse> GetSubtitlesInternal(SubtitleRequest request,
|
||||
private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new SubtitleResponse();
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
var idParts = id.Split(new[] { '-' }, 3);
|
||||
|
||||
var format = idParts[0];
|
||||
var language = idParts[1];
|
||||
var ossId = idParts[2];
|
||||
|
||||
var downloadsList = new[] { int.Parse(ossId, _usCulture) };
|
||||
|
||||
var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList);
|
||||
if (!(resultDownLoad is MethodResponseSubtitleDownload))
|
||||
{
|
||||
throw new ApplicationException("Invalid response type");
|
||||
}
|
||||
|
||||
var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First();
|
||||
var data = Convert.FromBase64String(res.Data);
|
||||
|
||||
return new SubtitleResponse
|
||||
{
|
||||
Format = format,
|
||||
Language = language,
|
||||
|
||||
Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data)))
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var imdbIdText = request.GetProviderId(MetadataProviders.Imdb);
|
||||
long imdbId;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(imdbIdText) ||
|
||||
long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId))
|
||||
!long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId))
|
||||
{
|
||||
return response;
|
||||
_logger.Debug("Imdb id missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
|
||||
|
||||
switch (request.ContentType)
|
||||
{
|
||||
case SubtitleMediaType.Episode:
|
||||
if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName))
|
||||
{
|
||||
_logger.Debug("Information Missing");
|
||||
return response;
|
||||
_logger.Debug("Episode information missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
break;
|
||||
case SubtitleMediaType.Movie:
|
||||
if (string.IsNullOrEmpty(request.Name))
|
||||
{
|
||||
_logger.Debug("Information Missing");
|
||||
return response;
|
||||
_logger.Debug("Movie name missing");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -76,16 +110,18 @@ namespace MediaBrowser.Providers.Subtitles
|
||||
if (string.IsNullOrEmpty(request.MediaPath))
|
||||
{
|
||||
_logger.Debug("Path Missing");
|
||||
return response;
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
|
||||
Utilities.HttpClient = _httpClient;
|
||||
OpenSubtitles.SetUserAgent("OS Test User Agent");
|
||||
var loginResponse = OpenSubtitles.LogIn("", "", "en");
|
||||
|
||||
var loginResponse = await OpenSubtitles.LogInAsync("", "", "en", cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!(loginResponse is MethodResponseLogIn))
|
||||
{
|
||||
_logger.Debug("Login error");
|
||||
return response;
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
|
||||
var subLanguageId = request.Language;
|
||||
@@ -105,54 +141,42 @@ namespace MediaBrowser.Providers.Subtitles
|
||||
var result = OpenSubtitles.SearchSubtitles(parms.ToArray());
|
||||
if (!(result is MethodResponseSubtitleSearch))
|
||||
{
|
||||
_logger.Debug("invalid response type");
|
||||
return null;
|
||||
_logger.Debug("Invalid response type");
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
|
||||
Predicate<SubtitleSearchResult> mediaFilter =
|
||||
x =>
|
||||
request.ContentType == SubtitleMediaType.Episode
|
||||
? int.Parse(x.SeriesSeason) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode) == request.IndexNumber
|
||||
: long.Parse(x.IDMovieImdb) == imdbId;
|
||||
? int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber
|
||||
: long.Parse(x.IDMovieImdb, _usCulture) == imdbId;
|
||||
|
||||
var results = ((MethodResponseSubtitleSearch)result).Results;
|
||||
var bestResult = results.Where(x => x.SubBad == "0" && mediaFilter(x))
|
||||
|
||||
// Avoid implicitly captured closure
|
||||
var hasCopy = hash;
|
||||
|
||||
return results.Where(x => x.SubBad == "0" && mediaFilter(x))
|
||||
.OrderBy(x => x.MovieHash == hash)
|
||||
.ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize) - movieByteSize))
|
||||
.ThenByDescending(x => int.Parse(x.SubDownloadsCnt))
|
||||
.ThenByDescending(x => double.Parse(x.SubRating))
|
||||
.ToList();
|
||||
.ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize, _usCulture) - movieByteSize))
|
||||
.ThenByDescending(x => int.Parse(x.SubDownloadsCnt, _usCulture))
|
||||
.ThenByDescending(x => double.Parse(x.SubRating, _usCulture))
|
||||
.Select(i => new RemoteSubtitleInfo
|
||||
{
|
||||
Author = i.UserNickName,
|
||||
Comment = i.SubAuthorComment,
|
||||
CommunityRating = float.Parse(i.SubRating, _usCulture),
|
||||
DownloadCount = int.Parse(i.SubDownloadsCnt, _usCulture),
|
||||
Format = i.SubFormat,
|
||||
ProviderName = Name,
|
||||
Language = i.SubLanguageID,
|
||||
|
||||
if (!bestResult.Any())
|
||||
{
|
||||
_logger.Debug("No Subtitles");
|
||||
return response;
|
||||
}
|
||||
Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitle,
|
||||
|
||||
_logger.Debug("Found " + bestResult.Count + " subtitles.");
|
||||
|
||||
var subtitle = bestResult.First();
|
||||
var downloadsList = new[] { int.Parse(subtitle.IDSubtitleFile) };
|
||||
|
||||
var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList);
|
||||
if (!(resultDownLoad is MethodResponseSubtitleDownload))
|
||||
{
|
||||
_logger.Debug("invalid response type");
|
||||
return response;
|
||||
}
|
||||
if (!((MethodResponseSubtitleDownload)resultDownLoad).Results.Any())
|
||||
{
|
||||
_logger.Debug("No Subtitle Downloads");
|
||||
return response;
|
||||
}
|
||||
|
||||
var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First();
|
||||
var data = Convert.FromBase64String(res.Data);
|
||||
|
||||
response.HasContent = true;
|
||||
response.Format = subtitle.SubFormat.ToUpper();
|
||||
response.Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data)));
|
||||
return response;
|
||||
Name = i.SubFileName,
|
||||
DateCreated = DateTime.Parse(i.SubAddDate, _usCulture),
|
||||
IsHashMatch = i.MovieHash == hasCopy
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
141
MediaBrowser.Providers/Subtitles/SubtitleManager.cs
Normal file
141
MediaBrowser.Providers/Subtitles/SubtitleManager.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.Subtitles
|
||||
{
|
||||
public class SubtitleManager : ISubtitleManager
|
||||
{
|
||||
private ISubtitleProvider[] _subtitleProviders;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILibraryMonitor _monitor;
|
||||
|
||||
public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor)
|
||||
{
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_monitor = monitor;
|
||||
}
|
||||
|
||||
public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
|
||||
{
|
||||
_subtitleProviders = subtitleProviders.ToArray();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var providers = _subtitleProviders
|
||||
.Where(i => i.SupportedMediaTypes.Contains(request.ContentType))
|
||||
.ToList();
|
||||
|
||||
var tasks = providers.Select(async i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return await i.SearchSubtitles(request, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error downloading subtitles from {0}", ex, i.Name);
|
||||
return new List<RemoteSubtitleInfo>();
|
||||
}
|
||||
});
|
||||
|
||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
return results.SelectMany(i => i);
|
||||
}
|
||||
|
||||
public async Task DownloadSubtitles(Video video,
|
||||
string subtitleId,
|
||||
string providerName,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var provider = _subtitleProviders.First(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var response = await provider.GetSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
using (var stream = response.Stream)
|
||||
{
|
||||
var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
|
||||
Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower() + "." + response.Format.ToLower());
|
||||
|
||||
_logger.Info("Saving subtitles to {0}", savePath);
|
||||
|
||||
_monitor.ReportFileSystemChangeBeginning(savePath);
|
||||
|
||||
try
|
||||
{
|
||||
using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
{
|
||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_monitor.ReportFileSystemChangeComplete(savePath, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(Video video, string language, CancellationToken cancellationToken)
|
||||
{
|
||||
if (video.LocationType != LocationType.FileSystem ||
|
||||
video.VideoType != VideoType.VideoFile)
|
||||
{
|
||||
return Task.FromResult<IEnumerable<RemoteSubtitleInfo>>(new List<RemoteSubtitleInfo>());
|
||||
}
|
||||
|
||||
SubtitleMediaType mediaType;
|
||||
|
||||
if (video is Episode)
|
||||
{
|
||||
mediaType = SubtitleMediaType.Episode;
|
||||
}
|
||||
else if (video is Movie)
|
||||
{
|
||||
mediaType = SubtitleMediaType.Movie;
|
||||
}
|
||||
else
|
||||
{
|
||||
// These are the only supported types
|
||||
return Task.FromResult<IEnumerable<RemoteSubtitleInfo>>(new List<RemoteSubtitleInfo>());
|
||||
}
|
||||
|
||||
var request = new SubtitleSearchRequest
|
||||
{
|
||||
ContentType = mediaType,
|
||||
IndexNumber = video.IndexNumber,
|
||||
Language = language,
|
||||
MediaPath = video.Path,
|
||||
Name = video.Name,
|
||||
ParentIndexNumber = video.ParentIndexNumber,
|
||||
ProductionYear = video.ProductionYear,
|
||||
ProviderIds = video.ProviderIds
|
||||
};
|
||||
|
||||
var episode = video as Episode;
|
||||
|
||||
if (episode != null)
|
||||
{
|
||||
request.IndexNumberEnd = episode.IndexNumberEnd;
|
||||
request.SeriesName = episode.SeriesName;
|
||||
}
|
||||
|
||||
return SearchSubtitles(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user