public async Task <ItemUpdateType> ProbeVideo <T>(T item,
                                                          MetadataRefreshOptions options,
                                                          CancellationToken cancellationToken)
            where T : Video
        {
            var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false);

            BlurayDiscInfo blurayDiscInfo = null;

            try
            {
                if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay))
                {
                    var inputPath = isoMount != null ? isoMount.MountedPath : item.Path;

                    blurayDiscInfo = GetBDInfo(inputPath);
                }

                OnPreFetch(item, isoMount, blurayDiscInfo);

                // If we didn't find any satisfying the min length, just take them all
                if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd))
                {
                    if (item.PlayableStreamFileNames.Count == 0)
                    {
                        _logger.Error("No playable vobs found in dvd structure, skipping ffprobe.");
                        return(ItemUpdateType.MetadataImport);
                    }
                }

                if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay))
                {
                    if (item.PlayableStreamFileNames.Count == 0)
                    {
                        _logger.Error("No playable vobs found in bluray structure, skipping ffprobe.");
                        return(ItemUpdateType.MetadataImport);
                    }
                }

                var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested();

                FFProbeHelpers.NormalizeFFProbeResult(result);

                cancellationToken.ThrowIfCancellationRequested();

                await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, options).ConfigureAwait(false);
            }
            finally
            {
                if (isoMount != null)
                {
                    isoMount.Dispose();
                }
            }

            return(ItemUpdateType.MetadataImport);
        }
Beispiel #2
0
        /// <summary>
        /// Gets the genres from the tags collection
        /// </summary>
        /// <param name="audio">The audio.</param>
        /// <param name="tags">The tags.</param>
        private void FetchGenres(Audio audio, Dictionary <string, string> tags)
        {
            var val = FFProbeHelpers.GetDictionaryValue(tags, "genre");

            if (!string.IsNullOrEmpty(val))
            {
                audio.Genres.Clear();

                foreach (var genre in Split(val, true))
                {
                    audio.AddGenre(genre);
                }
            }
        }
Beispiel #3
0
        public async Task <ItemUpdateType> Probe <T>(T item, CancellationToken cancellationToken)
            where T : Audio
        {
            var result = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            FFProbeHelpers.NormalizeFFProbeResult(result);

            cancellationToken.ThrowIfCancellationRequested();

            await Fetch(item, cancellationToken, result).ConfigureAwait(false);

            return(ItemUpdateType.MetadataImport);
        }
Beispiel #4
0
        /// <summary>
        /// Gets the studios from the tags collection
        /// </summary>
        /// <param name="audio">The audio.</param>
        /// <param name="tags">The tags.</param>
        /// <param name="tagName">Name of the tag.</param>
        private void FetchStudios(Audio audio, Dictionary <string, string> tags, string tagName)
        {
            var val = FFProbeHelpers.GetDictionaryValue(tags, tagName);

            if (!string.IsNullOrEmpty(val))
            {
                // Sometimes the artist name is listed here, account for that
                var studios = Split(val, true).Where(i => !audio.HasArtist(i));

                foreach (var studio in studios)
                {
                    audio.AddStudio(studio);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3'
        /// </summary>
        /// <param name="tags">The tags.</param>
        /// <param name="tagName">Name of the tag.</param>
        /// <returns>System.Nullable{System.Int32}.</returns>
        private int?GetDictionaryDiscValue(Dictionary <string, string> tags, string tagName)
        {
            var disc = FFProbeHelpers.GetDictionaryValue(tags, tagName);

            if (!string.IsNullOrEmpty(disc))
            {
                disc = disc.Split('/')[0];

                int num;

                if (int.TryParse(disc, out num))
                {
                    return(num);
                }
            }

            return(null);
        }
        public async Task <ItemUpdateType> Probe <T>(T item, CancellationToken cancellationToken)
            where T : Audio
        {
            if (item.IsArchive)
            {
                var ext = Path.GetExtension(item.Path) ?? string.Empty;
                item.Container = ext.TrimStart('.');
                return(ItemUpdateType.MetadataImport);
            }

            var result = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            FFProbeHelpers.NormalizeFFProbeResult(result);

            cancellationToken.ThrowIfCancellationRequested();

            await Fetch(item, cancellationToken, result).ConfigureAwait(false);

            return(ItemUpdateType.MetadataImport);
        }
Beispiel #7
0
        public const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames)

        private void FetchWtvInfo(Video video, InternalMediaInfoResult data)
        {
            if (data.format == null || data.format.tags == null)
            {
                return;
            }

            if (!video.LockedFields.Contains(MetadataFields.Genres))
            {
                var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/Genre");

                if (!string.IsNullOrWhiteSpace(genres))
                {
                    //genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre");
                }

                if (!string.IsNullOrWhiteSpace(genres))
                {
                    video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
                                   .Where(i => !string.IsNullOrWhiteSpace(i))
                                   .Select(i => i.Trim())
                                   .ToList();
                }
            }

            if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
            {
                var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating");

                if (!string.IsNullOrWhiteSpace(officialRating))
                {
                    video.OfficialRating = officialRating;
                }
            }

            if (!video.LockedFields.Contains(MetadataFields.Cast))
            {
                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 PersonInfo {
                        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 episode = video as Episode;

            if (episode != null)
            {
                var subTitle = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitle");

                // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/

                // Sometimes for TV Shows the Subtitle field is empty and the subtitle description contains the subtitle, extract if possible. See ticket https://mcebuddy2x.codeplex.com/workitem/1910
                // The format is -> EPISODE/TOTAL_EPISODES_IN_SEASON. SUBTITLE: DESCRIPTION
                // OR -> COMMENT. SUBTITLE: DESCRIPTION
                // e.g. -> 4/13. The Doctor's Wife: Science fiction drama. When he follows a Time Lord distress signal, the Doctor puts Amy, Rory and his beloved TARDIS in grave danger. Also in HD. [AD,S]
                // e.g. -> CBeebies Bedtime Hour. The Mystery: Animated adventures of two friends who live on an island in the middle of the big city. Some of Abney and Teal's favourite objects are missing. [S]
                if (String.IsNullOrWhiteSpace(subTitle) && !String.IsNullOrWhiteSpace(description) && description.Substring(0, Math.Min(description.Length, MaxSubtitleDescriptionExtractionLength)).Contains(":")) // Check within the Subtitle size limit, otherwise from description it can get too long creating an invalid filename
                {
                    string[] parts = description.Split(':');
                    if (parts.Length > 0)
                    {
                        string subtitle = parts[0];
                        try
                        {
                            if (subtitle.Contains("/")) // It contains a episode number and season number
                            {
                                string[] numbers = subtitle.Split(' ');
                                episode.IndexNumber = int.Parse(numbers[0].Replace(".", "").Split('/')[0]);
                                int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", "").Split('/')[1]);

                                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 (!video.LockedFields.Contains(MetadataFields.Overview))
            {
                if (!string.IsNullOrWhiteSpace(description))
                {
                    video.Overview = description;
                }
            }
        }
        private void FetchWtvInfo(Video video, InternalMediaInfoResult data)
        {
            if (data.format == null || data.format.tags == null)
            {
                return;
            }

            if (video.Genres.Count == 0)
            {
                if (!video.LockedFields.Contains(MetadataFields.Genres))
                {
                    var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre");

                    if (!string.IsNullOrEmpty(genres))
                    {
                        video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
                                       .Where(i => !string.IsNullOrWhiteSpace(i))
                                       .Select(i => i.Trim())
                                       .ToList();
                    }
                }
            }

            if (string.IsNullOrEmpty(video.Overview))
            {
                if (!video.LockedFields.Contains(MetadataFields.Overview))
                {
                    var overview = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription");

                    if (!string.IsNullOrWhiteSpace(overview))
                    {
                        video.Overview = overview;
                    }
                }
            }

            if (string.IsNullOrEmpty(video.OfficialRating))
            {
                var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating");

                if (!string.IsNullOrWhiteSpace(officialRating))
                {
                    if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
                    {
                        video.OfficialRating = officialRating;
                    }
                }
            }

            if (video.People.Count == 0)
            {
                if (!video.LockedFields.Contains(MetadataFields.Cast))
                {
                    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 PersonInfo {
                            Name = i.Trim(), Type = PersonType.Actor
                        })
                                       .ToList();
                    }
                }
            }

            if (!video.ProductionYear.HasValue)
            {
                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;
                    }
                }
            }
        }
Beispiel #9
0
        /// <summary>
        /// Fetches data from the tags dictionary
        /// </summary>
        /// <param name="audio">The audio.</param>
        /// <param name="tags">The tags.</param>
        private void FetchDataFromTags(Audio audio, Dictionary <string, string> tags)
        {
            var title = FFProbeHelpers.GetDictionaryValue(tags, "title");

            // Only set Name if title was found in the dictionary
            if (!string.IsNullOrEmpty(title))
            {
                audio.Name = title;
            }

            if (!audio.LockedFields.Contains(MetadataFields.Cast))
            {
                audio.People.Clear();

                var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer");

                if (!string.IsNullOrWhiteSpace(composer))
                {
                    foreach (var person in Split(composer, false))
                    {
                        audio.AddPerson(new PersonInfo {
                            Name = person, Type = PersonType.Composer
                        });
                    }
                }
            }

            audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album");

            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") ?? FFProbeHelpers.GetDictionaryValue(tags, "album artist") ?? 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;
            }

            if (!audio.LockedFields.Contains(MetadataFields.Genres))
            {
                FetchGenres(audio, tags);
            }

            if (!audio.LockedFields.Contains(MetadataFields.Studios))
            {
                audio.Studios.Clear();

                // There's several values in tags may or may not be present
                FetchStudios(audio, tags, "organization");
                FetchStudios(audio, tags, "ensemble");
                FetchStudios(audio, tags, "publisher");
            }

            audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"));
            audio.SetProviderId(MetadataProviders.MusicBrainzArtist, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"));
            audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"));
            audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"));
        }