private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames) private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) { if (data.format == null || data.format.tags == null) { return; } var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/Genre"); if (!string.IsNullOrWhiteSpace(genres)) { var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()) .ToList(); // If this is empty then don't overwrite genres that might have been fetched earlier if (genreList.Count > 0) { video.Genres = genreList; } } var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating"); if (!string.IsNullOrWhiteSpace(officialRating)) { video.OfficialRating = officialRating; } var people = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaCredits"); if (!string.IsNullOrEmpty(people)) { video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => new BaseItemPerson { Name = i.Trim(), Type = PersonType.Actor }) .ToList(); } var year = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/OriginalReleaseTime"); if (!string.IsNullOrWhiteSpace(year)) { int val; if (int.TryParse(year, NumberStyles.Integer, _usCulture, out val)) { video.ProductionYear = val; } } var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaOriginalBroadcastDateTime"); if (!string.IsNullOrWhiteSpace(premiereDateString)) { DateTime val; // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ // DateTime is reported along with timezone info (typically Z i.e. UTC hence assume None) if (DateTime.TryParse(year, null, DateTimeStyles.None, out val)) { video.PremiereDate = val.ToUniversalTime(); } } var description = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription"); var subTitle = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitle"); // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ // Sometimes for TV Shows the Subtitle field is empty and the subtitle description contains the subtitle, extract if possible. See ticket https://mcebuddy2x.codeplex.com/workitem/1910 // The format is -> EPISODE/TOTAL_EPISODES_IN_SEASON. SUBTITLE: DESCRIPTION // OR -> COMMENT. SUBTITLE: DESCRIPTION // e.g. -> 4/13. The Doctor's Wife: Science fiction drama. When he follows a Time Lord distress signal, the Doctor puts Amy, Rory and his beloved TARDIS in grave danger. Also in HD. [AD,S] // e.g. -> CBeebies Bedtime Hour. The Mystery: Animated adventures of two friends who live on an island in the middle of the big city. Some of Abney and Teal's favourite objects are missing. [S] if (String.IsNullOrWhiteSpace(subTitle) && !String.IsNullOrWhiteSpace(description) && description.Substring(0, Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)).Contains(":")) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename { string[] parts = description.Split(':'); if (parts.Length > 0) { string subtitle = parts[0]; try { if (subtitle.Contains("/")) // It contains a episode number and season number { string[] numbers = subtitle.Split(' '); video.IndexNumber = int.Parse(numbers[0].Replace(".", "").Split('/')[0]); int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", "").Split('/')[1]); description = String.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it } else { throw new Exception(); // Switch to default parsing } } catch // Default parsing { if (subtitle.Contains(".")) // skip the comment, keep the subtitle { description = String.Join(".", subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first } else { description = subtitle.Trim(); // Clean up whitespaces and save it } } } } if (!string.IsNullOrWhiteSpace(description)) { video.Overview = description; } }
public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol) { var info = new MediaInfo { Path = path, Protocol = protocol }; FFProbeHelpers.NormalizeFFProbeResult(data); SetSize(data, info); var internalStreams = data.streams ?? new MediaStreamInfo[] { }; info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.format)) .Where(i => i != null) .ToList(); if (data.format != null) { info.Container = data.format.format_name; if (!string.IsNullOrEmpty(data.format.bit_rate)) { int value; if (int.TryParse(data.format.bit_rate, NumberStyles.Any, _usCulture, out value)) { info.Bitrate = value; } } } 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.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase)); if (tagStream != null && tagStream.tags != null) { foreach (var pair in tagStream.tags) { tags[pair.Key] = pair.Value; } } } if (data.format != null && data.format.tags != null) { foreach (var pair in data.format.tags) { tags[pair.Key] = pair.Value; } } FetchGenres(info, tags); var shortOverview = FFProbeHelpers.GetDictionaryValue(tags, "description"); var overview = FFProbeHelpers.GetDictionaryValue(tags, "synopsis"); if (string.IsNullOrWhiteSpace(overview)) { overview = shortOverview; shortOverview = null; } if (string.IsNullOrWhiteSpace(overview)) { overview = FFProbeHelpers.GetDictionaryValue(tags, "desc"); } if (!string.IsNullOrWhiteSpace(overview)) { info.Overview = overview; } if (!string.IsNullOrWhiteSpace(shortOverview)) { info.ShortOverview = shortOverview; } var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); if (!string.IsNullOrWhiteSpace(title)) { info.Name = title; } info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); // Several different forms of retaildate info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date"); if (isAudio) { SetAudioRuntimeTicks(data, info); // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the info stream // so let's create a combined list of both SetAudioInfoFromTags(info, tags); } else { FetchStudios(info, tags, "copyright"); var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC"); if (!string.IsNullOrWhiteSpace(iTunEXTC)) { var parts = iTunEXTC.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); // Example // mpaa|G|100|For crude humor if (parts.Length > 1) { info.OfficialRating = parts[1]; if (parts.Length > 3) { info.OfficialRatingDescription = parts[3]; } } } var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI"); if (!string.IsNullOrWhiteSpace(itunesXml)) { FetchFromItunesInfo(itunesXml, info); } if (data.format != null && !string.IsNullOrEmpty(data.format.duration)) { info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks; } FetchWtvInfo(info, data); if (data.Chapters != null) { info.Chapters = data.Chapters.Select(GetChapterInfo).ToList(); } ExtractTimestamp(info); var stereoMode = GetDictionaryValue(tags, "stereo_mode"); if (string.Equals(stereoMode, "left_right", StringComparison.OrdinalIgnoreCase)) { info.Video3DFormat = Video3DFormat.FullSideBySide; } } return(info); }
private void SetAudioInfoFromTags(MediaInfo audio, Dictionary <string, string> tags) { var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); if (!string.IsNullOrWhiteSpace(composer)) { foreach (var person in Split(composer, false)) { audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); } } var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor"); if (!string.IsNullOrWhiteSpace(conductor)) { foreach (var person in Split(conductor, false)) { audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); } } var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist"); if (!string.IsNullOrWhiteSpace(lyricist)) { foreach (var person in Split(lyricist, false)) { audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); } } // Check for writer some music is tagged that way as alternative to composer/lyricist var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer"); if (!string.IsNullOrWhiteSpace(writer)) { foreach (var person in Split(writer, false)) { audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); } } audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); if (!string.IsNullOrWhiteSpace(artists)) { audio.Artists = SplitArtists(artists, new[] { '/', ';' }, false) .DistinctNames() .ToList(); } else { var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist"); if (string.IsNullOrWhiteSpace(artist)) { audio.Artists.Clear(); } else { audio.Artists = SplitArtists(artist, _nameDelimiters, true) .DistinctNames() .ToList(); } } var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist"); if (string.IsNullOrWhiteSpace(albumArtist)) { albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist"); } if (string.IsNullOrWhiteSpace(albumArtist)) { albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist"); } if (string.IsNullOrWhiteSpace(albumArtist)) { audio.AlbumArtists = new List <string>(); } else { audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true) .DistinctNames() .ToList(); } if (audio.AlbumArtists.Count == 0) { audio.AlbumArtists = audio.Artists.Take(1).ToList(); } // Track number audio.IndexNumber = GetDictionaryDiscValue(tags, "track"); // Disc number audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc"); // If we don't have a ProductionYear try and get it from PremiereDate if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) { audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; } // There's several values in tags may or may not be present FetchStudios(audio, tags, "organization"); FetchStudios(audio, tags, "ensemble"); 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")); if (mb == null) { mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); } audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mb); mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")); if (mb == null) { mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); } audio.SetProviderId(MetadataProviders.MusicBrainzArtist, mb); mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")); if (mb == null) { mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); } audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, mb); mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")); if (mb == null) { mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); } audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mb); mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")); if (mb == null) { mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); } audio.SetProviderId(MetadataProviders.MusicBrainzTrack, mb); }
private void SetAudioInfoFromTags(Model.MediaInfo.MediaInfo audio, Dictionary <string, string> tags) { var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); // Only set Name if title was found in the dictionary if (!string.IsNullOrEmpty(title)) { audio.Title = title; } var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); if (!string.IsNullOrWhiteSpace(composer)) { foreach (var person in Split(composer, false)) { audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); } } audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); if (!string.IsNullOrWhiteSpace(artists)) { audio.Artists = artists.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } else { var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist"); if (string.IsNullOrWhiteSpace(artist)) { audio.Artists.Clear(); } else { audio.Artists = SplitArtists(artist) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } } var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist"); if (string.IsNullOrWhiteSpace(albumArtist)) { albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist"); } if (string.IsNullOrWhiteSpace(albumArtist)) { albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist"); } if (string.IsNullOrWhiteSpace(albumArtist)) { audio.AlbumArtists = new List <string>(); } else { audio.AlbumArtists = SplitArtists(albumArtist) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } // Track number audio.IndexNumber = GetDictionaryDiscValue(tags, "track"); // Disc number audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc"); audio.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); // Several different forms of retaildate audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date"); // If we don't have a ProductionYear try and get it from PremiereDate if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) { audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; } FetchGenres(audio, tags); // There's several values in tags may or may not be present FetchStudios(audio, tags, "organization"); FetchStudios(audio, tags, "ensemble"); FetchStudios(audio, tags, "publisher"); FetchStudios(audio, tags, "label"); // These support mulitple values, but for now we only store the first. audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"))); audio.SetProviderId(MetadataProviders.MusicBrainzArtist, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"))); audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"))); audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"))); audio.SetProviderId(MetadataProviders.MusicBrainzTrack, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"))); }