mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-04-24 05:57:20 -04:00
Merge branch 'release-10.8.z' into fix-hevc-disable-option
This commit is contained in:
commit
85cfea4c50
36 changed files with 344 additions and 122 deletions
|
@ -6,8 +6,8 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Diacritics.Extensions;
|
||||
using Emby.Dlna.Didl;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
|
@ -314,7 +314,7 @@ namespace Emby.Naming.Common
|
|||
// This isn't a Kodi naming rule, but the expression below causes false positives,
|
||||
// so we make sure this one gets tested first.
|
||||
// "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
|
||||
},
|
||||
|
|
|
@ -11,7 +11,6 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Diacritics.Extensions;
|
||||
using Emby.Server.Implementations.Playlists;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
|
@ -5763,7 +5762,7 @@ AND Type = @InternalPersonType)");
|
|||
{
|
||||
var itemIdBlob = id.ToByteArray();
|
||||
|
||||
// First delete chapters
|
||||
// Delete existing mediastreams
|
||||
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
|
||||
|
||||
InsertMediaStreams(itemIdBlob, streams, db);
|
||||
|
@ -5867,10 +5866,10 @@ AND Type = @InternalPersonType)");
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chapter.
|
||||
/// Gets the media stream.
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader.</param>
|
||||
/// <returns>ChapterInfo.</returns>
|
||||
/// <returns>MediaStream.</returns>
|
||||
private MediaStream GetMediaStream(IReadOnlyList<ResultSetValue> reader)
|
||||
{
|
||||
var item = new MediaStream
|
||||
|
|
|
@ -151,7 +151,11 @@ namespace Emby.Server.Implementations.Library
|
|||
{
|
||||
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(
|
||||
new MetadataRefreshOptions(_directoryService)
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using System.Text;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
|
@ -48,12 +49,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(info.EpisodeTitle))
|
||||
{
|
||||
var tmpName = name;
|
||||
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)
|
||||
|
|
|
@ -1773,13 +1773,23 @@ namespace Jellyfin.Api.Controllers
|
|||
|
||||
var args = "-codec:v:0 " + codec;
|
||||
|
||||
// Prefer hvc1 to hev1.
|
||||
if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args += " -tag:v:0 hvc1";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
// if (state.EnableMpegtsM2TsMode)
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using BlurHashSharp.SkiaSharp;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
|
|
|
@ -8,9 +8,9 @@ using System.Linq;
|
|||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities.Audio
|
||||
|
|
|
@ -11,7 +11,6 @@ using System.Text;
|
|||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
|
||||
namespace MediaBrowser.Controller.Library
|
||||
{
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Diacritics" Version="3.3.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" 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" />
|
||||
|
|
|
@ -2215,13 +2215,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return state.IsInputVideo ? "-sn" : string.Empty;
|
||||
|
@ -2231,10 +2231,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
if (state.VideoStream != null)
|
||||
{
|
||||
int videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream);
|
||||
|
||||
args += string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"-map 0:{0}",
|
||||
state.VideoStream.Index);
|
||||
videoStreamIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2244,24 +2246,24 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
if (state.AudioStream != null)
|
||||
{
|
||||
int audioStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.AudioStream);
|
||||
if (state.AudioStream.IsExternal)
|
||||
{
|
||||
bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream;
|
||||
int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1;
|
||||
int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream);
|
||||
|
||||
args += string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -map {0}:{1}",
|
||||
externalAudioMapIndex,
|
||||
externalAudioStream);
|
||||
audioStreamIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
args += string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -map 0:{0}",
|
||||
state.AudioStream.Index);
|
||||
audioStreamIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2276,14 +2278,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
else if (subtitleMethod == SubtitleDeliveryMethod.Embed)
|
||||
{
|
||||
int subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
|
||||
|
||||
args += string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -map 0:{0}",
|
||||
state.SubtitleStream.Index);
|
||||
subtitleStreamIndex);
|
||||
}
|
||||
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;
|
||||
|
@ -2512,7 +2521,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
return string.Format(
|
||||
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,
|
||||
maxHeightParam,
|
||||
scaleVal);
|
||||
|
@ -2556,7 +2565,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
return string.Format(
|
||||
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,
|
||||
scaleVal);
|
||||
}
|
||||
|
@ -2568,7 +2577,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
return string.Format(
|
||||
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,
|
||||
scaleVal);
|
||||
}
|
||||
|
@ -2617,7 +2626,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
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)
|
||||
{
|
||||
// [0:s]scale=s=1280x720
|
||||
var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
||||
// [0:s]scale=expr
|
||||
var subSwScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
|
||||
subFilters.Add(subSwScaleFilter);
|
||||
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
|
||||
}
|
||||
|
@ -2958,7 +2967,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
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);
|
||||
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
|
||||
}
|
||||
|
@ -3156,7 +3167,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
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);
|
||||
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
|
||||
}
|
||||
|
@ -3402,7 +3415,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
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);
|
||||
overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0");
|
||||
}
|
||||
|
@ -3611,7 +3626,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
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);
|
||||
overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
|
||||
}
|
||||
|
@ -3858,7 +3875,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
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);
|
||||
overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
|
||||
|
||||
|
@ -4033,7 +4052,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
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);
|
||||
overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
|
||||
|
||||
|
@ -4129,9 +4150,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
string.Join(',', overlayFilters));
|
||||
|
||||
var mapPrefix = Convert.ToInt32(state.SubtitleStream.IsExternal);
|
||||
var subtitleStreamIndex = state.SubtitleStream.IsExternal
|
||||
? 0
|
||||
: state.SubtitleStream.Index;
|
||||
var subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream);
|
||||
var videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream);
|
||||
|
||||
if (hasSubs)
|
||||
{
|
||||
|
@ -4152,7 +4172,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
filterStr,
|
||||
mapPrefix,
|
||||
subtitleStreamIndex,
|
||||
state.VideoStream.Index,
|
||||
videoStreamIndex,
|
||||
mainStr,
|
||||
subStr,
|
||||
overlayStr);
|
||||
|
@ -5362,12 +5382,22 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
// opus will fail on 44100
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
|
||||
|
|
|
@ -141,6 +141,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
/// <returns>System.String.</returns>
|
||||
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>
|
||||
/// Gets the time parameter.
|
||||
/// </summary>
|
||||
|
|
|
@ -411,6 +411,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
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>
|
||||
/// Gets the media info internal.
|
||||
/// </summary>
|
||||
|
|
|
@ -195,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||
MediaStream subtitleStream,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!subtitleStream.IsExternal)
|
||||
if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string outputFormat;
|
||||
string outputCodec;
|
||||
|
@ -224,7 +224,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||
// Extract
|
||||
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFormat);
|
||||
|
||||
await ExtractTextSubtitle(mediaSource, subtitleStream.Index, outputCodec, outputPath, cancellationToken)
|
||||
await ExtractTextSubtitle(mediaSource, subtitleStream, outputCodec, outputPath, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false);
|
||||
|
@ -494,7 +494,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||
/// Extracts the text subtitle.
|
||||
/// </summary>
|
||||
/// <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="outputPath">The output path.</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>
|
||||
private async Task ExtractTextSubtitle(
|
||||
MediaSourceInfo mediaSource,
|
||||
int subtitleStreamIndex,
|
||||
MediaStream subtitleStream,
|
||||
string outputCodec,
|
||||
string outputPath,
|
||||
CancellationToken cancellationToken)
|
||||
|
@ -511,12 +511,21 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||
|
||||
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var subtitleStreamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream);
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(outputPath))
|
||||
{
|
||||
var args = _mediaEncoder.GetInputArgument(mediaSource.Path, mediaSource);
|
||||
|
||||
if (subtitleStream.IsExternal)
|
||||
{
|
||||
args = _mediaEncoder.GetExternalSubtitleInputArgument(subtitleStream.Path);
|
||||
}
|
||||
|
||||
await ExtractTextSubtitleInternal(
|
||||
_mediaEncoder.GetInputArgument(mediaSource.Path, mediaSource),
|
||||
args,
|
||||
subtitleStreamIndex,
|
||||
outputCodec,
|
||||
outputPath,
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration
|
|||
RequirePerfectSubtitleMatch = true;
|
||||
AllowEmbeddedSubtitles = EmbeddedSubtitleOptions.AllowAll;
|
||||
|
||||
AutomaticallyAddToCollection = true;
|
||||
AutomaticallyAddToCollection = false;
|
||||
EnablePhotos = true;
|
||||
SaveSubtitlesWithMedia = true;
|
||||
EnableRealtimeMonitor = true;
|
||||
|
|
|
@ -385,7 +385,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
// If device requirements are satisfied then allow both direct stream and direct play
|
||||
if (item.SupportsDirectPlay)
|
||||
{
|
||||
if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
|
||||
if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
|
||||
{
|
||||
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
|
||||
if (item.SupportsDirectStream)
|
||||
{
|
||||
if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
|
||||
if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
|
||||
{
|
||||
if (options.EnableDirectStream)
|
||||
{
|
||||
|
@ -604,11 +604,11 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
var videoStream = item.VideoStream;
|
||||
|
||||
var directPlayEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay);
|
||||
var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
|
||||
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult == 0);
|
||||
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directPlayEligibilityResult == 0);
|
||||
var transcodeReasons = directPlayEligibilityResult | directStreamEligibilityResult;
|
||||
var directPlayBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay);
|
||||
var directStreamBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
|
||||
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayBitrateEligibility == 0);
|
||||
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamBitrateEligibility == 0);
|
||||
var transcodeReasons = directPlayBitrateEligibility | directStreamBitrateEligibility;
|
||||
|
||||
_logger.LogDebug(
|
||||
"Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
|
||||
|
@ -625,7 +625,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
var directPlay = directPlayInfo.PlayMethod;
|
||||
transcodeReasons |= directPlayInfo.TranscodeReasons;
|
||||
|
||||
if (directPlay != null)
|
||||
if (directPlay.HasValue)
|
||||
{
|
||||
directPlayProfile = directPlayInfo.Profile;
|
||||
playlistItem.PlayMethod = directPlay.Value;
|
||||
|
@ -676,7 +676,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
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
|
||||
// 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);
|
||||
|
||||
playlistItem.PlayMethod = PlayMethod.Transcode;
|
||||
|
||||
if (subtitleStream != null)
|
||||
{
|
||||
var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
|
||||
|
@ -696,14 +698,9 @@ namespace MediaBrowser.Model.Dlna
|
|||
playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format };
|
||||
}
|
||||
|
||||
if (playlistItem.PlayMethod != PlayMethod.DirectPlay)
|
||||
if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0)
|
||||
{
|
||||
playlistItem.PlayMethod = PlayMethod.Transcode;
|
||||
|
||||
if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0)
|
||||
{
|
||||
ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true);
|
||||
}
|
||||
ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
// Prefer matching video codecs
|
||||
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
|
||||
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
|
||||
if (directVideoCodec != null)
|
||||
|
@ -782,7 +780,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
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;
|
||||
var qualifier = videoStream?.Codec;
|
||||
if (videoStream?.Level != null)
|
||||
|
@ -805,7 +803,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
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 directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
|
||||
playlistItem.AudioCodecs = audioCodecs;
|
||||
|
@ -815,7 +813,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
playlistItem.AudioStreamIndex = audioStream.Index;
|
||||
playlistItem.AudioCodecs = new[] { audioStream.Codec };
|
||||
|
||||
// copy matching audio codec options
|
||||
// Copy matching audio codec options
|
||||
playlistItem.AudioSampleRate = audioStream.SampleRate;
|
||||
playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString());
|
||||
|
||||
|
@ -1076,7 +1074,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
DeviceProfile profile = options.Profile;
|
||||
string container = mediaSource.Container;
|
||||
|
||||
// video
|
||||
// Video
|
||||
int? width = videoStream?.Width;
|
||||
int? height = videoStream?.Height;
|
||||
int? bitDepth = videoStream?.BitDepth;
|
||||
|
@ -1088,7 +1086,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
bool? isInterlaced = videoStream?.IsInterlaced;
|
||||
string videoCodecTag = videoStream?.CodecTag;
|
||||
bool? isAvc = videoStream?.IsAVC;
|
||||
// audio
|
||||
// Audio
|
||||
var defaultLanguage = audioStream?.Language ?? string.Empty;
|
||||
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);
|
||||
})
|
||||
.OrderByDescending(analysis => analysis.Result.PlayMethod)
|
||||
.ThenByDescending(analysis => analysis.Rank)
|
||||
.ThenBy(analysis => analysis.Order)
|
||||
.ToArray()
|
||||
.ToLookup(analysis => analysis.Result.PlayMethod != null);
|
||||
|
@ -1229,7 +1228,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
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)
|
||||
{
|
||||
failureReasons = TranscodeReason.DirectPlayError;
|
||||
|
@ -1275,13 +1274,13 @@ namespace MediaBrowser.Model.Dlna
|
|||
mediaSource.Path ?? "Unknown path");
|
||||
}
|
||||
|
||||
private TranscodeReason IsEligibleForDirectPlay(
|
||||
private TranscodeReason IsBitrateEligibleForDirectPlayback(
|
||||
MediaSourceInfo item,
|
||||
long maxBitrate,
|
||||
VideoOptions options,
|
||||
PlayMethod playMethod)
|
||||
{
|
||||
bool result = IsItemBitrateEligibleForDirectPlay(item, maxBitrate, playMethod);
|
||||
bool result = IsItemBitrateEligibleForDirectPlayback(item, maxBitrate, playMethod);
|
||||
if (!result)
|
||||
{
|
||||
return TranscodeReason.ContainerBitrateExceedsLimit;
|
||||
|
@ -1449,7 +1448,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
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
|
||||
if (item.IsRemote)
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Linq;
|
|||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" />
|
||||
<PackageReference Include="PlaylistsNET" Version="1.1.3" />
|
||||
<PackageReference Include="TMDbLib" Version="1.9.1" />
|
||||
<PackageReference Include="TMDbLib" Version="1.9.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -173,16 +173,30 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
IReadOnlyList<MediaAttachment> mediaAttachments;
|
||||
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)
|
||||
{
|
||||
mediaStreams = mediaInfo.MediaStreams.ToList();
|
||||
foreach (var mediaStream in mediaInfo.MediaStreams)
|
||||
{
|
||||
mediaStream.Index = startIndex++;
|
||||
mediaStreams.Add(mediaStream);
|
||||
}
|
||||
|
||||
mediaAttachments = mediaInfo.MediaAttachments;
|
||||
|
||||
video.TotalBitrate = mediaInfo.Bitrate;
|
||||
// video.FormatName = (mediaInfo.Container ?? string.Empty)
|
||||
// .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;
|
||||
|
||||
if (needToSetRuntime)
|
||||
|
@ -213,15 +227,20 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
}
|
||||
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>();
|
||||
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);
|
||||
|
||||
if (mediaInfo != null)
|
||||
|
@ -254,7 +273,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
|
||||
|
||||
_itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
|
||||
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
|
||||
|
||||
if (mediaAttachments.Any())
|
||||
{
|
||||
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
|
||||
}
|
||||
|
||||
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
|
||||
options.MetadataRefreshMode == MetadataRefreshMode.Default)
|
||||
|
|
|
@ -13,7 +13,7 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
|
6
debian/changelog
vendored
6
debian/changelog
vendored
|
@ -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
|
||||
|
||||
* New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2
|
||||
|
|
2
debian/metapackage/jellyfin
vendored
2
debian/metapackage/jellyfin
vendored
|
@ -5,7 +5,7 @@ Homepage: https://jellyfin.org
|
|||
Standards-Version: 3.9.2
|
||||
|
||||
Package: jellyfin
|
||||
Version: 10.8.0~beta2
|
||||
Version: 10.8.0~beta3
|
||||
Maintainer: Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
Depends: jellyfin-server, jellyfin-web
|
||||
Description: Provides the Jellyfin Free Software Media System
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
%endif
|
||||
|
||||
Name: jellyfin
|
||||
Version: 10.8.0~beta2
|
||||
Version: 10.8.0~beta3
|
||||
Release: 1%{?dist}
|
||||
Summary: The Free Software Media System
|
||||
License: GPLv3
|
||||
|
@ -153,6 +153,8 @@ fi
|
|||
%systemd_postun_with_restart jellyfin.service
|
||||
|
||||
%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>
|
||||
- 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>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jellyfin.Extensions
|
||||
{
|
||||
|
@ -7,6 +11,44 @@ namespace Jellyfin.Extensions
|
|||
/// </summary>
|
||||
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>
|
||||
/// Counts the number of occurrences of [needle] in the string.
|
||||
/// </summary>
|
||||
|
|
|
@ -5,6 +5,38 @@ namespace Jellyfin.Extensions.Tests
|
|||
{
|
||||
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]
|
||||
[InlineData("", '_', 0)]
|
||||
[InlineData("___", '_', 3)]
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Jellyfin.Model.Tests
|
|||
[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, "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-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
// Firefox
|
||||
|
@ -38,7 +38,7 @@ namespace Jellyfin.Model.Tests
|
|||
[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, "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-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
// 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-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", "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-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
// TranscodeMedia
|
||||
|
@ -177,7 +177,7 @@ namespace Jellyfin.Model.Tests
|
|||
[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, "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-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
// Firefox
|
||||
|
@ -187,7 +187,7 @@ namespace Jellyfin.Model.Tests
|
|||
[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, "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-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
// Safari
|
||||
|
@ -338,23 +338,23 @@ namespace Jellyfin.Model.Tests
|
|||
Assert.NotNull(mediaSource);
|
||||
var videoStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Video);
|
||||
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 uri = ParseUri(val);
|
||||
|
||||
if (playMethod == PlayMethod.DirectPlay)
|
||||
{
|
||||
// check expected container
|
||||
// Check expected container
|
||||
var containers = ContainerProfile.SplitValue(mediaSource.Container);
|
||||
// TODO: test transcode too
|
||||
// TODO: Test transcode too
|
||||
// Assert.Contains(uri.Extension, containers);
|
||||
|
||||
// check expected video codec (1)
|
||||
// Check expected video codec (1)
|
||||
Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec);
|
||||
Assert.Single(val.TargetVideoCodec);
|
||||
|
||||
// check expected audio codecs (1)
|
||||
// Check expected audio codecs (1)
|
||||
Assert.Contains(targetAudioStream.Codec, val.TargetAudioCodec);
|
||||
Assert.Single(val.TargetAudioCodec);
|
||||
// Assert.Single(val.AudioCodecs);
|
||||
|
@ -370,7 +370,7 @@ namespace Jellyfin.Model.Tests
|
|||
Assert.NotEmpty(val.VideoCodecs);
|
||||
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")
|
||||
{
|
||||
// Assert.Equal("webm", val.Container);
|
||||
|
@ -403,32 +403,39 @@ namespace Jellyfin.Model.Tests
|
|||
stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs));
|
||||
}
|
||||
|
||||
// todo: fill out tests here
|
||||
// TODO: Fill out tests here
|
||||
}
|
||||
|
||||
// DirectStream and Remux
|
||||
else
|
||||
{
|
||||
// check expected video codec (1)
|
||||
// Check expected video codec (1)
|
||||
Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec);
|
||||
Assert.Single(val.TargetVideoCodec);
|
||||
|
||||
if (transcodeMode == "DirectStream")
|
||||
{
|
||||
// Check expected audio codecs (1)
|
||||
if (!targetAudioStream.IsExternal)
|
||||
{
|
||||
// check expected audio codecs (1)
|
||||
Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs);
|
||||
if (val.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported))
|
||||
{
|
||||
Assert.Contains(targetAudioStream.Codec, val.AudioCodecs);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (transcodeMode == "Remux")
|
||||
{
|
||||
// check expected audio codecs (1)
|
||||
// Check expected audio codecs (1)
|
||||
Assert.Contains(targetAudioStream.Codec, val.AudioCodecs);
|
||||
Assert.Single(val.AudioCodecs);
|
||||
}
|
||||
|
||||
// video details
|
||||
// Video details
|
||||
var videoStream = targetVideoStream;
|
||||
Assert.False(val.EstimateContentLength);
|
||||
Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo);
|
||||
|
@ -437,10 +444,10 @@ namespace Jellyfin.Model.Tests
|
|||
Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth);
|
||||
Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue);
|
||||
|
||||
// audio codec not supported
|
||||
// Audio codec not supported
|
||||
if ((why & TranscodeReason.AudioCodecNotSupported) != 0)
|
||||
{
|
||||
// audio stream specified
|
||||
// Audio stream specified
|
||||
if (options.AudioStreamIndex >= 0)
|
||||
{
|
||||
// TODO:fixme
|
||||
|
@ -450,10 +457,10 @@ namespace Jellyfin.Model.Tests
|
|||
}
|
||||
}
|
||||
|
||||
// audio stream not specified
|
||||
// Audio stream not specified
|
||||
else
|
||||
{
|
||||
// TODO:fixme
|
||||
// TODO: Fixme
|
||||
Assert.All(audioStreams, stream =>
|
||||
{
|
||||
if (!stream.IsExternal)
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
},
|
||||
{
|
||||
"Container": "wmv",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"AudioCodec": "wma",
|
||||
"VideoCodec": "wmv,vc1",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
|
@ -59,8 +59,8 @@
|
|||
},
|
||||
{
|
||||
"Container": "asf",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"AudioCodec": "wma",
|
||||
"VideoCodec": "wmv,vc1",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
},
|
||||
{
|
||||
"Container": "wmv",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"AudioCodec": "wma",
|
||||
"VideoCodec": "wmv,vc1",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
|
@ -59,8 +59,8 @@
|
|||
},
|
||||
{
|
||||
"Container": "asf",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"AudioCodec": "wma",
|
||||
"VideoCodec": "wmv,vc1",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Emby.Naming.Common;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.TV;
|
||||
using Xunit;
|
||||
|
||||
|
@ -9,6 +9,7 @@ namespace Jellyfin.Naming.Tests.TV
|
|||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
|
||||
[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("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)]
|
||||
|
|
|
@ -85,6 +85,17 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue