aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Entities/Extensions.cs2
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs7
-rw-r--r--MediaBrowser.Controller/Entities/TagExtensions.cs2
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs105
-rw-r--r--MediaBrowser.Controller/MediaSegments/IMediaSegmentProvider.cs2
-rw-r--r--MediaBrowser.Controller/Plugins/IHasEmbeddedImage.cs17
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs3
8 files changed, 95 insertions, 45 deletions
diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs
index c56603a3eb..380041af84 100644
--- a/MediaBrowser.Controller/Entities/Extensions.cs
+++ b/MediaBrowser.Controller/Entities/Extensions.cs
@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Entities
}
else
{
- item.RemoteTrailers = [..item.RemoteTrailers, mediaUrl];
+ item.RemoteTrailers = [.. item.RemoteTrailers, mediaUrl];
}
}
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 1e5b5aa164..422c40ce5d 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -21,6 +21,7 @@ namespace MediaBrowser.Controller.Entities
AlbumArtistIds = [];
AlbumIds = [];
AncestorIds = [];
+ LinkedChildAncestorIds = [];
ArtistIds = [];
BlockUnratedItems = [];
BoxSetLibraryFolders = [];
@@ -265,6 +266,12 @@ namespace MediaBrowser.Controller.Entities
public Guid[] AncestorIds { get; set; }
+ /// <summary>
+ /// Gets or sets a list of ancestor ids that the item's linked children must descend from.
+ /// Useful for filtering BoxSets/Playlists to only those that contain items from a specific library.
+ /// </summary>
+ public Guid[] LinkedChildAncestorIds { get; set; }
+
public Guid[] TopParentIds { get; set; }
public CollectionType?[] PresetViews { get; set; }
diff --git a/MediaBrowser.Controller/Entities/TagExtensions.cs b/MediaBrowser.Controller/Entities/TagExtensions.cs
index c1e4d1db2f..4ddba9835b 100644
--- a/MediaBrowser.Controller/Entities/TagExtensions.cs
+++ b/MediaBrowser.Controller/Entities/TagExtensions.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Entities
}
else
{
- item.Tags = [..current, name];
+ item.Tags = [.. current, name];
}
}
}
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index b558ef73d5..c5e7ae4913 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -117,7 +117,7 @@ namespace MediaBrowser.Controller.Library
get
{
var paths = string.IsNullOrEmpty(Path) ? Array.Empty<string>() : [Path];
- return AdditionalLocations is null ? paths : [..paths, ..AdditionalLocations];
+ return AdditionalLocations is null ? paths : [.. paths, .. AdditionalLocations];
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 65f6b79656..8f6e36bce4 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1267,16 +1267,13 @@ namespace MediaBrowser.Controller.MediaEncoding
.Append(_mediaEncoder.GetInputPathArgument(state));
}
- // sub2video for external graphical subtitles
- if (state.SubtitleStream is not null
- && ShouldEncodeSubtitle(state)
- && !state.SubtitleStream.IsTextSubtitleStream
- && state.SubtitleStream.IsExternal)
+ if (NeedsExternalSubtitleMuxing(state))
{
var subtitlePath = state.SubtitleStream.Path;
- var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan());
+ var isGraphicalBurnIn = ShouldEncodeSubtitle(state) && !state.SubtitleStream.IsTextSubtitleStream;
// dvdsub/vobsub graphical subtitles use .sub+.idx pairs
+ var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan());
if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase))
{
var idxFile = Path.ChangeExtension(subtitlePath, ".idx");
@@ -1307,7 +1304,7 @@ namespace MediaBrowser.Controller.MediaEncoding
arg.Append(' ').Append(seekSubParam);
}
- if (!string.IsNullOrEmpty(canvasArgs))
+ if (isGraphicalBurnIn && !string.IsNullOrEmpty(canvasArgs))
{
arg.Append(canvasArgs);
}
@@ -1766,13 +1763,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
param += encoderPreset switch
{
- EncoderPreset.veryslow => " -preset p7",
- EncoderPreset.slower => " -preset p6",
- EncoderPreset.slow => " -preset p5",
- EncoderPreset.medium => " -preset p4",
- EncoderPreset.fast => " -preset p3",
- EncoderPreset.faster => " -preset p2",
- _ => " -preset p1"
+ EncoderPreset.veryslow => " -preset p7",
+ EncoderPreset.slower => " -preset p6",
+ EncoderPreset.slow => " -preset p5",
+ EncoderPreset.medium => " -preset p4",
+ EncoderPreset.fast => " -preset p3",
+ EncoderPreset.faster => " -preset p2",
+ _ => " -preset p1"
};
}
else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) // h264 (h264_amf)
@@ -1782,11 +1779,11 @@ namespace MediaBrowser.Controller.MediaEncoding
{
param += encoderPreset switch
{
- EncoderPreset.veryslow => " -quality quality",
- EncoderPreset.slower => " -quality quality",
- EncoderPreset.slow => " -quality quality",
- EncoderPreset.medium => " -quality balanced",
- _ => " -quality speed"
+ EncoderPreset.veryslow => " -quality quality",
+ EncoderPreset.slower => " -quality quality",
+ EncoderPreset.slow => " -quality quality",
+ EncoderPreset.medium => " -quality balanced",
+ _ => " -quality speed"
};
if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)
@@ -1806,11 +1803,11 @@ namespace MediaBrowser.Controller.MediaEncoding
{
param += encoderPreset switch
{
- EncoderPreset.veryslow => " -prio_speed 0",
- EncoderPreset.slower => " -prio_speed 0",
- EncoderPreset.slow => " -prio_speed 0",
- EncoderPreset.medium => " -prio_speed 0",
- _ => " -prio_speed 1"
+ EncoderPreset.veryslow => " -prio_speed 0",
+ EncoderPreset.slower => " -prio_speed 0",
+ EncoderPreset.slow => " -prio_speed 0",
+ EncoderPreset.medium => " -prio_speed 0",
+ _ => " -prio_speed 1"
};
}
@@ -2762,25 +2759,29 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
{
+#pragma warning disable SA1008
return (inputChannels, outputChannels) switch
{
- (>= 6, >= 6 or 0) => Math.Min(640000, bitrate),
- (> 0, > 0) => Math.Min(outputChannels * 128000, bitrate),
- (> 0, _) => Math.Min(inputChannels * 128000, bitrate),
+ ( >= 6, >= 6 or 0) => Math.Min(640000, bitrate),
+ ( > 0, > 0) => Math.Min(outputChannels * 128000, bitrate),
+ ( > 0, _) => Math.Min(inputChannels * 128000, bitrate),
(_, _) => Math.Min(384000, bitrate)
};
+#pragma warning restore SA1008
}
if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|| string.Equals(audioCodec, "dca", StringComparison.OrdinalIgnoreCase))
{
+#pragma warning disable SA1008
return (inputChannels, outputChannels) switch
{
- (>= 6, >= 6 or 0) => Math.Min(768000, bitrate),
- (> 0, > 0) => Math.Min(outputChannels * 136000, bitrate),
- (> 0, _) => Math.Min(inputChannels * 136000, bitrate),
+ ( >= 6, >= 6 or 0) => Math.Min(768000, bitrate),
+ ( > 0, > 0) => Math.Min(outputChannels * 136000, bitrate),
+ ( > 0, _) => Math.Min(inputChannels * 136000, bitrate),
(_, _) => Math.Min(672000, bitrate)
};
+#pragma warning restore SA1008
}
// Empty bitrate area is not allow on iOS
@@ -3072,11 +3073,8 @@ namespace MediaBrowser.Controller.MediaEncoding
int audioStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.AudioStream);
if (state.AudioStream.IsExternal)
{
- bool hasExternalGraphicsSubs = state.SubtitleStream is not null
- && ShouldEncodeSubtitle(state)
- && state.SubtitleStream.IsExternal
- && !state.SubtitleStream.IsTextSubtitleStream;
- int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1;
+ bool hasExternalSubAsInput = NeedsExternalSubtitleMuxing(state);
+ int externalAudioMapIndex = hasExternalSubAsInput ? 2 : 1;
args += string.Format(
CultureInfo.InvariantCulture,
@@ -3104,12 +3102,31 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (subtitleMethod == SubtitleDeliveryMethod.Embed)
{
- int subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
+ if (state.SubtitleStream.IsExternal)
+ {
+ // External subtitle file is added as second FFmpeg input.
+ // For single-stream files (SRT/ASS/VTT) the in-file index is always 0.
+ // For multi-stream containers (MKS) we count how many streams from
+ // the same file appear before the selected one.
+ var inFileIndex = state.MediaSource.MediaStreams
+ .Where(s => string.Equals(s.Path, state.SubtitleStream.Path, StringComparison.Ordinal))
+ .TakeWhile(s => s.Index != state.SubtitleStream.Index)
+ .Count();
- args += string.Format(
- CultureInfo.InvariantCulture,
- " -map 0:{0}",
- subtitleStreamIndex);
+ args += string.Format(
+ CultureInfo.InvariantCulture,
+ " -map 1:{0}",
+ inFileIndex);
+ }
+ else
+ {
+ int subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
+
+ args += string.Format(
+ CultureInfo.InvariantCulture,
+ " -map 0:{0}",
+ subtitleStreamIndex);
+ }
}
else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
{
@@ -7886,6 +7903,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|| (state.BaseRequest.AlwaysBurnInSubtitleWhenTranscoding && !IsCopyCodec(state.OutputVideoCodec));
}
+ private static bool NeedsExternalSubtitleMuxing(EncodingJobInfo state)
+ {
+ return state.SubtitleStream is not null
+ && state.SubtitleStream.IsExternal
+ && (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed
+ || (ShouldEncodeSubtitle(state) && !state.SubtitleStream.IsTextSubtitleStream));
+ }
+
public static string GetVideoSyncOption(string videoSync, Version encoderVersion)
{
if (string.IsNullOrEmpty(videoSync))
diff --git a/MediaBrowser.Controller/MediaSegments/IMediaSegmentProvider.cs b/MediaBrowser.Controller/MediaSegments/IMediaSegmentProvider.cs
index 54da218530..9bee653e2e 100644
--- a/MediaBrowser.Controller/MediaSegments/IMediaSegmentProvider.cs
+++ b/MediaBrowser.Controller/MediaSegments/IMediaSegmentProvider.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Plugins/IHasEmbeddedImage.cs b/MediaBrowser.Controller/Plugins/IHasEmbeddedImage.cs
new file mode 100644
index 0000000000..4196cd9f24
--- /dev/null
+++ b/MediaBrowser.Controller/Plugins/IHasEmbeddedImage.cs
@@ -0,0 +1,17 @@
+namespace MediaBrowser.Controller.Plugins;
+
+/// <summary>
+/// Marker interface for integrated/bundled plugins that ship their plugin image as an embedded
+/// resource inside the plugin assembly rather than as a file on disk.
+/// </summary>
+/// <remarks>
+/// This interface is intended for plugins compiled into the server. External plugins should
+/// continue to declare their image via the <c>imagePath</c> field in <c>meta.json</c>.
+/// </remarks>
+public interface IHasEmbeddedImage
+{
+ /// <summary>
+ /// Gets the name of the embedded resource in this plugin's assembly to serve as the plugin image.
+ /// </summary>
+ string ImageResourceName { get; }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
index 132765b719..eb38eeb503 100644
--- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
+++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
@@ -141,7 +141,8 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
_logger.LogError("Unable to set playing queue in group {GroupId}.", context.GroupId.ToString());
// Ignore request and return to previous state.
- IGroupState newState = prevState switch {
+ IGroupState newState = prevState switch
+ {
GroupStateType.Playing => new PlayingGroupState(LoggerFactory),
GroupStateType.Paused => new PausedGroupState(LoggerFactory),
_ => new IdleGroupState(LoggerFactory)