diff options
| author | Negulici-R. Barnabas <109497789+negulici-r-barnabas@users.noreply.github.com> | 2022-11-13 15:29:16 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-13 15:29:16 +0200 |
| commit | b7aa5ed862db11bbbc0a4ea5c92a67b772bfc35d (patch) | |
| tree | d8f396f581f3bdbd4be4c34d4a949df9fff72934 /MediaBrowser.Providers/MediaInfo | |
| parent | 1e41636e30b82518633ac6979564ff98bb40aca9 (diff) | |
| parent | 6655cf4e58285f51b612efb0bb6229f036da2591 (diff) | |
Merge branch 'jellyfin:master' into master
Diffstat (limited to 'MediaBrowser.Providers/MediaInfo')
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/AudioFileProber.cs | 215 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs | 172 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/ProbeProvider.cs (renamed from MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs) | 62 |
5 files changed, 271 insertions, 184 deletions
diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs new file mode 100644 index 0000000000..3699e8f497 --- /dev/null +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using TagLib; + +namespace MediaBrowser.Providers.MediaInfo +{ + /// <summary> + /// Probes audio files for metadata. + /// </summary> + public class AudioFileProber + { + private readonly IMediaEncoder _mediaEncoder; + private readonly IItemRepository _itemRepo; + private readonly ILibraryManager _libraryManager; + private readonly IMediaSourceManager _mediaSourceManager; + + /// <summary> + /// Initializes a new instance of the <see cref="AudioFileProber"/> class. + /// </summary> + /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> + /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> + /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param> + /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> + public AudioFileProber( + IMediaSourceManager mediaSourceManager, + IMediaEncoder mediaEncoder, + IItemRepository itemRepo, + ILibraryManager libraryManager) + { + _mediaEncoder = mediaEncoder; + _itemRepo = itemRepo; + _libraryManager = libraryManager; + _mediaSourceManager = mediaSourceManager; + } + + /// <summary> + /// Probes the specified item for metadata. + /// </summary> + /// <param name="item">The item to probe.</param> + /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param> + /// <typeparam name="T">The type of item to resolve.</typeparam> + /// <returns>A <see cref="Task"/> probing the item for metadata.</returns> + public async Task<ItemUpdateType> Probe<T>( + T item, + MetadataRefreshOptions options, + CancellationToken cancellationToken) + where T : Audio + { + var path = item.Path; + var protocol = item.PathProtocol ?? MediaProtocol.File; + + if (!item.IsShortcut || options.EnableRemoteContentProbe) + { + if (item.IsShortcut) + { + path = item.ShortcutPath; + protocol = _mediaSourceManager.GetPathProtocol(path); + } + + var result = await _mediaEncoder.GetMediaInfo( + new MediaInfoRequest + { + MediaType = DlnaProfileType.Audio, + MediaSource = new MediaSourceInfo + { + Path = path, + Protocol = protocol + } + }, + cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + Fetch(item, result, cancellationToken); + } + + return ItemUpdateType.MetadataImport; + } + + /// <summary> + /// Fetches the specified audio. + /// </summary> + /// <param name="audio">The <see cref="Audio"/>.</param> + /// <param name="mediaInfo">The <see cref="Model.MediaInfo.MediaInfo"/>.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param> + protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken) + { + audio.Container = mediaInfo.Container; + audio.TotalBitrate = mediaInfo.Bitrate; + + audio.RunTimeTicks = mediaInfo.RunTimeTicks; + audio.Size = mediaInfo.Size; + + FetchDataFromTags(audio); + + _itemRepo.SaveMediaStreams(audio.Id, mediaInfo.MediaStreams, cancellationToken); + } + + /// <summary> + /// Fetches data from the tags. + /// </summary> + /// <param name="audio">The <see cref="Audio"/>.</param> + private void FetchDataFromTags(Audio audio) + { + var file = TagLib.File.Create(audio.Path); + var tagTypes = file.TagTypesOnDisk; + Tag? tags = null; + + if (tagTypes.HasFlag(TagTypes.Id3v2)) + { + tags = file.GetTag(TagTypes.Id3v2); + } + else if (tagTypes.HasFlag(TagTypes.Ape)) + { + tags = file.GetTag(TagTypes.Ape); + } + else if (tagTypes.HasFlag(TagTypes.FlacMetadata)) + { + tags = file.GetTag(TagTypes.FlacMetadata); + } + else if (tagTypes.HasFlag(TagTypes.Apple)) + { + tags = file.GetTag(TagTypes.Apple); + } + else if (tagTypes.HasFlag(TagTypes.Xiph)) + { + tags = file.GetTag(TagTypes.Xiph); + } + else if (tagTypes.HasFlag(TagTypes.AudibleMetadata)) + { + tags = file.GetTag(TagTypes.AudibleMetadata); + } + else if (tagTypes.HasFlag(TagTypes.Id3v1)) + { + tags = file.GetTag(TagTypes.Id3v1); + } + + if (tags != null) + { + if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) + { + var people = new List<PersonInfo>(); + var albumArtists = tags.AlbumArtists; + foreach (var albumArtist in albumArtists) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = albumArtist, + Type = "AlbumArtist" + }); + } + + var performers = tags.Performers; + foreach (var performer in performers) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = performer, + Type = "Artist" + }); + } + + foreach (var composer in tags.Composers) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = composer, + Type = "Composer" + }); + } + + _libraryManager.UpdatePeople(audio, people); + audio.Artists = performers; + audio.AlbumArtists = albumArtists; + } + + audio.Name = tags.Title; + audio.Album = tags.Album; + audio.IndexNumber = Convert.ToInt32(tags.Track); + audio.ParentIndexNumber = Convert.ToInt32(tags.Disc); + if (tags.Year != 0) + { + var year = Convert.ToInt32(tags.Year); + audio.ProductionYear = year; + audio.PremiereDate = new DateTime(year, 01, 01); + } + + if (!audio.LockedFields.Contains(MetadataField.Genres)) + { + audio.Genres = tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); + } + + audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId); + audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId); + audio.SetProviderId(MetadataProvider.MusicBrainzTrack, tags.MusicBrainzTrackId); + } + } + } +} diff --git a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs index 96d7d139a4..d60d829def 100644 --- a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs @@ -31,13 +31,14 @@ namespace MediaBrowser.Providers.MediaInfo "poster", "folder", "cover", - "default" + "default", + "movie", + "show" }; private static readonly string[] _backdropImageFileNames = { "backdrop", - "fanart", "background", "art" }; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs deleted file mode 100644 index f22965436f..0000000000 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ /dev/null @@ -1,172 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.Providers.MediaInfo -{ - public class FFProbeAudioInfo - { - private readonly IMediaEncoder _mediaEncoder; - private readonly IItemRepository _itemRepo; - private readonly ILibraryManager _libraryManager; - private readonly IMediaSourceManager _mediaSourceManager; - - public FFProbeAudioInfo( - IMediaSourceManager mediaSourceManager, - IMediaEncoder mediaEncoder, - IItemRepository itemRepo, - ILibraryManager libraryManager) - { - _mediaEncoder = mediaEncoder; - _itemRepo = itemRepo; - _libraryManager = libraryManager; - _mediaSourceManager = mediaSourceManager; - } - - public async Task<ItemUpdateType> Probe<T>( - T item, - MetadataRefreshOptions options, - CancellationToken cancellationToken) - where T : Audio - { - var path = item.Path; - var protocol = item.PathProtocol ?? MediaProtocol.File; - - if (!item.IsShortcut || options.EnableRemoteContentProbe) - { - if (item.IsShortcut) - { - path = item.ShortcutPath; - protocol = _mediaSourceManager.GetPathProtocol(path); - } - - var result = await _mediaEncoder.GetMediaInfo( - new MediaInfoRequest - { - MediaType = DlnaProfileType.Audio, - MediaSource = new MediaSourceInfo - { - Path = path, - Protocol = protocol - } - }, - cancellationToken).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - - Fetch(item, result, cancellationToken); - } - - return ItemUpdateType.MetadataImport; - } - - /// <summary> - /// Fetches the specified audio. - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="mediaInfo">The media information.</param> - /// <param name="cancellationToken">The cancellation token.</param> - protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken) - { - audio.Container = mediaInfo.Container; - audio.TotalBitrate = mediaInfo.Bitrate; - - audio.RunTimeTicks = mediaInfo.RunTimeTicks; - audio.Size = mediaInfo.Size; - - // var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); - // audio.Container = extension; - - FetchDataFromTags(audio, mediaInfo); - - _itemRepo.SaveMediaStreams(audio.Id, mediaInfo.MediaStreams, cancellationToken); - } - - /// <summary> - /// Fetches data from the tags dictionary. - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="data">The data.</param> - private void 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 (!string.IsNullOrEmpty(data.ForcedSortName)) - { - audio.ForcedSortName = data.ForcedSortName; - } - - if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.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 - }); - } - - _libraryManager.UpdatePeople(audio, people); - } - - 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(MetadataField.Genres)) - { - audio.Genres = Array.Empty<string>(); - - foreach (var genre in data.Genres) - { - audio.AddGenre(genre); - } - } - - if (!audio.LockedFields.Contains(MetadataField.Studios)) - { - audio.SetStudios(data.Studios); - } - - audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)); - audio.SetProviderId(MetadataProvider.MusicBrainzArtist, data.GetProviderId(MetadataProvider.MusicBrainzArtist)); - audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, data.GetProviderId(MetadataProvider.MusicBrainzAlbum)); - audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)); - audio.SetProviderId(MetadataProvider.MusicBrainzTrack, data.GetProviderId(MetadataProvider.MusicBrainzTrack)); - } - } -} diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index d55cc44914..1bc2edfd88 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Providers.MediaInfo mediaStream.Index = startIndex++; mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault; mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced; + mediaStream.IsHearingImpaired = pathInfo.IsHearingImpaired || mediaStream.IsHearingImpaired; mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs index e58c0e281b..6591366076 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.IO; using System.Linq; @@ -27,7 +25,10 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo { - public class FFProbeProvider : ICustomMetadataProvider<Episode>, + /// <summary> + /// The probe provider. + /// </summary> + public class ProbeProvider : ICustomMetadataProvider<Episode>, ICustomMetadataProvider<MusicVideo>, ICustomMetadataProvider<Movie>, ICustomMetadataProvider<Trailer>, @@ -39,14 +40,30 @@ namespace MediaBrowser.Providers.MediaInfo IPreRefreshProvider, IHasItemChangeMonitor { - private readonly ILogger<FFProbeProvider> _logger; + private readonly ILogger<ProbeProvider> _logger; private readonly AudioResolver _audioResolver; private readonly SubtitleResolver _subtitleResolver; private readonly FFProbeVideoInfo _videoProber; - private readonly FFProbeAudioInfo _audioProber; + private readonly AudioFileProber _audioProber; private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); - public FFProbeProvider( + /// <summary> + /// Initializes a new instance of the <see cref="ProbeProvider"/> class. + /// </summary> + /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> + /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> + /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param> + /// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param> + /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> + /// <param name="subtitleManager">Instance of the <see cref="ISubtitleManager"/> interface.</param> + /// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param> + /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> + /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/>.</param> + /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> + /// <param name="namingOptions">The <see cref="NamingOptions"/>.</param> + public ProbeProvider( IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, @@ -61,7 +78,8 @@ namespace MediaBrowser.Providers.MediaInfo ILoggerFactory loggerFactory, NamingOptions namingOptions) { - _logger = loggerFactory.CreateLogger<FFProbeProvider>(); + _logger = loggerFactory.CreateLogger<ProbeProvider>(); + _audioProber = new AudioFileProber(mediaSourceManager, mediaEncoder, itemRepo, libraryManager); _audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions); _subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger<SubtitleResolver>(), localization, mediaEncoder, fileSystem, namingOptions); _videoProber = new FFProbeVideoInfo( @@ -78,14 +96,15 @@ namespace MediaBrowser.Providers.MediaInfo libraryManager, _audioResolver, _subtitleResolver); - _audioProber = new FFProbeAudioInfo(mediaSourceManager, mediaEncoder, itemRepo, libraryManager); } - public string Name => "ffprobe"; + /// <inheritdoc /> + public string Name => "Probe Provider"; - // Run last + /// <inheritdoc /> public int Order => 100; + /// <inheritdoc /> public bool HasChanged(BaseItem item, IDirectoryService directoryService) { var video = item as Video; @@ -127,41 +146,56 @@ namespace MediaBrowser.Providers.MediaInfo return false; } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchAudioInfo(item, options, cancellationToken); } + /// <inheritdoc /> public Task<ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchAudioInfo(item, options, cancellationToken); } + /// <summary> + /// Fetches video information for an item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param> + /// <typeparam name="T">The type of item to resolve.</typeparam> + /// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/> for an item.</returns> public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video { @@ -208,6 +242,14 @@ namespace MediaBrowser.Providers.MediaInfo .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith('#')); } + /// <summary> + /// Fetches audio information for an item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param> + /// <typeparam name="T">The type of item to resolve.</typeparam> + /// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/> for an item.</returns> public Task<ItemUpdateType> FetchAudioInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Audio { |
