commit
7b67ec6854
|
@ -158,22 +158,19 @@ namespace Emby.Drawing
|
||||||
}
|
}
|
||||||
|
|
||||||
var dateModified = options.Image.DateModified;
|
var dateModified = options.Image.DateModified;
|
||||||
var length = options.Image.Length;
|
|
||||||
|
|
||||||
if (options.CropWhiteSpace)
|
if (options.CropWhiteSpace)
|
||||||
{
|
{
|
||||||
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified, length).ConfigureAwait(false);
|
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
||||||
|
|
||||||
originalImagePath = tuple.Item1;
|
originalImagePath = tuple.Item1;
|
||||||
dateModified = tuple.Item2;
|
dateModified = tuple.Item2;
|
||||||
length = tuple.Item3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.Enhancers.Count > 0)
|
if (options.Enhancers.Count > 0)
|
||||||
{
|
{
|
||||||
var tuple = await GetEnhancedImage(new ItemImageInfo
|
var tuple = await GetEnhancedImage(new ItemImageInfo
|
||||||
{
|
{
|
||||||
Length = length,
|
|
||||||
DateModified = dateModified,
|
DateModified = dateModified,
|
||||||
Type = options.Image.Type,
|
Type = options.Image.Type,
|
||||||
Path = originalImagePath
|
Path = originalImagePath
|
||||||
|
@ -182,7 +179,6 @@ namespace Emby.Drawing
|
||||||
|
|
||||||
originalImagePath = tuple.Item1;
|
originalImagePath = tuple.Item1;
|
||||||
dateModified = tuple.Item2;
|
dateModified = tuple.Item2;
|
||||||
length = tuple.Item3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalImageSize = GetImageSize(originalImagePath, dateModified);
|
var originalImageSize = GetImageSize(originalImagePath, dateModified);
|
||||||
|
@ -199,7 +195,7 @@ namespace Emby.Drawing
|
||||||
var quality = options.Quality ?? 90;
|
var quality = options.Quality ?? 90;
|
||||||
|
|
||||||
var outputFormat = GetOutputFormat(options.OutputFormat);
|
var outputFormat = GetOutputFormat(options.OutputFormat);
|
||||||
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, length, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor);
|
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor);
|
||||||
|
|
||||||
var semaphore = GetLock(cacheFilePath);
|
var semaphore = GetLock(cacheFilePath);
|
||||||
|
|
||||||
|
@ -240,11 +236,10 @@ namespace Emby.Drawing
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Crops whitespace from an image, caches the result, and returns the cached path
|
/// Crops whitespace from an image, caches the result, and returns the cached path
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<Tuple<string, DateTime, long>> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified, long length)
|
private async Task<Tuple<string, DateTime>> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified)
|
||||||
{
|
{
|
||||||
var name = originalImagePath;
|
var name = originalImagePath;
|
||||||
name += "datemodified=" + dateModified.Ticks;
|
name += "datemodified=" + dateModified.Ticks;
|
||||||
name += "length=" + length;
|
|
||||||
|
|
||||||
var croppedImagePath = GetCachePath(CroppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath));
|
var croppedImagePath = GetCachePath(CroppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath));
|
||||||
|
|
||||||
|
@ -270,7 +265,7 @@ namespace Emby.Drawing
|
||||||
// We have to have a catch-all here because some of the .net image methods throw a plain old Exception
|
// We have to have a catch-all here because some of the .net image methods throw a plain old Exception
|
||||||
_logger.ErrorException("Error cropping image {0}", ex, originalImagePath);
|
_logger.ErrorException("Error cropping image {0}", ex, originalImagePath);
|
||||||
|
|
||||||
return new Tuple<string, DateTime, long>(originalImagePath, dateModified, length);
|
return new Tuple<string, DateTime>(originalImagePath, dateModified);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -280,11 +275,9 @@ namespace Emby.Drawing
|
||||||
return GetResult(croppedImagePath);
|
return GetResult(croppedImagePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tuple<string, DateTime, long> GetResult(string path)
|
private Tuple<string, DateTime> GetResult(string path)
|
||||||
{
|
{
|
||||||
var file = new FileInfo(path);
|
return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path));
|
||||||
|
|
||||||
return new Tuple<string, DateTime, long>(path, _fileSystem.GetLastWriteTimeUtc(file), file.Length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -295,7 +288,7 @@ namespace Emby.Drawing
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cache file path based on a set of parameters
|
/// Gets the cache file path based on a set of parameters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, long length, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor)
|
private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor)
|
||||||
{
|
{
|
||||||
var filename = originalPath;
|
var filename = originalPath;
|
||||||
|
|
||||||
|
@ -306,7 +299,6 @@ namespace Emby.Drawing
|
||||||
filename += "quality=" + quality;
|
filename += "quality=" + quality;
|
||||||
|
|
||||||
filename += "datemodified=" + dateModified.Ticks;
|
filename += "datemodified=" + dateModified.Ticks;
|
||||||
filename += "length=" + length;
|
|
||||||
|
|
||||||
filename += "f=" + format;
|
filename += "f=" + format;
|
||||||
|
|
||||||
|
@ -492,17 +484,16 @@ namespace Emby.Drawing
|
||||||
var originalImagePath = image.Path;
|
var originalImagePath = image.Path;
|
||||||
var dateModified = image.DateModified;
|
var dateModified = image.DateModified;
|
||||||
var imageType = image.Type;
|
var imageType = image.Type;
|
||||||
var length = image.Length;
|
|
||||||
|
|
||||||
// Optimization
|
// Optimization
|
||||||
if (imageEnhancers.Count == 0)
|
if (imageEnhancers.Count == 0)
|
||||||
{
|
{
|
||||||
return (originalImagePath + dateModified.Ticks + string.Empty + length).GetMD5().ToString("N");
|
return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache name is created with supported enhancers combined with the last config change so we pick up new config changes
|
// Cache name is created with supported enhancers combined with the last config change so we pick up new config changes
|
||||||
var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
|
var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
|
||||||
cacheKeys.Add(originalImagePath + dateModified.Ticks + string.Empty + length);
|
cacheKeys.Add(originalImagePath + dateModified.Ticks);
|
||||||
|
|
||||||
return string.Join("|", cacheKeys.ToArray()).GetMD5().ToString("N");
|
return string.Join("|", cacheKeys.ToArray()).GetMD5().ToString("N");
|
||||||
}
|
}
|
||||||
|
@ -525,7 +516,7 @@ namespace Emby.Drawing
|
||||||
return result.Item1;
|
return result.Item1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Tuple<string, DateTime, long>> GetEnhancedImage(ItemImageInfo image,
|
private async Task<Tuple<string, DateTime>> GetEnhancedImage(ItemImageInfo image,
|
||||||
IHasImages item,
|
IHasImages item,
|
||||||
int imageIndex,
|
int imageIndex,
|
||||||
List<IImageEnhancer> enhancers)
|
List<IImageEnhancer> enhancers)
|
||||||
|
@ -533,7 +524,6 @@ namespace Emby.Drawing
|
||||||
var originalImagePath = image.Path;
|
var originalImagePath = image.Path;
|
||||||
var dateModified = image.DateModified;
|
var dateModified = image.DateModified;
|
||||||
var imageType = image.Type;
|
var imageType = image.Type;
|
||||||
var length = image.Length;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -553,7 +543,7 @@ namespace Emby.Drawing
|
||||||
_logger.Error("Error enhancing image", ex);
|
_logger.Error("Error enhancing image", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Tuple<string, DateTime, long>(originalImagePath, dateModified, length);
|
return new Tuple<string, DateTime>(originalImagePath, dateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Plugins;
|
using MediaBrowser.Controller.Plugins;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
|
@ -40,6 +41,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
private readonly ISessionManager _sessionManager;
|
private readonly ISessionManager _sessionManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
|
|
||||||
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
|
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
@ -49,12 +51,15 @@ namespace MediaBrowser.Api
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="logger">The logger.</param>
|
||||||
/// <param name="sessionManager">The session manager.</param>
|
/// <param name="sessionManager">The session manager.</param>
|
||||||
/// <param name="config">The configuration.</param>
|
/// <param name="config">The configuration.</param>
|
||||||
public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem)
|
/// <param name="fileSystem">The file system.</param>
|
||||||
|
/// <param name="mediaSourceManager">The media source manager.</param>
|
||||||
|
public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
|
||||||
{
|
{
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
_mediaSourceManager = mediaSourceManager;
|
||||||
|
|
||||||
Instance = this;
|
Instance = this;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +119,7 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
var jobCount = _activeTranscodingJobs.Count;
|
var jobCount = _activeTranscodingJobs.Count;
|
||||||
|
|
||||||
Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, path => true));
|
Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, false, path => true));
|
||||||
|
|
||||||
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
|
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
|
||||||
if (jobCount > 0)
|
if (jobCount > 0)
|
||||||
|
@ -133,6 +138,7 @@ namespace MediaBrowser.Api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <param name="playSessionId">The play session identifier.</param>
|
/// <param name="playSessionId">The play session identifier.</param>
|
||||||
|
/// <param name="liveStreamId">The live stream identifier.</param>
|
||||||
/// <param name="transcodingJobId">The transcoding job identifier.</param>
|
/// <param name="transcodingJobId">The transcoding job identifier.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="process">The process.</param>
|
/// <param name="process">The process.</param>
|
||||||
|
@ -142,6 +148,7 @@ namespace MediaBrowser.Api
|
||||||
/// <returns>TranscodingJob.</returns>
|
/// <returns>TranscodingJob.</returns>
|
||||||
public TranscodingJob OnTranscodeBeginning(string path,
|
public TranscodingJob OnTranscodeBeginning(string path,
|
||||||
string playSessionId,
|
string playSessionId,
|
||||||
|
string liveStreamId,
|
||||||
string transcodingJobId,
|
string transcodingJobId,
|
||||||
TranscodingJobType type,
|
TranscodingJobType type,
|
||||||
Process process,
|
Process process,
|
||||||
|
@ -160,7 +167,8 @@ namespace MediaBrowser.Api
|
||||||
DeviceId = deviceId,
|
DeviceId = deviceId,
|
||||||
CancellationTokenSource = cancellationTokenSource,
|
CancellationTokenSource = cancellationTokenSource,
|
||||||
Id = transcodingJobId,
|
Id = transcodingJobId,
|
||||||
PlaySessionId = playSessionId
|
PlaySessionId = playSessionId,
|
||||||
|
LiveStreamId = liveStreamId
|
||||||
};
|
};
|
||||||
|
|
||||||
_activeTranscodingJobs.Add(job);
|
_activeTranscodingJobs.Add(job);
|
||||||
|
@ -323,7 +331,7 @@ namespace MediaBrowser.Api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PingTimer(TranscodingJob job, bool isProgressCheckIn)
|
private async void PingTimer(TranscodingJob job, bool isProgressCheckIn)
|
||||||
{
|
{
|
||||||
if (job.HasExited)
|
if (job.HasExited)
|
||||||
{
|
{
|
||||||
|
@ -331,7 +339,6 @@ namespace MediaBrowser.Api
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Lower this hls timeout
|
|
||||||
var timerDuration = job.Type == TranscodingJobType.Progressive ?
|
var timerDuration = job.Type == TranscodingJobType.Progressive ?
|
||||||
1000 :
|
1000 :
|
||||||
1800000;
|
1800000;
|
||||||
|
@ -339,17 +346,32 @@ namespace MediaBrowser.Api
|
||||||
// We can really reduce the timeout for apps that are using the newer api
|
// We can really reduce the timeout for apps that are using the newer api
|
||||||
if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive)
|
if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive)
|
||||||
{
|
{
|
||||||
timerDuration = 20000;
|
timerDuration = 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
job.PingTimeout = timerDuration;
|
||||||
|
job.LastPingDate = DateTime.UtcNow;
|
||||||
|
|
||||||
// Don't start the timer for playback checkins with progressive streaming
|
// Don't start the timer for playback checkins with progressive streaming
|
||||||
if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
|
if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
|
||||||
{
|
{
|
||||||
job.StartKillTimer(timerDuration, OnTranscodeKillTimerStopped);
|
job.StartKillTimer(OnTranscodeKillTimerStopped);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
job.ChangeKillTimerIfStarted(timerDuration);
|
job.ChangeKillTimerIfStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(job.LiveStreamId))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _mediaSourceManager.PingLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("Error closing live stream", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,9 +383,20 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
var job = (TranscodingJob)state;
|
var job = (TranscodingJob)state;
|
||||||
|
|
||||||
|
if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
|
||||||
|
{
|
||||||
|
var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds;
|
||||||
|
|
||||||
|
if (timeSinceLastPing < job.PingTimeout)
|
||||||
|
{
|
||||||
|
job.StartKillTimer(OnTranscodeKillTimerStopped, job.PingTimeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
|
Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
|
||||||
|
|
||||||
KillTranscodingJob(job, path => true);
|
KillTranscodingJob(job, true, path => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -411,7 +444,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
foreach (var job in jobs)
|
foreach (var job in jobs)
|
||||||
{
|
{
|
||||||
KillTranscodingJob(job, deleteFiles);
|
KillTranscodingJob(job, false, deleteFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,8 +452,9 @@ namespace MediaBrowser.Api
|
||||||
/// Kills the transcoding job.
|
/// Kills the transcoding job.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="job">The job.</param>
|
/// <param name="job">The job.</param>
|
||||||
|
/// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param>
|
||||||
/// <param name="delete">The delete.</param>
|
/// <param name="delete">The delete.</param>
|
||||||
private void KillTranscodingJob(TranscodingJob job, Func<string, bool> delete)
|
private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
|
||||||
{
|
{
|
||||||
job.DisposeKillTimer();
|
job.DisposeKillTimer();
|
||||||
|
|
||||||
|
@ -470,6 +504,18 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
|
DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("Error closing live stream for {0}", ex, job.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
|
private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
|
||||||
|
@ -578,6 +624,11 @@ namespace MediaBrowser.Api
|
||||||
/// <value>The play session identifier.</value>
|
/// <value>The play session identifier.</value>
|
||||||
public string PlaySessionId { get; set; }
|
public string PlaySessionId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Gets or sets the live stream identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The live stream identifier.</value>
|
||||||
|
public string LiveStreamId { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// Gets or sets the path.
|
/// Gets or sets the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The path.</value>
|
/// <value>The path.</value>
|
||||||
|
@ -627,6 +678,9 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
private readonly object _timerLock = new object();
|
private readonly object _timerLock = new object();
|
||||||
|
|
||||||
|
public DateTime LastPingDate { get; set; }
|
||||||
|
public int PingTimeout { get; set; }
|
||||||
|
|
||||||
public TranscodingJob(ILogger logger)
|
public TranscodingJob(ILogger logger)
|
||||||
{
|
{
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
|
@ -655,7 +709,12 @@ namespace MediaBrowser.Api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartKillTimer(int intervalMs, TimerCallback callback)
|
public void StartKillTimer(TimerCallback callback)
|
||||||
|
{
|
||||||
|
StartKillTimer(callback, PingTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartKillTimer(TimerCallback callback, int intervalMs)
|
||||||
{
|
{
|
||||||
CheckHasExited();
|
CheckHasExited();
|
||||||
|
|
||||||
|
@ -674,7 +733,7 @@ namespace MediaBrowser.Api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeKillTimerIfStarted(int intervalMs)
|
public void ChangeKillTimerIfStarted()
|
||||||
{
|
{
|
||||||
CheckHasExited();
|
CheckHasExited();
|
||||||
|
|
||||||
|
@ -682,6 +741,8 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
if (KillTimer != null)
|
if (KillTimer != null)
|
||||||
{
|
{
|
||||||
|
var intervalMs = PingTimeout;
|
||||||
|
|
||||||
Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
||||||
KillTimer.Change(intervalMs, Timeout.Infinite);
|
KillTimer.Change(intervalMs, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1010,6 +1010,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
||||||
state.Request.PlaySessionId,
|
state.Request.PlaySessionId,
|
||||||
|
state.MediaSource.LiveStreamId,
|
||||||
transcodingId,
|
transcodingId,
|
||||||
TranscodingJobType,
|
TranscodingJobType,
|
||||||
process,
|
process,
|
||||||
|
|
|
@ -543,7 +543,9 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.VideoRequest.VideoBitRate.HasValue;
|
// Having problems in android
|
||||||
|
return false;
|
||||||
|
//return state.VideoRequest.VideoBitRate.HasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup)
|
private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup)
|
||||||
|
|
|
@ -88,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
|
|
||||||
var views = user.RootFolder
|
var views = user.RootFolder
|
||||||
.GetChildren(user, true)
|
.GetChildren(user, true)
|
||||||
.OfType<CollectionFolder>()
|
.OfType<ICollectionFolder>()
|
||||||
.Where(i => IsEligibleForSpecialView(i))
|
.Where(i => IsEligibleForSpecialView(i))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -105,9 +105,9 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
return ToOptimizedResult(list);
|
return ToOptimizedResult(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsEligibleForSpecialView(CollectionFolder view)
|
private bool IsEligibleForSpecialView(ICollectionFolder view)
|
||||||
{
|
{
|
||||||
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music };
|
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
|
||||||
|
|
||||||
return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1512,7 +1512,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
image.Path = file.FullName;
|
image.Path = file.FullName;
|
||||||
image.DateModified = imageInfo.DateModified;
|
image.DateModified = imageInfo.DateModified;
|
||||||
image.Length = imageInfo.Length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1622,14 +1621,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileInfo = new FileInfo(path);
|
|
||||||
|
|
||||||
return new ItemImageInfo
|
return new ItemImageInfo
|
||||||
{
|
{
|
||||||
Path = path,
|
Path = path,
|
||||||
DateModified = FileSystem.GetLastWriteTimeUtc(fileInfo),
|
DateModified = FileSystem.GetLastWriteTimeUtc(path),
|
||||||
Type = imageType,
|
Type = imageType
|
||||||
Length = fileInfo.Length
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1690,7 +1686,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
||||||
existing.Length = ((FileInfo)newImage).Length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1716,8 +1711,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
Path = file.FullName,
|
Path = file.FullName,
|
||||||
Type = type,
|
Type = type,
|
||||||
DateModified = FileSystem.GetLastWriteTimeUtc(file),
|
DateModified = FileSystem.GetLastWriteTimeUtc(file)
|
||||||
Length = ((FileInfo)file).Length
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1756,15 +1750,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
FileSystem.SwapFiles(path1, path2);
|
FileSystem.SwapFiles(path1, path2);
|
||||||
|
|
||||||
var file1 = new FileInfo(info1.Path);
|
|
||||||
var file2 = new FileInfo(info2.Path);
|
|
||||||
|
|
||||||
// Refresh these values
|
// Refresh these values
|
||||||
info1.DateModified = FileSystem.GetLastWriteTimeUtc(file1);
|
info1.DateModified = FileSystem.GetLastWriteTimeUtc(info1.Path);
|
||||||
info2.DateModified = FileSystem.GetLastWriteTimeUtc(file2);
|
info2.DateModified = FileSystem.GetLastWriteTimeUtc(info2.Path);
|
||||||
|
|
||||||
info1.Length = file1.Length;
|
|
||||||
info2.Length = file2.Length;
|
|
||||||
|
|
||||||
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
|
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <value>The path.</value>
|
/// <value>The path.</value>
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the length.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The length.</value>
|
|
||||||
public long Length { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type.
|
/// Gets or sets the type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -121,7 +121,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
case CollectionType.Books:
|
case CollectionType.Books:
|
||||||
case CollectionType.Photos:
|
|
||||||
case CollectionType.HomeVideos:
|
case CollectionType.HomeVideos:
|
||||||
case CollectionType.MusicVideos:
|
case CollectionType.MusicVideos:
|
||||||
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
|
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
|
||||||
|
@ -138,6 +137,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
case CollectionType.BoxSets:
|
case CollectionType.BoxSets:
|
||||||
return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
|
return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
|
||||||
|
|
||||||
|
case CollectionType.Photos:
|
||||||
|
return await GetPhotosView(queryParent, user, query).ConfigureAwait(false);
|
||||||
|
|
||||||
case CollectionType.TvShows:
|
case CollectionType.TvShows:
|
||||||
return await GetTvView(queryParent, user, query).ConfigureAwait(false);
|
return await GetTvView(queryParent, user, query).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -247,16 +249,16 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return GetFavoriteSongs(queryParent, user, query);
|
return GetFavoriteSongs(queryParent, user, query);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
if (queryParent is UserView)
|
|
||||||
{
|
{
|
||||||
return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
|
if (queryParent is UserView)
|
||||||
|
{
|
||||||
|
return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,6 +647,19 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}), parent, query);
|
}), parent, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<QueryResult<BaseItem>> GetPhotosView(Folder queryParent, User user, InternalItemsQuery query)
|
||||||
|
{
|
||||||
|
if (query.Recursive)
|
||||||
|
{
|
||||||
|
var mediaTypes = new[] { MediaType.Video, MediaType.Photo };
|
||||||
|
var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Photos, string.Empty }, i => (i is PhotoAlbum || mediaTypes.Contains(i.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) && FilterItem(i, query));
|
||||||
|
|
||||||
|
return PostFilterAndSort(items, queryParent, null, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<QueryResult<BaseItem>> GetTvView(Folder parent, User user, InternalItemsQuery query)
|
private async Task<QueryResult<BaseItem>> GetTvView(Folder parent, User user, InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
if (query.Recursive)
|
if (query.Recursive)
|
||||||
|
|
|
@ -309,6 +309,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="parentId">The parent identifier.</param>
|
/// <param name="parentId">The parent identifier.</param>
|
||||||
/// <param name="viewType">Type of the view.</param>
|
/// <param name="viewType">Type of the view.</param>
|
||||||
/// <param name="sortName">Name of the sort.</param>
|
/// <param name="sortName">Name of the sort.</param>
|
||||||
|
/// <param name="uniqueId">The unique identifier.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task<UserView>.</returns>
|
/// <returns>Task<UserView>.</returns>
|
||||||
Task<UserView> GetNamedView(User user,
|
Task<UserView> GetNamedView(User user,
|
||||||
|
@ -316,6 +317,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
string parentId,
|
string parentId,
|
||||||
string viewType,
|
string viewType,
|
||||||
string sortName,
|
string sortName,
|
||||||
|
string uniqueId,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.LiveTv
|
namespace MediaBrowser.Controller.LiveTv
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,12 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
/// <value>The identifier.</value>
|
/// <value>The identifier.</value>
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the URL.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The URL.</value>
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the status.
|
/// Gets or sets the status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -488,6 +488,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
||||||
<Link>Dto\ItemIndex.cs</Link>
|
<Link>Dto\ItemIndex.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs">
|
||||||
|
<Link>Dto\ItemLayout.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
|
||||||
<Link>Dto\MediaSourceInfo.cs</Link>
|
<Link>Dto\MediaSourceInfo.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -453,6 +453,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
||||||
<Link>Dto\ItemIndex.cs</Link>
|
<Link>Dto\ItemIndex.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs">
|
||||||
|
<Link>Dto\ItemLayout.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
|
||||||
<Link>Dto\MediaSourceInfo.cs</Link>
|
<Link>Dto\MediaSourceInfo.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
foreach (var server in servers)
|
foreach (ServerInfo server in servers)
|
||||||
{
|
{
|
||||||
if (StringHelper.EqualsIgnoreCase(id, server.Id))
|
if (StringHelper.EqualsIgnoreCase(id, server.Id))
|
||||||
{
|
{
|
||||||
|
@ -110,5 +110,18 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServerInfo GetServer(string id)
|
||||||
|
{
|
||||||
|
foreach (ServerInfo server in Servers)
|
||||||
|
{
|
||||||
|
if (StringHelper.EqualsIgnoreCase(id, server.Id))
|
||||||
|
{
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
|
|
||||||
// The profile describes what the device supports
|
// The profile describes what the device supports
|
||||||
// If device requirements are satisfied then allow both direct stream and direct play
|
// If device requirements are satisfied then allow both direct stream and direct play
|
||||||
if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
|
if (item.SupportsDirectPlay && IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
|
||||||
{
|
{
|
||||||
playMethods.Add(PlayMethod.DirectPlay);
|
playMethods.Add(PlayMethod.DirectPlay);
|
||||||
}
|
}
|
||||||
|
@ -456,10 +456,8 @@ namespace MediaBrowser.Model.Dlna
|
||||||
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
|
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!playlistItem.AudioBitrate.HasValue)
|
int audioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
|
||||||
{
|
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
|
||||||
playlistItem.AudioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
|
|
||||||
}
|
|
||||||
|
|
||||||
int? maxBitrateSetting = options.GetMaxBitrate();
|
int? maxBitrateSetting = options.GetMaxBitrate();
|
||||||
// Honor max rate
|
// Honor max rate
|
||||||
|
@ -472,9 +470,9 @@ namespace MediaBrowser.Model.Dlna
|
||||||
videoBitrate -= playlistItem.AudioBitrate.Value;
|
videoBitrate -= playlistItem.AudioBitrate.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the video bitrate is lower than bitrate settings but at least 64k
|
||||||
int currentValue = playlistItem.VideoBitrate ?? videoBitrate;
|
int currentValue = playlistItem.VideoBitrate ?? videoBitrate;
|
||||||
|
playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000);
|
||||||
playlistItem.VideoBitrate = Math.Min(videoBitrate, currentValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,7 +638,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEligibleForDirectPlay)
|
if (isEligibleForDirectPlay && mediaSource.SupportsDirectPlay)
|
||||||
{
|
{
|
||||||
if (mediaSource.Protocol == MediaProtocol.Http)
|
if (mediaSource.Protocol == MediaProtocol.Http)
|
||||||
{
|
{
|
||||||
|
@ -659,12 +657,9 @@ namespace MediaBrowser.Model.Dlna
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEligibleForDirectStream)
|
if (isEligibleForDirectStream && mediaSource.SupportsDirectStream)
|
||||||
{
|
{
|
||||||
if (mediaSource.SupportsDirectStream)
|
return PlayMethod.DirectStream;
|
||||||
{
|
|
||||||
return PlayMethod.DirectStream;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
72
MediaBrowser.Model/Dto/ItemLayout.cs
Normal file
72
MediaBrowser.Model/Dto/ItemLayout.cs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Model.Dto
|
||||||
|
{
|
||||||
|
public static class ItemLayout
|
||||||
|
{
|
||||||
|
public static double? GetDisplayAspectRatio(BaseItemDto item)
|
||||||
|
{
|
||||||
|
List<BaseItemDto> items = new List<BaseItemDto>();
|
||||||
|
items.Add(item);
|
||||||
|
return GetDisplayAspectRatio(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double? GetDisplayAspectRatio(List<BaseItemDto> items)
|
||||||
|
{
|
||||||
|
List<double> values = new List<double>();
|
||||||
|
|
||||||
|
foreach (BaseItemDto item in items)
|
||||||
|
{
|
||||||
|
if (item.PrimaryImageAspectRatio.HasValue)
|
||||||
|
{
|
||||||
|
values.Add(item.PrimaryImageAspectRatio.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
values.Sort();
|
||||||
|
|
||||||
|
double halfDouble = values.Count;
|
||||||
|
halfDouble /= 2;
|
||||||
|
int half = Convert.ToInt32(Math.Floor(halfDouble));
|
||||||
|
|
||||||
|
double result;
|
||||||
|
|
||||||
|
if (values.Count % 2 > 0)
|
||||||
|
result = values[half];
|
||||||
|
else
|
||||||
|
result = (values[half - 1] + values[half]) / 2.0;
|
||||||
|
|
||||||
|
// If really close to 2:3 (poster image), just return 2:3
|
||||||
|
if (Math.Abs(0.66666666667 - result) <= .15)
|
||||||
|
{
|
||||||
|
return 0.66666666667;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If really close to 16:9 (episode image), just return 16:9
|
||||||
|
if (Math.Abs(1.777777778 - result) <= .2)
|
||||||
|
{
|
||||||
|
return 1.777777778;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If really close to 1 (square image), just return 1
|
||||||
|
if (Math.Abs(1 - result) <= .15)
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If really close to 4:3 (poster image), just return 2:3
|
||||||
|
if (Math.Abs(1.33333333333 - result) <= .15)
|
||||||
|
{
|
||||||
|
return 1.33333333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,12 @@ namespace MediaBrowser.Model.LiveTv
|
||||||
/// <value>The identifier.</value>
|
/// <value>The identifier.</value>
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the URL.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The URL.</value>
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the status.
|
/// Gets or sets the status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
<Compile Include="Drawing\ImageOrientation.cs" />
|
<Compile Include="Drawing\ImageOrientation.cs" />
|
||||||
<Compile Include="Dto\IHasServerId.cs" />
|
<Compile Include="Dto\IHasServerId.cs" />
|
||||||
<Compile Include="Dto\IHasSyncInfo.cs" />
|
<Compile Include="Dto\IHasSyncInfo.cs" />
|
||||||
|
<Compile Include="Dto\ItemLayout.cs" />
|
||||||
<Compile Include="Dto\MetadataEditorInfo.cs" />
|
<Compile Include="Dto\MetadataEditorInfo.cs" />
|
||||||
<Compile Include="Dto\NameIdPair.cs" />
|
<Compile Include="Dto\NameIdPair.cs" />
|
||||||
<Compile Include="Dto\NameValuePair.cs" />
|
<Compile Include="Dto\NameValuePair.cs" />
|
||||||
|
|
|
@ -384,7 +384,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
currentImage.DateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
|
currentImage.DateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
|
||||||
currentImage.Length = ((FileInfo) image.FileInfo).Length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -482,6 +482,11 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
protected virtual bool IsFullLocalMetadata(TItemType item)
|
protected virtual bool IsFullLocalMetadata(TItemType item)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(item.Name))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
FetchEmbeddedInfo(video, mediaInfo);
|
FetchEmbeddedInfo(video, mediaInfo, options);
|
||||||
|
|
||||||
video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270);
|
video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270);
|
||||||
|
|
||||||
|
@ -358,11 +358,13 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return _blurayExaminer.GetDiscInfo(path);
|
return _blurayExaminer.GetDiscInfo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data)
|
private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
|
||||||
{
|
{
|
||||||
|
var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
|
||||||
|
|
||||||
if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
|
if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(data.OfficialRating))
|
if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
|
||||||
{
|
{
|
||||||
video.OfficialRating = data.OfficialRating;
|
video.OfficialRating = data.OfficialRating;
|
||||||
}
|
}
|
||||||
|
@ -370,54 +372,75 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
if (!video.LockedFields.Contains(MetadataFields.Cast))
|
if (!video.LockedFields.Contains(MetadataFields.Cast))
|
||||||
{
|
{
|
||||||
video.People.Clear();
|
if (video.People.Count == 0 || isFullRefresh)
|
||||||
|
|
||||||
foreach (var person in data.People)
|
|
||||||
{
|
{
|
||||||
video.AddPerson(new PersonInfo
|
video.People.Clear();
|
||||||
|
|
||||||
|
foreach (var person in data.People)
|
||||||
{
|
{
|
||||||
Name = person.Name,
|
video.AddPerson(new PersonInfo
|
||||||
Type = person.Type,
|
{
|
||||||
Role = person.Role
|
Name = person.Name,
|
||||||
});
|
Type = person.Type,
|
||||||
|
Role = person.Role
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video.LockedFields.Contains(MetadataFields.Genres))
|
if (!video.LockedFields.Contains(MetadataFields.Genres))
|
||||||
{
|
{
|
||||||
video.Genres.Clear();
|
if (video.Genres.Count == 0 || isFullRefresh)
|
||||||
|
|
||||||
foreach (var genre in data.Genres)
|
|
||||||
{
|
{
|
||||||
video.AddGenre(genre);
|
video.Genres.Clear();
|
||||||
|
|
||||||
|
foreach (var genre in data.Genres)
|
||||||
|
{
|
||||||
|
video.AddGenre(genre);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video.LockedFields.Contains(MetadataFields.Studios))
|
if (!video.LockedFields.Contains(MetadataFields.Studios))
|
||||||
{
|
{
|
||||||
video.Studios.Clear();
|
if (video.Studios.Count == 0 || isFullRefresh)
|
||||||
|
|
||||||
foreach (var studio in data.Studios)
|
|
||||||
{
|
{
|
||||||
video.AddStudio(studio);
|
video.Studios.Clear();
|
||||||
|
|
||||||
|
foreach (var studio in data.Studios)
|
||||||
|
{
|
||||||
|
video.AddStudio(studio);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.ProductionYear.HasValue)
|
if (data.ProductionYear.HasValue)
|
||||||
{
|
{
|
||||||
video.ProductionYear = data.ProductionYear;
|
if (!video.ProductionYear.HasValue || isFullRefresh)
|
||||||
|
{
|
||||||
|
video.ProductionYear = data.ProductionYear;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (data.PremiereDate.HasValue)
|
if (data.PremiereDate.HasValue)
|
||||||
{
|
{
|
||||||
video.PremiereDate = data.PremiereDate;
|
if (!video.PremiereDate.HasValue || isFullRefresh)
|
||||||
|
{
|
||||||
|
video.PremiereDate = data.PremiereDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (data.IndexNumber.HasValue)
|
if (data.IndexNumber.HasValue)
|
||||||
{
|
{
|
||||||
video.IndexNumber = data.IndexNumber;
|
if (!video.IndexNumber.HasValue || isFullRefresh)
|
||||||
|
{
|
||||||
|
video.IndexNumber = data.IndexNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (data.ParentIndexNumber.HasValue)
|
if (data.ParentIndexNumber.HasValue)
|
||||||
{
|
{
|
||||||
video.ParentIndexNumber = data.ParentIndexNumber;
|
if (!video.ParentIndexNumber.HasValue || isFullRefresh)
|
||||||
|
{
|
||||||
|
video.ParentIndexNumber = data.ParentIndexNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have a ProductionYear try and get it from PremiereDate
|
// If we don't have a ProductionYear try and get it from PremiereDate
|
||||||
|
@ -428,7 +451,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
if (!video.LockedFields.Contains(MetadataFields.Overview))
|
if (!video.LockedFields.Contains(MetadataFields.Overview))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(data.Overview))
|
if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
|
||||||
{
|
{
|
||||||
video.Overview = data.Overview;
|
video.Overview = data.Overview;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,6 @@ namespace MediaBrowser.Providers.Movies
|
||||||
|
|
||||||
protected override bool IsFullLocalMetadata(Movie item)
|
protected override bool IsFullLocalMetadata(Movie item)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(item.Name))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(item.Overview))
|
if (string.IsNullOrWhiteSpace(item.Overview))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -77,10 +77,6 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
protected override bool IsFullLocalMetadata(Series item)
|
protected override bool IsFullLocalMetadata(Series item)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(item.Name))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(item.Overview))
|
if (string.IsNullOrWhiteSpace(item.Overview))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -243,7 +243,15 @@ namespace MediaBrowser.Providers.TV
|
||||||
await SanitizeXmlFile(file).ConfigureAwait(false);
|
await SanitizeXmlFile(file).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ExtractEpisodes(seriesDataPath, Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml"), lastTvDbUpdateTime).ConfigureAwait(false);
|
var downloadLangaugeXmlFile = Path.Combine(seriesDataPath, preferredMetadataLanguage + ".xml");
|
||||||
|
var saveAsLanguageXmlFile = Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml");
|
||||||
|
|
||||||
|
if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
File.Copy(downloadLangaugeXmlFile, saveAsLanguageXmlFile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ExtractEpisodes(seriesDataPath, downloadLangaugeXmlFile, lastTvDbUpdateTime).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TvdbOptions GetTvDbOptions()
|
public TvdbOptions GetTvDbOptions()
|
||||||
|
|
|
@ -828,14 +828,11 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
|
if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
|
||||||
{
|
{
|
||||||
var file = new FileInfo(chapterInfo.ImagePath);
|
|
||||||
|
|
||||||
dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo
|
dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo
|
||||||
{
|
{
|
||||||
Path = chapterInfo.ImagePath,
|
Path = chapterInfo.ImagePath,
|
||||||
Type = ImageType.Chapter,
|
Type = ImageType.Chapter,
|
||||||
DateModified = _fileSystem.GetLastWriteTimeUtc(file),
|
DateModified = _fileSystem.GetLastWriteTimeUtc(chapterInfo.ImagePath)
|
||||||
Length = file.Length
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Linq;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Plugins;
|
using MediaBrowser.Controller.Plugins;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
@ -139,55 +138,24 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
|
||||||
// On some systems the device discovered event seems to fire repeatedly
|
// On some systems the device discovered event seems to fire repeatedly
|
||||||
// This check will help ensure we're not trying to port map the same device over and over
|
// This check will help ensure we're not trying to port map the same device over and over
|
||||||
|
|
||||||
List<Mapping> currentMappings = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
currentMappings = device.GetAllMappings().ToList();
|
|
||||||
}
|
|
||||||
catch (NotSupportedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
var address = device.LocalAddress.ToString();
|
var address = device.LocalAddress.ToString();
|
||||||
|
|
||||||
if (!_createdRules.Contains(address))
|
if (!_createdRules.Contains(address))
|
||||||
{
|
{
|
||||||
_createdRules.Add(address);
|
_createdRules.Add(address);
|
||||||
|
|
||||||
CreatePortMap(device, currentMappings, _appHost.HttpPort, _config.Configuration.PublicPort);
|
CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
|
||||||
CreatePortMap(device, currentMappings, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
|
CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreatePortMap(INatDevice device, List<Mapping> currentMappings, int privatePort, int publicPort)
|
private void CreatePortMap(INatDevice device, int privatePort, int publicPort)
|
||||||
{
|
{
|
||||||
var hasMapping = false;
|
_logger.Debug("Creating port map on port {0}", privatePort);
|
||||||
|
device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
|
||||||
if (currentMappings != null)
|
|
||||||
{
|
{
|
||||||
hasMapping = currentMappings.Any(i => i.PublicPort == publicPort && i.PrivatePort == privatePort);
|
Description = _appHost.Name
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var mapping = device.GetSpecificMapping(Protocol.Tcp, publicPort);
|
|
||||||
hasMapping = mapping != null;
|
|
||||||
}
|
|
||||||
catch (NotSupportedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasMapping)
|
|
||||||
{
|
|
||||||
_logger.Debug("Creating port map on port {0}", privatePort);
|
|
||||||
device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
|
|
||||||
{
|
|
||||||
Description = _appHost.Name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// As I said before, this method will be never invoked. You can remove it.
|
// As I said before, this method will be never invoked. You can remove it.
|
||||||
|
|
|
@ -1602,7 +1602,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
if (ConfigurationManager.Configuration.EnableUserSpecificUserViews)
|
if (ConfigurationManager.Configuration.EnableUserSpecificUserViews)
|
||||||
{
|
{
|
||||||
return await GetNamedViewInternal(user, name, null, viewType, sortName, cancellationToken)
|
return await GetNamedViewInternal(user, name, null, viewType, sortName, null, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,6 +1662,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
string parentId,
|
string parentId,
|
||||||
string viewType,
|
string viewType,
|
||||||
string sortName,
|
string sortName,
|
||||||
|
string uniqueId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(parentId))
|
if (string.IsNullOrWhiteSpace(parentId))
|
||||||
|
@ -1669,7 +1670,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
throw new ArgumentNullException("parentId");
|
throw new ArgumentNullException("parentId");
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetNamedViewInternal(user, name, parentId, viewType, sortName, cancellationToken);
|
return GetNamedViewInternal(user, name, parentId, viewType, sortName, uniqueId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<UserView> GetNamedViewInternal(User user,
|
private async Task<UserView> GetNamedViewInternal(User user,
|
||||||
|
@ -1677,6 +1678,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
string parentId,
|
string parentId,
|
||||||
string viewType,
|
string viewType,
|
||||||
string sortName,
|
string sortName,
|
||||||
|
string uniqueId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
@ -1684,7 +1686,13 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
throw new ArgumentNullException("name");
|
throw new ArgumentNullException("name");
|
||||||
}
|
}
|
||||||
|
|
||||||
var id = GetNewItemId("37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
|
var idValues = "37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty);
|
||||||
|
if (!string.IsNullOrWhiteSpace(uniqueId))
|
||||||
|
{
|
||||||
|
idValues += uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = GetNewItemId(idValues, typeof(UserView));
|
||||||
|
|
||||||
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
|
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
|
||||||
|
|
||||||
|
@ -1723,7 +1731,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
|
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var refresh = isNew || (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 12;
|
var refresh = isNew || (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24;
|
||||||
|
|
||||||
if (refresh)
|
if (refresh)
|
||||||
{
|
{
|
||||||
|
|
|
@ -97,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (parents.Count > 0)
|
if (parents.Count > 0)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(parents, CollectionType.TvShows, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(parents, list, CollectionType.TvShows, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
|
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
|
||||||
|
@ -105,7 +105,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (parents.Count > 0)
|
if (parents.Count > 0)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(parents, CollectionType.Music, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(parents, list, CollectionType.Music, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
|
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
|
||||||
|
@ -113,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (parents.Count > 0)
|
if (parents.Count > 0)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(parents, CollectionType.Movies, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(parents, list, CollectionType.Movies, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
|
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -121,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (parents.Count > 0)
|
if (parents.Count > 0)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(parents, CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(parents, list, CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
|
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -129,7 +129,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (parents.Count > 0)
|
if (parents.Count > 0)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(parents, CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(parents, list, CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
|
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -137,12 +137,12 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (parents.Count > 0)
|
if (parents.Count > 0)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(parents, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(parents, list, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.Configuration.DisplayFoldersView)
|
if (user.Configuration.DisplayFoldersView)
|
||||||
{
|
{
|
||||||
list.Add(await GetUserView(new List<ICollectionFolder>(), CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(new List<ICollectionFolder>(), list, CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.IncludeExternalContent)
|
if (query.IncludeExternalContent)
|
||||||
|
@ -169,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
|
if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
|
||||||
{
|
{
|
||||||
//list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
|
//list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
|
||||||
list.Add(await GetUserView(new List<ICollectionFolder>(), CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
list.Add(await GetUserView(new List<ICollectionFolder>(), list, CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,9 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
public Task<UserView> GetUserSubView(string name, string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
|
public Task<UserView> GetUserSubView(string name, string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return _libraryManager.GetNamedView(user, name, parentId, type, sortName, cancellationToken);
|
var uniqueId = parentId + "subview" + type;
|
||||||
|
|
||||||
|
return _libraryManager.GetNamedView(user, name, parentId, type, sortName, uniqueId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<UserView> GetUserSubView(string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
|
public Task<UserView> GetUserSubView(string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
|
||||||
|
@ -200,16 +202,27 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
return GetUserSubView(name, parentId, type, user, sortName, cancellationToken);
|
return GetUserSubView(name, parentId, type, user, sortName, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserView> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, CancellationToken cancellationToken)
|
public async Task<UserView> GetUserView(List<ICollectionFolder> parents, List<Folder> currentViews, string viewType, string sortName, User user, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
|
||||||
|
|
||||||
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
|
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
var name = parents[0].Name;
|
if (!string.IsNullOrWhiteSpace(parents[0].Name))
|
||||||
|
{
|
||||||
|
name = parents[0].Name;
|
||||||
|
}
|
||||||
|
|
||||||
var parentId = parents[0].Id;
|
var parentId = parents[0].Id;
|
||||||
|
|
||||||
var enableRichView = !user.Configuration.PlainFolderViews.Contains(parentId.ToString("N"), StringComparer.OrdinalIgnoreCase);
|
var enableRichView = !user.Configuration.PlainFolderViews.Contains(parentId.ToString("N"), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (_config.Configuration.EnableUserSpecificUserViews || !enableRichView)
|
if (!enableRichView || currentViews.OfType<UserView>().Any(i => string.Equals(i.ViewType, viewType, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return await GetUserView(parentId, name, viewType, enableRichView, sortName, user, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_config.Configuration.EnableUserSpecificUserViews)
|
||||||
{
|
{
|
||||||
viewType = enableRichView ? viewType : null;
|
viewType = enableRichView ? viewType : null;
|
||||||
var view = await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
|
var view = await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
|
||||||
|
@ -224,18 +237,14 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
|
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
|
|
||||||
|
|
||||||
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
|
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<UserView> GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, User user, CancellationToken cancellationToken)
|
public Task<UserView> GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, User user, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
viewType = enableRichView ? viewType : null;
|
viewType = enableRichView ? viewType : null;
|
||||||
return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, cancellationToken);
|
return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, null, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request)
|
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request)
|
||||||
|
|
|
@ -293,7 +293,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
ProgramName = info.ProgramName,
|
ProgramName = info.ProgramName,
|
||||||
SourceType = info.SourceType,
|
SourceType = info.SourceType,
|
||||||
Status = info.Status,
|
Status = info.Status,
|
||||||
ChannelName = channelName
|
ChannelName = channelName,
|
||||||
|
Url = info.Url
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(info.ChannelId))
|
if (!string.IsNullOrEmpty(info.ChannelId))
|
||||||
|
|
|
@ -122,7 +122,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
|
|
||||||
private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
|
private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var inputPaths = new[] { mediaSource.Path };
|
var originalRuntime = mediaSource.RunTimeTicks;
|
||||||
|
|
||||||
var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
||||||
{
|
{
|
||||||
|
@ -131,8 +131,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
||||||
ExtractChapters = false
|
ExtractChapters = false
|
||||||
|
|
||||||
}, cancellationToken)
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
mediaSource.Bitrate = info.Bitrate;
|
mediaSource.Bitrate = info.Bitrate;
|
||||||
mediaSource.Container = info.Container;
|
mediaSource.Container = info.Container;
|
||||||
|
@ -146,6 +145,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
|
|
||||||
mediaSource.DefaultSubtitleStreamIndex = null;
|
mediaSource.DefaultSubtitleStreamIndex = null;
|
||||||
|
|
||||||
|
// Null this out so that it will be treated like a live stream
|
||||||
|
if (!originalRuntime.HasValue)
|
||||||
|
{
|
||||||
|
mediaSource.RunTimeTicks = null;
|
||||||
|
}
|
||||||
|
|
||||||
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
|
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
|
||||||
|
|
||||||
if (audioStream == null || audioStream.Index == -1)
|
if (audioStream == null || audioStream.Index == -1)
|
||||||
|
|
|
@ -1433,5 +1433,7 @@
|
||||||
"ToAccessPreferencesHelp": "To access your preferences later, click your user icon in the top right header and select My Preferences.",
|
"ToAccessPreferencesHelp": "To access your preferences later, click your user icon in the top right header and select My Preferences.",
|
||||||
"HeaderViewStyles": "View Styles",
|
"HeaderViewStyles": "View Styles",
|
||||||
"LabelSelectViewStyles": "Enable rich presentations for:",
|
"LabelSelectViewStyles": "Enable rich presentations for:",
|
||||||
"LabelSelectViewStylesHelp": "If enabled, views will be built with metadata to offer categories such as Suggestions, Latest, Genres, and more. If disabled, they'll be displayed with simple folders."
|
"LabelSelectViewStylesHelp": "If enabled, views will be built with metadata to offer categories such as Suggestions, Latest, Genres, and more. If disabled, they'll be displayed with simple folders.",
|
||||||
|
"TabPhotos": "Photos",
|
||||||
|
"TabVideos": "Videos"
|
||||||
}
|
}
|
||||||
|
|
|
@ -727,10 +727,14 @@ namespace MediaBrowser.Server.Implementations.Sync
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return jobItemResult.Items
|
var readyItems = jobItemResult.Items
|
||||||
.Select(GetJobItemInfo)
|
.Select(GetJobItemInfo)
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
_logger.Debug("Returning {0} ready sync items for targetId {1}", readyItems.Count, targetId);
|
||||||
|
|
||||||
|
return readyItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SyncDataResponse> SyncData(SyncDataRequest request)
|
public async Task<SyncDataResponse> SyncData(SyncDataRequest request)
|
||||||
|
|
|
@ -470,6 +470,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||||
"notificationlist.js",
|
"notificationlist.js",
|
||||||
"notificationsetting.js",
|
"notificationsetting.js",
|
||||||
"notificationsettings.js",
|
"notificationsettings.js",
|
||||||
|
"photos.js",
|
||||||
"playlists.js",
|
"playlists.js",
|
||||||
"playlistedit.js",
|
"playlistedit.js",
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,9 @@
|
||||||
<Content Include="dashboard-ui\mysyncjob.html">
|
<Content Include="dashboard-ui\mysyncjob.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\photos.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\dashboardhosting.js">
|
<Content Include="dashboard-ui\scripts\dashboardhosting.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -147,6 +150,9 @@
|
||||||
<Content Include="dashboard-ui\scripts\livetvitems.js">
|
<Content Include="dashboard-ui\scripts\livetvitems.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\scripts\photos.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\selectserver.js">
|
<Content Include="dashboard-ui\scripts\selectserver.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Common.Internal</id>
|
<id>MediaBrowser.Common.Internal</id>
|
||||||
<version>3.0.620</version>
|
<version>3.0.621</version>
|
||||||
<title>MediaBrowser.Common.Internal</title>
|
<title>MediaBrowser.Common.Internal</title>
|
||||||
<authors>Luke</authors>
|
<authors>Luke</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
||||||
<copyright>Copyright © Emby 2013</copyright>
|
<copyright>Copyright © Emby 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.620" />
|
<dependency id="MediaBrowser.Common" version="3.0.621" />
|
||||||
<dependency id="NLog" version="3.2.0.0" />
|
<dependency id="NLog" version="3.2.0.0" />
|
||||||
<dependency id="SimpleInjector" version="2.7.0" />
|
<dependency id="SimpleInjector" version="2.7.0" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Common</id>
|
<id>MediaBrowser.Common</id>
|
||||||
<version>3.0.620</version>
|
<version>3.0.621</version>
|
||||||
<title>MediaBrowser.Common</title>
|
<title>MediaBrowser.Common</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Model.Signed</id>
|
<id>MediaBrowser.Model.Signed</id>
|
||||||
<version>3.0.620</version>
|
<version>3.0.621</version>
|
||||||
<title>MediaBrowser.Model - Signed Edition</title>
|
<title>MediaBrowser.Model - Signed Edition</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Server.Core</id>
|
<id>MediaBrowser.Server.Core</id>
|
||||||
<version>3.0.620</version>
|
<version>3.0.621</version>
|
||||||
<title>Media Browser.Server.Core</title>
|
<title>Media Browser.Server.Core</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<description>Contains core components required to build plugins for Emby Server.</description>
|
<description>Contains core components required to build plugins for Emby Server.</description>
|
||||||
<copyright>Copyright © Emby 2013</copyright>
|
<copyright>Copyright © Emby 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.620" />
|
<dependency id="MediaBrowser.Common" version="3.0.621" />
|
||||||
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
//[assembly: AssemblyVersion("3.0.*")]
|
//[assembly: AssemblyVersion("3.0.*")]
|
||||||
[assembly: AssemblyVersion("3.0.5582.4")]
|
[assembly: AssemblyVersion("3.0.5588.0")]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user