diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2015-04-04 15:35:29 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2015-04-04 15:35:29 -0400 |
| commit | 2b7a80cfb5b9212260734c095a5b3439af7d64e2 (patch) | |
| tree | ab746cd05a2244b4c8c8432a30090b85c9d4d986 /MediaBrowser.Providers | |
| parent | 8c61abf6d23510da8eaddf3894f9e14f88dc5f22 (diff) | |
improve direct play of live streams
Diffstat (limited to 'MediaBrowser.Providers')
| -rw-r--r-- | MediaBrowser.Providers/MediaBrowser.Providers.csproj | 5 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs | 342 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs | 118 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 297 |
4 files changed, 102 insertions, 660 deletions
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 3b5103f209..b58c6648ab 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -102,7 +102,6 @@ <Compile Include="Manager\MetadataService.cs" /> <Compile Include="Manager\SeriesOrderManager.cs" /> <Compile Include="MediaInfo\FFProbeAudioInfo.cs" /> - <Compile Include="MediaInfo\FFProbeHelpers.cs" /> <Compile Include="MediaInfo\FFProbeProvider.cs" /> <Compile Include="MediaInfo\FFProbeVideoInfo.cs" /> <Compile Include="MediaInfo\SubtitleDownloader.cs" /> @@ -198,10 +197,6 @@ <Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project> <Name>MediaBrowser.Controller</Name> </ProjectReference> - <ProjectReference Include="..\MediaBrowser.MediaInfo\MediaBrowser.MediaInfo.csproj"> - <Project>{6e4145e4-c6d4-4e4d-94f2-87188db6e239}</Project> - <Name>MediaBrowser.MediaInfo</Name> - </ProjectReference> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj"> <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project> <Name>MediaBrowser.Model</Name> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index ea191dd08f..b1bed73109 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -8,8 +7,6 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -49,18 +46,14 @@ namespace MediaBrowser.Providers.MediaInfo cancellationToken.ThrowIfCancellationRequested(); - FFProbeHelpers.NormalizeFFProbeResult(result); - - cancellationToken.ThrowIfCancellationRequested(); - await Fetch(item, cancellationToken, result).ConfigureAwait(false); return ItemUpdateType.MetadataImport; } - private const string SchemaVersion = "1"; + private const string SchemaVersion = "2"; - private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, CancellationToken cancellationToken) + private async Task<Model.Entities.MediaInfo> GetMediaInfo(BaseItem item, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -71,7 +64,7 @@ namespace MediaBrowser.Providers.MediaInfo try { - return _json.DeserializeFromFile<InternalMediaInfoResult>(cachePath); + return _json.DeserializeFromFile<Model.Entities.MediaInfo>(cachePath); } catch (FileNotFoundException) { @@ -83,7 +76,7 @@ namespace MediaBrowser.Providers.MediaInfo var inputPath = new[] { item.Path }; - var result = await _mediaEncoder.GetMediaInfo(inputPath, MediaProtocol.File, false, cancellationToken).ConfigureAwait(false); + var result = await _mediaEncoder.GetMediaInfo(inputPath, item.Path, MediaProtocol.File, true, false, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); _json.SerializeToFile(result, cachePath); @@ -96,61 +89,23 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> /// <param name="audio">The audio.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="data">The data.</param> + /// <param name="mediaInfo">The media information.</param> /// <returns>Task.</returns> - protected Task Fetch(Audio audio, CancellationToken cancellationToken, InternalMediaInfoResult data) + protected Task Fetch(Audio audio, CancellationToken cancellationToken, Model.Entities.MediaInfo mediaInfo) { - var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data); var mediaStreams = mediaInfo.MediaStreams; - audio.FormatName = mediaInfo.Format; - audio.TotalBitrate = mediaInfo.TotalBitrate; + audio.FormatName = mediaInfo.Container; + audio.TotalBitrate = mediaInfo.Bitrate; audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.EmbeddedImage); - if (data.streams != null) - { - // Get the first audio stream - var stream = data.streams.FirstOrDefault(s => string.Equals(s.codec_type, "audio", StringComparison.OrdinalIgnoreCase)); + audio.RunTimeTicks = mediaInfo.RunTimeTicks; + audio.Size = mediaInfo.Size; - if (stream != null) - { - // Get duration from stream properties - var duration = stream.duration; + var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); + audio.Container = extension; - // If it's not there go into format properties - if (string.IsNullOrEmpty(duration)) - { - duration = data.format.duration; - } - - // If we got something, parse it - if (!string.IsNullOrEmpty(duration)) - { - audio.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks; - } - } - } - - if (data.format != null) - { - var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); - - audio.Container = extension; - - if (!string.IsNullOrEmpty(data.format.size)) - { - audio.Size = long.Parse(data.format.size, _usCulture); - } - else - { - audio.Size = null; - } - - if (data.format.tags != null) - { - FetchDataFromTags(audio, data.format.tags); - } - } + FetchDataFromTags(audio, mediaInfo); return _itemRepo.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken); } @@ -159,92 +114,36 @@ namespace MediaBrowser.Providers.MediaInfo /// Fetches data from the tags dictionary /// </summary> /// <param name="audio">The audio.</param> - /// <param name="tags">The tags.</param> - private void FetchDataFromTags(Audio audio, Dictionary<string, string> tags) + /// <param name="data">The data.</param> + private void FetchDataFromTags(Audio audio, Model.Entities.MediaInfo data) { - var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); - // Only set Name if title was found in the dictionary - if (!string.IsNullOrEmpty(title)) + if (!string.IsNullOrEmpty(data.Title)) { - audio.Name = title; + audio.Name = data.Title; } if (!audio.LockedFields.Contains(MetadataFields.Cast)) { audio.People.Clear(); - var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); - - if (!string.IsNullOrWhiteSpace(composer)) + foreach (var person in data.People) { - foreach (var person in Split(composer, false)) + audio.AddPerson(new PersonInfo { - audio.AddPerson(new PersonInfo { Name = person, Type = PersonType.Composer }); - } - } - } - - audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); - - var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); - - if (!string.IsNullOrWhiteSpace(artists)) - { - audio.Artists = artists.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - } - else - { - var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist"); - if (string.IsNullOrWhiteSpace(artist)) - { - audio.Artists.Clear(); + Name = person.Name, + Type = person.Type, + Role = person.Role + }); } - else - { - audio.Artists = SplitArtists(artist) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - } - } - - var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist"); - if (string.IsNullOrWhiteSpace(albumArtist)) - { - albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist"); - } - if (string.IsNullOrWhiteSpace(albumArtist)) - { - albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist"); } - if (string.IsNullOrWhiteSpace(albumArtist)) - { - audio.AlbumArtists = new List<string>(); - } - else - { - audio.AlbumArtists = SplitArtists(albumArtist) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - } - - // Track number - audio.IndexNumber = GetDictionaryDiscValue(tags, "track"); - - // Disc number - audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc"); - - audio.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); - - // Several different forms of retaildate - audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "date"); + audio.Album = data.Album; + audio.Artists = data.Artists; + audio.AlbumArtists = data.AlbumArtists; + audio.IndexNumber = data.IndexNumber; + audio.ProductionYear = data.ProductionYear; + audio.PremiereDate = data.PremiereDate; // If we don't have a ProductionYear try and get it from PremiereDate if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) @@ -254,192 +153,29 @@ namespace MediaBrowser.Providers.MediaInfo if (!audio.LockedFields.Contains(MetadataFields.Genres)) { - FetchGenres(audio, tags); - } - - if (!audio.LockedFields.Contains(MetadataFields.Studios)) - { - audio.Studios.Clear(); - - // There's several values in tags may or may not be present - FetchStudios(audio, tags, "organization"); - FetchStudios(audio, tags, "ensemble"); - FetchStudios(audio, tags, "publisher"); - } - - // These support mulitple values, but for now we only store the first. - audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"))); - audio.SetProviderId(MetadataProviders.MusicBrainzArtist, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"))); - - audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"))); - audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"))); - audio.SetProviderId(MetadataProviders.MusicBrainzTrack, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"))); - } - - private string GetMultipleMusicBrainzId(string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - return value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries) - .Select(i => i.Trim()) - .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); - } - - private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' }; - - /// <summary> - /// Splits the specified val. - /// </summary> - /// <param name="val">The val.</param> - /// <param name="allowCommaDelimiter">if set to <c>true</c> [allow comma delimiter].</param> - /// <returns>System.String[][].</returns> - private IEnumerable<string> Split(string val, bool allowCommaDelimiter) - { - // 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 = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i) != -1) ? - _nameDelimiters : - new[] { ',' }; - - return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(i => i.Trim()); - } - - private const string ArtistReplaceValue = " | "; - - private IEnumerable<string> SplitArtists(string val) - { - 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)) - { - 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; - - 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> - /// Gets the studios from the tags collection - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="tags">The tags.</param> - /// <param name="tagName">Name of the tag.</param> - private void FetchStudios(Audio audio, Dictionary<string, string> tags, string tagName) - { - var val = FFProbeHelpers.GetDictionaryValue(tags, tagName); - - if (!string.IsNullOrEmpty(val)) - { - // Sometimes the artist name is listed here, account for that - var studios = Split(val, true).Where(i => !audio.HasAnyArtist(i)); - - foreach (var studio in studios) - { - audio.AddStudio(studio); - } - } - } - - /// <summary> - /// Gets the genres from the tags collection - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="tags">The tags.</param> - private void FetchGenres(Audio audio, Dictionary<string, string> tags) - { - var val = FFProbeHelpers.GetDictionaryValue(tags, "genre"); - - if (!string.IsNullOrEmpty(val)) - { audio.Genres.Clear(); - foreach (var genre in Split(val, true)) + foreach (var genre in data.Genres) { audio.AddGenre(genre); } } - } - - /// <summary> - /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3' - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="tagName">Name of the tag.</param> - /// <returns>System.Nullable{System.Int32}.</returns> - private int? GetDictionaryDiscValue(Dictionary<string, string> tags, string tagName) - { - var disc = FFProbeHelpers.GetDictionaryValue(tags, tagName); - if (!string.IsNullOrEmpty(disc)) + if (!audio.LockedFields.Contains(MetadataFields.Studios)) { - disc = disc.Split('/')[0]; - - int num; + audio.Studios.Clear(); - if (int.TryParse(disc, out num)) + foreach (var studio in data.Studios) { - return num; + audio.AddStudio(studio); } } - return null; + audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist)); + audio.SetProviderId(MetadataProviders.MusicBrainzArtist, data.GetProviderId(MetadataProviders.MusicBrainzArtist)); + audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, data.GetProviderId(MetadataProviders.MusicBrainzAlbum)); + audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup)); + audio.SetProviderId(MetadataProviders.MusicBrainzTrack, data.GetProviderId(MetadataProviders.MusicBrainzTrack)); } - } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs b/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs deleted file mode 100644 index 2044979e4e..0000000000 --- a/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs +++ /dev/null @@ -1,118 +0,0 @@ -using MediaBrowser.Controller.MediaEncoding; -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Providers.MediaInfo -{ - public static class FFProbeHelpers - { - /// <summary> - /// Normalizes the FF probe result. - /// </summary> - /// <param name="result">The result.</param> - public static void NormalizeFFProbeResult(InternalMediaInfoResult result) - { - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (result.format != null && result.format.tags != null) - { - result.format.tags = ConvertDictionaryToCaseInSensitive(result.format.tags); - } - - if (result.streams != null) - { - // Convert all dictionaries to case insensitive - foreach (var stream in result.streams) - { - if (stream.tags != null) - { - stream.tags = ConvertDictionaryToCaseInSensitive(stream.tags); - } - - if (stream.disposition != null) - { - stream.disposition = ConvertDictionaryToCaseInSensitive(stream.disposition); - } - } - } - } - - /// <summary> - /// Gets a string from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.String.</returns> - public static string GetDictionaryValue(Dictionary<string, string> tags, string key) - { - if (tags == null) - { - return null; - } - - string val; - - tags.TryGetValue(key, out val); - return val; - } - - /// <summary> - /// Gets an int from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.Nullable{System.Int32}.</returns> - public static int? GetDictionaryNumericValue(Dictionary<string, string> tags, string key) - { - var val = GetDictionaryValue(tags, key); - - if (!string.IsNullOrEmpty(val)) - { - int i; - - if (int.TryParse(val, out i)) - { - return i; - } - } - - return null; - } - - /// <summary> - /// Gets a DateTime from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.Nullable{DateTime}.</returns> - public static DateTime? GetDictionaryDateTime(Dictionary<string, string> tags, string key) - { - var val = GetDictionaryValue(tags, key); - - if (!string.IsNullOrEmpty(val)) - { - DateTime i; - - if (DateTime.TryParse(val, out i)) - { - return i.ToUniversalTime(); - } - } - - return null; - } - - /// <summary> - /// Converts a dictionary to case insensitive - /// </summary> - /// <param name="dict">The dict.</param> - /// <returns>Dictionary{System.StringSystem.String}.</returns> - private static Dictionary<string, string> ConvertDictionaryToCaseInSensitive(Dictionary<string, string> dict) - { - return new Dictionary<string, string>(dict, StringComparer.OrdinalIgnoreCase); - } - } -} diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index ca11f858af..cec66f3c14 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -13,7 +13,6 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; -using MediaBrowser.MediaInfo; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; @@ -116,10 +115,6 @@ namespace MediaBrowser.Providers.MediaInfo cancellationToken.ThrowIfCancellationRequested(); - FFProbeHelpers.NormalizeFFProbeResult(result); - - cancellationToken.ThrowIfCancellationRequested(); - await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, options).ConfigureAwait(false); } @@ -136,7 +131,7 @@ namespace MediaBrowser.Providers.MediaInfo private const string SchemaVersion = "1"; - private async Task<InternalMediaInfoResult> GetMediaInfo(Video item, + private async Task<Model.Entities.MediaInfo> GetMediaInfo(Video item, IIsoMount isoMount, CancellationToken cancellationToken) { @@ -149,7 +144,7 @@ namespace MediaBrowser.Providers.MediaInfo try { - return _json.DeserializeFromFile<InternalMediaInfoResult>(cachePath); + return _json.DeserializeFromFile<Model.Entities.MediaInfo>(cachePath); } catch (FileNotFoundException) { @@ -165,7 +160,7 @@ namespace MediaBrowser.Providers.MediaInfo var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, protocol, isoMount, item.PlayableStreamFileNames); - var result = await _mediaEncoder.GetMediaInfo(inputPath, protocol, false, cancellationToken).ConfigureAwait(false); + var result = await _mediaEncoder.GetMediaInfo(inputPath, item.Path, protocol, false, true, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); _json.SerializeToFile(result, cachePath); @@ -175,52 +170,37 @@ namespace MediaBrowser.Providers.MediaInfo protected async Task Fetch(Video video, CancellationToken cancellationToken, - InternalMediaInfoResult data, + Model.Entities.MediaInfo mediaInfo, IIsoMount isoMount, BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { - var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data); var mediaStreams = mediaInfo.MediaStreams; - video.TotalBitrate = mediaInfo.TotalBitrate; - video.FormatName = (mediaInfo.Format ?? string.Empty) + video.TotalBitrate = mediaInfo.Bitrate; + video.FormatName = (mediaInfo.Container ?? string.Empty) .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); - if (data.format != null) - { - // For dvd's this may not always be accurate, so don't set the runtime if the item already has one - var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; - - if (needToSetRuntime && !string.IsNullOrEmpty(data.format.duration)) - { - video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks; - } + // For dvd's this may not always be accurate, so don't set the runtime if the item already has one + var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; - if (video.VideoType == VideoType.VideoFile) - { - var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); + if (needToSetRuntime) + { + video.RunTimeTicks = mediaInfo.RunTimeTicks; + } - video.Container = extension; - } - else - { - video.Container = null; - } + if (video.VideoType == VideoType.VideoFile) + { + var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); - if (!string.IsNullOrEmpty(data.format.size)) - { - video.Size = long.Parse(data.format.size, _usCulture); - } - else - { - video.Size = null; - } + video.Container = extension; + } + else + { + video.Container = null; } - var mediaChapters = (data.Chapters ?? new MediaChapter[] { }).ToList(); - var chapters = mediaChapters.Select(GetChapterInfo).ToList(); - + var chapters = mediaInfo.Chapters ?? new List<ChapterInfo>(); if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) { FetchBdInfo(video, chapters, mediaStreams, blurayInfo); @@ -228,7 +208,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - FetchWtvInfo(video, data); + FetchEmbeddedInfo(video, mediaInfo); video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270); @@ -238,9 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); - - ExtractTimestamp(video); - UpdateFromMediaInfo(video, videoStream); + video.Timestamp = mediaInfo.Timestamp; await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); @@ -283,29 +261,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - private void UpdateFromMediaInfo(Video video, MediaStream videoStream) - { - if (video.VideoType == VideoType.VideoFile && video.LocationType != LocationType.Remote && video.LocationType != LocationType.Virtual) - { - if (videoStream != null) - { - try - { - var result = new MediaInfoLib().GetVideoInfo(video.Path); - - videoStream.IsCabac = result.IsCabac ?? videoStream.IsCabac; - videoStream.IsInterlaced = result.IsInterlaced ?? videoStream.IsInterlaced; - videoStream.BitDepth = result.BitDepth ?? videoStream.BitDepth; - videoStream.RefFrames = result.RefFrames; - } - catch (Exception ex) - { - _logger.ErrorException("Error running MediaInfo on {0}", ex, video.Path); - } - } - } - } - private void NormalizeChapterNames(List<ChapterInfo> chapters) { var index = 1; @@ -325,32 +280,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - private ChapterInfo GetChapterInfo(MediaChapter chapter) - { - var info = new ChapterInfo(); - - if (chapter.tags != null) - { - string name; - if (chapter.tags.TryGetValue("title", out name)) - { - info.Name = name; - } - } - - // Limit accuracy to milliseconds to match xml saving - var secondsString = chapter.start_time; - double seconds; - - if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out seconds)) - { - var ms = Math.Round(TimeSpan.FromSeconds(seconds).TotalMilliseconds); - info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks; - } - - return info; - } - private void FetchBdInfo(BaseItem item, List<ChapterInfo> chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo) { var video = (Video)item; @@ -419,129 +348,79 @@ namespace MediaBrowser.Providers.MediaInfo return _blurayExaminer.GetDiscInfo(path); } - public const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames) - - private void FetchWtvInfo(Video video, InternalMediaInfoResult data) + private void FetchEmbeddedInfo(Video video, Model.Entities.MediaInfo data) { - if (data.format == null || data.format.tags == null) - { - return; - } - - if (!video.LockedFields.Contains(MetadataFields.Genres)) - { - var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/Genre"); - - if (!string.IsNullOrWhiteSpace(genres)) - { - //genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre"); - } - - if (!string.IsNullOrWhiteSpace(genres)) - { - video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(i => i.Trim()) - .ToList(); - } - } - if (!video.LockedFields.Contains(MetadataFields.OfficialRating)) { - var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating"); - - if (!string.IsNullOrWhiteSpace(officialRating)) + if (!string.IsNullOrWhiteSpace(data.OfficialRating)) { - video.OfficialRating = officialRating; + video.OfficialRating = data.OfficialRating; } } if (!video.LockedFields.Contains(MetadataFields.Cast)) { - var people = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaCredits"); + video.People.Clear(); - if (!string.IsNullOrEmpty(people)) + foreach (var person in data.People) { - video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(i => new PersonInfo { Name = i.Trim(), Type = PersonType.Actor }) - .ToList(); + video.AddPerson(new PersonInfo + { + Name = person.Name, + Type = person.Type, + Role = person.Role + }); } } - var year = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/OriginalReleaseTime"); - if (!string.IsNullOrWhiteSpace(year)) + if (!video.LockedFields.Contains(MetadataFields.Genres)) { - int val; + video.Genres.Clear(); - if (int.TryParse(year, NumberStyles.Integer, _usCulture, out val)) + foreach (var genre in data.Genres) { - video.ProductionYear = val; + video.AddGenre(genre); } } - var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaOriginalBroadcastDateTime"); - if (!string.IsNullOrWhiteSpace(premiereDateString)) + if (!video.LockedFields.Contains(MetadataFields.Studios)) { - DateTime val; + video.Studios.Clear(); - // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ - // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None) - if (DateTime.TryParse(year, null, DateTimeStyles.None, out val)) + foreach (var studio in data.Studios) { - video.PremiereDate = val.ToUniversalTime(); + video.AddStudio(studio); } } - var description = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription"); - - var episode = video as Episode; - if (episode != null) + if (data.ProductionYear.HasValue) { - var subTitle = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitle"); - - // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ - - // Sometimes for TV Shows the Subtitle field is empty and the subtitle description contains the subtitle, extract if possible. See ticket https://mcebuddy2x.codeplex.com/workitem/1910 - // The format is -> EPISODE/TOTAL_EPISODES_IN_SEASON. SUBTITLE: DESCRIPTION - // OR -> COMMENT. SUBTITLE: DESCRIPTION - // e.g. -> 4/13. The Doctor's Wife: Science fiction drama. When he follows a Time Lord distress signal, the Doctor puts Amy, Rory and his beloved TARDIS in grave danger. Also in HD. [AD,S] - // e.g. -> CBeebies Bedtime Hour. The Mystery: Animated adventures of two friends who live on an island in the middle of the big city. Some of Abney and Teal's favourite objects are missing. [S] - if (String.IsNullOrWhiteSpace(subTitle) && !String.IsNullOrWhiteSpace(description) && description.Substring(0, Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)).Contains(":")) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename - { - string[] parts = description.Split(':'); - if (parts.Length > 0) - { - string subtitle = parts[0]; - try - { - if (subtitle.Contains("/")) // It contains a episode number and season number - { - string[] numbers = subtitle.Split(' '); - episode.IndexNumber = int.Parse(numbers[0].Replace(".", "").Split('/')[0]); - int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", "").Split('/')[1]); + video.ProductionYear = data.ProductionYear; + } + if (data.PremiereDate.HasValue) + { + video.PremiereDate = data.PremiereDate; + } + if (data.IndexNumber.HasValue) + { + video.IndexNumber = data.IndexNumber; + } + if (data.ParentIndexNumber.HasValue) + { + video.ParentIndexNumber = data.ParentIndexNumber; + } - description = String.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it - } - else - throw new Exception(); // Switch to default parsing - } - catch // Default parsing - { - if (subtitle.Contains(".")) // skip the comment, keep the subtitle - description = String.Join(".", subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first - else - description = subtitle.Trim(); // Clean up whitespaces and save it - } - } - } + // If we don't have a ProductionYear try and get it from PremiereDate + if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue) + { + video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year; } if (!video.LockedFields.Contains(MetadataFields.Overview)) { - if (!string.IsNullOrWhiteSpace(description)) + if (!string.IsNullOrWhiteSpace(data.Overview)) { - video.Overview = description; + video.Overview = data.Overview; } } } @@ -709,56 +588,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - private void ExtractTimestamp(Video video) - { - if (video.VideoType == VideoType.VideoFile) - { - if (string.Equals(video.Container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) || - string.Equals(video.Container, "m2ts", StringComparison.OrdinalIgnoreCase) || - string.Equals(video.Container, "ts", StringComparison.OrdinalIgnoreCase)) - { - try - { - video.Timestamp = GetMpegTimestamp(video.Path); - - _logger.Debug("Video has {0} timestamp", video.Timestamp); - } - catch (Exception ex) - { - _logger.ErrorException("Error extracting timestamp info from {0}", ex, video.Path); - video.Timestamp = null; - } - } - } - } - - private TransportStreamTimestamp GetMpegTimestamp(string path) - { - var packetBuffer = new byte['Å']; - - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - fs.Read(packetBuffer, 0, packetBuffer.Length); - } - - if (packetBuffer[0] == 71) - { - return TransportStreamTimestamp.None; - } - - if ((packetBuffer[4] == 71) && (packetBuffer['Ä'] == 71)) - { - if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0)) - { - return TransportStreamTimestamp.Zero; - } - - return TransportStreamTimestamp.Valid; - } - - return TransportStreamTimestamp.None; - } - private void FetchFromDvdLib(Video item, IIsoMount mount) { var path = mount == null ? item.Path : mount.MountedPath; |
