diff options
Diffstat (limited to 'MediaBrowser.Providers/MediaInfo')
6 files changed, 365 insertions, 113 deletions
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 20ce952db..772f60163 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -1,5 +1,6 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Drawing; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.MediaInfo; @@ -7,7 +8,10 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,15 +22,19 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> public class AudioImageProvider : IDynamicImageProvider, IHasChangeMonitor { + private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); + private readonly IIsoManager _isoManager; private readonly IMediaEncoder _mediaEncoder; private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; - public AudioImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config) + public AudioImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, IFileSystem fileSystem) { _isoManager = isoManager; _mediaEncoder = mediaEncoder; _config = config; + _fileSystem = fileSystem; } /// <summary> @@ -65,21 +73,82 @@ namespace MediaBrowser.Providers.MediaInfo return Task.FromResult(new DynamicImageResponse { HasImage = false }); } - return GetVideoImage((Audio)item, cancellationToken); + return GetImage((Audio)item, cancellationToken); } - public async Task<DynamicImageResponse> GetVideoImage(Audio item, CancellationToken cancellationToken) + public async Task<DynamicImageResponse> GetImage(Audio item, CancellationToken cancellationToken) { - var stream = await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, cancellationToken).ConfigureAwait(false); + var path = GetAudioImagePath(item); + + if (!File.Exists(path)) + { + var semaphore = GetLock(path); + + // Acquire a lock + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + // Check again in case it was saved while waiting for the lock + if (!File.Exists(path)) + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + using (var stream = await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, cancellationToken).ConfigureAwait(false)) + { + using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + { + await stream.CopyToAsync(fileStream).ConfigureAwait(false); + } + } + } + } + finally + { + semaphore.Release(); + } + } return new DynamicImageResponse { - Format = ImageFormat.Jpg, HasImage = true, - Stream = stream + Path = path }; } + private string GetAudioImagePath(Audio item) + { + var album = item.Parent as MusicAlbum; + + var filename = item.Album ?? string.Empty; + filename += item.Artists.FirstOrDefault() ?? string.Empty; + filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary"; + + filename = filename.GetMD5() + ".jpg"; + + var prefix = filename.Substring(0, 1); + + return Path.Combine(AudioImagesPath, prefix, filename); + } + + public string AudioImagesPath + { + get + { + return Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images"); + } + } + + /// <summary> + /// Gets the lock. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>SemaphoreSlim.</returns> + private SemaphoreSlim GetLock(string filename) + { + return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); + } + public string Name { get { return "Embedded Image"; } @@ -90,7 +159,7 @@ namespace MediaBrowser.Providers.MediaInfo return item.LocationType == LocationType.FileSystem && item is Audio; } - public bool HasChanged(IHasMetadata item, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) { return item.DateModified > date; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 4fc92ddeb..ebb2f13d1 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -1,13 +1,16 @@ -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,13 +21,17 @@ namespace MediaBrowser.Providers.MediaInfo { private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; + private readonly IApplicationPaths _appPaths; + private readonly IJsonSerializer _json; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public FFProbeAudioInfo(IMediaEncoder mediaEncoder, IItemRepository itemRepo) + + public FFProbeAudioInfo(IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json) { _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; + _appPaths = appPaths; + _json = json; } public async Task<ItemUpdateType> Probe<T>(T item, CancellationToken cancellationToken) @@ -47,10 +54,30 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); + var idString = item.Id.ToString("N"); + var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-audio", idString.Substring(0, 2), idString, "v" + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); + + try + { + return _json.DeserializeFromFile<InternalMediaInfoResult>(cachePath); + } + catch (FileNotFoundException) + { + + } + catch (DirectoryNotFoundException) + { + } + const InputType type = InputType.File; var inputPath = new[] { item.Path }; - return await _mediaEncoder.GetMediaInfo(inputPath, type, false, cancellationToken).ConfigureAwait(false); + var result = await _mediaEncoder.GetMediaInfo(inputPath, type, false, cancellationToken).ConfigureAwait(false); + + Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); + _json.SerializeToFile(result, cachePath); + + return result; } /// <summary> @@ -178,9 +205,14 @@ namespace MediaBrowser.Providers.MediaInfo FetchStudios(audio, tags, "ensemble"); FetchStudios(audio, tags, "publisher"); } + + audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")); + audio.SetProviderId(MetadataProviders.MusicBrainzArtist, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")); + audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")); + audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")); } - private readonly char[] _nameDelimiters = new[] { '/', '|', ';', '\\' }; + private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' }; /// <summary> /// Splits the specified val. @@ -205,13 +237,63 @@ namespace MediaBrowser.Providers.MediaInfo val = val.Replace(" featuring ", ArtistReplaceValue, StringComparison.OrdinalIgnoreCase) .Replace(" feat. ", ArtistReplaceValue, StringComparison.OrdinalIgnoreCase); + var artistsFound = new List<string>(); + + foreach (var whitelistArtist in GetSplitWhitelist()) + { + var originalVal = val; + val = val.Replace(whitelistArtist, "|", StringComparison.OrdinalIgnoreCase); + + if (!string.Equals(originalVal, val, StringComparison.OrdinalIgnoreCase)) + { + // TODO: Preserve casing from original value + artistsFound.Add(whitelistArtist); + } + } + // Only use the comma as a delimeter if there are no slashes or pipes. // We want to be careful not to split names that have commas in them var delimeter = _nameDelimiters; - return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries) + var artists = val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()); + + artistsFound.AddRange(artists); + return artistsFound; + } + + + private List<string> _splitWhiteList = null; + + private IEnumerable<string> GetSplitWhitelist() + { + if (_splitWhiteList == null) + { + var file = GetType().Namespace + ".whitelist.txt"; + + using (var stream = GetType().Assembly.GetManifestResourceStream(file)) + { + using (var reader = new StreamReader(stream)) + { + var list = new List<string>(); + + while (!reader.EndOfStream) + { + var val = reader.ReadLine(); + + if (!string.IsNullOrWhiteSpace(val)) + { + list.Add(val); + } + } + + _splitWhiteList = list; + } + } + } + + return _splitWhiteList; } /// <summary> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index d55a42d11..7ac48655a 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; @@ -12,9 +13,11 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; using System; using System.Threading; using System.Threading.Tasks; +using System.Linq; namespace MediaBrowser.Providers.MediaInfo { @@ -27,7 +30,8 @@ namespace MediaBrowser.Providers.MediaInfo ICustomMetadataProvider<Trailer>, ICustomMetadataProvider<Video>, ICustomMetadataProvider<Audio>, - IHasChangeMonitor + IHasChangeMonitor, + IHasOrder { private readonly ILogger _logger; private readonly IIsoManager _isoManager; @@ -35,58 +39,60 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IItemRepository _itemRepo; private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; + private readonly IApplicationPaths _appPaths; + private readonly IJsonSerializer _json; public string Name { get { return "ffprobe"; } } - public Task<ItemUpdateType> FetchAsync(Episode item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Episode item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(MusicVideo item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(MusicVideo item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Movie item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Movie item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(AdultVideo item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(AdultVideo item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(LiveTvVideoRecording item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(LiveTvVideoRecording item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Trailer item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Trailer item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Video item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Video item, IDirectoryService directoryService, CancellationToken cancellationToken) { - return FetchVideoInfo(item, cancellationToken); + return FetchVideoInfo(item, directoryService, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Audio item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Audio item, IDirectoryService directoryService, CancellationToken cancellationToken) { return FetchAudioInfo(item, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(LiveTvAudioRecording item, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(LiveTvAudioRecording item, IDirectoryService directoryService, CancellationToken cancellationToken) { return FetchAudioInfo(item, cancellationToken); } - public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization) + public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json) { _logger = logger; _isoManager = isoManager; @@ -94,10 +100,12 @@ namespace MediaBrowser.Providers.MediaInfo _itemRepo = itemRepo; _blurayExaminer = blurayExaminer; _localization = localization; + _appPaths = appPaths; + _json = json; } - private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.Unspecified); - public Task<ItemUpdateType> FetchVideoInfo<T>(T item, CancellationToken cancellationToken) + private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); + public Task<ItemUpdateType> FetchVideoInfo<T>(T item, IDirectoryService directoryService, CancellationToken cancellationToken) where T : Video { if (item.LocationType != LocationType.FileSystem) @@ -115,9 +123,9 @@ namespace MediaBrowser.Providers.MediaInfo return _cachedTask; } - var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization); + var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json); - return prober.ProbeVideo(item, cancellationToken); + return prober.ProbeVideo(item, directoryService, cancellationToken); } public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken) @@ -128,14 +136,40 @@ namespace MediaBrowser.Providers.MediaInfo return _cachedTask; } - var prober = new FFProbeAudioInfo(_mediaEncoder, _itemRepo); + var prober = new FFProbeAudioInfo(_mediaEncoder, _itemRepo, _appPaths, _json); return prober.Probe(item, cancellationToken); } - public bool HasChanged(IHasMetadata item, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) { - return item.DateModified > date; + if (item.DateModified > date) + { + return true; + } + + if (item.SupportsLocalMetadata) + { + var video = item as Video; + + if (video != null) + { + var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json); + + return !video.SubtitleFiles.SequenceEqual(prober.GetSubtitleFiles(video, directoryService).Select(i => i.FullName).OrderBy(i => i), StringComparer.OrdinalIgnoreCase); + } + } + + return false; + } + + public int Order + { + get + { + // Run last + return 100; + } } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 9073441e4..074bcfdff 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -1,13 +1,16 @@ using DvdLib.Ifo; +using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Globalization; @@ -26,10 +29,12 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IItemRepository _itemRepo; private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; + private readonly IApplicationPaths _appPaths; + private readonly IJsonSerializer _json; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization) + public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json) { _logger = logger; _isoManager = isoManager; @@ -37,9 +42,11 @@ namespace MediaBrowser.Providers.MediaInfo _itemRepo = itemRepo; _blurayExaminer = blurayExaminer; _localization = localization; + _appPaths = appPaths; + _json = json; } - public async Task<ItemUpdateType> ProbeVideo<T>(T item, CancellationToken cancellationToken) + public async Task<ItemUpdateType> ProbeVideo<T>(T item, IDirectoryService directoryService, CancellationToken cancellationToken) where T : Video { var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false); @@ -66,7 +73,7 @@ namespace MediaBrowser.Providers.MediaInfo cancellationToken.ThrowIfCancellationRequested(); - await Fetch(item, cancellationToken, result, isoMount).ConfigureAwait(false); + await Fetch(item, cancellationToken, result, isoMount, directoryService).ConfigureAwait(false); } finally @@ -84,6 +91,23 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); + + var idString = item.Id.ToString("N"); + var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", idString.Substring(0, 2), idString, "v" + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); + + try + { + return _json.DeserializeFromFile<InternalMediaInfoResult>(cachePath); + } + catch (FileNotFoundException) + { + + } + catch (DirectoryNotFoundException) + { + } + var type = InputType.File; var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath }; @@ -94,10 +118,15 @@ namespace MediaBrowser.Providers.MediaInfo inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, out type); } - return await _mediaEncoder.GetMediaInfo(inputPath, type, false, cancellationToken).ConfigureAwait(false); + var result = await _mediaEncoder.GetMediaInfo(inputPath, type, false, cancellationToken).ConfigureAwait(false); + + Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); + _json.SerializeToFile(result, cachePath); + + return result; } - protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount) + protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, IDirectoryService directoryService) { if (data.format != null) { @@ -120,7 +149,7 @@ namespace MediaBrowser.Providers.MediaInfo FetchBdInfo(video, chapters, mediaStreams, inputPath, cancellationToken); } - AddExternalSubtitles(video, mediaStreams); + AddExternalSubtitles(video, mediaStreams, directoryService); FetchWtvInfo(video, data); @@ -314,76 +343,104 @@ namespace MediaBrowser.Providers.MediaInfo } } + public IEnumerable<FileInfo> GetSubtitleFiles(Video video, IDirectoryService directoryService) + { + var containingPath = video.ContainingFolderPath; + + if (string.IsNullOrEmpty(containingPath)) + { + throw new ArgumentException(string.Format("Cannot search for items that don't have a path: {0} {1}", video.Name, video.Id)); + } + + var files = directoryService.GetFiles(containingPath); + + var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); + + return files.Where(i => + { + if (!i.Attributes.HasFlag(FileAttributes.Directory) && + SubtitleExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase)) + { + var fullName = i.FullName; + + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName); + + if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + }); + } + /// <summary> /// Adds the external subtitles. /// </summary> /// <param name="video">The video.</param> /// <param name="currentStreams">The current streams.</param> - private void AddExternalSubtitles(Video video, List<MediaStream> currentStreams) + private void AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService) { - //var useParent = !video.ResolveArgs.IsDirectory; - - //if (useParent && video.Parent == null) - //{ - // return; - //} - - //var fileSystemChildren = useParent - // ? video.Parent.ResolveArgs.FileSystemChildren - // : video.ResolveArgs.FileSystemChildren; - - //var startIndex = currentStreams.Count; - //var streams = new List<MediaStream>(); - - //var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); - - //foreach (var file in fileSystemChildren - // .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && SubtitleExtensions.Contains(Path.GetExtension(f.FullName), StringComparer.OrdinalIgnoreCase))) - //{ - // var fullName = file.FullName; - - // var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName); - - // // If the subtitle file matches the video file name - // if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) - // { - // streams.Add(new MediaStream - // { - // Index = startIndex++, - // Type = MediaStreamType.Subtitle, - // IsExternal = true, - // Path = fullName, - // Codec = Path.GetExtension(fullName).ToLower().TrimStart('.') - // }); - // } - // else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase)) - // { - // // Support xbmc naming conventions - 300.spanish.srt - // var language = fileNameWithoutExtension.Split('.').LastOrDefault(); - - // // Try to translate to three character code - // // Be flexible and check against both the full and three character versions - // var culture = _localization.GetCultures() - // .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase)); - - // if (culture != null) - // { - // language = culture.ThreeLetterISOLanguageName; - // } - - // streams.Add(new MediaStream - // { - // Index = startIndex++, - // Type = MediaStreamType.Subtitle, - // IsExternal = true, - // Path = fullName, - // Codec = Path.GetExtension(fullName).ToLower().TrimStart('.'), - // Language = language - // }); - // } - //} - - //currentStreams.AddRange(streams); + var files = GetSubtitleFiles(video, directoryService); + + var startIndex = currentStreams.Count; + var streams = new List<MediaStream>(); + + var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); + + foreach (var file in files) + { + var fullName = file.FullName; + + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName); + + // If the subtitle file matches the video file name + if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) + { + streams.Add(new MediaStream + { + Index = startIndex++, + Type = MediaStreamType.Subtitle, + IsExternal = true, + Path = fullName, + Codec = Path.GetExtension(fullName).ToLower().TrimStart('.') + }); + } + else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase)) + { + // Support xbmc naming conventions - 300.spanish.srt + var language = fileNameWithoutExtension.Split('.').LastOrDefault(); + + // Try to translate to three character code + // Be flexible and check against both the full and three character versions + var culture = _localization.GetCultures() + .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase)); + + if (culture != null) + { + language = culture.ThreeLetterISOLanguageName; + } + + streams.Add(new MediaStream + { + Index = startIndex++, + Type = MediaStreamType.Subtitle, + IsExternal = true, + Path = fullName, + Codec = Path.GetExtension(fullName).ToLower().TrimStart('.'), + Language = language + }); + } + } + + video.SubtitleFiles = streams.Select(i => i.Path).OrderBy(i => i).ToList(); + + currentStreams.AddRange(streams); } /// <summary> diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 31d44f4ec..c230f65f9 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.MediaInfo { - public class VideoImageProvider : IDynamicImageProvider, IHasChangeMonitor + public class VideoImageProvider : IDynamicImageProvider, IHasChangeMonitor, IHasOrder { private readonly IIsoManager _isoManager; private readonly IMediaEncoder _mediaEncoder; @@ -126,9 +126,18 @@ namespace MediaBrowser.Providers.MediaInfo return item.LocationType == LocationType.FileSystem && item is Video; } - public bool HasChanged(IHasMetadata item, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) { return item.DateModified > date; } + + public int Order + { + get + { + // Make sure this comes after internet image providers + return 100; + } + } } } diff --git a/MediaBrowser.Providers/MediaInfo/whitelist.txt b/MediaBrowser.Providers/MediaInfo/whitelist.txt new file mode 100644 index 000000000..1fd366551 --- /dev/null +++ b/MediaBrowser.Providers/MediaInfo/whitelist.txt @@ -0,0 +1 @@ +AC/DC
\ No newline at end of file |
