private void FetchPeople(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
        {
            var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;

            if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Cast))
            {
                if (isFullRefresh || _libraryManager.GetPeople(video).Count == 0)
                {
                    var people = new List <PersonInfo>();

                    foreach (var person in data.People)
                    {
                        PeopleHelper.AddPerson(people, new PersonInfo
                        {
                            Name = person.Name,
                            Type = person.Type,
                            Role = person.Role
                        });
                    }

                    _libraryManager.UpdatePeople(video, people);
                }
            }
        }
Beispiel #2
0
        private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions)
        {
            var isFullRefresh = refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;

            if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.OfficialRating))
            {
                if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
                {
                    video.OfficialRating = data.OfficialRating;
                }
            }

            if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres))
            {
                if (video.Genres.Length == 0 || isFullRefresh)
                {
                    video.Genres = Array.Empty <string>();

                    foreach (var genre in data.Genres)
                    {
                        video.AddGenre(genre);
                    }
                }
            }

            if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Studios))
            {
                if (video.Studios.Length == 0 || isFullRefresh)
                {
                    video.SetStudios(data.Studios);
                }
            }

            if (data.ProductionYear.HasValue)
            {
                if (!video.ProductionYear.HasValue || isFullRefresh)
                {
                    video.ProductionYear = data.ProductionYear;
                }
            }
            if (data.PremiereDate.HasValue)
            {
                if (!video.PremiereDate.HasValue || isFullRefresh)
                {
                    video.PremiereDate = data.PremiereDate;
                }
            }
            if (data.IndexNumber.HasValue)
            {
                if (!video.IndexNumber.HasValue || isFullRefresh)
                {
                    video.IndexNumber = data.IndexNumber;
                }
            }
            if (data.ParentIndexNumber.HasValue)
            {
                if (!video.ParentIndexNumber.HasValue || isFullRefresh)
                {
                    video.ParentIndexNumber = data.ParentIndexNumber;
                }
            }

            if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Name))
            {
                if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles)
                {
                    // Don't use the embedded name for extras because it will often be the same name as the movie
                    if (!video.ExtraType.HasValue)
                    {
                        video.Name = data.Name;
                    }
                }
            }

            // If we don't have a ProductionYear try and get it from PremiereDate
            if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)
            {
                video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
            }

            if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Overview))
            {
                if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
                {
                    video.Overview = data.Overview;
                }
            }
        }
Beispiel #3
0
        protected async Task Fetch(Video video,
                                   CancellationToken cancellationToken,
                                   Model.MediaInfo.MediaInfo mediaInfo,
                                   BlurayDiscInfo blurayInfo,
                                   MetadataRefreshOptions options)
        {
            List <MediaStream> mediaStreams;
            List <ChapterInfo> chapters;

            if (mediaInfo != null)
            {
                mediaStreams = mediaInfo.MediaStreams;

                video.TotalBitrate = mediaInfo.Bitrate;
                //video.FormatName = (mediaInfo.Container ?? string.Empty)
                //    .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);

                // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
                var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;

                if (needToSetRuntime)
                {
                    video.RunTimeTicks = mediaInfo.RunTimeTicks;
                }
                video.Size = mediaInfo.Size;

                if (video.VideoType == VideoType.VideoFile)
                {
                    var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');

                    video.Container = extension;
                }
                else
                {
                    video.Container = null;
                }
                video.Container = mediaInfo.Container;

                chapters = mediaInfo.Chapters == null ? new List <ChapterInfo>() : mediaInfo.Chapters.ToList();
                if (blurayInfo != null)
                {
                    FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
                }
            }
            else
            {
                mediaStreams = new List <MediaStream>();
                chapters     = new List <ChapterInfo>();
            }

            await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);

            var libraryOptions = _libraryManager.GetLibraryOptions(video);

            if (mediaInfo != null)
            {
                FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions);
                FetchPeople(video, mediaInfo, options);
                video.Timestamp     = mediaInfo.Timestamp;
                video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat;
            }

            var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

            video.Height = videoStream == null ? 0 : videoStream.Height ?? 0;
            video.Width  = videoStream == null ? 0 : videoStream.Width ?? 0;

            video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;

            video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);

            _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);

            if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
                options.MetadataRefreshMode == MetadataRefreshMode.Default)
            {
                if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
                {
                    AddDummyChapters(video, chapters);
                }

                NormalizeChapterNames(chapters);

                var extractDuringScan = false;
                if (libraryOptions != null)
                {
                    extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan;
                }

                await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);

                _chapterManager.SaveChapters(video.Id.ToString(), chapters);
            }
        }
        public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
        {
            var info = new Model.MediaInfo.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;
                    }
                }
            }

            if (isAudio)
            {
                SetAudioRuntimeTicks(data, info);

                var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

                // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
                // so let's create a combined list of both

                if (data.streams != null)
                {
                    var audioStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, "audio", StringComparison.OrdinalIgnoreCase));

                    if (audioStream != null && audioStream.tags != null)
                    {
                        foreach (var pair in audioStream.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;
                    }
                }

                SetAudioInfoFromTags(info, tags);
            }
            else
            {
                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);
            }

            return info;
        }
Beispiel #5
0
        /// <summary>
        /// Fetches data from the tags dictionary
        /// </summary>
        /// <param name="audio">The audio.</param>
        /// <param name="data">The data.</param>
        private async Task FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data)
        {
            // Only set Name if title was found in the dictionary
            if (!string.IsNullOrEmpty(data.Name))
            {
                audio.Name = data.Name;
            }

            if (!audio.LockedFields.Contains(MetadataFields.Cast))
            {
                var people = new List <PersonInfo>();

                foreach (var person in data.People)
                {
                    PeopleHelper.AddPerson(people, new PersonInfo
                    {
                        Name = person.Name,
                        Type = person.Type,
                        Role = person.Role
                    });
                }

                await _libraryManager.UpdatePeople(audio, people).ConfigureAwait(false);
            }

            audio.Album             = data.Album;
            audio.Artists           = data.Artists;
            audio.AlbumArtists      = data.AlbumArtists;
            audio.IndexNumber       = data.IndexNumber;
            audio.ParentIndexNumber = data.ParentIndexNumber;
            audio.ProductionYear    = data.ProductionYear;
            audio.PremiereDate      = data.PremiereDate;

            // If we don't have a ProductionYear try and get it from PremiereDate
            if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue)
            {
                audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year;
            }

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

                foreach (var genre in data.Genres)
                {
                    audio.AddGenre(genre);
                }
            }

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

                foreach (var studio in data.Studios)
                {
                    audio.AddStudio(studio);
                }
            }

            audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist));
            audio.SetProviderId(MetadataProviders.MusicBrainzArtist, data.GetProviderId(MetadataProviders.MusicBrainzArtist));
            audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, data.GetProviderId(MetadataProviders.MusicBrainzAlbum));
            audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup));
            audio.SetProviderId(MetadataProviders.MusicBrainzTrack, data.GetProviderId(MetadataProviders.MusicBrainzTrack));
        }
Beispiel #6
0
        private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
        {
            var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;

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

            if (!video.LockedFields.Contains(MetadataFields.Genres))
            {
                if (video.Genres.Count == 0 || isFullRefresh)
                {
                    video.Genres.Clear();

                    foreach (var genre in data.Genres)
                    {
                        video.AddGenre(genre);
                    }
                }
            }

            if (!video.LockedFields.Contains(MetadataFields.Studios))
            {
                if (video.Studios.Count == 0 || isFullRefresh)
                {
                    video.Studios.Clear();

                    foreach (var studio in data.Studios)
                    {
                        video.AddStudio(studio);
                    }
                }
            }

            if (data.ProductionYear.HasValue)
            {
                if (!video.ProductionYear.HasValue || isFullRefresh)
                {
                    video.ProductionYear = data.ProductionYear;
                }
            }
            if (data.PremiereDate.HasValue)
            {
                if (!video.PremiereDate.HasValue || isFullRefresh)
                {
                    video.PremiereDate = data.PremiereDate;
                }
            }
            if (data.IndexNumber.HasValue)
            {
                if (!video.IndexNumber.HasValue || isFullRefresh)
                {
                    video.IndexNumber = data.IndexNumber;
                }
            }
            if (data.ParentIndexNumber.HasValue)
            {
                if (!video.ParentIndexNumber.HasValue || isFullRefresh)
                {
                    video.ParentIndexNumber = data.ParentIndexNumber;
                }
            }

            // If we don't have a ProductionYear try and get it from PremiereDate
            if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)
            {
                video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
            }

            if (!video.LockedFields.Contains(MetadataFields.Overview))
            {
                if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
                {
                    video.Overview = data.Overview;
                }
            }
        }
Beispiel #7
0
        protected async Task Fetch(Video video,
                                   CancellationToken cancellationToken,
                                   Model.MediaInfo.MediaInfo mediaInfo,
                                   IIsoMount isoMount,
                                   BlurayDiscInfo blurayInfo,
                                   MetadataRefreshOptions options)
        {
            var mediaStreams = mediaInfo.MediaStreams;

            video.TotalBitrate = mediaInfo.Bitrate;
            //video.FormatName = (mediaInfo.Container ?? string.Empty)
            //    .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);

            // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
            var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;

            if (needToSetRuntime)
            {
                video.RunTimeTicks = mediaInfo.RunTimeTicks;
            }

            if (video.VideoType == VideoType.VideoFile)
            {
                var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');

                video.Container = extension;
            }
            else
            {
                video.Container = null;
            }

            var chapters = mediaInfo.Chapters ?? new List <ChapterInfo>();

            if (blurayInfo != null)
            {
                FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
            }

            await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);

            FetchEmbeddedInfo(video, mediaInfo, options);
            await FetchPeople(video, mediaInfo, options).ConfigureAwait(false);

            video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270);

            var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

            video.VideoBitRate            = videoStream == null ? null : videoStream.BitRate;
            video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;

            video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
            video.Timestamp    = mediaInfo.Timestamp;

            await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false);

            if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
                options.MetadataRefreshMode == MetadataRefreshMode.Default)
            {
                var chapterOptions = _chapterManager.GetConfiguration();

                try
                {
                    var remoteChapters = await DownloadChapters(video, chapters, chapterOptions, cancellationToken).ConfigureAwait(false);

                    if (remoteChapters.Count > 0)
                    {
                        chapters = remoteChapters;
                    }
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error downloading chapters", ex);
                }

                if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
                {
                    AddDummyChapters(video, chapters);
                }

                NormalizeChapterNames(chapters);

                await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions
                {
                    Chapters      = chapters,
                    Video         = video,
                    ExtractImages = chapterOptions.ExtractDuringLibraryScan,
                    SaveChapters  = false
                }, cancellationToken).ConfigureAwait(false);

                await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
            }
        }
Beispiel #8
0
        private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
        {
            var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;

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

            if (!string.IsNullOrWhiteSpace(data.OfficialRatingDescription) || isFullRefresh)
            {
                video.OfficialRatingDescription = data.OfficialRatingDescription;
            }

            if (!video.LockedFields.Contains(MetadataFields.Genres))
            {
                if (video.Genres.Count == 0 || isFullRefresh)
                {
                    video.Genres.Clear();

                    foreach (var genre in data.Genres)
                    {
                        video.AddGenre(genre);
                    }
                }
            }

            if (!video.LockedFields.Contains(MetadataFields.Studios))
            {
                if (video.Studios.Count == 0 || isFullRefresh)
                {
                    video.Studios.Clear();

                    foreach (var studio in data.Studios)
                    {
                        video.AddStudio(studio);
                    }
                }
            }

            if (data.ProductionYear.HasValue)
            {
                if (!video.ProductionYear.HasValue || isFullRefresh)
                {
                    video.ProductionYear = data.ProductionYear;
                }
            }
            if (data.PremiereDate.HasValue)
            {
                if (!video.PremiereDate.HasValue || isFullRefresh)
                {
                    video.PremiereDate = data.PremiereDate;
                }
            }
            if (data.IndexNumber.HasValue)
            {
                if (!video.IndexNumber.HasValue || isFullRefresh)
                {
                    video.IndexNumber = data.IndexNumber;
                }
            }
            if (data.ParentIndexNumber.HasValue)
            {
                if (!video.ParentIndexNumber.HasValue || isFullRefresh)
                {
                    video.ParentIndexNumber = data.ParentIndexNumber;
                }
            }
            if (!string.IsNullOrWhiteSpace(data.Name))
            {
                if (string.IsNullOrWhiteSpace(video.Name) || (string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase) && !video.ProviderIds.Any()))
                {
                    // Don't use the embedded name for extras because it will often be the same name as the movie
                    if (!video.ExtraType.HasValue && !video.IsOwnedItem)
                    {
                        video.Name = data.Name;
                    }
                }
            }

            // If we don't have a ProductionYear try and get it from PremiereDate
            if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)
            {
                video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
            }

            if (!video.LockedFields.Contains(MetadataFields.Overview))
            {
                if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
                {
                    video.Overview = data.Overview;
                }
            }
        }
Beispiel #9
0
        private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames)

        private void FetchWtvInfo(Model.MediaInfo.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))
            {
                //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();
            }

            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;
            }
        }
Beispiel #10
0
        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");

            // 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")));
        }
Beispiel #11
0
        public Model.MediaInfo.MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
        {
            var info = new Model.MediaInfo.MediaInfo
            {
                Path     = path,
                Protocol = protocol
            };

            FFProbeHelpers.NormalizeFFProbeResult(data);
            SetSize(data, info);

            var internalStreams = data.streams ?? new MediaStreamInfo[] { };

            info.MediaStreams = internalStreams.Select(s => GetMediaStream(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))
                {
                    info.Bitrate = int.Parse(data.format.bit_rate, _usCulture);
                }
            }

            if (isAudio)
            {
                SetAudioRuntimeTicks(data, info);

                if (data.format != null && data.format.tags != null)
                {
                    SetAudioInfoFromTags(info, data.format.tags);
                }
            }
            else
            {
                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 videoStream = info.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

                if (videoStream != null && videoType == VideoType.VideoFile)
                {
                    UpdateFromMediaInfo(info, videoStream);
                }
            }

            return(info);
        }
Beispiel #12
0
        protected async Task Fetch(
            Video video,
            CancellationToken cancellationToken,
            Model.MediaInfo.MediaInfo mediaInfo,
            BlurayDiscInfo blurayInfo,
            MetadataRefreshOptions options)
        {
            List <MediaStream> mediaStreams;
            IReadOnlyList <MediaAttachment> mediaAttachments;

            ChapterInfo[] chapters;

            if (mediaInfo != null)
            {
                mediaStreams     = mediaInfo.MediaStreams.ToList();
                mediaAttachments = mediaInfo.MediaAttachments;

                video.TotalBitrate = mediaInfo.Bitrate;
                // video.FormatName = (mediaInfo.Container ?? string.Empty)
                //    .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);

                // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
                var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;

                if (needToSetRuntime)
                {
                    video.RunTimeTicks = mediaInfo.RunTimeTicks;
                }

                video.Size = mediaInfo.Size;

                if (video.VideoType == VideoType.VideoFile)
                {
                    var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');

                    video.Container = extension;
                }
                else
                {
                    video.Container = null;
                }

                video.Container = mediaInfo.Container;

                chapters = mediaInfo.Chapters ?? Array.Empty <ChapterInfo>();
                if (blurayInfo != null)
                {
                    FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo);
                }
            }
            else
            {
                mediaStreams     = new List <MediaStream>();
                mediaAttachments = Array.Empty <MediaAttachment>();
                chapters         = Array.Empty <ChapterInfo>();
            }

            await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);

            await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);

            var libraryOptions = _libraryManager.GetLibraryOptions(video);

            if (mediaInfo != null)
            {
                FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions);
                FetchPeople(video, mediaInfo, options);
                video.Timestamp = mediaInfo.Timestamp;
                video.Video3DFormat ??= mediaInfo.Video3DFormat;
            }

            if (libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowText || libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowNone)
            {
                _logger.LogDebug("Disabling embedded image subtitles for {Path} due to DisableEmbeddedImageSubtitles setting", video.Path);
                mediaStreams.RemoveAll(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal && !i.IsTextSubtitleStream);
            }

            if (libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowImage || libraryOptions.AllowEmbeddedSubtitles == EmbeddedSubtitleOptions.AllowNone)
            {
                _logger.LogDebug("Disabling embedded text subtitles for {Path} due to DisableEmbeddedTextSubtitles setting", video.Path);
                mediaStreams.RemoveAll(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal && i.IsTextSubtitleStream);
            }

            var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

            video.Height = videoStream?.Height ?? 0;
            video.Width  = videoStream?.Width ?? 0;

            video.DefaultVideoStreamIndex = videoStream?.Index;

            video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);

            _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
            _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);

            if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
                options.MetadataRefreshMode == MetadataRefreshMode.Default)
            {
                if (chapters.Length == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
                {
                    chapters = CreateDummyChapters(video);
                }

                NormalizeChapterNames(chapters);

                var extractDuringScan = false;
                if (libraryOptions != null)
                {
                    extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan;
                }

                await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);

                _chapterManager.SaveChapters(video.Id, chapters);
            }
        }
Beispiel #13
0
        public Model.MediaInfo.MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
        {
            var info = new Model.MediaInfo.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))
                {
                    info.Bitrate = int.Parse(data.format.bit_rate, _usCulture);
                }
            }

            if (isAudio)
            {
                SetAudioRuntimeTicks(data, info);

                var tags = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

                // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
                // so let's create a combined list of both

                if (data.streams != null)
                {
                    var audioStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, "audio", StringComparison.OrdinalIgnoreCase));

                    if (audioStream != null && audioStream.tags != null)
                    {
                        foreach (var pair in audioStream.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;
                    }
                }

                SetAudioInfoFromTags(info, tags);
            }
            else
            {
                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 videoStream = info.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

                if (videoStream != null && videoType == VideoType.VideoFile)
                {
                    UpdateFromMediaInfo(info, videoStream);
                }
            }

            return(info);
        }
        private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data)
        {
            if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
            {
                if (!string.IsNullOrWhiteSpace(data.OfficialRating))
                {
                    video.OfficialRating = data.OfficialRating;
                }
            }

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

                foreach (var person in data.People)
                {
                    video.AddPerson(new PersonInfo
                    {
                        Name = person.Name,
                        Type = person.Type,
                        Role = person.Role
                    });
                }
            }

            if (!video.LockedFields.Contains(MetadataFields.Genres))
            {
                video.Genres.Clear();

                foreach (var genre in data.Genres)
                {
                    video.AddGenre(genre);
                }
            }

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

                foreach (var studio in data.Studios)
                {
                    video.AddStudio(studio);
                }
            }

            if (data.ProductionYear.HasValue)
            {
                video.ProductionYear = data.ProductionYear;
            }
            if (data.PremiereDate.HasValue)
            {
                video.PremiereDate = data.PremiereDate;
            }
            if (data.IndexNumber.HasValue)
            {
                video.IndexNumber = data.IndexNumber;
            }
            if (data.ParentIndexNumber.HasValue)
            {
                video.ParentIndexNumber = data.ParentIndexNumber;
            }

            // If we don't have a ProductionYear try and get it from PremiereDate
            if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)
            {
                video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
            }

            if (!video.LockedFields.Contains(MetadataFields.Overview))
            {
                if (!string.IsNullOrWhiteSpace(data.Overview))
                {
                    video.Overview = data.Overview;
                }
            }
        }
Beispiel #15
0
        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
            {
                Model.MediaInfo.MediaInfo mediaInfoResult = null;

                if (!item.IsShortcut)
                {
                    string[] streamFileNames = null;

                    if (item.VideoType == VideoType.Iso)
                    {
                        item.IsoType = DetermineIsoType(isoMount);
                    }

                    if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd))
                    {
                        streamFileNames = FetchFromDvdLib(item, isoMount);

                        if (streamFileNames.Length == 0)
                        {
                            _logger.Error("No playable vobs found in dvd structure, skipping ffprobe.");
                            return(ItemUpdateType.MetadataImport);
                        }
                    }

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

                        blurayDiscInfo = GetBDInfo(inputPath);

                        streamFileNames = blurayDiscInfo.Files;

                        if (streamFileNames.Length == 0)
                        {
                            _logger.Error("No playable vobs found in bluray structure, skipping ffprobe.");
                            return(ItemUpdateType.MetadataImport);
                        }
                    }

                    if (streamFileNames == null)
                    {
                        streamFileNames = new string[] { };
                    }

                    mediaInfoResult = await GetMediaInfo(item, isoMount, streamFileNames, cancellationToken).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();
                }

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

            return(ItemUpdateType.MetadataImport);
        }