aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Providers
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Providers')
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Chapters/ChapterManager.cs26
-rw-r--r--MediaBrowser.Providers/Lyric/LyricScheduledTask.cs2
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs91
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs34
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj5
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioFileProber.cs36
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs23
-rw-r--r--MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/ProbeProvider.cs17
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs6
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs3
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs6
-rw-r--r--MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs2
21 files changed, 157 insertions, 127 deletions
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 32ab7716f..b51ab4c08 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.BoxSets
protected override bool EnableUpdatingPremiereDateFromChildren => true;
/// <inheritdoc />
- protected override IList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item)
+ protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item)
{
return item.GetLinkedChildren();
}
diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs
deleted file mode 100644
index 3cbfe7d4d..000000000
--- a/MediaBrowser.Providers/Chapters/ChapterManager.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Controller.Chapters;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Providers.Chapters
-{
- public class ChapterManager : IChapterManager
- {
- private readonly IItemRepository _itemRepo;
-
- public ChapterManager(IItemRepository itemRepo)
- {
- _itemRepo = itemRepo;
- }
-
- /// <inheritdoc />
- public void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters)
- {
- _itemRepo.SaveChapters(itemId, chapters);
- }
- }
-}
diff --git a/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
index 89d71e172..73912b579 100644
--- a/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
+++ b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
@@ -162,7 +162,7 @@ public class LyricScheduledTask : IScheduledTask
[
new TaskTriggerInfo
{
- Type = TaskTriggerInfo.TriggerInterval,
+ Type = TaskTriggerInfoType.IntervalTrigger,
IntervalTicks = TimeSpan.FromHours(24).Ticks
}
];
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 36a7c2fab..64954818a 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -229,9 +229,7 @@ namespace MediaBrowser.Providers.Manager
{
var mimeType = MimeTypes.GetMimeType(response.Path);
- var stream = AsyncFile.OpenRead(response.Path);
-
- await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
+ await _providerManager.SaveImage(item, response.Path, mimeType, imageType, null, null, cancellationToken).ConfigureAwait(false);
}
}
@@ -387,8 +385,8 @@ namespace MediaBrowser.Providers.Manager
item.RemoveImages(images);
- // Cleanup old metadata directory for episodes if empty
- if (item is Episode)
+ // Cleanup old metadata directory for episodes if empty, as long as it's not a virtual item
+ if (item is Episode && !item.IsVirtualItem)
{
var oldLocalMetadataDirectory = Path.Combine(item.ContainingFolderPath, "metadata");
if (_fileSystem.DirectoryExists(oldLocalMetadataDirectory) && !_fileSystem.GetFiles(oldLocalMetadataDirectory).Any())
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 7203bf115..778fbc712 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -74,10 +74,11 @@ namespace MediaBrowser.Providers.Manager
public virtual async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
var itemOfType = (TItemType)item;
-
var updateType = ItemUpdateType.None;
-
var libraryOptions = LibraryManager.GetLibraryOptions(item);
+ var isFirstRefresh = item.DateLastRefreshed == default;
+ var hasRefreshedMetadata = true;
+ var hasRefreshedImages = true;
var requiresRefresh = libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays;
@@ -131,9 +132,10 @@ namespace MediaBrowser.Providers.Manager
People = LibraryManager.GetPeople(item)
};
- bool hasRefreshedMetadata = true;
- bool hasRefreshedImages = true;
- var isFirstRefresh = item.DateLastRefreshed == default;
+ var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType);
+ updateType |= beforeSaveResult;
+
+ updateType = await SaveInternal(item, refreshOptions, updateType, isFirstRefresh, requiresRefresh, metadataResult, cancellationToken).ConfigureAwait(false);
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
@@ -188,43 +190,43 @@ namespace MediaBrowser.Providers.Manager
}
}
- var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType);
- updateType |= beforeSaveResult;
+ if (hasRefreshedMetadata && hasRefreshedImages)
+ {
+ item.DateLastRefreshed = DateTime.UtcNow;
+ }
+
+ updateType = await SaveInternal(item, refreshOptions, updateType, isFirstRefresh, requiresRefresh, metadataResult, cancellationToken).ConfigureAwait(false);
+
+ await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
- // Save if changes were made, or it's never been saved before
- if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh)
+ return updateType;
+
+ async Task<ItemUpdateType> SaveInternal(BaseItem item, MetadataRefreshOptions refreshOptions, ItemUpdateType updateType, bool isFirstRefresh, bool requiresRefresh, MetadataResult<TItemType> metadataResult, CancellationToken cancellationToken)
{
- if (item.IsFileProtocol)
+ // Save if changes were made, or it's never been saved before
+ if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh)
{
- var file = TryGetFile(item.Path, refreshOptions.DirectoryService);
- if (file is not null)
+ if (item.IsFileProtocol)
{
- item.DateModified = file.LastWriteTimeUtc;
+ var file = TryGetFile(item.Path, refreshOptions.DirectoryService);
+ if (file is not null)
+ {
+ item.DateModified = file.LastWriteTimeUtc;
+ }
}
- }
- // If any of these properties are set then make sure the updateType is not None, just to force everything to save
- if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
- {
- updateType |= ItemUpdateType.MetadataDownload;
- }
+ // If any of these properties are set then make sure the updateType is not None, just to force everything to save
+ if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
+ {
+ updateType |= ItemUpdateType.MetadataDownload;
+ }
- if (hasRefreshedMetadata && hasRefreshedImages)
- {
- item.DateLastRefreshed = DateTime.UtcNow;
- }
- else
- {
- item.DateLastRefreshed = default;
+ // Save to database
+ await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false);
}
- // Save to database
- await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false);
+ return updateType;
}
-
- await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
-
- return updateType;
}
private void ApplySearchResult(ItemLookupInfo lookupInfo, RemoteSearchResult result)
@@ -322,17 +324,17 @@ namespace MediaBrowser.Providers.Manager
return false;
}
- protected virtual IList<BaseItem> GetChildrenForMetadataUpdates(TItemType item)
+ protected virtual IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(TItemType item)
{
if (item is Folder folder)
{
return folder.GetRecursiveChildren();
}
- return Array.Empty<BaseItem>();
+ return [];
}
- protected virtual ItemUpdateType UpdateMetadataFromChildren(TItemType item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected virtual ItemUpdateType UpdateMetadataFromChildren(TItemType item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
{
var updateType = ItemUpdateType.None;
@@ -371,7 +373,7 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IList<BaseItem> children)
+ private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IReadOnlyList<BaseItem> children)
{
if (item is Folder folder && folder.SupportsCumulativeRunTimeTicks)
{
@@ -395,7 +397,7 @@ namespace MediaBrowser.Providers.Manager
return ItemUpdateType.None;
}
- private ItemUpdateType UpdateDateLastMediaAdded(TItemType item, IList<BaseItem> children)
+ private ItemUpdateType UpdateDateLastMediaAdded(TItemType item, IReadOnlyList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
@@ -429,7 +431,7 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
+ private ItemUpdateType UpdatePremiereDate(TItemType item, IReadOnlyList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
@@ -467,7 +469,7 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- private ItemUpdateType UpdateGenres(TItemType item, IList<BaseItem> children)
+ private ItemUpdateType UpdateGenres(TItemType item, IReadOnlyList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
@@ -488,7 +490,7 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- private ItemUpdateType UpdateStudios(TItemType item, IList<BaseItem> children)
+ private ItemUpdateType UpdateStudios(TItemType item, IReadOnlyList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
@@ -509,7 +511,7 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
- private ItemUpdateType UpdateOfficialRating(TItemType item, IList<BaseItem> children)
+ private ItemUpdateType UpdateOfficialRating(TItemType item, IReadOnlyList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
@@ -1142,13 +1144,8 @@ namespace MediaBrowser.Providers.Manager
}
}
- private static void MergePeople(List<PersonInfo> source, List<PersonInfo> target)
+ private static void MergePeople(IReadOnlyList<PersonInfo> source, IReadOnlyList<PersonInfo> target)
{
- if (target is null)
- {
- target = new List<PersonInfo>();
- }
-
foreach (var person in target)
{
var normalizedName = person.Name.RemoveDiacritics();
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index 81a9af68b..6813cfa91 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
+using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using AsyncKeyedLock;
@@ -47,7 +48,7 @@ namespace MediaBrowser.Providers.Manager
/// </summary>
public class ProviderManager : IProviderManager, IDisposable
{
- private readonly object _refreshQueueLock = new();
+ private readonly Lock _refreshQueueLock = new();
private readonly ILogger<ProviderManager> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryMonitor _libraryMonitor;
@@ -199,12 +200,20 @@ namespace MediaBrowser.Providers.Manager
// TODO: Isolate this hack into the tvh plugin
if (string.IsNullOrEmpty(contentType))
{
+ // Special case for imagecache
if (url.Contains("/imagecache/", StringComparison.OrdinalIgnoreCase))
{
contentType = MediaTypeNames.Image.Png;
}
else
{
+ // Deduce content type from file extension
+ contentType = MimeTypes.GetMimeType(new Uri(url).GetLeftPart(UriPartial.Path));
+ }
+
+ // Throw if we still can't determine the content type
+ if (string.IsNullOrEmpty(contentType))
+ {
throw new HttpRequestException("Invalid image received: contentType not set.", null, response.StatusCode);
}
}
@@ -251,15 +260,31 @@ namespace MediaBrowser.Providers.Manager
}
/// <inheritdoc/>
- public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
+ public async Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(source))
{
throw new ArgumentNullException(nameof(source));
}
- var fileStream = AsyncFile.OpenRead(source);
- return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
+ try
+ {
+ var fileStream = AsyncFile.OpenRead(source);
+ await new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger)
+ .SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(source);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Source file {Source} not found or in use, skip removing", source);
+ }
+ }
}
/// <inheritdoc/>
@@ -1016,7 +1041,6 @@ namespace MediaBrowser.Providers.Manager
/// <inheritdoc/>
public void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority)
{
- ArgumentNullException.ThrowIfNull(itemId);
if (itemId.IsEmpty())
{
throw new ArgumentException("Guid can't be empty", nameof(itemId));
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 9a65852f0..34b3104b0 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -23,15 +23,14 @@
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="PlaylistsNET" />
- <PackageReference Include="z440.atl.core"/>
+ <PackageReference Include="z440.atl.core" />
<PackageReference Include="TMDbLib" />
</ItemGroup>
<PropertyGroup>
- <TargetFramework>net8.0</TargetFramework>
+ <TargetFramework>net9.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
- <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<!-- Code Analyzers -->
diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
index 27f6d120f..b4e3a860e 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
@@ -36,6 +36,7 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IMediaSourceManager _mediaSourceManager;
private readonly LyricResolver _lyricResolver;
private readonly ILyricManager _lyricManager;
+ private readonly IMediaStreamRepository _mediaStreamRepository;
/// <summary>
/// Initializes a new instance of the <see cref="AudioFileProber"/> class.
@@ -47,6 +48,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="lyricResolver">Instance of the <see cref="LyricResolver"/> interface.</param>
/// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
+ /// <param name="mediaStreamRepository">Instance of the <see cref="IMediaStreamRepository"/>.</param>
public AudioFileProber(
ILogger<AudioFileProber> logger,
IMediaSourceManager mediaSourceManager,
@@ -54,7 +56,8 @@ namespace MediaBrowser.Providers.MediaInfo
IItemRepository itemRepo,
ILibraryManager libraryManager,
LyricResolver lyricResolver,
- ILyricManager lyricManager)
+ ILyricManager lyricManager,
+ IMediaStreamRepository mediaStreamRepository)
{
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
@@ -63,6 +66,7 @@ namespace MediaBrowser.Providers.MediaInfo
_mediaSourceManager = mediaSourceManager;
_lyricResolver = lyricResolver;
_lyricManager = lyricManager;
+ _mediaStreamRepository = mediaStreamRepository;
ATL.Settings.DisplayValueSeparator = InternalValueSeparator;
ATL.Settings.UseFileNameWhenNoTitle = false;
ATL.Settings.ID3v2_separatev2v3Values = false;
@@ -149,7 +153,7 @@ namespace MediaBrowser.Providers.MediaInfo
audio.HasLyrics = mediaStreams.Any(s => s.Type == MediaStreamType.Lyric);
- _itemRepo.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken);
+ _mediaStreamRepository.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken);
}
/// <summary>
@@ -347,7 +351,8 @@ namespace MediaBrowser.Providers.MediaInfo
|| track.AdditionalFields.TryGetValue("MusicBrainz Artist Id", out musicBrainzArtistTag))
&& !string.IsNullOrEmpty(musicBrainzArtistTag))
{
- audio.TrySetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzArtistTag);
+ var id = GetFirstMusicBrainzId(musicBrainzArtistTag, libraryOptions.UseCustomTagDelimiters, libraryOptions.GetCustomTagDelimiters(), libraryOptions.DelimiterWhitelist);
+ audio.TrySetProviderId(MetadataProvider.MusicBrainzArtist, id);
}
}
@@ -357,7 +362,8 @@ namespace MediaBrowser.Providers.MediaInfo
|| track.AdditionalFields.TryGetValue("MusicBrainz Album Artist Id", out musicBrainzReleaseArtistIdTag))
&& !string.IsNullOrEmpty(musicBrainzReleaseArtistIdTag))
{
- audio.TrySetProviderId(MetadataProvider.MusicBrainzAlbumArtist, musicBrainzReleaseArtistIdTag);
+ var id = GetFirstMusicBrainzId(musicBrainzReleaseArtistIdTag, libraryOptions.UseCustomTagDelimiters, libraryOptions.GetCustomTagDelimiters(), libraryOptions.DelimiterWhitelist);
+ audio.TrySetProviderId(MetadataProvider.MusicBrainzAlbumArtist, id);
}
}
@@ -367,7 +373,8 @@ namespace MediaBrowser.Providers.MediaInfo
|| track.AdditionalFields.TryGetValue("MusicBrainz Album Id", out musicBrainzReleaseIdTag))
&& !string.IsNullOrEmpty(musicBrainzReleaseIdTag))
{
- audio.TrySetProviderId(MetadataProvider.MusicBrainzAlbum, musicBrainzReleaseIdTag);
+ var id = GetFirstMusicBrainzId(musicBrainzReleaseIdTag, libraryOptions.UseCustomTagDelimiters, libraryOptions.GetCustomTagDelimiters(), libraryOptions.DelimiterWhitelist);
+ audio.TrySetProviderId(MetadataProvider.MusicBrainzAlbum, id);
}
}
@@ -377,7 +384,8 @@ namespace MediaBrowser.Providers.MediaInfo
|| track.AdditionalFields.TryGetValue("MusicBrainz Release Group Id", out musicBrainzReleaseGroupIdTag))
&& !string.IsNullOrEmpty(musicBrainzReleaseGroupIdTag))
{
- audio.TrySetProviderId(MetadataProvider.MusicBrainzReleaseGroup, musicBrainzReleaseGroupIdTag);
+ var id = GetFirstMusicBrainzId(musicBrainzReleaseGroupIdTag, libraryOptions.UseCustomTagDelimiters, libraryOptions.GetCustomTagDelimiters(), libraryOptions.DelimiterWhitelist);
+ audio.TrySetProviderId(MetadataProvider.MusicBrainzReleaseGroup, id);
}
}
@@ -387,7 +395,8 @@ namespace MediaBrowser.Providers.MediaInfo
|| track.AdditionalFields.TryGetValue("MusicBrainz Release Track Id", out trackMbId))
&& !string.IsNullOrEmpty(trackMbId))
{
- audio.TrySetProviderId(MetadataProvider.MusicBrainzTrack, trackMbId);
+ var id = GetFirstMusicBrainzId(trackMbId, libraryOptions.UseCustomTagDelimiters, libraryOptions.GetCustomTagDelimiters(), libraryOptions.DelimiterWhitelist);
+ audio.TrySetProviderId(MetadataProvider.MusicBrainzTrack, id);
}
}
@@ -441,5 +450,18 @@ namespace MediaBrowser.Providers.MediaInfo
return items;
}
+
+ // MusicBrainz IDs are multi-value tags, so we need to split them
+ // However, our current provider can only have one single ID, which means we need to pick the first one
+ private string? GetFirstMusicBrainzId(string tag, bool useCustomTagDelimiters, char[] tagDelimiters, string[] whitelist)
+ {
+ var val = tag.Split(InternalValueSeparator).FirstOrDefault();
+ if (val is not null && useCustomTagDelimiters)
+ {
+ val = SplitWithCustomDelimiter(val, tagDelimiters, whitelist).FirstOrDefault();
+ }
+
+ return val;
+ }
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index d1c0ddb37..cc2b3face 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -1,4 +1,4 @@
-#nullable disable
+#pragma warning disable CA1826 // CA1826 Do not use Enumerable methods on Indexable collections.
using System;
using System.Collections.Generic;
@@ -74,18 +74,17 @@ namespace MediaBrowser.Providers.MediaInfo
return GetImage((Audio)item, imageStreams, cancellationToken);
}
- private async Task<DynamicImageResponse> GetImage(Audio item, List<MediaStream> imageStreams, CancellationToken cancellationToken)
+ private async Task<DynamicImageResponse> GetImage(Audio item, IReadOnlyList<MediaStream> imageStreams, CancellationToken cancellationToken)
{
var path = GetAudioImagePath(item);
if (!File.Exists(path))
{
- Directory.CreateDirectory(Path.GetDirectoryName(path));
-
+ var directoryName = Path.GetDirectoryName(path) ?? throw new InvalidOperationException($"Invalid path '{path}'");
+ Directory.CreateDirectory(directoryName);
var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).Contains("front", StringComparison.OrdinalIgnoreCase)) ??
imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).Contains("cover", StringComparison.OrdinalIgnoreCase)) ??
imageStreams.FirstOrDefault();
-
var imageStreamIndex = imageStream?.Index;
var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 246ba2733..16f3175d2 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -31,6 +31,7 @@ namespace MediaBrowser.Providers.MediaInfo
public class FFProbeVideoInfo
{
private readonly ILogger<FFProbeVideoInfo> _logger;
+ private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer;
@@ -38,11 +39,12 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IEncodingManager _encodingManager;
private readonly IServerConfigurationManager _config;
private readonly ISubtitleManager _subtitleManager;
- private readonly IChapterManager _chapterManager;
+ private readonly IChapterRepository _chapterManager;
private readonly ILibraryManager _libraryManager;
private readonly AudioResolver _audioResolver;
private readonly SubtitleResolver _subtitleResolver;
- private readonly IMediaSourceManager _mediaSourceManager;
+ private readonly IMediaAttachmentRepository _mediaAttachmentRepository;
+ private readonly IMediaStreamRepository _mediaStreamRepository;
public FFProbeVideoInfo(
ILogger<FFProbeVideoInfo> logger,
@@ -54,10 +56,12 @@ namespace MediaBrowser.Providers.MediaInfo
IEncodingManager encodingManager,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
- IChapterManager chapterManager,
+ IChapterRepository chapterManager,
ILibraryManager libraryManager,
AudioResolver audioResolver,
- SubtitleResolver subtitleResolver)
+ SubtitleResolver subtitleResolver,
+ IMediaAttachmentRepository mediaAttachmentRepository,
+ IMediaStreamRepository mediaStreamRepository)
{
_logger = logger;
_mediaSourceManager = mediaSourceManager;
@@ -72,6 +76,9 @@ namespace MediaBrowser.Providers.MediaInfo
_libraryManager = libraryManager;
_audioResolver = audioResolver;
_subtitleResolver = subtitleResolver;
+ _mediaAttachmentRepository = mediaAttachmentRepository;
+ _mediaStreamRepository = mediaStreamRepository;
+ _mediaStreamRepository = mediaStreamRepository;
}
public async Task<ItemUpdateType> ProbeVideo<T>(
@@ -267,11 +274,11 @@ namespace MediaBrowser.Providers.MediaInfo
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
- _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
+ _mediaStreamRepository.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
if (mediaAttachments.Any())
{
- _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
+ _mediaAttachmentRepository.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
}
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh
@@ -354,7 +361,7 @@ namespace MediaBrowser.Providers.MediaInfo
blurayVideoStream.Codec = ffmpegVideoStream.Codec;
blurayVideoStream.BitRate = blurayVideoStream.BitRate.GetValueOrDefault() == 0 ? ffmpegVideoStream.BitRate : blurayVideoStream.BitRate;
blurayVideoStream.Width = blurayVideoStream.Width.GetValueOrDefault() == 0 ? ffmpegVideoStream.Width : blurayVideoStream.Width;
- blurayVideoStream.Height = blurayVideoStream.Height.GetValueOrDefault() == 0 ? ffmpegVideoStream.Width : blurayVideoStream.Height;
+ blurayVideoStream.Height = blurayVideoStream.Height.GetValueOrDefault() == 0 ? ffmpegVideoStream.Height : blurayVideoStream.Height;
blurayVideoStream.ColorRange = ffmpegVideoStream.ColorRange;
blurayVideoStream.ColorSpace = ffmpegVideoStream.ColorSpace;
blurayVideoStream.ColorTransfer = ffmpegVideoStream.ColorTransfer;
@@ -627,7 +634,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var runtime = video.RunTimeTicks.GetValueOrDefault();
- // Only process files with a runtime higher than 0 and lower than 12h. The latter are likely corrupted.
+ // Only process files with a runtime greater than 0 and less than 12h. The latter are likely corrupted.
if (runtime < 0 || runtime > TimeSpan.FromHours(12).Ticks)
{
throw new ArgumentException(
diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs
index fbec4e963..f12390bc2 100644
--- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs
@@ -121,7 +121,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;
+ mediaStream.IsHearingImpaired = pathInfo.IsHearingImpaired || mediaStream.IsHearingImpaired.GetValueOrDefault();
mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
}
diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
index 04da8fb88..1c2f8b913 100644
--- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
@@ -61,12 +61,14 @@ namespace MediaBrowser.Providers.MediaInfo
/// <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="chapterManager">Instance of the <see cref="IChapterRepository"/> 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>
/// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
+ /// <param name="mediaAttachmentRepository">Instance of the <see cref="IMediaAttachmentRepository"/> interface.</param>
+ /// <param name="mediaStreamRepository">Instance of the <see cref="IMediaStreamRepository"/> interface.</param>
public ProbeProvider(
IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder,
@@ -76,12 +78,14 @@ namespace MediaBrowser.Providers.MediaInfo
IEncodingManager encodingManager,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
- IChapterManager chapterManager,
+ IChapterRepository chapterManager,
ILibraryManager libraryManager,
IFileSystem fileSystem,
ILoggerFactory loggerFactory,
NamingOptions namingOptions,
- ILyricManager lyricManager)
+ ILyricManager lyricManager,
+ IMediaAttachmentRepository mediaAttachmentRepository,
+ IMediaStreamRepository mediaStreamRepository)
{
_logger = loggerFactory.CreateLogger<ProbeProvider>();
_audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
@@ -101,7 +105,9 @@ namespace MediaBrowser.Providers.MediaInfo
chapterManager,
libraryManager,
_audioResolver,
- _subtitleResolver);
+ _subtitleResolver,
+ mediaAttachmentRepository,
+ mediaStreamRepository);
_audioProber = new AudioFileProber(
loggerFactory.CreateLogger<AudioFileProber>(),
@@ -110,7 +116,8 @@ namespace MediaBrowser.Providers.MediaInfo
itemRepo,
libraryManager,
_lyricResolver,
- lyricManager);
+ lyricManager,
+ mediaStreamRepository);
}
/// <inheritdoc />
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
index 20fb4dab9..227f31025 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.MediaInfo
public async Task<List<string>> DownloadSubtitles(
Video video,
- List<MediaStream> mediaStreams,
+ IReadOnlyList<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,
bool requirePerfectMatch,
@@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.MediaInfo
public Task<bool> DownloadSubtitles(
Video video,
- List<MediaStream> mediaStreams,
+ IReadOnlyList<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,
bool requirePerfectMatch,
@@ -120,7 +120,7 @@ namespace MediaBrowser.Providers.MediaInfo
private async Task<bool> DownloadSubtitles(
Video video,
- List<MediaStream> mediaStreams,
+ IReadOnlyList<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,
bool requirePerfectMatch,
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index 6eb75891a..938f3cb32 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -217,7 +217,7 @@ namespace MediaBrowser.Providers.MediaInfo
return new[]
{
// Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
+ new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index ba7ad4072..3d446053b 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CA1826 // CA1826 Do not use Enumerable methods on Indexable collections.
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index a39bd16ce..25698d8cb 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -47,11 +47,11 @@ namespace MediaBrowser.Providers.Music
protected override bool EnableUpdatingStudiosFromChildren => true;
/// <inheritdoc />
- protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
+ protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
=> item.GetRecursiveChildren(i => i is Audio);
/// <inheritdoc />
- protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
{
var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index 1f342c0db..c47f9a500 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System.Collections.Generic;
+using System.Collections.Immutable;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -28,7 +29,7 @@ namespace MediaBrowser.Providers.Music
protected override bool EnableUpdatingGenresFromChildren => true;
/// <inheritdoc />
- protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item)
+ protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item)
{
return item.IsAccessedByName
? item.GetTaggedItems(new InternalItemsQuery
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index 43889bfbf..7be54453f 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Playlists
protected override bool EnableUpdatingStudiosFromChildren => true;
/// <inheritdoc />
- protected override IList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
+ protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
=> item.GetLinkedChildren();
/// <inheritdoc />
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index 8d68e2dcf..eef08b251 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -198,7 +198,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
};
movie.SetProviderId(MetadataProvider.Tmdb, tmdbId);
- movie.SetProviderId(MetadataProvider.Imdb, movieResult.ImdbId);
+ movie.TrySetProviderId(MetadataProvider.Imdb, movieResult.ImdbId);
if (movieResult.BelongsToCollection is not null)
{
movie.SetProviderId(MetadataProvider.TmdbCollection, movieResult.BelongsToCollection.Id.ToString(CultureInfo.InvariantCulture));
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index 8b690193e..b27ccaa6a 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -80,11 +80,11 @@ namespace MediaBrowser.Providers.TV
}
/// <inheritdoc />
- protected override IList<BaseItem> GetChildrenForMetadataUpdates(Season item)
+ protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(Season item)
=> item.GetEpisodes();
/// <inheritdoc />
- protected override ItemUpdateType UpdateMetadataFromChildren(Season item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ protected override ItemUpdateType UpdateMetadataFromChildren(Season item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
{
var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
@@ -96,7 +96,7 @@ namespace MediaBrowser.Providers.TV
return updateType;
}
- private ItemUpdateType SaveIsVirtualItem(Season item, IList<BaseItem> episodes)
+ private ItemUpdateType SaveIsVirtualItem(Season item, IReadOnlyList<BaseItem> episodes)
{
var isVirtualItem = item.LocationType == LocationType.Virtual && (episodes.Count == 0 || episodes.All(i => i.LocationType == LocationType.Virtual));
diff --git a/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs b/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs
index 31c0eeb31..4310f93d4 100644
--- a/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs
+++ b/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs
@@ -63,7 +63,7 @@ public class TrickplayImagesTask : IScheduledTask
{
new TaskTriggerInfo
{
- Type = TaskTriggerInfo.TriggerDaily,
+ Type = TaskTriggerInfoType.DailyTrigger,
TimeOfDayTicks = TimeSpan.FromHours(3).Ticks
}
};