aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci-codeql-analysis.yml6
-rw-r--r--.github/workflows/ci-openapi.yml4
-rw-r--r--Directory.Packages.props4
-rw-r--r--Jellyfin.Api/Controllers/LyricsController.cs12
-rw-r--r--MediaBrowser.Controller/Lyrics/ILyricManager.cs17
-rw-r--r--MediaBrowser.Providers/Lyric/LyricManager.cs51
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioFileProber.cs103
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs27
-rw-r--r--MediaBrowser.Providers/MediaInfo/ProbeProvider.cs8
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs14
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs16
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs16
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs13
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs45
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json2
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-srt-2600k.json2
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aac-srt-15200k.json2
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aacDef-srt-15200k.json89
18 files changed, 310 insertions, 121 deletions
diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml
index 6e2da9737..20307dd7d 100644
--- a/.github/workflows/ci-codeql-analysis.yml
+++ b/.github/workflows/ci-codeql-analysis.yml
@@ -27,11 +27,11 @@ jobs:
dotnet-version: '8.0.x'
- name: Initialize CodeQL
- uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
+ uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
- uses: github/codeql-action/autobuild@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
+ uses: github/codeql-action/autobuild@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
+ uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml
index c56349941..b5ccafb86 100644
--- a/.github/workflows/ci-openapi.yml
+++ b/.github/workflows/ci-openapi.yml
@@ -78,12 +78,12 @@ jobs:
- openapi-base
steps:
- name: Download openapi-head
- uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3
+ uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
name: openapi-head
path: openapi-head
- name: Download openapi-base
- uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3
+ uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
name: openapi-base
path: openapi-base
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1d7ebfaf4..7400edd93 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
<PackageVersion Include="Diacritics" Version="3.3.27" />
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
<PackageVersion Include="DotNet.Glob" Version="3.1.3" />
- <PackageVersion Include="EFCoreSecondLevelCacheInterceptor" Version="4.2.2" />
+ <PackageVersion Include="EFCoreSecondLevelCacheInterceptor" Version="4.2.3" />
<PackageVersion Include="FsCheck.Xunit" Version="2.16.6" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.1" />
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.7" />
@@ -72,7 +72,7 @@
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
- <PackageVersion Include="Svg.Skia" Version="1.0.0.13" />
+ <PackageVersion Include="Svg.Skia" Version="1.0.0.14" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageVersion Include="System.Globalization" Version="4.3.0" />
diff --git a/Jellyfin.Api/Controllers/LyricsController.cs b/Jellyfin.Api/Controllers/LyricsController.cs
index 4fccf2cb4..f2b312b47 100644
--- a/Jellyfin.Api/Controllers/LyricsController.cs
+++ b/Jellyfin.Api/Controllers/LyricsController.cs
@@ -146,13 +146,11 @@ public class LyricsController : BaseJellyfinApiController
await using (stream.ConfigureAwait(false))
{
await Request.Body.CopyToAsync(stream).ConfigureAwait(false);
- var uploadedLyric = await _lyricManager.UploadLyricAsync(
- audio,
- new LyricResponse
- {
- Format = format,
- Stream = stream
- }).ConfigureAwait(false);
+ var uploadedLyric = await _lyricManager.SaveLyricAsync(
+ audio,
+ format,
+ stream)
+ .ConfigureAwait(false);
if (uploadedLyric is null)
{
diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs
index f4376a1ee..1e71b87eb 100644
--- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs
+++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
@@ -69,12 +70,22 @@ public interface ILyricManager
CancellationToken cancellationToken);
/// <summary>
- /// Upload new lyrics.
+ /// Saves new lyrics.
/// </summary>
/// <param name="audio">The audio file the lyrics belong to.</param>
- /// <param name="lyricResponse">The lyric response.</param>
+ /// <param name="format">The lyrics format.</param>
+ /// <param name="lyrics">The lyrics.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
- Task<LyricDto?> UploadLyricAsync(Audio audio, LyricResponse lyricResponse);
+ Task<LyricDto?> SaveLyricAsync(Audio audio, string format, string lyrics);
+
+ /// <summary>
+ /// Saves new lyrics.
+ /// </summary>
+ /// <param name="audio">The audio file the lyrics belong to.</param>
+ /// <param name="format">The lyrics format.</param>
+ /// <param name="lyrics">The lyrics.</param>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ Task<LyricDto?> SaveLyricAsync(Audio audio, string format, Stream lyrics);
/// <summary>
/// Get the remote lyrics.
diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs
index 60734b89a..f4b18a8c1 100644
--- a/MediaBrowser.Providers/Lyric/LyricManager.cs
+++ b/MediaBrowser.Providers/Lyric/LyricManager.cs
@@ -155,13 +155,13 @@ public class LyricManager : ILyricManager
return null;
}
- var parsedLyrics = await InternalParseRemoteLyricsAsync(response, cancellationToken).ConfigureAwait(false);
+ var parsedLyrics = await InternalParseRemoteLyricsAsync(response.Format, response.Stream, cancellationToken).ConfigureAwait(false);
if (parsedLyrics is null)
{
return null;
}
- await TrySaveLyric(audio, libraryOptions, response).ConfigureAwait(false);
+ await TrySaveLyric(audio, libraryOptions, response.Format, response.Stream).ConfigureAwait(false);
return parsedLyrics;
}
catch (RateLimitExceededException)
@@ -182,19 +182,33 @@ public class LyricManager : ILyricManager
}
/// <inheritdoc />
- public async Task<LyricDto?> UploadLyricAsync(Audio audio, LyricResponse lyricResponse)
+ public async Task<LyricDto?> SaveLyricAsync(Audio audio, string format, string lyrics)
{
ArgumentNullException.ThrowIfNull(audio);
- ArgumentNullException.ThrowIfNull(lyricResponse);
+ ArgumentException.ThrowIfNullOrEmpty(format);
+ ArgumentException.ThrowIfNullOrEmpty(lyrics);
+
+ var bytes = Encoding.UTF8.GetBytes(lyrics);
+ using var lyricStream = new MemoryStream(bytes, 0, bytes.Length, false, true);
+ return await SaveLyricAsync(audio, format, lyricStream).ConfigureAwait(false);
+ }
+
+ /// <inheritdoc />
+ public async Task<LyricDto?> SaveLyricAsync(Audio audio, string format, Stream lyrics)
+ {
+ ArgumentNullException.ThrowIfNull(audio);
+ ArgumentException.ThrowIfNullOrEmpty(format);
+ ArgumentNullException.ThrowIfNull(lyrics);
+
var libraryOptions = BaseItem.LibraryManager.GetLibraryOptions(audio);
- var parsed = await InternalParseRemoteLyricsAsync(lyricResponse, CancellationToken.None).ConfigureAwait(false);
+ var parsed = await InternalParseRemoteLyricsAsync(format, lyrics, CancellationToken.None).ConfigureAwait(false);
if (parsed is null)
{
return null;
}
- await TrySaveLyric(audio, libraryOptions, lyricResponse).ConfigureAwait(false);
+ await TrySaveLyric(audio, libraryOptions, format, lyrics).ConfigureAwait(false);
return parsed;
}
@@ -209,7 +223,7 @@ public class LyricManager : ILyricManager
return null;
}
- return await InternalParseRemoteLyricsAsync(lyricResponse, cancellationToken).ConfigureAwait(false);
+ return await InternalParseRemoteLyricsAsync(lyricResponse.Format, lyricResponse.Stream, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
@@ -289,12 +303,12 @@ public class LyricManager : ILyricManager
private string GetProviderId(string name)
=> name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
- private async Task<LyricDto?> InternalParseRemoteLyricsAsync(LyricResponse lyricResponse, CancellationToken cancellationToken)
+ private async Task<LyricDto?> InternalParseRemoteLyricsAsync(string format, Stream lyricStream, CancellationToken cancellationToken)
{
- lyricResponse.Stream.Seek(0, SeekOrigin.Begin);
- using var streamReader = new StreamReader(lyricResponse.Stream, leaveOpen: true);
+ lyricStream.Seek(0, SeekOrigin.Begin);
+ using var streamReader = new StreamReader(lyricStream, leaveOpen: true);
var lyrics = await streamReader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
- var lyricFile = new LyricFile($"lyric.{lyricResponse.Format}", lyrics);
+ var lyricFile = new LyricFile($"lyric.{format}", lyrics);
foreach (var parser in _lyricParsers)
{
var parsedLyrics = parser.ParseLyrics(lyricFile);
@@ -334,7 +348,7 @@ public class LyricManager : ILyricManager
var parsedResults = new List<RemoteLyricInfoDto>();
foreach (var result in searchResults)
{
- var parsedLyrics = await InternalParseRemoteLyricsAsync(result.Lyrics, cancellationToken).ConfigureAwait(false);
+ var parsedLyrics = await InternalParseRemoteLyricsAsync(result.Lyrics.Format, result.Lyrics.Stream, cancellationToken).ConfigureAwait(false);
if (parsedLyrics is null)
{
continue;
@@ -361,24 +375,23 @@ public class LyricManager : ILyricManager
private async Task TrySaveLyric(
Audio audio,
LibraryOptions libraryOptions,
- LyricResponse lyricResponse)
+ string format,
+ Stream lyricStream)
{
var saveInMediaFolder = libraryOptions.SaveLyricsWithMedia;
var memoryStream = new MemoryStream();
await using (memoryStream.ConfigureAwait(false))
{
- var stream = lyricResponse.Stream;
-
- await using (stream.ConfigureAwait(false))
+ await using (lyricStream.ConfigureAwait(false))
{
- stream.Seek(0, SeekOrigin.Begin);
- await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
+ lyricStream.Seek(0, SeekOrigin.Begin);
+ await lyricStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Seek(0, SeekOrigin.Begin);
}
var savePaths = new List<string>();
- var saveFileName = Path.GetFileNameWithoutExtension(audio.Path) + "." + lyricResponse.Format.ReplaceLineEndings(string.Empty).ToLowerInvariant();
+ var saveFileName = Path.GetFileNameWithoutExtension(audio.Path) + "." + format.ReplaceLineEndings(string.Empty).ToLowerInvariant();
if (saveInMediaFolder)
{
diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
index fb86e254f..4d4b59b8c 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
@@ -10,6 +10,7 @@ using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
@@ -36,6 +37,7 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILibraryManager _libraryManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly LyricResolver _lyricResolver;
+ private readonly ILyricManager _lyricManager;
/// <summary>
/// Initializes a new instance of the <see cref="AudioFileProber"/> class.
@@ -46,13 +48,15 @@ namespace MediaBrowser.Providers.MediaInfo
/// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
/// <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>
public AudioFileProber(
ILogger<AudioFileProber> logger,
IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
ILibraryManager libraryManager,
- LyricResolver lyricResolver)
+ LyricResolver lyricResolver,
+ ILyricManager lyricManager)
{
_logger = logger;
_mediaEncoder = mediaEncoder;
@@ -60,6 +64,7 @@ namespace MediaBrowser.Providers.MediaInfo
_libraryManager = libraryManager;
_mediaSourceManager = mediaSourceManager;
_lyricResolver = lyricResolver;
+ _lyricManager = lyricManager;
}
[GeneratedRegex(@"I:\s+(.*?)\s+LUFS")]
@@ -107,7 +112,7 @@ namespace MediaBrowser.Providers.MediaInfo
cancellationToken.ThrowIfCancellationRequested();
- Fetch(item, result, options, cancellationToken);
+ await FetchAsync(item, result, options, cancellationToken).ConfigureAwait(false);
}
var libraryOptions = _libraryManager.GetLibraryOptions(item);
@@ -211,7 +216,8 @@ namespace MediaBrowser.Providers.MediaInfo
/// <param name="mediaInfo">The <see cref="Model.MediaInfo.MediaInfo"/>.</param>
/// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
- protected void Fetch(
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ private async Task FetchAsync(
Audio audio,
Model.MediaInfo.MediaInfo mediaInfo,
MetadataRefreshOptions options,
@@ -225,7 +231,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (!audio.IsLocked)
{
- FetchDataFromTags(audio);
+ await FetchDataFromTags(audio, options).ConfigureAwait(false);
}
var mediaStreams = new List<MediaStream>(mediaInfo.MediaStreams);
@@ -240,9 +246,10 @@ namespace MediaBrowser.Providers.MediaInfo
/// Fetches data from the tags.
/// </summary>
/// <param name="audio">The <see cref="Audio"/>.</param>
- private void FetchDataFromTags(Audio audio)
+ /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
+ private async Task FetchDataFromTags(Audio audio, MetadataRefreshOptions options)
{
- var file = TagLib.File.Create(audio.Path);
+ using var file = TagLib.File.Create(audio.Path);
var tagTypes = file.TagTypesOnDisk;
Tag? tags = null;
@@ -319,14 +326,45 @@ namespace MediaBrowser.Providers.MediaInfo
}
_libraryManager.UpdatePeople(audio, people);
- audio.Artists = performers;
- audio.AlbumArtists = albumArtists;
+
+ if (options.ReplaceAllMetadata && performers.Length != 0)
+ {
+ audio.Artists = performers;
+ }
+ else if (!options.ReplaceAllMetadata
+ && (audio.Artists is null || audio.Artists.Count == 0))
+ {
+ audio.Artists = performers;
+ }
+
+ if (options.ReplaceAllMetadata && albumArtists.Length != 0)
+ {
+ audio.AlbumArtists = albumArtists;
+ }
+ else if (!options.ReplaceAllMetadata
+ && (audio.AlbumArtists is null || audio.AlbumArtists.Count == 0))
+ {
+ audio.AlbumArtists = albumArtists;
+ }
+ }
+
+ if (!audio.LockedFields.Contains(MetadataField.Name))
+ {
+ audio.Name = options.ReplaceAllMetadata || string.IsNullOrEmpty(audio.Name) ? tags.Title : audio.Name;
}
- audio.Name = tags.Title;
- audio.Album = tags.Album;
- audio.IndexNumber = Convert.ToInt32(tags.Track);
- audio.ParentIndexNumber = Convert.ToInt32(tags.Disc);
+ if (options.ReplaceAllMetadata)
+ {
+ audio.Album = tags.Album;
+ audio.IndexNumber = Convert.ToInt32(tags.Track);
+ audio.ParentIndexNumber = Convert.ToInt32(tags.Disc);
+ }
+ else
+ {
+ audio.Album ??= tags.Album;
+ audio.IndexNumber ??= Convert.ToInt32(tags.Track);
+ audio.ParentIndexNumber ??= Convert.ToInt32(tags.Disc);
+ }
if (tags.Year != 0)
{
@@ -337,14 +375,43 @@ namespace MediaBrowser.Providers.MediaInfo
if (!audio.LockedFields.Contains(MetadataField.Genres))
{
- audio.Genres = tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
+ audio.Genres = options.ReplaceAllMetadata || audio.Genres == null || audio.Genres.Length == 0
+ ? tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray()
+ : audio.Genres;
}
- 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);
+ if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out _))
+ {
+ audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId);
+ }
+
+ if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbumArtist, out _))
+ {
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId);
+ }
+
+ if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbum, out _))
+ {
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId);
+ }
+
+ if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out _))
+ {
+ audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId);
+ }
+
+ if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzTrack, out _))
+ {
+ audio.SetProviderId(MetadataProvider.MusicBrainzTrack, tags.MusicBrainzTrackId);
+ }
+
+ // Save extracted lyrics if they exist,
+ // and if we are replacing all metadata or the audio doesn't yet have lyrics.
+ if (!string.IsNullOrWhiteSpace(tags.Lyrics)
+ && (options.ReplaceAllMetadata || audio.GetMediaStreams().All(s => s.Type != MediaStreamType.Lyric)))
+ {
+ await _lyricManager.SaveLyricAsync(audio, "lrc", tags.Lyrics).ConfigureAwait(false);
+ }
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 35ea04d21..5d0fccbe1 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -324,20 +324,7 @@ namespace MediaBrowser.Providers.MediaInfo
return;
}
- // Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output
- int? currentHeight = null;
- int? currentWidth = null;
- int? currentBitRate = null;
-
- var videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
-
- // Grab the values that ffprobe recorded
- if (videoStream is not null)
- {
- currentBitRate = videoStream.BitRate;
- currentWidth = videoStream.Width;
- currentHeight = videoStream.Height;
- }
+ var ffmpegVideoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
// Fill video properties from the BDInfo result
mediaStreams.Clear();
@@ -361,14 +348,16 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
+ var blurayVideoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
// Use the ffprobe values if these are empty
- if (videoStream is not null)
+ if (blurayVideoStream is not null && ffmpegVideoStream is not null)
{
- videoStream.BitRate = videoStream.BitRate.GetValueOrDefault() == 0 ? currentBitRate : videoStream.BitRate;
- videoStream.Width = videoStream.Width.GetValueOrDefault() == 0 ? currentWidth : videoStream.Width;
- videoStream.Height = videoStream.Height.GetValueOrDefault() == 0 ? currentHeight : videoStream.Height;
+ // Always use ffmpeg's detected codec since that is what the rest of the codebase expects.
+ 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;
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
index 8bb874f0d..8bb8d5bb4 100644
--- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
@@ -13,6 +13,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
@@ -64,6 +65,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// <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>
public ProbeProvider(
IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder,
@@ -77,7 +79,8 @@ namespace MediaBrowser.Providers.MediaInfo
ILibraryManager libraryManager,
IFileSystem fileSystem,
ILoggerFactory loggerFactory,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ ILyricManager lyricManager)
{
_logger = loggerFactory.CreateLogger<ProbeProvider>();
_audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
@@ -105,7 +108,8 @@ namespace MediaBrowser.Providers.MediaInfo
mediaEncoder,
itemRepo,
libraryManager,
- _lyricResolver);
+ _lyricResolver,
+ lyricManager);
}
/// <inheritdoc />
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
index a4c6cb47d..18cdba7a0 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
@@ -46,14 +46,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
}
/// <inheritdoc />
- public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
- {
- return new ImageType[]
- {
- ImageType.Primary,
- ImageType.Backdrop
- };
- }
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item) =>
+ [
+ ImageType.Primary,
+ ImageType.Backdrop,
+ ImageType.Thumb
+ ];
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
index bfec48e7c..1696a2c49 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
@@ -47,15 +47,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
}
/// <inheritdoc />
- public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
- {
- return new ImageType[]
- {
- ImageType.Primary,
- ImageType.Backdrop,
- ImageType.Logo
- };
- }
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item) =>
+ [
+ ImageType.Primary,
+ ImageType.Backdrop,
+ ImageType.Logo,
+ ImageType.Thumb
+ ];
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
index 192fb052d..2cb4fe1c1 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
@@ -46,15 +46,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
}
/// <inheritdoc />
- public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
- {
- return new ImageType[]
- {
- ImageType.Primary,
- ImageType.Backdrop,
- ImageType.Logo
- };
- }
+ public IEnumerable<ImageType> GetSupportedImages(BaseItem item) =>
+ [
+ ImageType.Primary,
+ ImageType.Backdrop,
+ ImageType.Logo,
+ ImageType.Thumb
+ ];
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index 82f2c54f1..d704a5f49 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -591,6 +591,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
{
var image = images[i];
+ var imageType = type;
+ var language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, requestLanguage);
+
+ // Return Backdrops with a language specified (it has text) as Thumb.
+ if (imageType == ImageType.Backdrop && !string.IsNullOrEmpty(language))
+ {
+ imageType = ImageType.Thumb;
+ }
+
yield return new RemoteImageInfo
{
Url = GetUrl(size, image.FilePath),
@@ -598,9 +607,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
VoteCount = image.VoteCount,
Width = scaleImage ? null : image.Width,
Height = scaleImage ? null : image.Height,
- Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, requestLanguage),
+ Language = language,
ProviderName = TmdbUtils.ProviderName,
- Type = type,
+ Type = imageType,
RatingType = RatingType.Score
};
}
diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
index d9dceee55..909de8f72 100644
--- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
+++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
@@ -21,23 +21,25 @@ namespace Jellyfin.Model.Tests
[Theory]
// Chrome
[InlineData("Chrome", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
- [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
[InlineData("Chrome", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
[InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
- [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")]
+ [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
+ [InlineData("Chrome", "mp4-hevc-ac3-aacDef-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")]
[InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450
[InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported | TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// Firefox
[InlineData("Firefox", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
- [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
[InlineData("Firefox", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
[InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
- [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")]
+ [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
+ [InlineData("Firefox", "mp4-hevc-ac3-aacDef-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")]
[InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450
[InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported | TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
@@ -59,18 +61,20 @@ namespace Jellyfin.Model.Tests
[InlineData("AndroidPixel", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
// Yatse
[InlineData("Yatse", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
- [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Yatse", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
[InlineData("Yatse", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
[InlineData("Yatse", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
- [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Yatse", "mp4-hevc-ac3-aacDef-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
// RokuSSPlus
[InlineData("RokuSSPlus", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
- [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 should be DirectPlay
[InlineData("RokuSSPlus", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("RokuSSPlus", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
- [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-aacDef-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
[InlineData("RokuSSPlus", "mp4-hevc-ac3-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
// JellyfinMediaPlayer
[InlineData("JellyfinMediaPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
@@ -83,21 +87,24 @@ namespace Jellyfin.Model.Tests
[InlineData("JellyfinMediaPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)] // #6450
// Chrome-NoHLS
[InlineData("Chrome-NoHLS", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
- [InlineData("Chrome-NoHLS", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
+ [InlineData("Chrome-NoHLS", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome-NoHLS", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450
[InlineData("Chrome-NoHLS", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
[InlineData("Chrome-NoHLS", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome-NoHLS", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")]
- [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode", "http")]
+ [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode", "http")]
+ [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aacDef-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode", "http")]
[InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450
[InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported | TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// TranscodeMedia
[InlineData("TranscodeMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
- [InlineData("TranscodeMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
+ [InlineData("TranscodeMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "HLS.mp4")]
+ [InlineData("TranscodeMedia", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
[InlineData("TranscodeMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "HLS.mp4")]
[InlineData("TranscodeMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
- [InlineData("TranscodeMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
+ [InlineData("TranscodeMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "HLS.mp4")]
+ [InlineData("TranscodeMedia", "mp4-hevc-ac3-aacDef-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
[InlineData("TranscodeMedia", "mkv-av1-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "http")]
[InlineData("TranscodeMedia", "mkv-av1-vorbis-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "http")]
[InlineData("TranscodeMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "http")]
@@ -459,8 +466,16 @@ namespace Jellyfin.Model.Tests
// Audio stream not specified
else
{
- // TODO: Fixme
- Assert.All(audioStreams, stream =>
+ bool isDefault = targetAudioStream?.IsDefault == true;
+ var language = targetAudioStream?.Language;
+
+ // Collect candidate audio streams
+ var candidateAudioStreams = audioStreams.Where(stream =>
+ {
+ return isDefault ? stream.IsDefault : (stream.Language == language);
+ });
+
+ Assert.All(candidateAudioStreams, stream =>
{
if (!stream.IsExternal)
{
diff --git a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json
index 9d819c4ad..ede1c8d3d 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json
@@ -57,7 +57,7 @@
"BitRate": 164741,
"Channels": 2,
"SampleRate": 48000,
- "IsDefault": true,
+ "IsDefault": false,
"Profile": "LC",
"Index": 2,
"Score": 203
diff --git a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-srt-2600k.json b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-srt-2600k.json
index 70bbb9d0d..7ef224957 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-srt-2600k.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-srt-2600k.json
@@ -57,7 +57,7 @@
"BitRate": 164741,
"Channels": 2,
"SampleRate": 48000,
- "IsDefault": true,
+ "IsDefault": false,
"Profile": "LC",
"Index": 2,
"Score": 203
diff --git a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aac-srt-15200k.json b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aac-srt-15200k.json
index 385bb7260..84ed8b52c 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aac-srt-15200k.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aac-srt-15200k.json
@@ -59,7 +59,7 @@
"BitRate": 164741,
"Channels": 2,
"SampleRate": 48000,
- "IsDefault": true,
+ "IsDefault": false,
"Profile": "LC",
"Index": 2,
"Score": 203
diff --git a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aacDef-srt-15200k.json b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aacDef-srt-15200k.json
new file mode 100644
index 000000000..f653313b2
--- /dev/null
+++ b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-ac3-aacDef-srt-15200k.json
@@ -0,0 +1,89 @@
+{
+ "Id": "f6eab7118618ab26e61e495a1853481a",
+ "Path": "/Media/MyVideo-WEBDL-2160p.mp4",
+ "Container": "mov,mp4,m4a,3gp,3g2,mj2",
+ "Size": 6521110016,
+ "Name": "MyVideo WEBDL-2160p",
+ "ETag": "a2fb84b618ba2467fe377543f879e9bf",
+ "RunTimeTicks": 34318510080,
+ "SupportsTranscoding": true,
+ "SupportsDirectStream": true,
+ "SupportsDirectPlay": true,
+ "SupportsProbing": true,
+ "MediaStreams": [
+ {
+ "Codec": "hevc",
+ "CodecTag": "hev1",
+ "Language": "eng",
+ "ColorSpace": "bt2020nc",
+ "ColorTransfer": "smpte2084",
+ "ColorPrimaries": "bt2020",
+ "TimeBase": "1/16000",
+ "VideoRange": "HDR",
+ "DisplayTitle": "4K HEVC HDR",
+ "BitRate": 14715079,
+ "BitDepth": 8,
+ "RefFrames": 1,
+ "IsDefault": true,
+ "Height": 2160,
+ "Width": 3840,
+ "AverageFrameRate": 23.976,
+ "RealFrameRate": 23.976,
+ "Profile": "Main 10",
+ "Type": 1,
+ "AspectRatio": "16:9",
+ "PixelFormat": "yuv420p10le",
+ "Level": 150
+ },
+ {
+ "Codec": "ac3",
+ "CodecTag": "ac-3",
+ "Language": "eng",
+ "TimeBase": "1/48000",
+ "DisplayTitle": "En - Dolby Digital - 5.1 - Default",
+ "ChannelLayout": "5.1",
+ "BitRate": 384000,
+ "Channels": 6,
+ "SampleRate": 48000,
+ "IsDefault": true,
+ "Index": 1,
+ "Score": 202
+ },
+ {
+ "Codec": "aac",
+ "CodecTag": "mp4a",
+ "Language": "eng",
+ "TimeBase": "1/48000",
+ "DisplayTitle": "En - AAC - Stereo - Default",
+ "ChannelLayout": "stereo",
+ "BitRate": 164741,
+ "Channels": 2,
+ "SampleRate": 48000,
+ "IsDefault": true,
+ "Profile": "LC",
+ "Index": 2,
+ "Score": 203
+ },
+ {
+ "Codec": "srt",
+ "Language": "eng",
+ "TimeBase": "1/1000000",
+ "localizedUndefined": "Undefined",
+ "localizedDefault": "Default",
+ "localizedForced": "Forced",
+ "DisplayTitle": "En - Default",
+ "BitRate": 92,
+ "IsDefault": true,
+ "Type": 2,
+ "Index": 3,
+ "Score": 6421,
+ "IsExternal": true,
+ "IsTextSubtitleStream": true,
+ "SupportsExternalStream": true,
+ "Path": "/Media/MyVideo-WEBDL-2160p.default.eng.srt"
+ }
+ ],
+ "Bitrate": 15201382,
+ "DefaultAudioStreamIndex": 2,
+ "DefaultSubtitleStreamIndex": 3
+}