mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-07 03:48:06 +02:00
Merge branch 'dev' into beta
This commit is contained in:
commit
e72f59f8cd
26 changed files with 467 additions and 222 deletions
|
@ -68,7 +68,6 @@ namespace MediaBrowser.Api
|
||||||
_config.Configuration.EnableLocalizedGuids = true;
|
_config.Configuration.EnableLocalizedGuids = true;
|
||||||
_config.Configuration.EnableCustomPathSubFolders = true;
|
_config.Configuration.EnableCustomPathSubFolders = true;
|
||||||
_config.Configuration.EnableDateLastRefresh = true;
|
_config.Configuration.EnableDateLastRefresh = true;
|
||||||
_config.Configuration.MergeMetadataAndImagesByName = true;
|
|
||||||
_config.SaveConfiguration();
|
_config.SaveConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
|
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
|
||||||
}
|
}
|
||||||
|
else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
|
||||||
|
}
|
||||||
|
|
||||||
// Default list type = children
|
// Default list type = children
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ namespace MediaBrowser.Dlna.Server
|
||||||
builder.Append("<serialNumber>" + SecurityElement.Escape(_profile.SerialNumber) + "</serialNumber>");
|
builder.Append("<serialNumber>" + SecurityElement.Escape(_profile.SerialNumber) + "</serialNumber>");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Append("<UDN>uuid:" + SecurityElement.Escape(_serverUdn) + "</UDN>");
|
builder.Append("<UDN>uuid:" + SecurityElement.Escape(_serverId) + "</UDN>");
|
||||||
builder.Append("<presentationURL>" + SecurityElement.Escape(_serverAddress) + "</presentationURL>");
|
builder.Append("<presentationURL>" + SecurityElement.Escape(_serverAddress) + "</presentationURL>");
|
||||||
|
|
||||||
if (!EnableAbsoluteUrls)
|
if (!EnableAbsoluteUrls)
|
||||||
|
|
|
@ -176,7 +176,7 @@ namespace MediaBrowser.LocalMetadata.Images
|
||||||
"default"
|
"default"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (item is MusicAlbum || item is MusicArtist)
|
if (item is MusicAlbum || item is MusicArtist || item is Photo)
|
||||||
{
|
{
|
||||||
// these prefer folder
|
// these prefer folder
|
||||||
names.Insert(0, "poster");
|
names.Insert(0, "poster");
|
||||||
|
|
|
@ -19,38 +19,40 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetCommandLineArguments(EncodingJob job)
|
protected override string GetCommandLineArguments(EncodingJob state)
|
||||||
{
|
{
|
||||||
var audioTranscodeParams = new List<string>();
|
var audioTranscodeParams = new List<string>();
|
||||||
|
|
||||||
var bitrate = job.OutputAudioBitrate;
|
var bitrate = state.OutputAudioBitrate;
|
||||||
|
|
||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue)
|
||||||
{
|
{
|
||||||
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(UsCulture));
|
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job.OutputAudioChannels.HasValue)
|
if (state.OutputAudioChannels.HasValue)
|
||||||
{
|
{
|
||||||
audioTranscodeParams.Add("-ac " + job.OutputAudioChannels.Value.ToString(UsCulture));
|
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job.OutputAudioSampleRate.HasValue)
|
if (state.OutputAudioSampleRate.HasValue)
|
||||||
{
|
{
|
||||||
audioTranscodeParams.Add("-ar " + job.OutputAudioSampleRate.Value.ToString(UsCulture));
|
audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
var threads = GetNumberOfThreads(job, false);
|
const string vn = " -vn";
|
||||||
|
|
||||||
var inputModifier = GetInputModifier(job);
|
var threads = GetNumberOfThreads(state, false);
|
||||||
|
|
||||||
|
var inputModifier = GetInputModifier(state);
|
||||||
|
|
||||||
return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
|
return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
|
||||||
inputModifier,
|
inputModifier,
|
||||||
GetInputArgument(job),
|
GetInputArgument(state),
|
||||||
threads,
|
threads,
|
||||||
" -vn",
|
vn,
|
||||||
string.Join(" ", audioTranscodeParams.ToArray()),
|
string.Join(" ", audioTranscodeParams.ToArray()),
|
||||||
job.OutputFilePath).Trim();
|
state.OutputFilePath).Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetOutputFileExtension(EncodingJob state)
|
protected override string GetOutputFileExtension(EncodingJob state)
|
||||||
|
|
|
@ -303,15 +303,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return job.Options.CpuCoreLimit ?? 0;
|
return job.Options.CpuCoreLimit ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string GetInputModifier(EncodingJob job, bool genPts = true)
|
protected string GetInputModifier(EncodingJob state, bool genPts = true)
|
||||||
{
|
{
|
||||||
var inputModifier = string.Empty;
|
var inputModifier = string.Empty;
|
||||||
|
|
||||||
var probeSize = GetProbeSizeArgument(job);
|
var probeSize = GetProbeSizeArgument(state);
|
||||||
inputModifier += " " + probeSize;
|
inputModifier += " " + probeSize;
|
||||||
inputModifier = inputModifier.Trim();
|
inputModifier = inputModifier.Trim();
|
||||||
|
|
||||||
var userAgentParam = GetUserAgentParam(job);
|
var userAgentParam = GetUserAgentParam(state);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(userAgentParam))
|
if (!string.IsNullOrWhiteSpace(userAgentParam))
|
||||||
{
|
{
|
||||||
|
@ -320,35 +320,43 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
inputModifier = inputModifier.Trim();
|
inputModifier = inputModifier.Trim();
|
||||||
|
|
||||||
inputModifier += " " + GetFastSeekCommandLineParameter(job.Options);
|
inputModifier += " " + GetFastSeekCommandLineParameter(state.Options);
|
||||||
inputModifier = inputModifier.Trim();
|
inputModifier = inputModifier.Trim();
|
||||||
|
|
||||||
if (job.IsVideoRequest && genPts)
|
if (state.IsVideoRequest && genPts)
|
||||||
{
|
{
|
||||||
inputModifier += " -fflags +genpts";
|
inputModifier += " -fflags +genpts";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(job.InputAudioSync))
|
if (!string.IsNullOrEmpty(state.InputAudioSync))
|
||||||
{
|
{
|
||||||
inputModifier += " -async " + job.InputAudioSync;
|
inputModifier += " -async " + state.InputAudioSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(job.InputVideoSync))
|
if (!string.IsNullOrEmpty(state.InputVideoSync))
|
||||||
{
|
{
|
||||||
inputModifier += " -vsync " + job.InputVideoSync;
|
inputModifier += " -vsync " + state.InputVideoSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job.ReadInputAtNativeFramerate)
|
if (state.ReadInputAtNativeFramerate)
|
||||||
{
|
{
|
||||||
inputModifier += " -re";
|
inputModifier += " -re";
|
||||||
}
|
}
|
||||||
|
|
||||||
var videoDecoder = GetVideoDecoder(job);
|
var videoDecoder = GetVideoDecoder(state);
|
||||||
if (!string.IsNullOrWhiteSpace(videoDecoder))
|
if (!string.IsNullOrWhiteSpace(videoDecoder))
|
||||||
{
|
{
|
||||||
inputModifier += " " + videoDecoder;
|
inputModifier += " " + videoDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if (state.IsVideoRequest)
|
||||||
|
//{
|
||||||
|
// if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase))
|
||||||
|
// {
|
||||||
|
// //inputModifier += " -noaccurate_seek";
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
return inputModifier;
|
return inputModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,11 +400,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetUserAgentParam(EncodingJob job)
|
private string GetUserAgentParam(EncodingJob state)
|
||||||
{
|
{
|
||||||
string useragent = null;
|
string useragent = null;
|
||||||
|
|
||||||
job.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
|
state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(useragent))
|
if (!string.IsNullOrWhiteSpace(useragent))
|
||||||
{
|
{
|
||||||
|
@ -409,31 +417,31 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the probe size argument.
|
/// Gets the probe size argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="job">The job.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
private string GetProbeSizeArgument(EncodingJob job)
|
private string GetProbeSizeArgument(EncodingJob state)
|
||||||
{
|
{
|
||||||
if (job.PlayableStreamFileNames.Count > 0)
|
if (state.PlayableStreamFileNames.Count > 0)
|
||||||
{
|
{
|
||||||
return MediaEncoder.GetProbeSizeArgument(job.PlayableStreamFileNames.ToArray(), job.InputProtocol);
|
return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MediaEncoder.GetProbeSizeArgument(new[] { job.MediaPath }, job.InputProtocol);
|
return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the fast seek command line parameter.
|
/// Gets the fast seek command line parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="request">The request.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
/// <value>The fast seek command line parameter.</value>
|
/// <value>The fast seek command line parameter.</value>
|
||||||
protected string GetFastSeekCommandLineParameter(EncodingJobOptions options)
|
protected string GetFastSeekCommandLineParameter(EncodingJobOptions request)
|
||||||
{
|
{
|
||||||
var time = options.StartTimeTicks;
|
var time = request.StartTimeTicks ?? 0;
|
||||||
|
|
||||||
if (time.HasValue && time.Value > 0)
|
if (time > 0)
|
||||||
{
|
{
|
||||||
return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time.Value));
|
return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
@ -442,34 +450,35 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the input argument.
|
/// Gets the input argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="job">The job.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected string GetInputArgument(EncodingJob job)
|
protected string GetInputArgument(EncodingJob state)
|
||||||
{
|
{
|
||||||
var arg = "-i " + GetInputPathArgument(job);
|
var arg = string.Format("-i {0}", GetInputPathArgument(state));
|
||||||
|
|
||||||
if (job.SubtitleStream != null)
|
if (state.SubtitleStream != null)
|
||||||
{
|
{
|
||||||
if (job.SubtitleStream.IsExternal && !job.SubtitleStream.IsTextSubtitleStream)
|
if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
|
||||||
{
|
{
|
||||||
arg += " -i \"" + job.SubtitleStream.Path + "\"";
|
arg += " -i \"" + state.SubtitleStream.Path + "\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return arg;
|
return arg.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetInputPathArgument(EncodingJob job)
|
private string GetInputPathArgument(EncodingJob state)
|
||||||
{
|
{
|
||||||
var protocol = job.InputProtocol;
|
var protocol = state.InputProtocol;
|
||||||
|
var mediaPath = state.MediaPath ?? string.Empty;
|
||||||
|
|
||||||
var inputPath = new[] { job.MediaPath };
|
var inputPath = new[] { mediaPath };
|
||||||
|
|
||||||
if (job.IsInputVideo)
|
if (state.IsInputVideo)
|
||||||
{
|
{
|
||||||
if (!(job.VideoType == VideoType.Iso && job.IsoMount == null))
|
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
|
||||||
{
|
{
|
||||||
inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, job.MediaPath, job.InputProtocol, job.IsoMount, job.PlayableStreamFileNames);
|
inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +500,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
}, false, cancellationToken).ConfigureAwait(false);
|
}, false, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options);
|
AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.Options);
|
||||||
|
|
||||||
if (state.IsVideoRequest)
|
if (state.IsVideoRequest)
|
||||||
{
|
{
|
||||||
|
@ -505,11 +514,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AttachMediaStreamInfo(EncodingJob state,
|
private void AttachMediaSourceInfo(EncodingJob state,
|
||||||
MediaSourceInfo mediaSource,
|
MediaSourceInfo mediaSource,
|
||||||
EncodingJobOptions videoRequest)
|
EncodingJobOptions videoRequest)
|
||||||
{
|
{
|
||||||
EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest);
|
EncodingJobFactory.AttachMediaSourceInfo(state, mediaSource, videoRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -572,7 +581,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
param = "-preset superfast";
|
param = "-preset superfast";
|
||||||
|
|
||||||
param += " -crf 28";
|
param += " -crf 23";
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -582,6 +591,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
param += " -crf 28";
|
param += " -crf 28";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// h264 (h264_qsv)
|
||||||
|
else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
param = "-preset 7 -look_ahead 0";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// h264 (libnvenc)
|
||||||
|
else if (string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
param = "-preset high-performance";
|
||||||
|
}
|
||||||
|
|
||||||
// webm
|
// webm
|
||||||
else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -644,9 +666,53 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
param += " -profile:v " + state.Options.Profile;
|
param += " -profile:v " + state.Options.Profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.Options.Level.HasValue)
|
var levelString = state.Options.Level.HasValue ? state.Options.Level.Value.ToString(CultureInfo.InvariantCulture) : null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(levelString))
|
||||||
{
|
{
|
||||||
param += " -level " + state.Options.Level.Value.ToString(UsCulture);
|
var h264Encoder = EncodingJobFactory.GetH264Encoder(state, GetEncodingOptions());
|
||||||
|
|
||||||
|
// h264_qsv and libnvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
|
||||||
|
if (String.Equals(h264Encoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || String.Equals(h264Encoder, "libnvenc", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
switch (levelString)
|
||||||
|
{
|
||||||
|
case "30":
|
||||||
|
param += " -level 3";
|
||||||
|
break;
|
||||||
|
case "31":
|
||||||
|
param += " -level 3.1";
|
||||||
|
break;
|
||||||
|
case "32":
|
||||||
|
param += " -level 3.2";
|
||||||
|
break;
|
||||||
|
case "40":
|
||||||
|
param += " -level 4";
|
||||||
|
break;
|
||||||
|
case "41":
|
||||||
|
param += " -level 4.1";
|
||||||
|
break;
|
||||||
|
case "42":
|
||||||
|
param += " -level 4.2";
|
||||||
|
break;
|
||||||
|
case "50":
|
||||||
|
param += " -level 5";
|
||||||
|
break;
|
||||||
|
case "51":
|
||||||
|
param += " -level 5.1";
|
||||||
|
break;
|
||||||
|
case "52":
|
||||||
|
param += " -level 5.2";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
param += " -level " + levelString;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
param += " -level " + levelString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "-pix_fmt yuv420p " + param;
|
return "-pix_fmt yuv420p " + param;
|
||||||
|
@ -658,15 +724,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue)
|
||||||
{
|
{
|
||||||
var hasFixedResolution = state.Options.HasFixedResolution;
|
|
||||||
|
|
||||||
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (hasFixedResolution)
|
|
||||||
{
|
|
||||||
return string.Format(" -minrate:v ({0}*.90) -maxrate:v ({0}*1.10) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
// With vpx when crf is used, b:v becomes a max rate
|
// With vpx when crf is used, b:v becomes a max rate
|
||||||
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
|
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
|
||||||
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
|
@ -677,26 +736,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
// H264
|
// h264
|
||||||
if (hasFixedResolution)
|
|
||||||
{
|
|
||||||
if (isHls)
|
if (isHls)
|
||||||
{
|
{
|
||||||
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
|
||||||
|
bitrate.Value.ToString(UsCulture),
|
||||||
|
(bitrate.Value * 2).ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(" -maxrate {0} -bufsize {1}",
|
|
||||||
bitrate.Value.ToString(UsCulture),
|
|
||||||
(bitrate.Value * 2).ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected double? GetFramerateParam(EncodingJob state)
|
protected double? GetFramerateParam(EncodingJob state)
|
||||||
|
{
|
||||||
|
if (state.Options != null)
|
||||||
{
|
{
|
||||||
if (state.Options.Framerate.HasValue)
|
if (state.Options.Framerate.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -714,6 +770,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return maxrate;
|
return maxrate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -852,7 +909,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
||||||
|
|
||||||
filters.Add(string.Format("scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam));
|
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a max height was requested
|
// If a max height was requested
|
||||||
|
@ -863,6 +920,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
|
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (filters.Count > 1)
|
||||||
|
{
|
||||||
|
//filters[filters.Count - 1] += ":flags=fast_bilinear";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var output = string.Empty;
|
var output = string.Empty;
|
||||||
|
|
||||||
if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
|
if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
|
||||||
|
@ -917,8 +982,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
seconds.ToString(UsCulture));
|
seconds.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mediaPath = state.MediaPath ?? string.Empty;
|
||||||
|
|
||||||
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
|
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
|
||||||
MediaEncoder.EscapeSubtitleFilterPath(state.MediaPath),
|
MediaEncoder.EscapeSubtitleFilterPath(mediaPath),
|
||||||
state.InternalSubtitleStreamOffset.ToString(UsCulture),
|
state.InternalSubtitleStreamOffset.ToString(UsCulture),
|
||||||
seconds.ToString(UsCulture));
|
seconds.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using MediaBrowser.Model.MediaInfo;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -66,19 +67,32 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
? mediaSources.First()
|
? mediaSources.First()
|
||||||
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
|
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
|
||||||
|
|
||||||
AttachMediaStreamInfo(state, mediaSource, options);
|
var videoRequest = state.Options;
|
||||||
|
|
||||||
state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream);
|
AttachMediaSourceInfo(state, mediaSource, videoRequest);
|
||||||
|
|
||||||
|
//var container = Path.GetExtension(state.RequestedUrl);
|
||||||
|
|
||||||
|
//if (string.IsNullOrEmpty(container))
|
||||||
|
//{
|
||||||
|
// container = request.Static ?
|
||||||
|
// state.InputContainer :
|
||||||
|
// (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
|
||||||
|
//}
|
||||||
|
|
||||||
|
//state.OutputContainer = (container ?? string.Empty).TrimStart('.');
|
||||||
|
|
||||||
|
state.OutputAudioBitrate = GetAudioBitrateParam(state.Options, state.AudioStream);
|
||||||
state.OutputAudioSampleRate = request.AudioSampleRate;
|
state.OutputAudioSampleRate = request.AudioSampleRate;
|
||||||
|
|
||||||
state.OutputAudioCodec = GetAudioCodec(request);
|
state.OutputAudioCodec = state.Options.AudioCodec;
|
||||||
|
|
||||||
state.OutputAudioChannels = GetNumAudioChannelsParam(request, state.AudioStream, state.OutputAudioCodec);
|
state.OutputAudioChannels = GetNumAudioChannelsParam(state.Options, state.AudioStream, state.OutputAudioCodec);
|
||||||
|
|
||||||
if (isVideoRequest)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
state.OutputVideoCodec = GetVideoCodec(request);
|
state.OutputVideoCodec = state.Options.VideoCodec;
|
||||||
state.OutputVideoBitrate = GetVideoBitrateParamValue(request, state.VideoStream);
|
state.OutputVideoBitrate = GetVideoBitrateParamValue(state.Options, state.VideoStream);
|
||||||
|
|
||||||
if (state.OutputVideoBitrate.HasValue)
|
if (state.OutputVideoBitrate.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -87,17 +101,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
state.OutputVideoBitrate.Value,
|
state.OutputVideoBitrate.Value,
|
||||||
state.VideoStream == null ? null : state.VideoStream.Codec,
|
state.VideoStream == null ? null : state.VideoStream.Codec,
|
||||||
state.OutputVideoCodec,
|
state.OutputVideoCodec,
|
||||||
request.MaxWidth,
|
videoRequest.MaxWidth,
|
||||||
request.MaxHeight);
|
videoRequest.MaxHeight);
|
||||||
|
|
||||||
request.MaxWidth = resolution.MaxWidth;
|
videoRequest.MaxWidth = resolution.MaxWidth;
|
||||||
request.MaxHeight = resolution.MaxHeight;
|
videoRequest.MaxHeight = resolution.MaxHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyDeviceProfileSettings(state);
|
ApplyDeviceProfileSettings(state);
|
||||||
|
|
||||||
TryStreamCopy(state, request);
|
if (videoRequest != null)
|
||||||
|
{
|
||||||
|
TryStreamCopy(state, videoRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
//state.OutputFilePath = GetOutputFilePath(state);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +138,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void AttachMediaStreamInfo(EncodingJob state,
|
internal static void AttachMediaSourceInfo(EncodingJob state,
|
||||||
MediaSourceInfo mediaSource,
|
MediaSourceInfo mediaSource,
|
||||||
EncodingJobOptions videoRequest)
|
EncodingJobOptions videoRequest)
|
||||||
{
|
{
|
||||||
|
@ -131,11 +150,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
state.RunTimeTicks = mediaSource.RunTimeTicks;
|
state.RunTimeTicks = mediaSource.RunTimeTicks;
|
||||||
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
|
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
|
||||||
|
|
||||||
if (mediaSource.ReadAtNativeFramerate)
|
|
||||||
{
|
|
||||||
state.ReadInputAtNativeFramerate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mediaSource.VideoType.HasValue)
|
if (mediaSource.VideoType.HasValue)
|
||||||
{
|
{
|
||||||
state.VideoType = mediaSource.VideoType.Value;
|
state.VideoType = mediaSource.VideoType.Value;
|
||||||
|
@ -156,6 +170,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
|
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
|
||||||
state.InputBitrate = mediaSource.Bitrate;
|
state.InputBitrate = mediaSource.Bitrate;
|
||||||
state.InputFileSize = mediaSource.Size;
|
state.InputFileSize = mediaSource.Size;
|
||||||
|
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
|
||||||
|
|
||||||
if (state.ReadInputAtNativeFramerate ||
|
if (state.ReadInputAtNativeFramerate ||
|
||||||
mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
|
mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -165,6 +180,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
state.InputAudioSync = "1";
|
state.InputAudioSync = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Seeing some stuttering when transcoding wma to audio-only HLS
|
||||||
|
state.InputAudioSync = "1";
|
||||||
|
}
|
||||||
|
|
||||||
var mediaStreams = mediaSource.MediaStreams;
|
var mediaStreams = mediaSource.MediaStreams;
|
||||||
|
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
|
@ -210,19 +231,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// <returns>System.Nullable{VideoCodecs}.</returns>
|
/// <returns>System.Nullable{VideoCodecs}.</returns>
|
||||||
private static string InferVideoCodec(string container)
|
private static string InferVideoCodec(string container)
|
||||||
{
|
{
|
||||||
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
|
var ext = "." + (container ?? string.Empty);
|
||||||
|
|
||||||
|
if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "wmv";
|
return "wmv";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "webm", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "vpx";
|
return "vpx";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "theora";
|
return "theora";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "h264";
|
return "h264";
|
||||||
}
|
}
|
||||||
|
@ -232,35 +255,37 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
private string InferAudioCodec(string container)
|
private string InferAudioCodec(string container)
|
||||||
{
|
{
|
||||||
if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
|
var ext = "." + (container ?? string.Empty);
|
||||||
|
|
||||||
|
if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "mp3";
|
return "mp3";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "aac", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "aac";
|
return "aac";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "wma", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "wma";
|
return "wma";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "vorbis";
|
return "vorbis";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "oga", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "vorbis";
|
return "vorbis";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "vorbis";
|
return "vorbis";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "webm", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "vorbis";
|
return "vorbis";
|
||||||
}
|
}
|
||||||
if (string.Equals(container, "webma", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "vorbis";
|
return "vorbis";
|
||||||
}
|
}
|
||||||
|
@ -398,15 +423,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue)
|
||||||
{
|
{
|
||||||
var hasFixedResolution = state.Options.HasFixedResolution;
|
|
||||||
|
|
||||||
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (hasFixedResolution)
|
|
||||||
{
|
|
||||||
return string.Format(" -minrate:v ({0}*.90) -maxrate:v ({0}*1.10) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
// With vpx when crf is used, b:v becomes a max rate
|
// With vpx when crf is used, b:v becomes a max rate
|
||||||
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
|
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
|
||||||
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
|
@ -417,22 +435,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
// H264
|
// h264
|
||||||
if (hasFixedResolution)
|
|
||||||
{
|
|
||||||
if (isHls)
|
if (isHls)
|
||||||
{
|
{
|
||||||
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
|
||||||
|
bitrate.Value.ToString(UsCulture),
|
||||||
|
(bitrate.Value * 2).ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(" -maxrate {0} -bufsize {1}",
|
|
||||||
bitrate.Value.ToString(UsCulture),
|
|
||||||
(bitrate.Value * 2).ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,11 +479,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the output audio codec
|
/// Gets the name of the output audio codec
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
private string GetAudioCodec(EncodingJobOptions request)
|
internal static string GetAudioEncoder(EncodingJob state)
|
||||||
{
|
{
|
||||||
var codec = request.AudioCodec;
|
var codec = state.OutputAudioCodec;
|
||||||
|
|
||||||
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -489,25 +502,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return "wmav2";
|
return "wmav2";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (codec ?? string.Empty).ToLower();
|
return codec.ToLower();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the output video codec
|
/// Gets the name of the output video codec
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="state">The state.</param>
|
||||||
|
/// <param name="options">The options.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
private string GetVideoCodec(EncodingJobOptions request)
|
internal static string GetVideoEncoder(EncodingJob state, EncodingOptions options)
|
||||||
{
|
{
|
||||||
var codec = request.VideoCodec;
|
var codec = state.OutputVideoCodec;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "libx264";
|
return GetH264Encoder(state, options);
|
||||||
}
|
|
||||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return "libx265";
|
|
||||||
}
|
}
|
||||||
if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -522,7 +534,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return "libtheora";
|
return "libtheora";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (codec ?? string.Empty).ToLower();
|
return codec.ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "copy";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetH264Encoder(EncodingJob state, EncodingOptions options)
|
||||||
|
{
|
||||||
|
if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// It's currently failing on live tv
|
||||||
|
if (state.RunTimeTicks.HasValue)
|
||||||
|
{
|
||||||
|
return "h264_qsv";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "libx264";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool CanStreamCopyVideo(EncodingJobOptions request, MediaStream videoStream)
|
internal static bool CanStreamCopyVideo(EncodingJobOptions request, MediaStream videoStream)
|
||||||
|
|
|
@ -21,14 +21,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
protected override string GetCommandLineArguments(EncodingJob state)
|
protected override string GetCommandLineArguments(EncodingJob state)
|
||||||
{
|
{
|
||||||
// Get the output codec name
|
// Get the output codec name
|
||||||
var videoCodec = state.OutputVideoCodec;
|
var videoCodec = EncodingJobFactory.GetVideoEncoder(state, GetEncodingOptions());
|
||||||
|
|
||||||
var format = string.Empty;
|
var format = string.Empty;
|
||||||
var keyFrame = string.Empty;
|
var keyFrame = string.Empty;
|
||||||
|
|
||||||
if (string.Equals(Path.GetExtension(state.OutputFilePath), ".mp4", StringComparison.OrdinalIgnoreCase) &&
|
if (string.Equals(Path.GetExtension(state.OutputFilePath), ".mp4", StringComparison.OrdinalIgnoreCase))
|
||||||
state.Options.Context == EncodingContext.Streaming)
|
|
||||||
{
|
{
|
||||||
|
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
|
||||||
format = " -f mp4 -movflags frag_keyframe+empty_moov";
|
format = " -f mp4 -movflags frag_keyframe+empty_moov";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,42 +53,49 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// Gets video arguments to pass to ffmpeg
|
/// Gets video arguments to pass to ffmpeg
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="codec">The video codec.</param>
|
/// <param name="videoCodec">The video codec.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
private string GetVideoArguments(EncodingJob state, string codec)
|
private string GetVideoArguments(EncodingJob state, string videoCodec)
|
||||||
{
|
{
|
||||||
var args = "-codec:v:0 " + codec;
|
var args = "-codec:v:0 " + videoCodec;
|
||||||
|
|
||||||
if (state.EnableMpegtsM2TsMode)
|
if (state.EnableMpegtsM2TsMode)
|
||||||
{
|
{
|
||||||
args += " -mpegts_m2ts_mode 1";
|
args += " -mpegts_m2ts_mode 1";
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we can save come cpu cycles by avoiding encoding
|
var isOutputMkv = string.Equals(state.Options.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase);
|
||||||
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
||||||
|
if (state.RunTimeTicks.HasValue)
|
||||||
{
|
{
|
||||||
return state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) ?
|
//args += " -copyts -avoid_negative_ts disabled -start_at_zero";
|
||||||
args + " -bsf:v h264_mp4toannexb" :
|
|
||||||
args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.Options.Context == EncodingContext.Streaming)
|
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
if (state.VideoStream != null && IsH264(state.VideoStream) &&
|
||||||
|
(string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) || isOutputMkv))
|
||||||
|
{
|
||||||
|
args += " -bsf:v h264_mp4toannexb";
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
|
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
|
||||||
5.ToString(UsCulture));
|
5.ToString(UsCulture));
|
||||||
|
|
||||||
args += keyFrameArg;
|
args += keyFrameArg;
|
||||||
}
|
|
||||||
|
|
||||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
|
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
|
||||||
|
|
||||||
// Add resolution params, if specified
|
// Add resolution params, if specified
|
||||||
if (!hasGraphicalSubs)
|
if (!hasGraphicalSubs)
|
||||||
{
|
{
|
||||||
args += GetOutputSizeParam(state, codec);
|
args += GetOutputSizeParam(state, videoCodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
var qualityParam = GetVideoQualityParam(state, codec, false);
|
var qualityParam = GetVideoQualityParam(state, videoCodec, false);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(qualityParam))
|
if (!string.IsNullOrEmpty(qualityParam))
|
||||||
{
|
{
|
||||||
|
@ -98,7 +105,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
// This is for internal graphical subs
|
// This is for internal graphical subs
|
||||||
if (hasGraphicalSubs)
|
if (hasGraphicalSubs)
|
||||||
{
|
{
|
||||||
args += GetGraphicalSubtitleParam(state, codec);
|
args += GetGraphicalSubtitleParam(state, videoCodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
|
@ -118,11 +125,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the output codec name
|
// Get the output codec name
|
||||||
var codec = state.OutputAudioCodec;
|
var codec = EncodingJobFactory.GetAudioEncoder(state);
|
||||||
|
|
||||||
var args = "-codec:a:0 " + codec;
|
var args = "-codec:a:0 " + codec;
|
||||||
|
|
||||||
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,12 @@ namespace MediaBrowser.Model.Configuration
|
||||||
public bool EnableIntrosFromUpcomingStreamingMovies { get; set; }
|
public bool EnableIntrosFromUpcomingStreamingMovies { get; set; }
|
||||||
|
|
||||||
public int TrailerLimit { get; set; }
|
public int TrailerLimit { get; set; }
|
||||||
public string[] Tags { get; set; }
|
|
||||||
|
|
||||||
public CinemaModeConfiguration()
|
public CinemaModeConfiguration()
|
||||||
{
|
{
|
||||||
EnableIntrosParentalControl = true;
|
EnableIntrosParentalControl = true;
|
||||||
EnableIntrosFromSimilarMovies = true;
|
EnableIntrosFromSimilarMovies = true;
|
||||||
TrailerLimit = 2;
|
TrailerLimit = 2;
|
||||||
Tags = new[] { "thx" };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,8 +162,6 @@ namespace MediaBrowser.Model.Configuration
|
||||||
/// <value>The dashboard source path.</value>
|
/// <value>The dashboard source path.</value>
|
||||||
public string DashboardSourcePath { get; set; }
|
public string DashboardSourcePath { get; set; }
|
||||||
|
|
||||||
public bool MergeMetadataAndImagesByName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the image saving convention.
|
/// Gets or sets the image saving convention.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -375,7 +375,12 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
string filename;
|
string filename;
|
||||||
var folderName = item is MusicAlbum || item is MusicArtist ? "folder" : "poster";
|
var folderName = item is MusicAlbum ||
|
||||||
|
item is MusicArtist ||
|
||||||
|
item is PhotoAlbum ||
|
||||||
|
(saveLocally && _config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy) ?
|
||||||
|
"folder" :
|
||||||
|
"poster";
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,6 @@ namespace MediaBrowser.Server.Implementations.Configuration
|
||||||
public ServerConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
public ServerConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
||||||
: base(applicationPaths, logManager, xmlSerializer, fileSystem)
|
: base(applicationPaths, logManager, xmlSerializer, fileSystem)
|
||||||
{
|
{
|
||||||
UpdateItemsByNamePath();
|
|
||||||
UpdateMetadataPath();
|
UpdateMetadataPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +72,6 @@ namespace MediaBrowser.Server.Implementations.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void OnConfigurationUpdated()
|
protected override void OnConfigurationUpdated()
|
||||||
{
|
{
|
||||||
UpdateItemsByNamePath();
|
|
||||||
UpdateMetadataPath();
|
UpdateMetadataPath();
|
||||||
|
|
||||||
base.OnConfigurationUpdated();
|
base.OnConfigurationUpdated();
|
||||||
|
@ -86,19 +84,6 @@ namespace MediaBrowser.Server.Implementations.Configuration
|
||||||
UpdateTranscodingTempPath();
|
UpdateTranscodingTempPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the items by name path.
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateItemsByNamePath()
|
|
||||||
{
|
|
||||||
if (!Configuration.MergeMetadataAndImagesByName)
|
|
||||||
{
|
|
||||||
((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ?
|
|
||||||
null :
|
|
||||||
Configuration.ItemsByNamePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the metadata path.
|
/// Updates the metadata path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -17,6 +17,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
|
using MoreLinq;
|
||||||
|
|
||||||
namespace MediaBrowser.Server.Implementations.Intros
|
namespace MediaBrowser.Server.Implementations.Intros
|
||||||
{
|
{
|
||||||
|
@ -28,8 +29,9 @@ namespace MediaBrowser.Server.Implementations.Intros
|
||||||
private readonly IConfigurationManager _serverConfig;
|
private readonly IConfigurationManager _serverConfig;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
|
|
||||||
public DefaultIntroProvider(ISecurityManager security, IChannelManager channelManager, ILocalizationManager localization, IConfigurationManager serverConfig, ILibraryManager libraryManager, IFileSystem fileSystem)
|
public DefaultIntroProvider(ISecurityManager security, IChannelManager channelManager, ILocalizationManager localization, IConfigurationManager serverConfig, ILibraryManager libraryManager, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
|
||||||
{
|
{
|
||||||
_security = security;
|
_security = security;
|
||||||
_channelManager = channelManager;
|
_channelManager = channelManager;
|
||||||
|
@ -37,6 +39,7 @@ namespace MediaBrowser.Server.Implementations.Intros
|
||||||
_serverConfig = serverConfig;
|
_serverConfig = serverConfig;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
_mediaSourceManager = mediaSourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user)
|
public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user)
|
||||||
|
@ -82,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Intros
|
||||||
{
|
{
|
||||||
IncludeItemTypes = new[] { typeof(Movie).Name }
|
IncludeItemTypes = new[] { typeof(Movie).Name }
|
||||||
|
|
||||||
}, new string[]{});
|
}, new string[] { });
|
||||||
|
|
||||||
var itemsWithTrailers = inputItems
|
var itemsWithTrailers = inputItems
|
||||||
.Where(i =>
|
.Where(i =>
|
||||||
|
@ -164,6 +167,10 @@ namespace MediaBrowser.Server.Implementations.Intros
|
||||||
GetCustomIntros(config) :
|
GetCustomIntros(config) :
|
||||||
new List<IntroInfo>();
|
new List<IntroInfo>();
|
||||||
|
|
||||||
|
var mediaInfoIntros = !string.IsNullOrWhiteSpace(config.MediaInfoIntroPath) ?
|
||||||
|
GetMediaInfoIntros(config, item) :
|
||||||
|
new List<IntroInfo>();
|
||||||
|
|
||||||
var trailerLimit = config.TrailerLimit;
|
var trailerLimit = config.TrailerLimit;
|
||||||
|
|
||||||
// Avoid implicitly captured closure
|
// Avoid implicitly captured closure
|
||||||
|
@ -185,7 +192,8 @@ namespace MediaBrowser.Server.Implementations.Intros
|
||||||
.ThenByDescending(i => (i.IsPlayed ? 0 : 1))
|
.ThenByDescending(i => (i.IsPlayed ? 0 : 1))
|
||||||
.Select(i => i.IntroInfo)
|
.Select(i => i.IntroInfo)
|
||||||
.Take(trailerLimit)
|
.Take(trailerLimit)
|
||||||
.Concat(customIntros.Take(1));
|
.Concat(customIntros.Take(1))
|
||||||
|
.Concat(mediaInfoIntros);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsDuplicate(BaseItem playingContent, BaseItem test)
|
private bool IsDuplicate(BaseItem playingContent, BaseItem test)
|
||||||
|
@ -228,6 +236,134 @@ namespace MediaBrowser.Server.Implementations.Intros
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IntroInfo> GetMediaInfoIntros(CinemaModeConfiguration options, BaseItem item)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var hasMediaSources = item as IHasMediaSources;
|
||||||
|
|
||||||
|
if (hasMediaSources == null)
|
||||||
|
{
|
||||||
|
return new List<IntroInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var mediaSource = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (mediaSource == null)
|
||||||
|
{
|
||||||
|
return new List<IntroInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
|
||||||
|
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
|
||||||
|
|
||||||
|
var allIntros = GetCustomIntroFiles(options, false, true)
|
||||||
|
.OrderBy(i => Guid.NewGuid())
|
||||||
|
.Select(i => new IntroInfo
|
||||||
|
{
|
||||||
|
Path = i
|
||||||
|
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var returnResult = new List<IntroInfo>();
|
||||||
|
|
||||||
|
if (videoStream != null)
|
||||||
|
{
|
||||||
|
returnResult.AddRange(GetMediaInfoIntrosByVideoStream(allIntros, videoStream).Take(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioStream != null)
|
||||||
|
{
|
||||||
|
returnResult.AddRange(GetMediaInfoIntrosByAudioStream(allIntros, audioStream).Take(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
returnResult.AddRange(GetMediaInfoIntrosByTags(allIntros, item.Tags).Take(1));
|
||||||
|
|
||||||
|
return returnResult.DistinctBy(i => i.Path, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
return new List<IntroInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IntroInfo> GetMediaInfoIntrosByVideoStream(List<IntroInfo> allIntros, MediaStream stream)
|
||||||
|
{
|
||||||
|
var codec = stream.Codec;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(codec))
|
||||||
|
{
|
||||||
|
return new List<IntroInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return allIntros
|
||||||
|
.Where(i => IsMatch(i.Path, codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IntroInfo> GetMediaInfoIntrosByAudioStream(List<IntroInfo> allIntros, MediaStream stream)
|
||||||
|
{
|
||||||
|
var codec = stream.Codec;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(codec))
|
||||||
|
{
|
||||||
|
return new List<IntroInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return allIntros
|
||||||
|
.Where(i => IsAudioMatch(i.Path, stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IntroInfo> GetMediaInfoIntrosByTags(List<IntroInfo> allIntros, List<string> tags)
|
||||||
|
{
|
||||||
|
return allIntros
|
||||||
|
.Where(i => tags.Any(t => IsMatch(i.Path, t)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsMatch(string file, string attribute)
|
||||||
|
{
|
||||||
|
var filename = Path.GetFileNameWithoutExtension(file) ?? string.Empty;
|
||||||
|
filename = Normalize(filename);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(filename))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribute = Normalize(attribute);
|
||||||
|
if (string.IsNullOrWhiteSpace(attribute))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Equals(filename, attribute, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Normalize(string value)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAudioMatch(string path, MediaStream stream)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(stream.Codec))
|
||||||
|
{
|
||||||
|
if (IsMatch(path, stream.Codec))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(stream.Profile))
|
||||||
|
{
|
||||||
|
if (IsMatch(path, stream.Profile))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options, bool enableCustomIntros, bool enableMediaInfoIntros)
|
private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options, bool enableCustomIntros, bool enableMediaInfoIntros)
|
||||||
{
|
{
|
||||||
var list = new List<string>();
|
var list = new List<string>();
|
||||||
|
|
|
@ -1536,6 +1536,10 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
video = dbItem;
|
video = dbItem;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -742,7 +742,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None);
|
var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false);
|
||||||
var mediaStreamInfo = result.Item1;
|
var mediaStreamInfo = result.Item1;
|
||||||
var isResourceOpen = true;
|
var isResourceOpen = true;
|
||||||
|
|
||||||
|
@ -771,14 +771,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
||||||
httpRequestOptions.CancellationToken = linkedToken;
|
httpRequestOptions.CancellationToken = linkedToken;
|
||||||
_logger.Info("Writing file to path: " + recordPath);
|
_logger.Info("Writing file to path: " + recordPath);
|
||||||
using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET"))
|
_logger.Info("Opening recording stream from tuner provider");
|
||||||
|
using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
_logger.Info("Opened recording stream from tuner provider");
|
||||||
|
|
||||||
using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
{
|
{
|
||||||
result.Item2.Release();
|
result.Item2.Release();
|
||||||
isResourceOpen = false;
|
isResourceOpen = false;
|
||||||
|
|
||||||
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
|
_logger.Info("Copying recording stream to file stream");
|
||||||
|
|
||||||
|
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "Bitte warten Sie w\u00e4hrend die Emby Datenbank aktualisiert wird. {0}% verarbeitet.",
|
||||||
"AppDeviceValues": "App: {0}, Ger\u00e4t: {1}",
|
"AppDeviceValues": "App: {0}, Ger\u00e4t: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} l\u00e4dt {1} herunter",
|
"UserDownloadingItemWithValues": "{0} l\u00e4dt {1} herunter",
|
||||||
"FolderTypeMixed": "Gemischte Inhalte",
|
"FolderTypeMixed": "Gemischte Inhalte",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "Por favor espere mientras la base de datos de su Servidor Emby es actualizada. {0}% completo.",
|
||||||
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
|
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} esta descargando {1}",
|
"UserDownloadingItemWithValues": "{0} esta descargando {1}",
|
||||||
"FolderTypeMixed": "Contenido mezclado",
|
"FolderTypeMixed": "Contenido mezclado",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "Veuillez patienter pendant que la base de donn\u00e9e de votre Emby Serveur se met \u00e0 jour. Termin\u00e9e \u00e0 {0}%.",
|
||||||
"AppDeviceValues": "Application : {0}, Appareil: {1}",
|
"AppDeviceValues": "Application : {0}, Appareil: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} est en train de t\u00e9l\u00e9charger {1}",
|
"UserDownloadingItemWithValues": "{0} est en train de t\u00e9l\u00e9charger {1}",
|
||||||
"FolderTypeMixed": "Contenus m\u00e9lang\u00e9s",
|
"FolderTypeMixed": "Contenus m\u00e9lang\u00e9s",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "Emby Server \u0434\u0435\u0440\u0435\u043a\u049b\u043e\u0440\u044b\u04a3\u044b\u0437\u0434\u044b\u04a3 \u0436\u0430\u04a3\u0493\u044b\u0440\u0442\u044b\u043b\u0443\u044b\u043d \u043a\u04af\u0442\u0435 \u0442\u04b1\u0440\u044b\u04a3\u044b\u0437. {0} % \u0430\u044f\u049b\u0442\u0430\u043b\u0434\u044b.",
|
||||||
"AppDeviceValues": "\u049a\u043e\u043b\u0434\u0430\u043d\u0431\u0430: {0}, \u049a\u04b1\u0440\u044b\u043b\u0493\u044b: {1}",
|
"AppDeviceValues": "\u049a\u043e\u043b\u0434\u0430\u043d\u0431\u0430: {0}, \u049a\u04b1\u0440\u044b\u043b\u0493\u044b: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} \u043c\u044b\u043d\u0430\u043d\u044b \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u0443\u0434\u0430: {1}",
|
"UserDownloadingItemWithValues": "{0} \u043c\u044b\u043d\u0430\u043d\u044b \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u0443\u0434\u0430: {1}",
|
||||||
"FolderTypeMixed": "\u0410\u0440\u0430\u043b\u0430\u0441 \u043c\u0430\u0437\u043c\u04b1\u043d",
|
"FolderTypeMixed": "\u0410\u0440\u0430\u043b\u0430\u0441 \u043c\u0430\u0437\u043c\u04b1\u043d",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "Even geduld svp terwijl de Emby Server database ge-upgrade wordt. {0}% gereed.",
|
||||||
"AppDeviceValues": "App: {0}, Apparaat: {1}",
|
"AppDeviceValues": "App: {0}, Apparaat: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} download {1}",
|
"UserDownloadingItemWithValues": "{0} download {1}",
|
||||||
"FolderTypeMixed": "Gemengde inhoud",
|
"FolderTypeMixed": "Gemengde inhoud",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "Por favor, aguarde enquanto a base de dados do Servidor Emby \u00e9 atualizada. {0}% completado.",
|
||||||
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
|
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} est\u00e1 fazendo download de {1}",
|
"UserDownloadingItemWithValues": "{0} est\u00e1 fazendo download de {1}",
|
||||||
"FolderTypeMixed": "Conte\u00fado misto",
|
"FolderTypeMixed": "Conte\u00fado misto",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
|
"DbUpgradeMessage": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0430\u0448\u0435\u043c Emby Server \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f. {0} % \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e.",
|
||||||
"AppDeviceValues": "\u041f\u0440\u0438\u043b.: {0}, \u0423\u0441\u0442\u0440.: {1}",
|
"AppDeviceValues": "\u041f\u0440\u0438\u043b.: {0}, \u0423\u0441\u0442\u0440.: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 {1}",
|
"UserDownloadingItemWithValues": "{0} \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 {1}",
|
||||||
"FolderTypeMixed": "\u0420\u0430\u0437\u043d\u043e\u0442\u0438\u043f\u043d\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435",
|
"FolderTypeMixed": "\u0420\u0430\u0437\u043d\u043e\u0442\u0438\u043f\u043d\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435",
|
||||||
|
|
|
@ -11,6 +11,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Model.MediaInfo;
|
||||||
|
|
||||||
namespace MediaBrowser.Server.Implementations.Sync
|
namespace MediaBrowser.Server.Implementations.Sync
|
||||||
{
|
{
|
||||||
|
|
|
@ -356,6 +356,11 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "st");
|
DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "st");
|
||||||
DeleteFoldersByName(Path.Combine(bowerPath, "Swiper"), "src");
|
DeleteFoldersByName(Path.Combine(bowerPath, "Swiper"), "src");
|
||||||
|
|
||||||
|
_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "marked"), true);
|
||||||
|
_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "marked-element"), true);
|
||||||
|
_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "prism"), true);
|
||||||
|
_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "prism-element"), true);
|
||||||
|
|
||||||
if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// Delete things that are unneeded in an attempt to keep the output as trim as possible
|
// Delete things that are unneeded in an attempt to keep the output as trim as possible
|
||||||
|
|
|
@ -107,6 +107,9 @@
|
||||||
<Content Include="dashboard-ui\components\recordingcreator\recordingcreator.template.html">
|
<Content Include="dashboard-ui\components\recordingcreator\recordingcreator.template.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\components\remotecontrol.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\components\remotecontrolautoplay.js">
|
<Content Include="dashboard-ui\components\remotecontrolautoplay.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -161,9 +164,6 @@
|
||||||
<Content Include="dashboard-ui\components\playlisteditor\playlisteditor.js">
|
<Content Include="dashboard-ui\components\playlisteditor\playlisteditor.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\components\prompt.js">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="dashboard-ui\components\tvguide\tvguide.js">
|
<Content Include="dashboard-ui\components\tvguide\tvguide.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
Loading…
Reference in a new issue