@ -8,6 +8,7 @@ using System.IO;
using System.Linq ;
using System.Text ;
using System.Xml ;
using Jellyfin.Extensions ;
using MediaBrowser.Controller.Library ;
using MediaBrowser.Model.Dto ;
using MediaBrowser.Model.Entities ;
@ -80,68 +81,33 @@ namespace MediaBrowser.MediaEncoding.Probing
var tags = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
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 ( key , value ) in tagStream . Tags )
{
foreach ( var pair 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 . V alue;
tags [ key] = v alue;
}
}
FetchGenres ( info , tags ) ;
var overview = FFProbeHelpers . GetDictionaryValue ( tags , "synopsis" ) ;
if ( string . IsNullOrWhiteSpace ( overview ) )
{
overview = FFProbeHelpers . GetDictionaryValue ( tags , "description" ) ;
}
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 . Name = tags . GetFirstNotNullNorWhiteSpaceValue ( "title" , "title-eng" ) ;
info . ForcedSortName = tags . GetFirstNotNullNorWhiteSpaceValue ( "sort_name" , "title-sort" , "titlesort" ) ;
info . Overview = tags . GetFirstNotNullNorWhiteSpaceValue ( "synopsis" , "description" , "desc" ) ;
info . IndexNumber = FFProbeHelpers . GetDictionaryNumericValue ( tags , "episode_sort" ) ;
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" ) ;
// Several different forms of retail/premiere date
@ -153,32 +119,21 @@ namespace MediaBrowser.MediaEncoding.Probing
FFProbeHelpers . GetDictionaryDateTime ( tags , "date" ) ;
// Set common metadata for music (audio) and music videos (video)
info . Album = FFProbeHelpers . GetDictionaryValue ( tags , "album" ) ;
var artists = FFProbeHelpers . GetDictionaryValue ( tags , "artists" ) ;
info . Album = tags . GetValueOrDefault ( "album" ) ;
if ( ! string . IsNullOrWhiteSpace ( artists ) )
if ( tags . TryGetValue ( "artists" , out var artists ) & & ! string . IsNullOrWhiteSpace ( artists ) )
{
info . Artists = SplitArtists ( artists , new [ ] { '/' , ';' } , false )
. DistinctNames ( )
. ToArray ( ) ;
info . Artists = SplitDistinctArtists ( artists , new [ ] { '/' , ';' } , false ) . ToArray ( ) ;
}
else
{
var artist = FFProbeHelpers . GetDictionaryValue ( tags , "artist" ) ;
if ( string . IsNullOrWhiteSpace ( artist ) )
{
info . Artists = Array . Empty < string > ( ) ;
}
else
{
info . Artists = SplitArtists ( artist , _nameDelimiters , true )
. DistinctNames ( )
. ToArray ( ) ;
}
var artist = tags . GetFirstNotNullNorWhiteSpaceValue ( "artist" ) ;
info . Artists = artist = = null
? Array . Empty < string > ( )
: SplitDistinctArtists ( artist , _nameDelimiters , true ) . 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 )
{
info . ProductionYear = info . PremiereDate . Value . Year ;
@ -198,10 +153,10 @@ namespace MediaBrowser.MediaEncoding.Probing
{
FetchStudios ( info , tags , "copyright" ) ;
var iTunE XTC = FFProbeHelpers . GetDictionaryValue ( tags , "iTunEXTC" ) ;
if ( ! string . IsNullOrWhiteSpace ( iTunEXTC ) )
var iTunE xtc = tags . GetFirstNotNullNorWhiteSpaceValue ( "iTunEXTC" ) ;
if ( iTunExtc ! = null )
{
var parts = iTunE XTC . Split ( '|' , StringSplitOptions . RemoveEmptyEntries ) ;
var parts = iTunE xtc . Split ( '|' , StringSplitOptions . RemoveEmptyEntries ) ;
// Example
// mpaa|G|100|For crude humor
if ( parts . Length > 1 )
@ -215,10 +170,10 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
var i tunesXml = FFProbeHelpers . GetDictionaryValue ( tags , "iTunMOVI" ) ;
if ( ! string . IsNullOrWhiteSpace ( itunesXml ) )
var i TunXml = tags . GetFirstNotNullNorWhiteSpaceValue ( "iTunMOVI" ) ;
if ( iTunXml ! = null )
{
FetchFromItunesInfo ( i tunes Xml, info ) ;
FetchFromItunesInfo ( i Tun Xml, info ) ;
}
if ( data . Format ! = null & & ! string . IsNullOrEmpty ( data . Format . Duration ) )
@ -235,8 +190,7 @@ namespace MediaBrowser.MediaEncoding.Probing
ExtractTimestamp ( info ) ;
var stereoMode = GetDictionaryValue ( tags , "stereo_mode" ) ;
if ( string . Equals ( stereoMode , "left_right" , StringComparison . OrdinalIgnoreCase ) )
if ( tags . TryGetValue ( "stereo_mode" , out var stereoMode ) & & string . Equals ( stereoMode , "left_right" , StringComparison . OrdinalIgnoreCase ) )
{
info . Video3DFormat = Video3DFormat . FullSideBySide ;
}
@ -289,42 +243,36 @@ namespace MediaBrowser.MediaEncoding.Probing
if ( string . Equals ( codec , "aac" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( codec , "mp3" , StringComparison . OrdinalIgnoreCase ) )
{
if ( channelsValue < = 2 )
{
return 192000 ;
}
if ( channelsValue > = 5 )
switch ( channelsValue )
{
return 320000 ;
case < = 2 :
return 192000 ;
case > = 5 :
return 320000 ;
}
}
if ( string . Equals ( codec , "ac3" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( codec , "eac3" , StringComparison . OrdinalIgnoreCase ) )
{
if ( channelsValue < = 2 )
switch ( channelsValue )
{
return 192000 ;
}
if ( channelsValue > = 5 )
{
return 640000 ;
case < = 2 :
return 192000 ;
case > = 5 :
return 640000 ;
}
}
if ( string . Equals ( codec , "flac" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( codec , "alac" , StringComparison . OrdinalIgnoreCase ) )
{
if ( channelsValue < = 2 )
{
return 960000 ;
}
if ( channelsValue > = 5 )
switch ( channelsValue )
{
return 2880000 ;
case < = 2 :
return 960000 ;
case > = 5 :
return 2880000 ;
}
}
@ -854,7 +802,7 @@ namespace MediaBrowser.MediaEncoding.Probing
| | string . Equals ( streamInfo . CodecType , "video" , StringComparison . OrdinalIgnoreCase ) ) )
{
var bps = GetBPSFromTags ( streamInfo ) ;
if ( bps != null & & bps > 0 )
if ( bps > 0 )
{
stream . BitRate = bps ;
}
@ -923,6 +871,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
tags . TryGetValue ( key , out var val ) ;
return val ;
}
@ -930,7 +879,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
if ( string . IsNullOrEmpty ( input ) )
{
return input ;
return null ;
}
return input . Split ( '(' ) . FirstOrDefault ( ) ;
@ -1018,64 +967,64 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <returns>System.Nullable{System.Single}.</returns>
private float? GetFrameRate ( string value )
{
if ( ! string . IsNullOrEmpty ( value ) )
if ( string . IsNullOrEmpty ( value ) )
{
var parts = value . Split ( '/' ) ;
return null ;
}
float result ;
var parts = value . Split ( '/' ) ;
if ( parts . Length = = 2 )
{
result = float . Parse ( parts [ 0 ] , _usCulture ) / float . Parse ( parts [ 1 ] , _usCulture ) ;
}
else
{
result = float . Parse ( parts [ 0 ] , _usCulture ) ;
}
float result ;
return float . IsNaN ( result ) ? ( float? ) null : result ;
if ( parts . Length = = 2 )
{
result = float . Parse ( parts [ 0 ] , _usCulture ) / float . Parse ( parts [ 1 ] , _usCulture ) ;
}
else
{
result = float . Parse ( parts [ 0 ] , _usCulture ) ;
}
return null ;
return float . IsNaN ( result ) ? null : result ;
}
private void SetAudioRuntimeTicks ( InternalMediaInfoResult result , MediaInfo data )
{
if ( result . Streams ! = null )
// Get the first info stream
var stream = result . Streams ? . FirstOrDefault ( s = > string . Equals ( s . CodecType , "audio" , StringComparison . OrdinalIgnoreCase ) ) ;
if ( stream = = null )
{
// Get the first info stream
var stream = result . Streams . FirstOrDefault ( s = > string . Equals ( s . CodecType , "audio" , StringComparison . OrdinalIgnoreCase ) ) ;
return ;
}
if ( stream ! = null )
{
// Get duration from stream properties
var duration = stream . Duration ;
// Get duration from stream properties
var duration = stream . Duration ;
// If it's not there go into format properties
if ( string . IsNullOrEmpty ( duration ) )
{
duration = result . Format . Duration ;
}
// If it's not there go into format properties
if ( string . IsNullOrEmpty ( duration ) )
{
duration = result . Format . Duration ;
}
// If we got something, parse it
if ( ! string . IsNullOrEmpty ( duration ) )
{
data . RunTimeTicks = TimeSpan . FromSeconds ( double . Parse ( duration , _usCulture ) ) . Ticks ;
}
}
// If we got something, parse it
if ( ! string . IsNullOrEmpty ( duration ) )
{
data . RunTimeTicks = TimeSpan . FromSeconds ( double . Parse ( duration , _usCulture ) ) . Ticks ;
}
}
private int? GetBPSFromTags ( MediaStreamInfo streamInfo )
{
if ( streamInfo ! = null & & streamInfo . Tags ! = null )
if ( streamInfo ? . Tags = = null )
{
var bps = GetDictionaryValue ( streamInfo . Tags , "BPS-eng" ) ? ? GetDictionaryValue ( streamInfo . Tags , "BPS" ) ;
if ( ! string . IsNullOrEmpty ( bps )
& & int . TryParse ( bps , NumberStyles . Integer , CultureInfo . InvariantCulture , out var parsedBps ) )
{
return parsedBps ;
}
return null ;
}
var bps = GetDictionaryValue ( streamInfo . Tags , "BPS-eng" ) ? ? GetDictionaryValue ( streamInfo . Tags , "BPS" ) ;
if ( ! string . IsNullOrEmpty ( bps )
& & int . TryParse ( bps , NumberStyles . Integer , CultureInfo . InvariantCulture , out var parsedBps ) )
{
return parsedBps ;
}
return null ;
@ -1083,13 +1032,15 @@ namespace MediaBrowser.MediaEncoding.Probing
private double? GetRuntimeSecondsFromTags ( MediaStreamInfo streamInfo )
{
if ( streamInfo ! = null & & streamInfo . Tags ! = null )
if ( streamInfo ? . Tags = = null )
{
var duration = GetDictionaryValue ( streamInfo . Tags , "DURATION-eng" ) ? ? GetDictionaryValue ( streamInfo . Tags , "DURATION" ) ;
if ( ! string . IsNullOrEmpty ( duration ) & & TimeSpan . TryParse ( duration , out var parsedDuration ) )
{
return parsedDuration . TotalSeconds ;
}
return null ;
}
var duration = GetDictionaryValue ( streamInfo . Tags , "DURATION-eng" ) ? ? GetDictionaryValue ( streamInfo . Tags , "DURATION" ) ;
if ( ! string . IsNullOrEmpty ( duration ) & & TimeSpan . TryParse ( duration , out var parsedDuration ) )
{
return parsedDuration . TotalSeconds ;
}
return null ;
@ -1097,14 +1048,17 @@ namespace MediaBrowser.MediaEncoding.Probing
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" ) ;
if ( ! string . IsNullOrEmpty ( numberOfBytes )
& & long . TryParse ( numberOfBytes , NumberStyles . Integer , CultureInfo . InvariantCulture , out var parsedBytes ) )
{
return parsedBytes ;
}
return null ;
}
var numberOfBytes = GetDictionaryValue ( streamInfo . Tags , "NUMBER_OF_BYTES-eng" )
? ? GetDictionaryValue ( streamInfo . Tags , "NUMBER_OF_BYTES" ) ;
if ( ! string . IsNullOrEmpty ( numberOfBytes )
& & long . TryParse ( numberOfBytes , NumberStyles . Integer , CultureInfo . InvariantCulture , out var parsedBytes ) )
{
return parsedBytes ;
}
return null ;
@ -1112,24 +1066,18 @@ namespace MediaBrowser.MediaEncoding.Probing
private void SetSize ( InternalMediaInfoResult data , MediaInfo info )
{
if ( data . Format ! = null )
if ( data . Format = = null )
{
if ( ! string . IsNullOrEmpty ( data . Format . Size ) )
{
info . Size = long . Parse ( data . Format . Size , _usCulture ) ;
}
else
{
info . Size = null ;
}
return ;
}
info . Size = string . IsNullOrEmpty ( data . Format . Size ) ? null : long . Parse ( data . Format . Size , _usCulture ) ;
}
private void SetAudioInfoFromTags ( MediaInfo audio , Dictionary< string , string > tags )
private void SetAudioInfoFromTags ( MediaInfo audio , IReadOnlyDictionary < string , string > tags )
{
var people = new List < BaseItemPerson > ( ) ;
var composer = FFProbeHelpers . GetDictionaryValue ( tags , "composer" ) ;
if ( ! string . IsNullOrWhiteSpace ( composer ) )
if ( tags . TryGetValue ( "composer" , out var composer ) & & ! string . IsNullOrWhiteSpace ( composer ) )
{
foreach ( var person in Split ( composer , false ) )
{
@ -1137,8 +1085,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
var conductor = FFProbeHelpers . GetDictionaryValue ( tags , "conductor" ) ;
if ( ! string . IsNullOrWhiteSpace ( conductor ) )
if ( tags . TryGetValue ( "conductor" , out var conductor ) & & ! string . IsNullOrWhiteSpace ( conductor ) )
{
foreach ( var person in Split ( conductor , false ) )
{
@ -1146,8 +1093,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
var lyricist = FFProbeHelpers . GetDictionaryValue ( tags , "lyricist" ) ;
if ( ! string . IsNullOrWhiteSpace ( lyricist ) )
if ( tags . TryGetValue ( "lyricist" , out var lyricist ) & & ! string . IsNullOrWhiteSpace ( lyricist ) )
{
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
var writer = FFProbeHelpers . GetDictionaryValue ( tags , "writer" ) ;
if ( ! string . IsNullOrWhiteSpace ( writer ) )
if ( tags . TryGetValue ( "writer" , out var writer ) & & ! string . IsNullOrWhiteSpace ( writer ) )
{
foreach ( var person in Split ( writer , false ) )
{
@ -1167,38 +1112,23 @@ namespace MediaBrowser.MediaEncoding.Probing
audio . People = people . ToArray ( ) ;
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 = Array . Empty < string > ( ) ;
}
else
{
audio . AlbumArtists = SplitArtists ( albumArtist , _nameDelimiters , true )
. DistinctNames ( )
. ToArray ( ) ;
}
// Set album artist
var albumArtist = tags . GetFirstNotNullNorWhiteSpaceValue ( "albumartist" , "album artist" , "album_artist" ) ;
audio . AlbumArtists = albumArtist ! = null
? SplitDistinctArtists ( albumArtist , _nameDelimiters , true ) . ToArray ( )
: Array . Empty < string > ( ) ;
// Set album artist to artist if empty
if ( audio . AlbumArtists . Length = = 0 )
{
audio . AlbumArtists = audio . Artists ;
}
// Track number
audio . IndexNumber = GetDictionary DiscValue ( tags , "track" ) ;
audio . IndexNumber = GetDictionaryTrackOrDiscNumber ( tags , "track" ) ;
// Disc number
audio . ParentIndexNumber = GetDictionary DiscValue ( tags , "disc" ) ;
audio . ParentIndexNumber = GetDictionaryTrackOrDiscNumber ( tags , "disc" ) ;
// There's several values in tags may or may not be present
FetchStudios ( audio , tags , "organization" ) ;
@ -1206,30 +1136,25 @@ namespace MediaBrowser.MediaEncoding.Probing
FetchStudios ( audio , tags , "publisher" ) ;
FetchStudios ( audio , tags , "label" ) ;
// These support mulitple values, but for now we only store the first.
var mb = GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MusicBrainz Album Artist Id" ) )
? ? GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MUSICBRAINZ_ALBUMARTISTID" ) ) ;
// These support multiple values, but for now we only store the first.
var mb = GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MusicBrainz Album Artist Id" ) )
? ? GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MUSICBRAINZ_ALBUMARTISTID" ) ) ;
audio . SetProviderId ( MetadataProvider . MusicBrainzAlbumArtist , mb ) ;
mb = GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MusicBrainz Artist Id" ) )
? ? GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MUSICBRAINZ_ARTISTID" ) ) ;
mb = GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MusicBrainz Artist Id" ) )
? ? GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MUSICBRAINZ_ARTISTID" ) ) ;
audio . SetProviderId ( MetadataProvider . MusicBrainzArtist , mb ) ;
mb = GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MusicBrainz Album Id" ) )
? ? GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MUSICBRAINZ_ALBUMID" ) ) ;
mb = GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MusicBrainz Album Id" ) )
? ? GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MUSICBRAINZ_ALBUMID" ) ) ;
audio . SetProviderId ( MetadataProvider . MusicBrainzAlbum , mb ) ;
mb = GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MusicBrainz Release Group Id" ) )
? ? GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MUSICBRAINZ_RELEASEGROUPID" ) ) ;
mb = GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MusicBrainz Release Group Id" ) )
? ? GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MUSICBRAINZ_RELEASEGROUPID" ) ) ;
audio . SetProviderId ( MetadataProvider . MusicBrainzReleaseGroup , mb ) ;
mb = GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MusicBrainz Release Track Id" ) )
? ? GetMultipleMusicBrainzId ( FFProbeHelpers . GetDictionaryValue ( tags , "MUSICBRAINZ_RELEASETRACKID" ) ) ;
mb = GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MusicBrainz Release Track Id" ) )
? ? GetMultipleMusicBrainzId ( tags . GetValueOrDefault ( "MUSICBRAINZ_RELEASETRACKID" ) ) ;
audio . SetProviderId ( MetadataProvider . MusicBrainzTrack , mb ) ;
}
@ -1253,18 +1178,18 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <returns>System.String[][].</returns>
private IEnumerable < string > Split ( string val , bool allowCommaDelimiter )
{
// Only use the comma as a delim e ter if there are no slashes or pipes.
// Only use the comma as a delim i ter if there are no slashes or pipes.
// We want to be careful not to split names that have commas in them
var delim e ter = ! allowCommaDelimiter | | _nameDelimiters . Any ( i = > val . IndexOf ( i , StringComparison . Ordinal ) ! = - 1 ) ?
var delim i ter = ! allowCommaDelimiter | | _nameDelimiters . Any ( i = > val . IndexOf ( i , StringComparison . Ordinal ) ! = - 1 ) ?
_nameDelimiters :
new [ ] { ',' } ;
return val . Split ( delim e ter, StringSplitOptions . RemoveEmptyEntries )
return val . Split ( delim i ter, StringSplitOptions . RemoveEmptyEntries )
. Where ( i = > ! string . IsNullOrWhiteSpace ( i ) )
. Select ( i = > i . Trim ( ) ) ;
}
private IEnumerable < string > Split Artists( string val , char [ ] delimiters , bool splitFeaturing )
private IEnumerable < string > Split Distinct Artists( string val , char [ ] delimiters , bool splitFeaturing )
{
if ( splitFeaturing )
{
@ -1290,7 +1215,7 @@ namespace MediaBrowser.MediaEncoding.Probing
. Select ( i = > i . Trim ( ) ) ;
artistsFound . AddRange ( artists ) ;
return artistsFound ;
return artistsFound .DistinctNames ( ) ;
}
/// <summary>
@ -1299,36 +1224,38 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <param name="info">The info.</param>
/// <param name="tags">The tags.</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 , IReadOnly Dictionary< string , string > tags , string tagName )
{
var val = FFProbeHelpers. GetDictionaryValue ( tags , tagName ) ;
var val = tags. GetValueOrDefault ( tagName ) ;
if ( ! string . IsNullOrEmpty ( val ) )
if ( string . IsNullOrEmpty ( val ) )
{
var studios = Split ( val , true ) ;
var studioList = new List < string > ( ) ;
return ;
}
foreach ( var studio in studios )
{
// Sometimes the artist name is listed here, account for that
if ( info . Artists . Contains ( studio , StringComparer . OrdinalIgnoreCase ) )
{
continue ;
}
var studios = Split ( val , true ) ;
var studioList = new List < string > ( ) ;
if ( info . AlbumArtists . Contains ( studio , StringComparer . OrdinalIgnoreCase ) )
{
continue ;
}
foreach ( var studio in studios )
{
if ( string . IsNullOrWhiteSpace ( studio ) )
{
continue ;
}
studioList . Add ( studio ) ;
// 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 ;
}
info . Studios = studioList
. Where ( i = > ! string . IsNullOrWhiteSpace ( i ) )
. Distinct ( StringComparer . OrdinalIgnoreCase )
. ToArray ( ) ;
studioList . Add ( studio ) ;
}
info . Studios = studioList
. Distinct ( StringComparer . OrdinalIgnoreCase )
. ToArray ( ) ;
}
/// <summary>
@ -1336,58 +1263,55 @@ namespace MediaBrowser.MediaEncoding.Probing
/// </summary>
/// <param name="info">The information.</param>
/// <param name="tags">The tags.</param>
private void FetchGenres ( MediaInfo info , Dictionary< string , string > tags )
private void FetchGenres ( MediaInfo info , IReadOnly Dictionary< 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 ) ;
foreach ( var genre in Split ( genreVal , true ) )
{
var genres = new List < string > ( info . Genres ) ;
foreach ( var genre in Split ( val , true ) )
if ( string . IsNullOrWhiteSpace ( genre ) )
{
genres . Add ( genre ) ;
continue ;
}
info . Genres = genres
. Where ( i = > ! string . IsNullOrWhiteSpace ( i ) )
. Distinct ( StringComparer . OrdinalIgnoreCase )
. ToArray ( ) ;
genres . Add ( genre ) ;
}
info . Genres = genres
. Distinct ( StringComparer . OrdinalIgnoreCase )
. ToArray ( ) ;
}
/// <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>
/// <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 )
/// <returns> The track or disc number, or null, if missing or not parseable .</returns>
private static int? GetDictionaryTrackOrDiscNumber ( IReadOnly Dictionary< 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 ] ;
if ( int . TryParse ( disc , out var num ) )
{
return num ;
}
return discNum ;
}
return null ;
}
private ChapterInfo GetChapterInfo ( MediaChapter chapter )
private static ChapterInfo GetChapterInfo ( MediaChapter chapter )
{
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
@ -1404,14 +1328,14 @@ namespace MediaBrowser.MediaEncoding.Probing
private void FetchWtvInfo ( MediaInfo video , InternalMediaInfoResult data )
{
if ( data . Format = = null | | data . Format . Tags = = null )
var tags = data . Format ? . Tags ;
if ( tags = = null )
{
return ;
}
var genres = FFProbeHelpers . GetDictionaryValue ( data . Format . Tags , "WM/Genre" ) ;
if ( ! string . IsNullOrWhiteSpace ( genres ) )
if ( tags . TryGetValue ( "WM/Genre" , out var genres ) & & ! string . IsNullOrWhiteSpace ( genres ) )
{
var genreList = genres . Split ( new [ ] { ';' , '/' , ',' } , StringSplitOptions . RemoveEmptyEntries )
. Where ( i = > ! string . IsNullOrWhiteSpace ( i ) )
@ -1425,16 +1349,12 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
var officialRating = FFProbeHelpers . GetDictionaryValue ( data . Format . Tags , "WM/ParentalRating" ) ;
if ( ! string . IsNullOrWhiteSpace ( officialRating ) )
if ( tags . TryGetValue ( "WM/ParentalRating" , out var officialRating ) & & ! string . IsNullOrWhiteSpace ( officialRating ) )
{
video . OfficialRating = officialRating ;
}
var people = FFProbeHelpers . GetDictionaryValue ( data . Format . Tags , "WM/MediaCredits" ) ;
if ( ! string . IsNullOrEmpty ( people ) )
if ( tags . TryGetValue ( "WM/MediaCredits" , out var people ) & & ! string . IsNullOrEmpty ( people ) )
{
video . People = people . Split ( new [ ] { ';' , '/' } , StringSplitOptions . RemoveEmptyEntries )
. Where ( i = > ! string . IsNullOrWhiteSpace ( i ) )
@ -1442,29 +1362,21 @@ namespace MediaBrowser.MediaEncoding.Probing
. ToArray ( ) ;
}
var year = FFProbeHelpers . GetDictionaryValue ( data . Format . Tags , "WM/OriginalReleaseTime" ) ;
if ( ! string . IsNullOrWhiteSpace ( year ) )
if ( tags . TryGetValue ( "WM/OriginalReleaseTime" , out var year ) & & int . TryParse ( year , NumberStyles . Integer , _usCulture , out var parsedYear ) )
{
if ( int . TryParse ( year , NumberStyles . Integer , _usCulture , out var val ) )
{
video . ProductionYear = val ;
}
video . ProductionYear = parsedYear ;
}
var premiereDateString = FFProbeHelpers . GetDictionaryValue ( data . Format . Tags , "WM/MediaOriginalBroadcastDateTime" ) ;
if ( ! string . IsNullOrWhiteSpace ( premiereDateString ) )
// Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/
// DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None)
if ( tags . TryGetValue ( "WM/MediaOriginalBroadcastDateTime" , out var premiereDateString ) & & DateTime . TryParse ( year , null , DateTimeStyles . None , out var parsedDate ) )
{
// 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 var val ) )
{
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/
@ -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]
if ( string . IsNullOrWhiteSpace ( subTitle )
& & ! 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 ( ':' ) ;
if ( parts. Length > 0 )
string [ ] descri ptionP arts = description . Split ( ':' ) ;
if ( descri ptionP arts. Length > 0 )
{
string subtitle = parts[ 0 ] ;
string subtitle = descri ptionP arts[ 0 ] ;
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 ( ' ' ) ;
video . IndexNumber = int . Parse ( numbers [ 0 ] . Replace ( "." , string . Empty , StringComparison . Ordinal ) . Split ( '/' ) [ 0 ] , CultureInfo . InvariantCulture ) ;
int totalEpisodesInSeason = int . Parse ( numbers [ 0 ] . Replace ( "." , string . Empty , StringComparison . Ordinal ) . Split ( '/' ) [ 1 ] , CultureInfo . InvariantCulture ) ;
string [ ] subtitleParts = subtitle . Split ( ' ' ) ;
string [ ] numbers = subtitleParts [ 0 ] . Replace ( "." , string . Empty , StringComparison . Ordinal ) . Split ( '/' ) ;
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
{
// Switch to default parsing
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
}
description = subtitle . Trim ( ) ;
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error while parsing subtitle field" ) ;
// D efault parsing
// Fallback to default parsing
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
var subtitleParts = subtitle . Split ( '.' ) ;
description = string . Join ( '.' , subtitle Parts, 1 , subtitleParts . Length - 1 ) . Trim ( ) ;
}
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 )
{
if ( video . VideoType = = VideoType . VideoFile )
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 ) ;
return ;
}
_logger . LogDebug ( "Video has {Timestamp} timestamp" , video . Timestamp ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error extracting timestamp info from {Path}" , video . Path ) ;
video . Timestamp = null ;
}
}
if ( ! string . Equals ( video . Container , "mpeg2ts" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( video . Container , "m2ts" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( video . Container , "ts" , StringComparison . OrdinalIgnoreCase ) )
{
return ;
}
try
{
video . Timestamp = GetMpegTimestamp ( video . Path ) ;
_logger . LogDebug ( "Video has {Timestamp} timestamp" , video . Timestamp ) ;
}
catch ( Exception ex )
{
video . Timestamp = null ;
_logger . LogError ( ex , "Error extracting timestamp info from {Path}" , video . Path ) ;
}
}
@ -1567,17 +1481,17 @@ namespace MediaBrowser.MediaEncoding.Probing
return TransportStreamTimestamp . None ;
}
if ( ( packetBuffer [ 4 ] == 71 ) & & ( packetBuffer [ 196 ] = = 71 ) )
if ( ( packetBuffer [ 4 ] != 71 ) | | ( packetBuffer [ 196 ] ! = 71 ) )
{
if ( ( packetBuffer [ 0 ] = = 0 ) & & ( packetBuffer [ 1 ] = = 0 ) & & ( packetBuffer [ 2 ] = = 0 ) & & ( packetBuffer [ 3 ] = = 0 ) )
{
return TransportStreamTimestamp . Zero ;
}
return TransportStreamTimestamp . None ;
}
return TransportStreamTimestamp . Valid ;
if ( ( packetBuffer [ 0 ] = = 0 ) & & ( packetBuffer [ 1 ] = = 0 ) & & ( packetBuffer [ 2 ] = = 0 ) & & ( packetBuffer [ 3 ] = = 0 ) )
{
return TransportStreamTimestamp . Zero ;
}
return TransportStreamTimestamp . None ;
return TransportStreamTimestamp . Valid ;
}
}
}