Merge branch 'release-10.8.z' into fix-hevc-disable-option

This commit is contained in:
Joshua M. Boniface 2022-05-15 20:24:52 -04:00 committed by GitHub
commit 85cfea4c50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 344 additions and 122 deletions

View File

@ -6,8 +6,8 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Diacritics.Extensions;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -314,7 +314,7 @@ namespace Emby.Naming.Common
// This isn't a Kodi naming rule, but the expression below causes false positives, // This isn't a Kodi naming rule, but the expression below causes false positives,
// so we make sure this one gets tested first. // so we make sure this one gets tested first.
// "Foo Bar 889" // "Foo Bar 889"
new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/x]*$") new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,4})(-(?<endingepnumber>[0-9]{2,4}))*[^\\\/x]*$")
{ {
IsNamed = true IsNamed = true
}, },

View File

@ -11,7 +11,6 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using Diacritics.Extensions;
using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Playlists;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions; using Jellyfin.Extensions;
@ -5763,7 +5762,7 @@ AND Type = @InternalPersonType)");
{ {
var itemIdBlob = id.ToByteArray(); var itemIdBlob = id.ToByteArray();
// First delete chapters // Delete existing mediastreams
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob); db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
InsertMediaStreams(itemIdBlob, streams, db); InsertMediaStreams(itemIdBlob, streams, db);
@ -5867,10 +5866,10 @@ AND Type = @InternalPersonType)");
} }
/// <summary> /// <summary>
/// Gets the chapter. /// Gets the media stream.
/// </summary> /// </summary>
/// <param name="reader">The reader.</param> /// <param name="reader">The reader.</param>
/// <returns>ChapterInfo.</returns> /// <returns>MediaStream.</returns>
private MediaStream GetMediaStream(IReadOnlyList<ResultSetValue> reader) private MediaStream GetMediaStream(IReadOnlyList<ResultSetValue> reader)
{ {
var item = new MediaStream var item = new MediaStream

View File

@ -151,7 +151,11 @@ namespace Emby.Server.Implementations.Library
{ {
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video)) // If file is strm or main media stream is missing, force a metadata refresh with remote probing
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder
&& (item.Path.EndsWith(".strm", StringComparison.OrdinalIgnoreCase)
|| (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video))
|| (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio))))
{ {
await item.RefreshMetadata( await item.RefreshMetadata(
new MetadataRefreshOptions(_directoryService) new MetadataRefreshOptions(_directoryService)

View File

@ -5,9 +5,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Diacritics.Extensions;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using System.Text;
namespace Emby.Server.Implementations.LiveTv.EmbyTV namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
@ -48,12 +49,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!string.IsNullOrWhiteSpace(info.EpisodeTitle)) if (!string.IsNullOrWhiteSpace(info.EpisodeTitle))
{ {
var tmpName = name;
if (addHyphen) if (addHyphen)
{ {
name += " -"; tmpName += " -";
} }
name += " " + info.EpisodeTitle; tmpName += " " + info.EpisodeTitle;
// Since the filename will be used with file ext. (.mp4, .ts, etc)
if (Encoding.UTF8.GetByteCount(tmpName) < 250)
{
name = tmpName;
}
} }
} }
else if (info.IsMovie && info.ProductionYear != null) else if (info.IsMovie && info.ProductionYear != null)

View File

@ -1773,14 +1773,24 @@ namespace Jellyfin.Api.Controllers
var args = "-codec:v:0 " + codec; var args = "-codec:v:0 " + codec;
// Prefer hvc1 to hev1.
if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
{ {
if (EncodingHelper.IsCopyCodec(codec)
&& (string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase)))
{
// Prefer dvh1 to dvhe
args += " -tag:v:0 dvh1";
}
else
{
// Prefer hvc1 to hev1
args += " -tag:v:0 hvc1"; args += " -tag:v:0 hvc1";
} }
}
// if (state.EnableMpegtsM2TsMode) // if (state.EnableMpegtsM2TsMode)
// { // {

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using BlurHashSharp.SkiaSharp; using BlurHashSharp.SkiaSharp;
using Diacritics.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;

View File

@ -8,9 +8,9 @@ using System.Linq;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Diacritics.Extensions;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -5,8 +5,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Diacritics.Extensions;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities.Audio namespace MediaBrowser.Controller.Entities.Audio

View File

@ -11,7 +11,6 @@ using System.Text;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Diacritics.Extensions;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions; using Jellyfin.Extensions;

View File

@ -5,8 +5,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Diacritics.Extensions;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities

View File

@ -5,7 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Diacritics.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -5,7 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Diacritics.Extensions; using Jellyfin.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities

View File

@ -3,7 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Diacritics.Extensions; using Jellyfin.Extensions;
namespace MediaBrowser.Controller.Library namespace MediaBrowser.Controller.Library
{ {

View File

@ -18,7 +18,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Diacritics" Version="3.3.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />

View File

@ -2215,13 +2215,13 @@ namespace MediaBrowser.Controller.MediaEncoding
return state.IsInputVideo ? "-sn" : string.Empty; return state.IsInputVideo ? "-sn" : string.Empty;
} }
// We have media info, but we don't know the stream indexes // We have media info, but we don't know the stream index
if (state.VideoStream != null && state.VideoStream.Index == -1) if (state.VideoStream != null && state.VideoStream.Index == -1)
{ {
return "-sn"; return "-sn";
} }
// We have media info, but we don't know the stream indexes // We have media info, but we don't know the stream index
if (state.AudioStream != null && state.AudioStream.Index == -1) if (state.AudioStream != null && state.AudioStream.Index == -1)
{ {
return state.IsInputVideo ? "-sn" : string.Empty; return state.IsInputVideo ? "-sn" : string.Empty;
@ -2231,10 +2231,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.VideoStream != null) if (state.VideoStream != null)
{ {
int videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream);
args += string.Format( args += string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"-map 0:{0}", "-map 0:{0}",
state.VideoStream.Index); videoStreamIndex);
} }
else else
{ {
@ -2244,24 +2246,24 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.AudioStream != null) if (state.AudioStream != null)
{ {
int audioStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.AudioStream);
if (state.AudioStream.IsExternal) if (state.AudioStream.IsExternal)
{ {
bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream; bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream;
int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1; int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1;
int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream);
args += string.Format( args += string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
" -map {0}:{1}", " -map {0}:{1}",
externalAudioMapIndex, externalAudioMapIndex,
externalAudioStream); audioStreamIndex);
} }
else else
{ {
args += string.Format( args += string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
" -map 0:{0}", " -map 0:{0}",
state.AudioStream.Index); audioStreamIndex);
} }
} }
else else
@ -2276,14 +2278,21 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
else if (subtitleMethod == SubtitleDeliveryMethod.Embed) else if (subtitleMethod == SubtitleDeliveryMethod.Embed)
{ {
int subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
args += string.Format( args += string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
" -map 0:{0}", " -map 0:{0}",
state.SubtitleStream.Index); subtitleStreamIndex);
} }
else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
{ {
args += " -map 1:0 -sn"; int externalSubtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
args += string.Format(
CultureInfo.InvariantCulture,
" -map 1:{0} -sn",
externalSubtitleStreamIndex);
} }
return args; return args;
@ -2512,7 +2521,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format( return string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/{2})*{2}:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2",
maxWidthParam, maxWidthParam,
maxHeightParam, maxHeightParam,
scaleVal); scaleVal);
@ -2556,7 +2565,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format( return string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"scale=trunc(min(max(iw\\,ih*dar)\\,{0})/{1})*{1}:trunc(ow/dar/2)*2", "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2",
maxWidthParam, maxWidthParam,
scaleVal); scaleVal);
} }
@ -2568,7 +2577,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format( return string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"scale=trunc(oh*a/{1})*{1}:min(max(iw/dar\\,ih)\\,{0})", "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})",
maxHeightParam, maxHeightParam,
scaleVal); scaleVal);
} }
@ -2617,7 +2626,7 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
else else
{ {
filter = "scale={0}:trunc({0}/dar/2)*2"; filter = "scale={0}:trunc({0}/a/2)*2";
} }
} }
@ -2771,8 +2780,8 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
else if (hasGraphicalSubs) else if (hasGraphicalSubs)
{ {
// [0:s]scale=s=1280x720 // [0:s]scale=expr
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
} }
@ -2958,7 +2967,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (hasGraphicalSubs) if (hasGraphicalSubs)
{ {
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = isSwDecoder
? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
: GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
} }
@ -3156,7 +3167,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (hasGraphicalSubs) if (hasGraphicalSubs)
{ {
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = isSwDecoder
? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
: GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
} }
@ -3402,7 +3415,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (hasGraphicalSubs) if (hasGraphicalSubs)
{ {
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = isSwDecoder
? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
: GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
} }
@ -3611,7 +3626,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (hasGraphicalSubs) if (hasGraphicalSubs)
{ {
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = isSwDecoder
? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
: GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
} }
@ -3858,7 +3875,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (hasGraphicalSubs) if (hasGraphicalSubs)
{ {
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = isSwDecoder
? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
: GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
@ -4033,7 +4052,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (hasGraphicalSubs) if (hasGraphicalSubs)
{ {
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var subSwScaleFilter = isSwDecoder
? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
: GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subSwScaleFilter); subFilters.Add(subSwScaleFilter);
overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0"); overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
@ -4129,9 +4150,8 @@ namespace MediaBrowser.Controller.MediaEncoding
string.Join(',', overlayFilters)); string.Join(',', overlayFilters));
var mapPrefix = Convert.ToInt32(state.SubtitleStream.IsExternal); var mapPrefix = Convert.ToInt32(state.SubtitleStream.IsExternal);
var subtitleStreamIndex = state.SubtitleStream.IsExternal var subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
? 0 var videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream);
: state.SubtitleStream.Index;
if (hasSubs) if (hasSubs)
{ {
@ -4152,7 +4172,7 @@ namespace MediaBrowser.Controller.MediaEncoding
filterStr, filterStr,
mapPrefix, mapPrefix,
subtitleStreamIndex, subtitleStreamIndex,
state.VideoStream.Index, videoStreamIndex,
mainStr, mainStr,
subStr, subStr,
overlayStr); overlayStr);
@ -5362,12 +5382,22 @@ namespace MediaBrowser.Controller.MediaEncoding
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
} }
// opus will fail on 44100
if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
{ {
if (state.OutputAudioSampleRate.HasValue) // opus only supports specific sampling rates
var sampleRate = state.OutputAudioSampleRate;
if (sampleRate.HasValue)
{ {
audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)); var sampleRateValue = sampleRate.Value switch
{
<= 8000 => 8000,
<= 12000 => 12000,
<= 16000 => 16000,
<= 24000 => 24000,
_ => 48000
};
audioTranscodeParams.Add("-ar " + sampleRateValue.ToString(CultureInfo.InvariantCulture));
} }
} }
@ -5389,6 +5419,28 @@ namespace MediaBrowser.Controller.MediaEncoding
string.Empty).Trim(); string.Empty).Trim();
} }
public static int FindIndex(IReadOnlyList<MediaStream> mediaStreams, MediaStream streamToFind)
{
var index = 0;
var length = mediaStreams.Count;
for (var i = 0; i < length; i++)
{
var currentMediaStream = mediaStreams[i];
if (currentMediaStream == streamToFind)
{
return index;
}
if (string.Equals(currentMediaStream.Path, streamToFind.Path, StringComparison.Ordinal))
{
index++;
}
}
return -1;
}
public static bool IsCopyCodec(string codec) public static bool IsCopyCodec(string codec)
{ {
return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);

View File

@ -141,6 +141,13 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetInputArgument(string inputFile, MediaSourceInfo mediaSource); string GetInputArgument(string inputFile, MediaSourceInfo mediaSource);
/// <summary>
/// Gets the input argument for an external subtitle file.
/// </summary>
/// <param name="inputFile">The input file.</param>
/// <returns>System.String.</returns>
string GetExternalSubtitleInputArgument(string inputFile);
/// <summary> /// <summary>
/// Gets the time parameter. /// Gets the time parameter.
/// </summary> /// </summary>

View File

@ -411,6 +411,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol); return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol);
} }
/// <summary>
/// Gets the input argument for an external subtitle file.
/// </summary>
/// <param name="inputFile">The input file.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType.</exception>
public string GetExternalSubtitleInputArgument(string inputFile)
{
const string Prefix = "file";
return EncodingUtils.GetInputArgument(Prefix, inputFile, MediaProtocol.File);
}
/// <summary> /// <summary>
/// Gets the media info internal. /// Gets the media info internal.
/// </summary> /// </summary>

View File

@ -195,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
MediaStream subtitleStream, MediaStream subtitleStream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (!subtitleStream.IsExternal) if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
{ {
string outputFormat; string outputFormat;
string outputCodec; string outputCodec;
@ -224,7 +224,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
// Extract // Extract
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFormat); var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFormat);
await ExtractTextSubtitle(mediaSource, subtitleStream.Index, outputCodec, outputPath, cancellationToken) await ExtractTextSubtitle(mediaSource, subtitleStream, outputCodec, outputPath, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false); return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false);
@ -494,7 +494,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// Extracts the text subtitle. /// Extracts the text subtitle.
/// </summary> /// </summary>
/// <param name="mediaSource">The mediaSource.</param> /// <param name="mediaSource">The mediaSource.</param>
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> /// <param name="subtitleStream">The subtitle stream.</param>
/// <param name="outputCodec">The output codec.</param> /// <param name="outputCodec">The output codec.</param>
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
@ -502,7 +502,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <exception cref="ArgumentException">Must use inputPath list overload.</exception> /// <exception cref="ArgumentException">Must use inputPath list overload.</exception>
private async Task ExtractTextSubtitle( private async Task ExtractTextSubtitle(
MediaSourceInfo mediaSource, MediaSourceInfo mediaSource,
int subtitleStreamIndex, MediaStream subtitleStream,
string outputCodec, string outputCodec,
string outputPath, string outputPath,
CancellationToken cancellationToken) CancellationToken cancellationToken)
@ -511,12 +511,21 @@ namespace MediaBrowser.MediaEncoding.Subtitles
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
var subtitleStreamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream);
try try
{ {
if (!File.Exists(outputPath)) if (!File.Exists(outputPath))
{ {
var args = _mediaEncoder.GetInputArgument(mediaSource.Path, mediaSource);
if (subtitleStream.IsExternal)
{
args = _mediaEncoder.GetExternalSubtitleInputArgument(subtitleStream.Path);
}
await ExtractTextSubtitleInternal( await ExtractTextSubtitleInternal(
_mediaEncoder.GetInputArgument(mediaSource.Path, mediaSource), args,
subtitleStreamIndex, subtitleStreamIndex,
outputCodec, outputCodec,
outputPath, outputPath,

View File

@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration
RequirePerfectSubtitleMatch = true; RequirePerfectSubtitleMatch = true;
AllowEmbeddedSubtitles = EmbeddedSubtitleOptions.AllowAll; AllowEmbeddedSubtitles = EmbeddedSubtitleOptions.AllowAll;
AutomaticallyAddToCollection = true; AutomaticallyAddToCollection = false;
EnablePhotos = true; EnablePhotos = true;
SaveSubtitlesWithMedia = true; SaveSubtitlesWithMedia = true;
EnableRealtimeMonitor = true; EnableRealtimeMonitor = true;

View File

@ -385,7 +385,7 @@ namespace MediaBrowser.Model.Dlna
// If device requirements are satisfied then allow both direct stream and direct play // If device requirements are satisfied then allow both direct stream and direct play
if (item.SupportsDirectPlay) if (item.SupportsDirectPlay)
{ {
if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay)) if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
{ {
if (options.EnableDirectPlay) if (options.EnableDirectPlay)
{ {
@ -401,7 +401,7 @@ namespace MediaBrowser.Model.Dlna
// While options takes the network and other factors into account. Only applies to direct stream // While options takes the network and other factors into account. Only applies to direct stream
if (item.SupportsDirectStream) if (item.SupportsDirectStream)
{ {
if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream)) if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
{ {
if (options.EnableDirectStream) if (options.EnableDirectStream)
{ {
@ -604,11 +604,11 @@ namespace MediaBrowser.Model.Dlna
var videoStream = item.VideoStream; var videoStream = item.VideoStream;
var directPlayEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay); var directPlayBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay);
var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream); var directStreamBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult == 0); bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayBitrateEligibility == 0);
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directPlayEligibilityResult == 0); bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamBitrateEligibility == 0);
var transcodeReasons = directPlayEligibilityResult | directStreamEligibilityResult; var transcodeReasons = directPlayBitrateEligibility | directStreamBitrateEligibility;
_logger.LogDebug( _logger.LogDebug(
"Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", "Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
@ -625,7 +625,7 @@ namespace MediaBrowser.Model.Dlna
var directPlay = directPlayInfo.PlayMethod; var directPlay = directPlayInfo.PlayMethod;
transcodeReasons |= directPlayInfo.TranscodeReasons; transcodeReasons |= directPlayInfo.TranscodeReasons;
if (directPlay != null) if (directPlay.HasValue)
{ {
directPlayProfile = directPlayInfo.Profile; directPlayProfile = directPlayInfo.Profile;
playlistItem.PlayMethod = directPlay.Value; playlistItem.PlayMethod = directPlay.Value;
@ -676,7 +676,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.TranscodeReasons = transcodeReasons; playlistItem.TranscodeReasons = transcodeReasons;
if (playlistItem.PlayMethod != PlayMethod.DirectStream || !options.EnableDirectStream) if (playlistItem.PlayMethod != PlayMethod.DirectStream && playlistItem.PlayMethod != PlayMethod.DirectPlay)
{ {
// Can't direct play, find the transcoding profile // Can't direct play, find the transcoding profile
// If we do this for direct-stream we will overwrite the info // If we do this for direct-stream we will overwrite the info
@ -687,6 +687,8 @@ namespace MediaBrowser.Model.Dlna
BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, transcodingProfile.Container, transcodingProfile.VideoCodec, transcodingProfile.AudioCodec); BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, transcodingProfile.Container, transcodingProfile.VideoCodec, transcodingProfile.AudioCodec);
playlistItem.PlayMethod = PlayMethod.Transcode;
if (subtitleStream != null) if (subtitleStream != null)
{ {
var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol); var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
@ -696,17 +698,12 @@ namespace MediaBrowser.Model.Dlna
playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format }; playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format };
} }
if (playlistItem.PlayMethod != PlayMethod.DirectPlay)
{
playlistItem.PlayMethod = PlayMethod.Transcode;
if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0) if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0)
{ {
ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true); ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true);
} }
} }
} }
}
_logger.LogInformation( _logger.LogInformation(
"StreamBuilder.BuildVideoItem( Profile={0}, Path={1}, AudioStreamIndex={2}, SubtitleStreamIndex={3} ) => ( PlayMethod={4}, TranscodeReason={5} ) {6}", "StreamBuilder.BuildVideoItem( Profile={0}, Path={1}, AudioStreamIndex={2}, SubtitleStreamIndex={3} ) => ( PlayMethod={4}, TranscodeReason={5} ) {6}",
@ -771,6 +768,7 @@ namespace MediaBrowser.Model.Dlna
private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec) private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec)
{ {
// Prefer matching video codecs
var videoCodecs = ContainerProfile.SplitValue(videoCodec); var videoCodecs = ContainerProfile.SplitValue(videoCodec);
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null; var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
if (directVideoCodec != null) if (directVideoCodec != null)
@ -782,7 +780,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.VideoCodecs = videoCodecs; playlistItem.VideoCodecs = videoCodecs;
// copy video codec options as a starting point, this applies to transcode and direct-stream // Copy video codec options as a starting point, this applies to transcode and direct-stream
playlistItem.MaxFramerate = videoStream?.AverageFrameRate; playlistItem.MaxFramerate = videoStream?.AverageFrameRate;
var qualifier = videoStream?.Codec; var qualifier = videoStream?.Codec;
if (videoStream?.Level != null) if (videoStream?.Level != null)
@ -805,7 +803,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString()); playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString());
} }
// prefer matching audio codecs, could do better here // Prefer matching audio codecs, could do better here
var audioCodecs = ContainerProfile.SplitValue(audioCodec); var audioCodecs = ContainerProfile.SplitValue(audioCodec);
var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec)); var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
playlistItem.AudioCodecs = audioCodecs; playlistItem.AudioCodecs = audioCodecs;
@ -815,7 +813,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioStreamIndex = audioStream.Index; playlistItem.AudioStreamIndex = audioStream.Index;
playlistItem.AudioCodecs = new[] { audioStream.Codec }; playlistItem.AudioCodecs = new[] { audioStream.Codec };
// copy matching audio codec options // Copy matching audio codec options
playlistItem.AudioSampleRate = audioStream.SampleRate; playlistItem.AudioSampleRate = audioStream.SampleRate;
playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString()); playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString());
@ -1076,7 +1074,7 @@ namespace MediaBrowser.Model.Dlna
DeviceProfile profile = options.Profile; DeviceProfile profile = options.Profile;
string container = mediaSource.Container; string container = mediaSource.Container;
// video // Video
int? width = videoStream?.Width; int? width = videoStream?.Width;
int? height = videoStream?.Height; int? height = videoStream?.Height;
int? bitDepth = videoStream?.BitDepth; int? bitDepth = videoStream?.BitDepth;
@ -1088,7 +1086,7 @@ namespace MediaBrowser.Model.Dlna
bool? isInterlaced = videoStream?.IsInterlaced; bool? isInterlaced = videoStream?.IsInterlaced;
string videoCodecTag = videoStream?.CodecTag; string videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC; bool? isAvc = videoStream?.IsAVC;
// audio // Audio
var defaultLanguage = audioStream?.Language ?? string.Empty; var defaultLanguage = audioStream?.Language ?? string.Empty;
var defaultMarked = audioStream?.IsDefault ?? false; var defaultMarked = audioStream?.IsDefault ?? false;
@ -1217,6 +1215,7 @@ namespace MediaBrowser.Model.Dlna
return (Result: (Profile: directPlayProfile, PlayMethod: playMethod, AudioStreamIndex: selectedAudioStream?.Index, TranscodeReason: failureReasons), Order: order, Rank: ranked); return (Result: (Profile: directPlayProfile, PlayMethod: playMethod, AudioStreamIndex: selectedAudioStream?.Index, TranscodeReason: failureReasons), Order: order, Rank: ranked);
}) })
.OrderByDescending(analysis => analysis.Result.PlayMethod) .OrderByDescending(analysis => analysis.Result.PlayMethod)
.ThenByDescending(analysis => analysis.Rank)
.ThenBy(analysis => analysis.Order) .ThenBy(analysis => analysis.Order)
.ToArray() .ToArray()
.ToLookup(analysis => analysis.Result.PlayMethod != null); .ToLookup(analysis => analysis.Result.PlayMethod != null);
@ -1229,7 +1228,7 @@ namespace MediaBrowser.Model.Dlna
return profileMatch; return profileMatch;
} }
var failureReasons = analyzedProfiles[false].OrderBy(a => a.Result.TranscodeReason).ThenBy(analysis => analysis.Order).FirstOrDefault().Result.TranscodeReason; var failureReasons = analyzedProfiles[false].Select(analysis => analysis.Result).FirstOrDefault().TranscodeReason;
if (failureReasons == 0) if (failureReasons == 0)
{ {
failureReasons = TranscodeReason.DirectPlayError; failureReasons = TranscodeReason.DirectPlayError;
@ -1275,13 +1274,13 @@ namespace MediaBrowser.Model.Dlna
mediaSource.Path ?? "Unknown path"); mediaSource.Path ?? "Unknown path");
} }
private TranscodeReason IsEligibleForDirectPlay( private TranscodeReason IsBitrateEligibleForDirectPlayback(
MediaSourceInfo item, MediaSourceInfo item,
long maxBitrate, long maxBitrate,
VideoOptions options, VideoOptions options,
PlayMethod playMethod) PlayMethod playMethod)
{ {
bool result = IsItemBitrateEligibleForDirectPlay(item, maxBitrate, playMethod); bool result = IsItemBitrateEligibleForDirectPlayback(item, maxBitrate, playMethod);
if (!result) if (!result)
{ {
return TranscodeReason.ContainerBitrateExceedsLimit; return TranscodeReason.ContainerBitrateExceedsLimit;
@ -1449,7 +1448,7 @@ namespace MediaBrowser.Model.Dlna
return null; return null;
} }
private bool IsItemBitrateEligibleForDirectPlay(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod) private bool IsItemBitrateEligibleForDirectPlayback(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod)
{ {
// Don't restrict by bitrate if coming from an external domain // Don't restrict by bitrate if coming from an external domain
if (item.IsRemote) if (item.IsRemote)

View File

@ -8,7 +8,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Diacritics.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;

View File

@ -22,7 +22,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" /> <PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" />
<PackageReference Include="PlaylistsNET" Version="1.1.3" /> <PackageReference Include="PlaylistsNET" Version="1.1.3" />
<PackageReference Include="TMDbLib" Version="1.9.1" /> <PackageReference Include="TMDbLib" Version="1.9.2" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>

View File

@ -173,16 +173,30 @@ namespace MediaBrowser.Providers.MediaInfo
IReadOnlyList<MediaAttachment> mediaAttachments; IReadOnlyList<MediaAttachment> mediaAttachments;
ChapterInfo[] chapters; ChapterInfo[] chapters;
mediaStreams = new List<MediaStream>();
// Add external streams before adding the streams from the file to preserve stream IDs on remote videos
await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Max(i => i.Index) + 1);
if (mediaInfo != null) if (mediaInfo != null)
{ {
mediaStreams = mediaInfo.MediaStreams.ToList(); foreach (var mediaStream in mediaInfo.MediaStreams)
{
mediaStream.Index = startIndex++;
mediaStreams.Add(mediaStream);
}
mediaAttachments = mediaInfo.MediaAttachments; mediaAttachments = mediaInfo.MediaAttachments;
video.TotalBitrate = mediaInfo.Bitrate; video.TotalBitrate = mediaInfo.Bitrate;
// video.FormatName = (mediaInfo.Container ?? string.Empty) // video.FormatName = (mediaInfo.Container ?? string.Empty)
// .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); // .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
// For dvd's this may not always be accurate, so don't set the runtime if the item already has one // For DVDs this may not always be accurate, so don't set the runtime if the item already has one
var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;
if (needToSetRuntime) if (needToSetRuntime)
@ -213,15 +227,20 @@ namespace MediaBrowser.Providers.MediaInfo
} }
else else
{ {
mediaStreams = new List<MediaStream>(); var currentMediaStreams = video.GetMediaStreams();
foreach (var mediaStream in currentMediaStreams)
{
if (!mediaStream.IsExternal)
{
mediaStream.Index = startIndex++;
mediaStreams.Add(mediaStream);
}
}
mediaAttachments = Array.Empty<MediaAttachment>(); mediaAttachments = Array.Empty<MediaAttachment>();
chapters = Array.Empty<ChapterInfo>(); chapters = Array.Empty<ChapterInfo>();
} }
await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
var libraryOptions = _libraryManager.GetLibraryOptions(video); var libraryOptions = _libraryManager.GetLibraryOptions(video);
if (mediaInfo != null) if (mediaInfo != null)
@ -254,7 +273,11 @@ namespace MediaBrowser.Providers.MediaInfo
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
_itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken); _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
if (mediaAttachments.Any())
{
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken); _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
}
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
options.MetadataRefreshMode == MetadataRefreshMode.Default) options.MetadataRefreshMode == MetadataRefreshMode.Default)

View File

@ -13,7 +13,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Diacritics.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
jellyfin-server (10.8.0~beta3) unstable; urgency=medium
* New upstream version 10.8.0-beta3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta3
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 15 May 2022 20:15:43 -0400
jellyfin-server (10.8.0~beta2) unstable; urgency=medium jellyfin-server (10.8.0~beta2) unstable; urgency=medium
* New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2 * New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2

View File

@ -5,7 +5,7 @@ Homepage: https://jellyfin.org
Standards-Version: 3.9.2 Standards-Version: 3.9.2
Package: jellyfin Package: jellyfin
Version: 10.8.0~beta2 Version: 10.8.0~beta3
Maintainer: Jellyfin Packaging Team <packaging@jellyfin.org> Maintainer: Jellyfin Packaging Team <packaging@jellyfin.org>
Depends: jellyfin-server, jellyfin-web Depends: jellyfin-server, jellyfin-web
Description: Provides the Jellyfin Free Software Media System Description: Provides the Jellyfin Free Software Media System

View File

@ -7,7 +7,7 @@
%endif %endif
Name: jellyfin Name: jellyfin
Version: 10.8.0~beta2 Version: 10.8.0~beta3
Release: 1%{?dist} Release: 1%{?dist}
Summary: The Free Software Media System Summary: The Free Software Media System
License: GPLv3 License: GPLv3
@ -153,6 +153,8 @@ fi
%systemd_postun_with_restart jellyfin.service %systemd_postun_with_restart jellyfin.service
%changelog %changelog
* Sun May 15 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.0-beta3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta3
* Sun Apr 17 2022 Jellyfin Packaging Team <packaging@jellyfin.org> * Sun Apr 17 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2 - New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2
* Fri Mar 25 2022 Jellyfin Packaging Team <packaging@jellyfin.org> * Fri Mar 25 2022 Jellyfin Packaging Team <packaging@jellyfin.org>

View File

@ -1,4 +1,8 @@
using System; using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
namespace Jellyfin.Extensions namespace Jellyfin.Extensions
{ {
@ -7,6 +11,44 @@ namespace Jellyfin.Extensions
/// </summary> /// </summary>
public static class StringExtensions public static class StringExtensions
{ {
// Matches non-conforming unicode chars
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(\ufffd)");
/// <summary>
/// Removes the diacritics character from the strings.
/// </summary>
/// <param name="text">The string to act on.</param>
/// <returns>The string without diacritics character.</returns>
public static string RemoveDiacritics(this string text)
{
string withDiactritics = _nonConformingUnicode
.Replace(text, string.Empty)
.Normalize(NormalizationForm.FormD);
var withoutDiactritics = new StringBuilder();
foreach (char c in withDiactritics)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
if (uc != UnicodeCategory.NonSpacingMark)
{
withoutDiactritics.Append(c);
}
}
return withoutDiactritics.ToString().Normalize(NormalizationForm.FormC);
}
/// <summary>
/// Checks wether or not the specified string has diacritics in it.
/// </summary>
/// <param name="text">The string to check.</param>
/// <returns>True if the string has diacritics, false otherwise.</returns>
public static bool HasDiacritics(this string text)
{
return !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal);
}
/// <summary> /// <summary>
/// Counts the number of occurrences of [needle] in the string. /// Counts the number of occurrences of [needle] in the string.
/// </summary> /// </summary>

View File

@ -5,6 +5,38 @@ namespace Jellyfin.Extensions.Tests
{ {
public class StringExtensionsTests public class StringExtensionsTests
{ {
[Theory]
[InlineData("", "")] // Identity edge-case (no diactritics)
[InlineData("Indiana Jones", "Indiana Jones")] // Identity (no diactritics)
[InlineData("a\ud800b", "ab")] // Invalid UTF-16 char stripping
[InlineData("Jön", "Jon")] // Issue #7484
[InlineData("Jönssonligan", "Jonssonligan")] // Issue #7484
[InlineData("Kieślowski", "Kieslowski")] // Issue #7450
[InlineData("Cidadão Kane", "Cidadao Kane")] // Issue #7560
[InlineData("운명처럼 널 사랑해", "운명처럼 널 사랑해")] // Issue #6393 (Korean language support)
[InlineData("애타는 로맨스", "애타는 로맨스")] // Issue #6393
public void RemoveDiacritics_ValidInput_Corrects(string input, string expectedResult)
{
string result = input.RemoveDiacritics();
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("", false)] // Identity edge-case (no diactritics)
[InlineData("Indiana Jones", false)] // Identity (no diactritics)
[InlineData("a\ud800b", true)] // Invalid UTF-16 char stripping
[InlineData("Jön", true)] // Issue #7484
[InlineData("Jönssonligan", true)] // Issue #7484
[InlineData("Kieślowski", true)] // Issue #7450
[InlineData("Cidadão Kane", true)] // Issue #7560
[InlineData("운명처럼 널 사랑해", false)] // Issue #6393 (Korean language support)
[InlineData("애타는 로맨스", false)] // Issue #6393
public void HasDiacritics_ValidInput_Corrects(string input, bool expectedResult)
{
bool result = input.HasDiacritics();
Assert.Equal(expectedResult, result);
}
[Theory] [Theory]
[InlineData("", '_', 0)] [InlineData("", '_', 0)]
[InlineData("___", '_', 3)] [InlineData("___", '_', 3)]

View File

@ -27,7 +27,7 @@ namespace Jellyfin.Model.Tests
[InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #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-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450
[InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// Firefox // Firefox
@ -38,7 +38,7 @@ namespace Jellyfin.Model.Tests
[InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #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-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450
[InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// Safari // Safari
@ -89,7 +89,7 @@ namespace Jellyfin.Model.Tests
[InlineData("Chrome-NoHLS", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #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-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")]
[InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")] [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")]
[InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450
[InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// TranscodeMedia // TranscodeMedia
@ -177,7 +177,7 @@ namespace Jellyfin.Model.Tests
[InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #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-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450
[InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// Firefox // Firefox
@ -187,7 +187,7 @@ namespace Jellyfin.Model.Tests
[InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #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-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
[InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450
[InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
// Safari // Safari
@ -338,23 +338,23 @@ namespace Jellyfin.Model.Tests
Assert.NotNull(mediaSource); Assert.NotNull(mediaSource);
var videoStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Video); var videoStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Video);
var audioStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio); var audioStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio);
// TODO: check AudioStreamIndex vs options.AudioStreamIndex // TODO: Check AudioStreamIndex vs options.AudioStreamIndex
var inputAudioStream = mediaSource.GetDefaultAudioStream(audioStreamIndexInput ?? mediaSource.DefaultAudioStreamIndex); var inputAudioStream = mediaSource.GetDefaultAudioStream(audioStreamIndexInput ?? mediaSource.DefaultAudioStreamIndex);
var uri = ParseUri(val); var uri = ParseUri(val);
if (playMethod == PlayMethod.DirectPlay) if (playMethod == PlayMethod.DirectPlay)
{ {
// check expected container // Check expected container
var containers = ContainerProfile.SplitValue(mediaSource.Container); var containers = ContainerProfile.SplitValue(mediaSource.Container);
// TODO: test transcode too // TODO: Test transcode too
// Assert.Contains(uri.Extension, containers); // Assert.Contains(uri.Extension, containers);
// check expected video codec (1) // Check expected video codec (1)
Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec);
Assert.Single(val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec);
// check expected audio codecs (1) // Check expected audio codecs (1)
Assert.Contains(targetAudioStream.Codec, val.TargetAudioCodec); Assert.Contains(targetAudioStream.Codec, val.TargetAudioCodec);
Assert.Single(val.TargetAudioCodec); Assert.Single(val.TargetAudioCodec);
// Assert.Single(val.AudioCodecs); // Assert.Single(val.AudioCodecs);
@ -370,7 +370,7 @@ namespace Jellyfin.Model.Tests
Assert.NotEmpty(val.VideoCodecs); Assert.NotEmpty(val.VideoCodecs);
Assert.NotEmpty(val.AudioCodecs); Assert.NotEmpty(val.AudioCodecs);
// check expected container (todo: this could be a test param) // Check expected container (todo: this could be a test param)
if (transcodeProtocol == "http") if (transcodeProtocol == "http")
{ {
// Assert.Equal("webm", val.Container); // Assert.Equal("webm", val.Container);
@ -403,32 +403,39 @@ namespace Jellyfin.Model.Tests
stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs)); stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs));
} }
// todo: fill out tests here // TODO: Fill out tests here
} }
// DirectStream and Remux // DirectStream and Remux
else else
{ {
// check expected video codec (1) // Check expected video codec (1)
Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec);
Assert.Single(val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec);
if (transcodeMode == "DirectStream") if (transcodeMode == "DirectStream")
{ {
// Check expected audio codecs (1)
if (!targetAudioStream.IsExternal) if (!targetAudioStream.IsExternal)
{ {
// check expected audio codecs (1) if (val.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported))
{
Assert.Contains(targetAudioStream.Codec, val.AudioCodecs);
}
else
{
Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs); Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs);
} }
} }
}
else if (transcodeMode == "Remux") else if (transcodeMode == "Remux")
{ {
// check expected audio codecs (1) // Check expected audio codecs (1)
Assert.Contains(targetAudioStream.Codec, val.AudioCodecs); Assert.Contains(targetAudioStream.Codec, val.AudioCodecs);
Assert.Single(val.AudioCodecs); Assert.Single(val.AudioCodecs);
} }
// video details // Video details
var videoStream = targetVideoStream; var videoStream = targetVideoStream;
Assert.False(val.EstimateContentLength); Assert.False(val.EstimateContentLength);
Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo); Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo);
@ -437,10 +444,10 @@ namespace Jellyfin.Model.Tests
Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth); Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth);
Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue); Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue);
// audio codec not supported // Audio codec not supported
if ((why & TranscodeReason.AudioCodecNotSupported) != 0) if ((why & TranscodeReason.AudioCodecNotSupported) != 0)
{ {
// audio stream specified // Audio stream specified
if (options.AudioStreamIndex >= 0) if (options.AudioStreamIndex >= 0)
{ {
// TODO:fixme // TODO:fixme
@ -450,10 +457,10 @@ namespace Jellyfin.Model.Tests
} }
} }
// audio stream not specified // Audio stream not specified
else else
{ {
// TODO:fixme // TODO: Fixme
Assert.All(audioStreams, stream => Assert.All(audioStreams, stream =>
{ {
if (!stream.IsExternal) if (!stream.IsExternal)

View File

@ -45,8 +45,8 @@
}, },
{ {
"Container": "wmv", "Container": "wmv",
"AudioCodec": "", "AudioCodec": "wma",
"VideoCodec": "", "VideoCodec": "wmv,vc1",
"Type": "Video", "Type": "Video",
"$type": "DirectPlayProfile" "$type": "DirectPlayProfile"
}, },
@ -59,8 +59,8 @@
}, },
{ {
"Container": "asf", "Container": "asf",
"AudioCodec": "", "AudioCodec": "wma",
"VideoCodec": "", "VideoCodec": "wmv,vc1",
"Type": "Video", "Type": "Video",
"$type": "DirectPlayProfile" "$type": "DirectPlayProfile"
}, },

View File

@ -45,8 +45,8 @@
}, },
{ {
"Container": "wmv", "Container": "wmv",
"AudioCodec": "", "AudioCodec": "wma",
"VideoCodec": "", "VideoCodec": "wmv,vc1",
"Type": "Video", "Type": "Video",
"$type": "DirectPlayProfile" "$type": "DirectPlayProfile"
}, },
@ -59,8 +59,8 @@
}, },
{ {
"Container": "asf", "Container": "asf",
"AudioCodec": "", "AudioCodec": "wma",
"VideoCodec": "", "VideoCodec": "wmv,vc1",
"Type": "Video", "Type": "Video",
"$type": "DirectPlayProfile" "$type": "DirectPlayProfile"
}, },

View File

@ -1,4 +1,4 @@
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Naming.TV; using Emby.Naming.TV;
using Xunit; using Xunit;
@ -9,6 +9,7 @@ namespace Jellyfin.Naming.Tests.TV
private readonly NamingOptions _namingOptions = new NamingOptions(); private readonly NamingOptions _namingOptions = new NamingOptions();
[Theory] [Theory]
[InlineData("Season 21/One Piece 1001", 1001)]
[InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", 3)] [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", 3)]
[InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 22)] [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 22)]
[InlineData("Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", 1)] [InlineData("Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", 1)]

View File

@ -85,6 +85,17 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
EpisodeTitle = "The VCR Illumination" EpisodeTitle = "The VCR Illumination"
}); });
data.Add(
"Lorem ipsum dolor sit amet: consect 2018_12_06_21_06_00",
new TimerInfo
{
Name = "Lorem ipsum dolor sit amet: consect",
IsProgramSeries = true,
StartDate = new DateTime(2018, 12, 6, 21, 6, 0, DateTimeKind.Local),
OriginalAirDate = new DateTime(2018, 12, 6),
EpisodeTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor"
});
return data; return data;
} }