mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-10-31 02:27:18 -04:00 
			
		
		
		
	Refactor ProbeResultNormalizer
Improve code structure and readability
This commit is contained in:
		
							parent
							
								
									40c73e96a9
								
							
						
					
					
						commit
						11a5551218
					
				| @ -1,5 +1,3 @@ | |||||||
| #nullable disable |  | ||||||
| 
 |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Globalization; | using System.Globalization; | ||||||
| @ -22,7 +20,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 throw new ArgumentNullException(nameof(result)); |                 throw new ArgumentNullException(nameof(result)); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (result.Format != null && result.Format.Tags != null) |             if (result.Format?.Tags != null) | ||||||
|             { |             { | ||||||
|                 result.Format.Tags = ConvertDictionaryToCaseInsensitive(result.Format.Tags); |                 result.Format.Tags = ConvertDictionaryToCaseInsensitive(result.Format.Tags); | ||||||
|             } |             } | ||||||
| @ -40,40 +38,18 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <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(IReadOnlyDictionary<string, string> tags, string key) |  | ||||||
|         { |  | ||||||
|             if (tags == null) |  | ||||||
|             { |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             tags.TryGetValue(key, out var val); |  | ||||||
|             return val; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Gets an int from an FFProbeResult tags dictionary. |         /// Gets an int from an FFProbeResult tags dictionary. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="tags">The tags.</param> |         /// <param name="tags">The tags.</param> | ||||||
|         /// <param name="key">The key.</param> |         /// <param name="key">The key.</param> | ||||||
|         /// <returns>System.Nullable{System.Int32}.</returns> |         /// <returns>System.Nullable{System.Int32}.</returns> | ||||||
|         public static int? GetDictionaryNumericValue(Dictionary<string, string> tags, string key) |         public static int? GetDictionaryNumericValue(IReadOnlyDictionary<string, string> tags, string key) | ||||||
|         { |         { | ||||||
|             var val = GetDictionaryValue(tags, key); |             if (tags.TryGetValue(key, out var val) && int.TryParse(val, out var i)) | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrEmpty(val)) |  | ||||||
|             { |  | ||||||
|                 if (int.TryParse(val, out var i)) |  | ||||||
|             { |             { | ||||||
|                 return i; |                 return i; | ||||||
|             } |             } | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| @ -84,18 +60,12 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|         /// <param name="tags">The tags.</param> |         /// <param name="tags">The tags.</param> | ||||||
|         /// <param name="key">The key.</param> |         /// <param name="key">The key.</param> | ||||||
|         /// <returns>System.Nullable{DateTime}.</returns> |         /// <returns>System.Nullable{DateTime}.</returns> | ||||||
|         public static DateTime? GetDictionaryDateTime(Dictionary<string, string> tags, string key) |         public static DateTime? GetDictionaryDateTime(IReadOnlyDictionary<string, string> tags, string key) | ||||||
|         { |         { | ||||||
|             var val = GetDictionaryValue(tags, key); |             if (tags.TryGetValue(key, out var val) | ||||||
| 
 |                 && DateTime.TryParse(val, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AssumeUniversal, out var dateTime)) | ||||||
|             if (string.IsNullOrEmpty(val)) |  | ||||||
|             { |             { | ||||||
|                 return null; |                 return dateTime.ToUniversalTime(); | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (DateTime.TryParse(val, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AssumeUniversal, out var i)) |  | ||||||
|             { |  | ||||||
|                 return i.ToUniversalTime(); |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return null; |             return null; | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ using System.IO; | |||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Text; | using System.Text; | ||||||
| using System.Xml; | using System.Xml; | ||||||
|  | using Jellyfin.Extensions; | ||||||
| using MediaBrowser.Controller.Library; | using MediaBrowser.Controller.Library; | ||||||
| using MediaBrowser.Model.Dto; | using MediaBrowser.Model.Dto; | ||||||
| using MediaBrowser.Model.Entities; | using MediaBrowser.Model.Entities; | ||||||
| @ -80,68 +81,33 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); |             var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); | ||||||
|             var tagStreamType = isAudio ? "audio" : "video"; |             var tagStreamType = isAudio ? "audio" : "video"; | ||||||
| 
 | 
 | ||||||
|             if (data.Streams != null) |             var tagStream = data.Streams?.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase)); | ||||||
|             { |  | ||||||
|                 var tagStream = data.Streams.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase)); |  | ||||||
| 
 | 
 | ||||||
|                 if (tagStream != null && tagStream.Tags != null) |             if (tagStream?.Tags != null) | ||||||
|             { |             { | ||||||
|                     foreach (var pair in tagStream.Tags) |                 foreach (var (key, value) in tagStream.Tags) | ||||||
|                 { |                 { | ||||||
|                         tags[pair.Key] = pair.Value; |                     tags[key] = value; | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (data.Format != null && data.Format.Tags != null) |             if (data.Format?.Tags != null) | ||||||
|             { |             { | ||||||
|                 foreach (var pair in data.Format.Tags) |                 foreach (var (key, value) in data.Format.Tags) | ||||||
|                 { |                 { | ||||||
|                     tags[pair.Key] = pair.Value; |                     tags[key] = value; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             FetchGenres(info, tags); |             FetchGenres(info, tags); | ||||||
|             var overview = FFProbeHelpers.GetDictionaryValue(tags, "synopsis"); |  | ||||||
| 
 | 
 | ||||||
|             if (string.IsNullOrWhiteSpace(overview)) |             info.Name = tags.GetFirstNotNullNorWhiteSpaceValue("title", "title-eng"); | ||||||
|             { |             info.ForcedSortName = tags.GetFirstNotNullNorWhiteSpaceValue("sort_name", "title-sort", "titlesort"); | ||||||
|                 overview = FFProbeHelpers.GetDictionaryValue(tags, "description"); |             info.Overview = tags.GetFirstNotNullNorWhiteSpaceValue("synopsis", "description", "desc"); | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (string.IsNullOrWhiteSpace(overview)) |  | ||||||
|             { |  | ||||||
|                 overview = FFProbeHelpers.GetDictionaryValue(tags, "desc"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrWhiteSpace(overview)) |  | ||||||
|             { |  | ||||||
|                 info.Overview = overview; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); |  | ||||||
|             if (!string.IsNullOrWhiteSpace(title)) |  | ||||||
|             { |  | ||||||
|                 info.Name = title; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 title = FFProbeHelpers.GetDictionaryValue(tags, "title-eng"); |  | ||||||
|                 if (!string.IsNullOrWhiteSpace(title)) |  | ||||||
|                 { |  | ||||||
|                     info.Name = title; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             var titleSort = FFProbeHelpers.GetDictionaryValue(tags, "titlesort"); |  | ||||||
|             if (!string.IsNullOrWhiteSpace(titleSort)) |  | ||||||
|             { |  | ||||||
|                 info.ForcedSortName = titleSort; |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort"); |             info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort"); | ||||||
|             info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number"); |             info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number"); | ||||||
|             info.ShowName = FFProbeHelpers.GetDictionaryValue(tags, "show_name"); |             info.ShowName = tags.GetValueOrDefault("show_name"); | ||||||
|             info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); |             info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); | ||||||
| 
 | 
 | ||||||
|             // Several different forms of retail/premiere date |             // Several different forms of retail/premiere date | ||||||
| @ -153,32 +119,21 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 FFProbeHelpers.GetDictionaryDateTime(tags, "date"); |                 FFProbeHelpers.GetDictionaryDateTime(tags, "date"); | ||||||
| 
 | 
 | ||||||
|             // Set common metadata for music (audio) and music videos (video) |             // Set common metadata for music (audio) and music videos (video) | ||||||
|             info.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); |             info.Album = tags.GetValueOrDefault("album"); | ||||||
| 
 | 
 | ||||||
|             var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); |             if (tags.TryGetValue("artists", out var artists) && !string.IsNullOrWhiteSpace(artists)) | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrWhiteSpace(artists)) |  | ||||||
|             { |             { | ||||||
|                 info.Artists = SplitArtists(artists, new[] { '/', ';' }, false) |                 info.Artists = SplitDistinctArtists(artists, new[] { '/', ';' }, false).ToArray(); | ||||||
|                     .DistinctNames() |  | ||||||
|                     .ToArray(); |  | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist"); |                 var artist = tags.GetFirstNotNullNorWhiteSpaceValue("artist"); | ||||||
|                 if (string.IsNullOrWhiteSpace(artist)) |                 info.Artists = artist == null | ||||||
|                 { |                     ? Array.Empty<string>() | ||||||
|                     info.Artists = Array.Empty<string>(); |                     : SplitDistinctArtists(artist, _nameDelimiters, true).ToArray(); | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     info.Artists = SplitArtists(artist, _nameDelimiters, true) |  | ||||||
|                         .DistinctNames() |  | ||||||
|                         .ToArray(); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // If we don't have a ProductionYear try and get it from PremiereDate |             // Guess ProductionYear from PremiereDate if missing | ||||||
|             if (!info.ProductionYear.HasValue && info.PremiereDate.HasValue) |             if (!info.ProductionYear.HasValue && info.PremiereDate.HasValue) | ||||||
|             { |             { | ||||||
|                 info.ProductionYear = info.PremiereDate.Value.Year; |                 info.ProductionYear = info.PremiereDate.Value.Year; | ||||||
| @ -198,10 +153,10 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             { |             { | ||||||
|                 FetchStudios(info, tags, "copyright"); |                 FetchStudios(info, tags, "copyright"); | ||||||
| 
 | 
 | ||||||
|                 var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC"); |                 var iTunExtc = tags.GetFirstNotNullNorWhiteSpaceValue("iTunEXTC"); | ||||||
|                 if (!string.IsNullOrWhiteSpace(iTunEXTC)) |                 if (iTunExtc != null) | ||||||
|                 { |                 { | ||||||
|                     var parts = iTunEXTC.Split('|', StringSplitOptions.RemoveEmptyEntries); |                     var parts = iTunExtc.Split('|', StringSplitOptions.RemoveEmptyEntries); | ||||||
|                     // Example |                     // Example | ||||||
|                     // mpaa|G|100|For crude humor |                     // mpaa|G|100|For crude humor | ||||||
|                     if (parts.Length > 1) |                     if (parts.Length > 1) | ||||||
| @ -215,10 +170,10 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI"); |                 var iTunXml = tags.GetFirstNotNullNorWhiteSpaceValue("iTunMOVI"); | ||||||
|                 if (!string.IsNullOrWhiteSpace(itunesXml)) |                 if (iTunXml != null) | ||||||
|                 { |                 { | ||||||
|                     FetchFromItunesInfo(itunesXml, info); |                     FetchFromItunesInfo(iTunXml, info); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (data.Format != null && !string.IsNullOrEmpty(data.Format.Duration)) |                 if (data.Format != null && !string.IsNullOrEmpty(data.Format.Duration)) | ||||||
| @ -235,8 +190,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
| 
 | 
 | ||||||
|                 ExtractTimestamp(info); |                 ExtractTimestamp(info); | ||||||
| 
 | 
 | ||||||
|                 var stereoMode = GetDictionaryValue(tags, "stereo_mode"); |                 if (tags.TryGetValue("stereo_mode", out var stereoMode) && string.Equals(stereoMode, "left_right", StringComparison.OrdinalIgnoreCase)) | ||||||
|                 if (string.Equals(stereoMode, "left_right", StringComparison.OrdinalIgnoreCase)) |  | ||||||
|                 { |                 { | ||||||
|                     info.Video3DFormat = Video3DFormat.FullSideBySide; |                     info.Video3DFormat = Video3DFormat.FullSideBySide; | ||||||
|                 } |                 } | ||||||
| @ -289,13 +243,11 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) |             if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) | ||||||
|                 || string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) |                 || string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) | ||||||
|             { |             { | ||||||
|                 if (channelsValue <= 2) |                 switch (channelsValue) | ||||||
|                 { |                 { | ||||||
|  |                     case <= 2: | ||||||
|                         return 192000; |                         return 192000; | ||||||
|                 } |                     case >= 5: | ||||||
| 
 |  | ||||||
|                 if (channelsValue >= 5) |  | ||||||
|                 { |  | ||||||
|                         return 320000; |                         return 320000; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -303,13 +255,11 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase) |             if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase) | ||||||
|                 || string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase)) |                 || string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase)) | ||||||
|             { |             { | ||||||
|                 if (channelsValue <= 2) |                 switch (channelsValue) | ||||||
|                 { |                 { | ||||||
|  |                     case <= 2: | ||||||
|                         return 192000; |                         return 192000; | ||||||
|                 } |                     case >= 5: | ||||||
| 
 |  | ||||||
|                 if (channelsValue >= 5) |  | ||||||
|                 { |  | ||||||
|                         return 640000; |                         return 640000; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -317,13 +267,11 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase) |             if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase) | ||||||
|                 || string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase)) |                 || string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase)) | ||||||
|             { |             { | ||||||
|                 if (channelsValue <= 2) |                 switch (channelsValue) | ||||||
|                 { |                 { | ||||||
|  |                     case <= 2: | ||||||
|                         return 960000; |                         return 960000; | ||||||
|                 } |                     case >= 5: | ||||||
| 
 |  | ||||||
|                 if (channelsValue >= 5) |  | ||||||
|                 { |  | ||||||
|                         return 2880000; |                         return 2880000; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -854,7 +802,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                     || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))) |                     || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))) | ||||||
|             { |             { | ||||||
|                 var bps = GetBPSFromTags(streamInfo); |                 var bps = GetBPSFromTags(streamInfo); | ||||||
|                 if (bps != null && bps > 0) |                 if (bps > 0) | ||||||
|                 { |                 { | ||||||
|                     stream.BitRate = bps; |                     stream.BitRate = bps; | ||||||
|                 } |                 } | ||||||
| @ -923,6 +871,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             tags.TryGetValue(key, out var val); |             tags.TryGetValue(key, out var val); | ||||||
|  | 
 | ||||||
|             return val; |             return val; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -930,7 +879,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|         { |         { | ||||||
|             if (string.IsNullOrEmpty(input)) |             if (string.IsNullOrEmpty(input)) | ||||||
|             { |             { | ||||||
|                 return input; |                 return null; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return input.Split('(').FirstOrDefault(); |             return input.Split('(').FirstOrDefault(); | ||||||
| @ -1018,8 +967,11 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|         /// <returns>System.Nullable{System.Single}.</returns> |         /// <returns>System.Nullable{System.Single}.</returns> | ||||||
|         private float? GetFrameRate(string value) |         private float? GetFrameRate(string value) | ||||||
|         { |         { | ||||||
|             if (!string.IsNullOrEmpty(value)) |             if (string.IsNullOrEmpty(value)) | ||||||
|             { |             { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             var parts = value.Split('/'); |             var parts = value.Split('/'); | ||||||
| 
 | 
 | ||||||
|             float result; |             float result; | ||||||
| @ -1033,21 +985,18 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 result = float.Parse(parts[0], _usCulture); |                 result = float.Parse(parts[0], _usCulture); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|                 return float.IsNaN(result) ? (float?)null : result; |             return float.IsNaN(result) ? null : result; | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return null; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data) |         private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data) | ||||||
|         { |  | ||||||
|             if (result.Streams != null) |  | ||||||
|         { |         { | ||||||
|             // Get the first info stream |             // Get the first info stream | ||||||
|                 var stream = result.Streams.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase)); |             var stream = result.Streams?.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase)); | ||||||
| 
 |             if (stream == null) | ||||||
|                 if (stream != null) |  | ||||||
|             { |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             // Get duration from stream properties |             // Get duration from stream properties | ||||||
|             var duration = stream.Duration; |             var duration = stream.Duration; | ||||||
| 
 | 
 | ||||||
| @ -1063,73 +1012,72 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 data.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks; |                 data.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         private int? GetBPSFromTags(MediaStreamInfo streamInfo) |         private int? GetBPSFromTags(MediaStreamInfo streamInfo) | ||||||
|         { |         { | ||||||
|             if (streamInfo != null && streamInfo.Tags != null) |             if (streamInfo?.Tags == null) | ||||||
|             { |             { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS"); |             var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS"); | ||||||
|             if (!string.IsNullOrEmpty(bps) |             if (!string.IsNullOrEmpty(bps) | ||||||
|                 && int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) |                 && int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) | ||||||
|             { |             { | ||||||
|                 return parsedBps; |                 return parsedBps; | ||||||
|             } |             } | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo) |         private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo) | ||||||
|         { |         { | ||||||
|             if (streamInfo != null && streamInfo.Tags != null) |             if (streamInfo?.Tags == null) | ||||||
|             { |             { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION"); |             var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION"); | ||||||
|             if (!string.IsNullOrEmpty(duration) && TimeSpan.TryParse(duration, out var parsedDuration)) |             if (!string.IsNullOrEmpty(duration) && TimeSpan.TryParse(duration, out var parsedDuration)) | ||||||
|             { |             { | ||||||
|                 return parsedDuration.TotalSeconds; |                 return parsedDuration.TotalSeconds; | ||||||
|             } |             } | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo) |         private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo) | ||||||
|         { |         { | ||||||
|             if (streamInfo != null && streamInfo.Tags != null) |             if (streamInfo?.Tags == null) | ||||||
|             { |             { | ||||||
|                 var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES"); |                 return null; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") | ||||||
|  |                                 ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES"); | ||||||
|             if (!string.IsNullOrEmpty(numberOfBytes) |             if (!string.IsNullOrEmpty(numberOfBytes) | ||||||
|                 && long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) |                 && long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) | ||||||
|             { |             { | ||||||
|                 return parsedBytes; |                 return parsedBytes; | ||||||
|             } |             } | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void SetSize(InternalMediaInfoResult data, MediaInfo info) |         private void SetSize(InternalMediaInfoResult data, MediaInfo info) | ||||||
|         { |         { | ||||||
|             if (data.Format != null) |             if (data.Format == null) | ||||||
|             { |             { | ||||||
|                 if (!string.IsNullOrEmpty(data.Format.Size)) |                 return; | ||||||
|                 { |  | ||||||
|                     info.Size = long.Parse(data.Format.Size, _usCulture); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     info.Size = null; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags) |             info.Size = string.IsNullOrEmpty(data.Format.Size) ? null : long.Parse(data.Format.Size, _usCulture); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SetAudioInfoFromTags(MediaInfo audio, IReadOnlyDictionary<string, string> tags) | ||||||
|         { |         { | ||||||
|             var people = new List<BaseItemPerson>(); |             var people = new List<BaseItemPerson>(); | ||||||
|             var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); |             if (tags.TryGetValue("composer", out var composer) && !string.IsNullOrWhiteSpace(composer)) | ||||||
|             if (!string.IsNullOrWhiteSpace(composer)) |  | ||||||
|             { |             { | ||||||
|                 foreach (var person in Split(composer, false)) |                 foreach (var person in Split(composer, false)) | ||||||
|                 { |                 { | ||||||
| @ -1137,8 +1085,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor"); |             if (tags.TryGetValue("conductor", out var conductor) && !string.IsNullOrWhiteSpace(conductor)) | ||||||
|             if (!string.IsNullOrWhiteSpace(conductor)) |  | ||||||
|             { |             { | ||||||
|                 foreach (var person in Split(conductor, false)) |                 foreach (var person in Split(conductor, false)) | ||||||
|                 { |                 { | ||||||
| @ -1146,8 +1093,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist"); |             if (tags.TryGetValue("lyricist", out var lyricist) && !string.IsNullOrWhiteSpace(lyricist)) | ||||||
|             if (!string.IsNullOrWhiteSpace(lyricist)) |  | ||||||
|             { |             { | ||||||
|                 foreach (var person in Split(lyricist, false)) |                 foreach (var person in Split(lyricist, false)) | ||||||
|                 { |                 { | ||||||
| @ -1156,8 +1102,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Check for writer some music is tagged that way as alternative to composer/lyricist |             // Check for writer some music is tagged that way as alternative to composer/lyricist | ||||||
|             var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer"); |             if (tags.TryGetValue("writer", out var writer) && !string.IsNullOrWhiteSpace(writer)) | ||||||
|             if (!string.IsNullOrWhiteSpace(writer)) |  | ||||||
|             { |             { | ||||||
|                 foreach (var person in Split(writer, false)) |                 foreach (var person in Split(writer, false)) | ||||||
|                 { |                 { | ||||||
| @ -1167,38 +1112,23 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
| 
 | 
 | ||||||
|             audio.People = people.ToArray(); |             audio.People = people.ToArray(); | ||||||
| 
 | 
 | ||||||
|             var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist"); |             // Set album artist | ||||||
|             if (string.IsNullOrWhiteSpace(albumArtist)) |             var albumArtist = tags.GetFirstNotNullNorWhiteSpaceValue("albumartist", "album artist", "album_artist"); | ||||||
|             { |             audio.AlbumArtists = albumArtist != null | ||||||
|                 albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist"); |                 ? SplitDistinctArtists(albumArtist, _nameDelimiters, true).ToArray() | ||||||
|             } |                 : Array.Empty<string>(); | ||||||
| 
 |  | ||||||
|             if (string.IsNullOrWhiteSpace(albumArtist)) |  | ||||||
|             { |  | ||||||
|                 albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (string.IsNullOrWhiteSpace(albumArtist)) |  | ||||||
|             { |  | ||||||
|                 audio.AlbumArtists = Array.Empty<string>(); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true) |  | ||||||
|                     .DistinctNames() |  | ||||||
|                     .ToArray(); |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|  |             // Set album artist to artist if empty | ||||||
|             if (audio.AlbumArtists.Length == 0) |             if (audio.AlbumArtists.Length == 0) | ||||||
|             { |             { | ||||||
|                 audio.AlbumArtists = audio.Artists; |                 audio.AlbumArtists = audio.Artists; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Track number |             // Track number | ||||||
|             audio.IndexNumber = GetDictionaryDiscValue(tags, "track"); |             audio.IndexNumber = GetDictionaryTrackOrDiscNumber(tags, "track"); | ||||||
| 
 | 
 | ||||||
|             // Disc number |             // Disc number | ||||||
|             audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc"); |             audio.ParentIndexNumber = GetDictionaryTrackOrDiscNumber(tags, "disc"); | ||||||
| 
 | 
 | ||||||
|             // There's several values in tags may or may not be present |             // There's several values in tags may or may not be present | ||||||
|             FetchStudios(audio, tags, "organization"); |             FetchStudios(audio, tags, "organization"); | ||||||
| @ -1206,30 +1136,25 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             FetchStudios(audio, tags, "publisher"); |             FetchStudios(audio, tags, "publisher"); | ||||||
|             FetchStudios(audio, tags, "label"); |             FetchStudios(audio, tags, "label"); | ||||||
| 
 | 
 | ||||||
|             // These support mulitple values, but for now we only store the first. |             // These support multiple values, but for now we only store the first. | ||||||
|             var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")) |             var mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Album Artist Id")) | ||||||
|                 ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); |                 ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ALBUMARTISTID")); | ||||||
| 
 |  | ||||||
|             audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb); |             audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb); | ||||||
| 
 | 
 | ||||||
|             mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")) |             mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Artist Id")) | ||||||
|                 ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); |                 ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ARTISTID")); | ||||||
| 
 |  | ||||||
|             audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb); |             audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb); | ||||||
| 
 | 
 | ||||||
|             mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")) |             mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Album Id")) | ||||||
|                 ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); |                 ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ALBUMID")); | ||||||
| 
 |  | ||||||
|             audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb); |             audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb); | ||||||
| 
 | 
 | ||||||
|             mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")) |             mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Release Group Id")) | ||||||
|                 ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); |                  ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_RELEASEGROUPID")); | ||||||
| 
 |  | ||||||
|             audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb); |             audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb); | ||||||
| 
 | 
 | ||||||
|             mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")) |             mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Release Track Id")) | ||||||
|                 ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); |                  ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_RELEASETRACKID")); | ||||||
| 
 |  | ||||||
|             audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb); |             audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -1253,18 +1178,18 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|         /// <returns>System.String[][].</returns> |         /// <returns>System.String[][].</returns> | ||||||
|         private IEnumerable<string> Split(string val, bool allowCommaDelimiter) |         private IEnumerable<string> Split(string val, bool allowCommaDelimiter) | ||||||
|         { |         { | ||||||
|             // Only use the comma as a delimeter if there are no slashes or pipes. |             // Only use the comma as a delimiter if there are no slashes or pipes. | ||||||
|             // We want to be careful not to split names that have commas in them |             // We want to be careful not to split names that have commas in them | ||||||
|             var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i, StringComparison.Ordinal) != -1) ? |             var delimiter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i, StringComparison.Ordinal) != -1) ? | ||||||
|                 _nameDelimiters : |                 _nameDelimiters : | ||||||
|                 new[] { ',' }; |                 new[] { ',' }; | ||||||
| 
 | 
 | ||||||
|             return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries) |             return val.Split(delimiter, StringSplitOptions.RemoveEmptyEntries) | ||||||
|                 .Where(i => !string.IsNullOrWhiteSpace(i)) |                 .Where(i => !string.IsNullOrWhiteSpace(i)) | ||||||
|                 .Select(i => i.Trim()); |                 .Select(i => i.Trim()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private IEnumerable<string> SplitArtists(string val, char[] delimiters, bool splitFeaturing) |         private IEnumerable<string> SplitDistinctArtists(string val, char[] delimiters, bool splitFeaturing) | ||||||
|         { |         { | ||||||
|             if (splitFeaturing) |             if (splitFeaturing) | ||||||
|             { |             { | ||||||
| @ -1290,7 +1215,7 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 .Select(i => i.Trim()); |                 .Select(i => i.Trim()); | ||||||
| 
 | 
 | ||||||
|             artistsFound.AddRange(artists); |             artistsFound.AddRange(artists); | ||||||
|             return artistsFound; |             return artistsFound.DistinctNames(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @ -1299,24 +1224,28 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|         /// <param name="info">The info.</param> |         /// <param name="info">The info.</param> | ||||||
|         /// <param name="tags">The tags.</param> |         /// <param name="tags">The tags.</param> | ||||||
|         /// <param name="tagName">Name of the tag.</param> |         /// <param name="tagName">Name of the tag.</param> | ||||||
|         private void FetchStudios(MediaInfo info, Dictionary<string, string> tags, string tagName) |         private void FetchStudios(MediaInfo info, IReadOnlyDictionary<string, string> tags, string tagName) | ||||||
|         { |         { | ||||||
|             var val = FFProbeHelpers.GetDictionaryValue(tags, tagName); |             var val = tags.GetValueOrDefault(tagName); | ||||||
| 
 | 
 | ||||||
|             if (!string.IsNullOrEmpty(val)) |             if (string.IsNullOrEmpty(val)) | ||||||
|             { |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             var studios = Split(val, true); |             var studios = Split(val, true); | ||||||
|             var studioList = new List<string>(); |             var studioList = new List<string>(); | ||||||
| 
 | 
 | ||||||
|             foreach (var studio in studios) |             foreach (var studio in studios) | ||||||
|             { |             { | ||||||
|                     // Sometimes the artist name is listed here, account for that |                 if (string.IsNullOrWhiteSpace(studio)) | ||||||
|                     if (info.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase)) |  | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                     if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase)) |                 // Don't add artist/album artist name to studios, even if it's listed there | ||||||
|  |                 if (info.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase) | ||||||
|  |                     || info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase)) | ||||||
|                 { |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| @ -1325,70 +1254,65 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             info.Studios = studioList |             info.Studios = studioList | ||||||
|                     .Where(i => !string.IsNullOrWhiteSpace(i)) |  | ||||||
|                 .Distinct(StringComparer.OrdinalIgnoreCase) |                 .Distinct(StringComparer.OrdinalIgnoreCase) | ||||||
|                 .ToArray(); |                 .ToArray(); | ||||||
|         } |         } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Gets the genres from the tags collection. |         /// Gets the genres from the tags collection. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="info">The information.</param> |         /// <param name="info">The information.</param> | ||||||
|         /// <param name="tags">The tags.</param> |         /// <param name="tags">The tags.</param> | ||||||
|         private void FetchGenres(MediaInfo info, Dictionary<string, string> tags) |         private void FetchGenres(MediaInfo info, IReadOnlyDictionary<string, string> tags) | ||||||
|         { |         { | ||||||
|             var val = FFProbeHelpers.GetDictionaryValue(tags, "genre"); |             var genreVal = tags.GetValueOrDefault("genre"); | ||||||
|  |             if (string.IsNullOrEmpty(genreVal)) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             if (!string.IsNullOrEmpty(val)) |  | ||||||
|             { |  | ||||||
|             var genres = new List<string>(info.Genres); |             var genres = new List<string>(info.Genres); | ||||||
|                 foreach (var genre in Split(val, true)) |             foreach (var genre in Split(genreVal, true)) | ||||||
|             { |             { | ||||||
|  |                 if (string.IsNullOrWhiteSpace(genre)) | ||||||
|  |                 { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 genres.Add(genre); |                 genres.Add(genre); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             info.Genres = genres |             info.Genres = genres | ||||||
|                     .Where(i => !string.IsNullOrWhiteSpace(i)) |  | ||||||
|                 .Distinct(StringComparer.OrdinalIgnoreCase) |                 .Distinct(StringComparer.OrdinalIgnoreCase) | ||||||
|                 .ToArray(); |                 .ToArray(); | ||||||
|         } |         } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3'. |         /// Gets the track or disc number, which can be in the form of '1', or '1/3'. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="tags">The tags.</param> |         /// <param name="tags">The tags.</param> | ||||||
|         /// <param name="tagName">Name of the tag.</param> |         /// <param name="tagName">Name of the tag.</param> | ||||||
|         /// <returns>System.Nullable{System.Int32}.</returns> |         /// <returns>The track or disc number, or null, if missing or not parseable.</returns> | ||||||
|         private int? GetDictionaryDiscValue(Dictionary<string, string> tags, string tagName) |         private static int? GetDictionaryTrackOrDiscNumber(IReadOnlyDictionary<string, string> tags, string tagName) | ||||||
|         { |         { | ||||||
|             var disc = FFProbeHelpers.GetDictionaryValue(tags, tagName); |             var disc = tags.GetValueOrDefault(tagName); | ||||||
| 
 | 
 | ||||||
|             if (!string.IsNullOrEmpty(disc)) |             if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.Split('/')[0], out var discNum)) | ||||||
|             { |             { | ||||||
|                 disc = disc.Split('/')[0]; |                 return discNum; | ||||||
| 
 |  | ||||||
|                 if (int.TryParse(disc, out var num)) |  | ||||||
|                 { |  | ||||||
|                     return num; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private ChapterInfo GetChapterInfo(MediaChapter chapter) |         private static ChapterInfo GetChapterInfo(MediaChapter chapter) | ||||||
|         { |         { | ||||||
|             var info = new ChapterInfo(); |             var info = new ChapterInfo(); | ||||||
| 
 | 
 | ||||||
|             if (chapter.Tags != null) |             if (chapter.Tags != null && chapter.Tags.TryGetValue("title", out string name)) | ||||||
|             { |  | ||||||
|                 if (chapter.Tags.TryGetValue("title", out string name)) |  | ||||||
|             { |             { | ||||||
|                 info.Name = name; |                 info.Name = name; | ||||||
|             } |             } | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             // Limit accuracy to milliseconds to match xml saving |             // Limit accuracy to milliseconds to match xml saving | ||||||
|             var secondsString = chapter.StartTime; |             var secondsString = chapter.StartTime; | ||||||
| @ -1404,14 +1328,14 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
| 
 | 
 | ||||||
|         private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) |         private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) | ||||||
|         { |         { | ||||||
|             if (data.Format == null || data.Format.Tags == null) |             var tags = data.Format?.Tags; | ||||||
|  | 
 | ||||||
|  |             if (tags == null) | ||||||
|             { |             { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var genres = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/Genre"); |             if (tags.TryGetValue("WM/Genre", out var genres) && !string.IsNullOrWhiteSpace(genres)) | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrWhiteSpace(genres)) |  | ||||||
|             { |             { | ||||||
|                 var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries) |                 var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries) | ||||||
|                     .Where(i => !string.IsNullOrWhiteSpace(i)) |                     .Where(i => !string.IsNullOrWhiteSpace(i)) | ||||||
| @ -1425,16 +1349,12 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var officialRating = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/ParentalRating"); |             if (tags.TryGetValue("WM/ParentalRating", out var officialRating) && !string.IsNullOrWhiteSpace(officialRating)) | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrWhiteSpace(officialRating)) |  | ||||||
|             { |             { | ||||||
|                 video.OfficialRating = officialRating; |                 video.OfficialRating = officialRating; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var people = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaCredits"); |             if (tags.TryGetValue("WM/MediaCredits", out var people) && !string.IsNullOrEmpty(people)) | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrEmpty(people)) |  | ||||||
|             { |             { | ||||||
|                 video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries) |                 video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries) | ||||||
|                     .Where(i => !string.IsNullOrWhiteSpace(i)) |                     .Where(i => !string.IsNullOrWhiteSpace(i)) | ||||||
| @ -1442,29 +1362,21 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                     .ToArray(); |                     .ToArray(); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var year = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/OriginalReleaseTime"); |             if (tags.TryGetValue("WM/OriginalReleaseTime", out var year) && int.TryParse(year, NumberStyles.Integer, _usCulture, out var parsedYear)) | ||||||
|             if (!string.IsNullOrWhiteSpace(year)) |  | ||||||
|             { |             { | ||||||
|                 if (int.TryParse(year, NumberStyles.Integer, _usCulture, out var val)) |                 video.ProductionYear = parsedYear; | ||||||
|                 { |  | ||||||
|                     video.ProductionYear = val; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaOriginalBroadcastDateTime"); |  | ||||||
|             if (!string.IsNullOrWhiteSpace(premiereDateString)) |  | ||||||
|             { |  | ||||||
|             // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ |             // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ | ||||||
|             // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None) |             // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None) | ||||||
|                 if (DateTime.TryParse(year, null, DateTimeStyles.None, out var val)) |             if (tags.TryGetValue("WM/MediaOriginalBroadcastDateTime", out var premiereDateString) && DateTime.TryParse(year, null, DateTimeStyles.None, out var parsedDate)) | ||||||
|             { |             { | ||||||
|                     video.PremiereDate = val.ToUniversalTime(); |                 video.PremiereDate = parsedDate.ToUniversalTime(); | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var description = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitleDescription"); |             var description = tags.GetValueOrDefault("WM/SubTitleDescription"); | ||||||
| 
 | 
 | ||||||
|             var subTitle = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitle"); |             var subTitle = tags.GetValueOrDefault("WM/SubTitle"); | ||||||
| 
 | 
 | ||||||
|             // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ |             // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ | ||||||
| 
 | 
 | ||||||
| @ -1475,49 +1387,48 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|             // 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] |             // 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) |             if (string.IsNullOrWhiteSpace(subTitle) | ||||||
|                 && !string.IsNullOrWhiteSpace(description) |                 && !string.IsNullOrWhiteSpace(description) | ||||||
|                 && description.AsSpan().Slice(0, Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)).IndexOf(':') != -1) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename |                 && description.AsSpan()[0..Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)].IndexOf(':') != -1) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename | ||||||
|             { |             { | ||||||
|                 string[] parts = description.Split(':'); |                 string[] descriptionParts = description.Split(':'); | ||||||
|                 if (parts.Length > 0) |                 if (descriptionParts.Length > 0) | ||||||
|                 { |                 { | ||||||
|                     string subtitle = parts[0]; |                     string subtitle = descriptionParts[0]; | ||||||
|                     try |                     try | ||||||
|                     { |                     { | ||||||
|                         if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number |                         // Check if it contains a episode number and season number | ||||||
|  |                         if (subtitle.Contains('/', StringComparison.Ordinal)) | ||||||
|                         { |                         { | ||||||
|                             string[] numbers = subtitle.Split(' '); |                             string[] subtitleParts = subtitle.Split(' '); | ||||||
|                             video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0], CultureInfo.InvariantCulture); |                             string[] numbers = subtitleParts[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/'); | ||||||
|                             int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1], CultureInfo.InvariantCulture); |                             video.IndexNumber = int.Parse(numbers[0], CultureInfo.InvariantCulture); | ||||||
|  |                             // int totalEpisodesInSeason = int.Parse(numbers[1], CultureInfo.InvariantCulture); | ||||||
| 
 | 
 | ||||||
|                             description = string.Join(' ', numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it |                             // Skip the numbers, concatenate the rest, trim and set as new description | ||||||
|  |                             description = string.Join(' ', subtitleParts, 1, subtitleParts.Length - 1).Trim(); | ||||||
|  |                         } | ||||||
|  |                         else if (subtitle.Contains('.', StringComparison.Ordinal)) | ||||||
|  |                         { | ||||||
|  |                             var subtitleParts = subtitle.Split('.'); | ||||||
|  |                             description = string.Join('.', subtitleParts, 1, subtitleParts.Length - 1).Trim(); | ||||||
|                         } |                         } | ||||||
|                         else |                         else | ||||||
|                         { |                         { | ||||||
|                             // Switch to default parsing |                             description = subtitle.Trim(); | ||||||
|                             if (subtitle.Contains('.', StringComparison.Ordinal)) |  | ||||||
|                             { |  | ||||||
|                                 // 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 |  | ||||||
|                             } |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     catch (Exception ex) |                     catch (Exception ex) | ||||||
|                     { |                     { | ||||||
|                         _logger.LogError(ex, "Error while parsing subtitle field"); |                         _logger.LogError(ex, "Error while parsing subtitle field"); | ||||||
| 
 | 
 | ||||||
|                         // Default parsing |                         // Fallback to default parsing | ||||||
|                         if (subtitle.Contains('.', StringComparison.Ordinal)) |                         if (subtitle.Contains('.', StringComparison.Ordinal)) | ||||||
|                         { |                         { | ||||||
|                             // skip the comment, keep the subtitle |                             var subtitleParts = subtitle.Split('.'); | ||||||
|                             description = string.Join('.', subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first |                             description = string.Join('.', subtitleParts, 1, subtitleParts.Length - 1).Trim(); | ||||||
|                         } |                         } | ||||||
|                         else |                         else | ||||||
|                         { |                         { | ||||||
|                             description = subtitle.Trim(); // Clean up whitespaces and save it |                             description = subtitle.Trim(); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @ -1531,24 +1442,27 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
| 
 | 
 | ||||||
|         private void ExtractTimestamp(MediaInfo video) |         private void ExtractTimestamp(MediaInfo video) | ||||||
|         { |         { | ||||||
|             if (video.VideoType == VideoType.VideoFile) |             if (video.VideoType != VideoType.VideoFile) | ||||||
|             { |             { | ||||||
|                 if (string.Equals(video.Container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) || |                 return; | ||||||
|                     string.Equals(video.Container, "m2ts", StringComparison.OrdinalIgnoreCase) || |             } | ||||||
|                     string.Equals(video.Container, "ts", StringComparison.OrdinalIgnoreCase)) | 
 | ||||||
|  |             if (!string.Equals(video.Container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) | ||||||
|  |                 && !string.Equals(video.Container, "m2ts", StringComparison.OrdinalIgnoreCase) | ||||||
|  |                 && !string.Equals(video.Container, "ts", StringComparison.OrdinalIgnoreCase)) | ||||||
|             { |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 video.Timestamp = GetMpegTimestamp(video.Path); |                 video.Timestamp = GetMpegTimestamp(video.Path); | ||||||
| 
 |  | ||||||
|                 _logger.LogDebug("Video has {Timestamp} timestamp", video.Timestamp); |                 _logger.LogDebug("Video has {Timestamp} timestamp", video.Timestamp); | ||||||
|             } |             } | ||||||
|             catch (Exception ex) |             catch (Exception ex) | ||||||
|             { |             { | ||||||
|                         _logger.LogError(ex, "Error extracting timestamp info from {Path}", video.Path); |  | ||||||
|                 video.Timestamp = null; |                 video.Timestamp = null; | ||||||
|                     } |                 _logger.LogError(ex, "Error extracting timestamp info from {Path}", video.Path); | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -1567,8 +1481,11 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
|                 return TransportStreamTimestamp.None; |                 return TransportStreamTimestamp.None; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if ((packetBuffer[4] == 71) && (packetBuffer[196] == 71)) |             if ((packetBuffer[4] != 71) || (packetBuffer[196] != 71)) | ||||||
|             { |             { | ||||||
|  |                 return TransportStreamTimestamp.None; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0)) |             if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0)) | ||||||
|             { |             { | ||||||
|                 return TransportStreamTimestamp.Zero; |                 return TransportStreamTimestamp.Zero; | ||||||
| @ -1576,8 +1493,5 @@ namespace MediaBrowser.MediaEncoding.Probing | |||||||
| 
 | 
 | ||||||
|             return TransportStreamTimestamp.Valid; |             return TransportStreamTimestamp.Valid; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|             return TransportStreamTimestamp.None; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										65
									
								
								src/Jellyfin.Extensions/DictionaryExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/Jellyfin.Extensions/DictionaryExtensions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | 
 | ||||||
|  | namespace Jellyfin.Extensions | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Static extensions for the <see cref="IReadOnlyDictionary{TKey,TValue}"/> interface. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class DictionaryExtensions | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets a string from a string dictionary, checking all keys sequentially, | ||||||
|  |         /// stopping at the first key that returns a result that's neither null nor blank. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="dictionary">The dictionary.</param> | ||||||
|  |         /// <param name="key1">The first checked key.</param> | ||||||
|  |         /// <returns>System.String.</returns> | ||||||
|  |         public static string? GetFirstNotNullNorWhiteSpaceValue(this IReadOnlyDictionary<string, string> dictionary, string key1) | ||||||
|  |         { | ||||||
|  |             return dictionary.GetFirstNotNullNorWhiteSpaceValue(key1, string.Empty, string.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets a string from a string dictionary, checking all keys sequentially, | ||||||
|  |         /// stopping at the first key that returns a result that's neither null nor blank. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="dictionary">The dictionary.</param> | ||||||
|  |         /// <param name="key1">The first checked key.</param> | ||||||
|  |         /// <param name="key2">The second checked key.</param> | ||||||
|  |         /// <returns>System.String.</returns> | ||||||
|  |         public static string? GetFirstNotNullNorWhiteSpaceValue(this IReadOnlyDictionary<string, string> dictionary, string key1, string key2) | ||||||
|  |         { | ||||||
|  |             return dictionary.GetFirstNotNullNorWhiteSpaceValue(key1, key2, string.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets a string from a string dictionary, checking all keys sequentially, | ||||||
|  |         /// stopping at the first key that returns a result that's neither null nor blank. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="dictionary">The dictionary.</param> | ||||||
|  |         /// <param name="key1">The first checked key.</param> | ||||||
|  |         /// <param name="key2">The second checked key.</param> | ||||||
|  |         /// <param name="key3">The third checked key.</param> | ||||||
|  |         /// <returns>System.String.</returns> | ||||||
|  |         public static string? GetFirstNotNullNorWhiteSpaceValue(this IReadOnlyDictionary<string, string> dictionary, string key1, string key2, string key3) | ||||||
|  |         { | ||||||
|  |             if (dictionary.TryGetValue(key1, out var val) && !string.IsNullOrWhiteSpace(val)) | ||||||
|  |             { | ||||||
|  |                 return val; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!string.IsNullOrEmpty(key2) && dictionary.TryGetValue(key2, out val) && !string.IsNullOrWhiteSpace(val)) | ||||||
|  |             { | ||||||
|  |                 return val; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!string.IsNullOrEmpty(key3) && dictionary.TryGetValue(key3, out val) && !string.IsNullOrWhiteSpace(val)) | ||||||
|  |             { | ||||||
|  |                 return val; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user