Merge pull request #1087 from MediaBrowser/dev

3.0.5588.0
This commit is contained in:
Luke 2015-04-20 14:42:56 -04:00
commit 7b67ec6854
39 changed files with 380 additions and 200 deletions

View File

@ -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>

View File

@ -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);
} }

View File

@ -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,

View File

@ -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)

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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>

View File

@ -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)

View File

@ -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&lt;UserView&gt;.</returns> /// <returns>Task&lt;UserView&gt;.</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
} }
} }

View File

@ -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;

View 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;
}
}
}

View File

@ -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>

View File

@ -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" />

View File

@ -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

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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
}); });
} }

View File

@ -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.

View File

@ -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)
{ {

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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"
} }

View File

@ -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)

View File

@ -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",

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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")]