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 length = options.Image.Length;
if (options.CropWhiteSpace)
{
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified, length).ConfigureAwait(false);
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
originalImagePath = tuple.Item1;
dateModified = tuple.Item2;
length = tuple.Item3;
}
if (options.Enhancers.Count > 0)
{
var tuple = await GetEnhancedImage(new ItemImageInfo
{
Length = length,
DateModified = dateModified,
Type = options.Image.Type,
Path = originalImagePath
@ -182,7 +179,6 @@ namespace Emby.Drawing
originalImagePath = tuple.Item1;
dateModified = tuple.Item2;
length = tuple.Item3;
}
var originalImageSize = GetImageSize(originalImagePath, dateModified);
@ -199,7 +195,7 @@ namespace Emby.Drawing
var quality = options.Quality ?? 90;
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);
@ -240,11 +236,10 @@ namespace Emby.Drawing
/// <summary>
/// Crops whitespace from an image, caches the result, and returns the cached path
/// </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;
name += "datemodified=" + dateModified.Ticks;
name += "length=" + length;
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
_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
{
@ -280,11 +275,9 @@ namespace Emby.Drawing
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, long>(path, _fileSystem.GetLastWriteTimeUtc(file), file.Length);
return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path));
}
/// <summary>
@ -295,7 +288,7 @@ namespace Emby.Drawing
/// <summary>
/// Gets the cache file path based on a set of parameters
/// </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;
@ -306,7 +299,6 @@ namespace Emby.Drawing
filename += "quality=" + quality;
filename += "datemodified=" + dateModified.Ticks;
filename += "length=" + length;
filename += "f=" + format;
@ -492,17 +484,16 @@ namespace Emby.Drawing
var originalImagePath = image.Path;
var dateModified = image.DateModified;
var imageType = image.Type;
var length = image.Length;
// Optimization
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
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");
}
@ -525,7 +516,7 @@ namespace Emby.Drawing
return result.Item1;
}
private async Task<Tuple<string, DateTime, long>> GetEnhancedImage(ItemImageInfo image,
private async Task<Tuple<string, DateTime>> GetEnhancedImage(ItemImageInfo image,
IHasImages item,
int imageIndex,
List<IImageEnhancer> enhancers)
@ -533,7 +524,6 @@ namespace Emby.Drawing
var originalImagePath = image.Path;
var dateModified = image.DateModified;
var imageType = image.Type;
var length = image.Length;
try
{
@ -553,7 +543,7 @@ namespace Emby.Drawing
_logger.Error("Error enhancing image", ex);
}
return new Tuple<string, DateTime, long>(originalImagePath, dateModified, length);
return new Tuple<string, DateTime>(originalImagePath, dateModified);
}
/// <summary>

View File

@ -2,6 +2,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
@ -40,6 +41,7 @@ namespace MediaBrowser.Api
private readonly ISessionManager _sessionManager;
private readonly IFileSystem _fileSystem;
private readonly IMediaSourceManager _mediaSourceManager;
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
@ -49,12 +51,15 @@ namespace MediaBrowser.Api
/// <param name="logger">The logger.</param>
/// <param name="sessionManager">The session manager.</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;
_sessionManager = sessionManager;
_config = config;
_fileSystem = fileSystem;
_mediaSourceManager = mediaSourceManager;
Instance = this;
}
@ -114,7 +119,7 @@ namespace MediaBrowser.Api
{
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
if (jobCount > 0)
@ -133,6 +138,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="path">The path.</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="type">The type.</param>
/// <param name="process">The process.</param>
@ -142,6 +148,7 @@ namespace MediaBrowser.Api
/// <returns>TranscodingJob.</returns>
public TranscodingJob OnTranscodeBeginning(string path,
string playSessionId,
string liveStreamId,
string transcodingJobId,
TranscodingJobType type,
Process process,
@ -160,7 +167,8 @@ namespace MediaBrowser.Api
DeviceId = deviceId,
CancellationTokenSource = cancellationTokenSource,
Id = transcodingJobId,
PlaySessionId = playSessionId
PlaySessionId = playSessionId,
LiveStreamId = liveStreamId
};
_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)
{
@ -331,7 +339,6 @@ namespace MediaBrowser.Api
return;
}
// TODO: Lower this hls timeout
var timerDuration = job.Type == TranscodingJobType.Progressive ?
1000 :
1800000;
@ -339,17 +346,32 @@ namespace MediaBrowser.Api
// We can really reduce the timeout for apps that are using the newer api
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
if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
{
job.StartKillTimer(timerDuration, OnTranscodeKillTimerStopped);
job.StartKillTimer(OnTranscodeKillTimerStopped);
}
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;
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);
KillTranscodingJob(job, path => true);
KillTranscodingJob(job, true, path => true);
}
/// <summary>
@ -411,7 +444,7 @@ namespace MediaBrowser.Api
foreach (var job in jobs)
{
KillTranscodingJob(job, deleteFiles);
KillTranscodingJob(job, false, deleteFiles);
}
}
@ -419,8 +452,9 @@ namespace MediaBrowser.Api
/// Kills the transcoding job.
/// </summary>
/// <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>
private void KillTranscodingJob(TranscodingJob job, Func<string, bool> delete)
private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
{
job.DisposeKillTimer();
@ -470,6 +504,18 @@ namespace MediaBrowser.Api
{
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)
@ -578,6 +624,11 @@ namespace MediaBrowser.Api
/// <value>The play session identifier.</value>
public string PlaySessionId { get; set; }
/// <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.
/// </summary>
/// <value>The path.</value>
@ -627,6 +678,9 @@ namespace MediaBrowser.Api
private readonly object _timerLock = new object();
public DateTime LastPingDate { get; set; }
public int PingTimeout { get; set; }
public TranscodingJob(ILogger 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();
@ -674,7 +733,7 @@ namespace MediaBrowser.Api
}
}
public void ChangeKillTimerIfStarted(int intervalMs)
public void ChangeKillTimerIfStarted()
{
CheckHasExited();
@ -682,6 +741,8 @@ namespace MediaBrowser.Api
{
if (KillTimer != null)
{
var intervalMs = PingTimeout;
Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer.Change(intervalMs, Timeout.Infinite);
}

View File

@ -1010,6 +1010,7 @@ namespace MediaBrowser.Api.Playback
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
state.Request.PlaySessionId,
state.MediaSource.LiveStreamId,
transcodingId,
TranscodingJobType,
process,

View File

@ -543,7 +543,9 @@ namespace MediaBrowser.Api.Playback.Hls
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)

View File

@ -88,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary
var views = user.RootFolder
.GetChildren(user, true)
.OfType<CollectionFolder>()
.OfType<ICollectionFolder>()
.Where(i => IsEligibleForSpecialView(i))
.ToList();
@ -105,9 +105,9 @@ namespace MediaBrowser.Api.UserLibrary
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);
}

View File

@ -1512,7 +1512,6 @@ namespace MediaBrowser.Controller.Entities
image.Path = file.FullName;
image.DateModified = imageInfo.DateModified;
image.Length = imageInfo.Length;
}
}
@ -1622,14 +1621,11 @@ namespace MediaBrowser.Controller.Entities
return null;
}
var fileInfo = new FileInfo(path);
return new ItemImageInfo
{
Path = path,
DateModified = FileSystem.GetLastWriteTimeUtc(fileInfo),
Type = imageType,
Length = fileInfo.Length
DateModified = FileSystem.GetLastWriteTimeUtc(path),
Type = imageType
};
}
@ -1690,7 +1686,6 @@ namespace MediaBrowser.Controller.Entities
else
{
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
existing.Length = ((FileInfo)newImage).Length;
}
}
@ -1716,8 +1711,7 @@ namespace MediaBrowser.Controller.Entities
{
Path = file.FullName,
Type = type,
DateModified = FileSystem.GetLastWriteTimeUtc(file),
Length = ((FileInfo)file).Length
DateModified = FileSystem.GetLastWriteTimeUtc(file)
};
}
@ -1756,15 +1750,9 @@ namespace MediaBrowser.Controller.Entities
FileSystem.SwapFiles(path1, path2);
var file1 = new FileInfo(info1.Path);
var file2 = new FileInfo(info2.Path);
// Refresh these values
info1.DateModified = FileSystem.GetLastWriteTimeUtc(file1);
info2.DateModified = FileSystem.GetLastWriteTimeUtc(file2);
info1.Length = file1.Length;
info2.Length = file2.Length;
info1.DateModified = FileSystem.GetLastWriteTimeUtc(info1.Path);
info2.DateModified = FileSystem.GetLastWriteTimeUtc(info2.Path);
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}

View File

@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the length.
/// </summary>
/// <value>The length.</value>
public long Length { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>

View File

@ -121,7 +121,6 @@ namespace MediaBrowser.Controller.Entities
}
case CollectionType.Books:
case CollectionType.Photos:
case CollectionType.HomeVideos:
case CollectionType.MusicVideos:
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
@ -138,6 +137,9 @@ namespace MediaBrowser.Controller.Entities
case CollectionType.BoxSets:
return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.Photos:
return await GetPhotosView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.TvShows:
return await GetTvView(queryParent, user, query).ConfigureAwait(false);
@ -645,6 +647,19 @@ namespace MediaBrowser.Controller.Entities
}), 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)
{
if (query.Recursive)

View File

@ -309,6 +309,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="parentId">The parent identifier.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
/// <param name="uniqueId">The unique identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;UserView&gt;.</returns>
Task<UserView> GetNamedView(User user,
@ -316,6 +317,7 @@ namespace MediaBrowser.Controller.Library
string parentId,
string viewType,
string sortName,
string uniqueId,
CancellationToken cancellationToken);
/// <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
{
@ -23,6 +23,12 @@ namespace MediaBrowser.Controller.LiveTv
/// <value>The identifier.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
public string Url { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>

View File

@ -488,6 +488,9 @@
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
<Link>Dto\ItemIndex.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs">
<Link>Dto\ItemLayout.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
<Link>Dto\MediaSourceInfo.cs</Link>
</Compile>

View File

@ -453,6 +453,9 @@
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
<Link>Dto\ItemIndex.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs">
<Link>Dto\ItemLayout.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
<Link>Dto\MediaSourceInfo.cs</Link>
</Compile>

View File

@ -98,7 +98,7 @@ namespace MediaBrowser.Model.ApiClient
{
var index = 0;
foreach (var server in servers)
foreach (ServerInfo server in servers)
{
if (StringHelper.EqualsIgnoreCase(id, server.Id))
{
@ -110,5 +110,18 @@ namespace MediaBrowser.Model.ApiClient
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
// 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);
}
@ -456,10 +456,8 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
}
if (!playlistItem.AudioBitrate.HasValue)
{
playlistItem.AudioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
}
int audioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
int? maxBitrateSetting = options.GetMaxBitrate();
// Honor max rate
@ -472,9 +470,9 @@ namespace MediaBrowser.Model.Dlna
videoBitrate -= playlistItem.AudioBitrate.Value;
}
// Make sure the video bitrate is lower than bitrate settings but at least 64k
int currentValue = playlistItem.VideoBitrate ?? videoBitrate;
playlistItem.VideoBitrate = Math.Min(videoBitrate, currentValue);
playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000);
}
}
@ -640,7 +638,7 @@ namespace MediaBrowser.Model.Dlna
}
}
if (isEligibleForDirectPlay)
if (isEligibleForDirectPlay && mediaSource.SupportsDirectPlay)
{
if (mediaSource.Protocol == MediaProtocol.Http)
{
@ -659,13 +657,10 @@ namespace MediaBrowser.Model.Dlna
}
}
if (isEligibleForDirectStream)
{
if (mediaSource.SupportsDirectStream)
if (isEligibleForDirectStream && mediaSource.SupportsDirectStream)
{
return PlayMethod.DirectStream;
}
}
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>
public string Id { get; set; }
/// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
public string Url { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>

View File

@ -139,6 +139,7 @@
<Compile Include="Drawing\ImageOrientation.cs" />
<Compile Include="Dto\IHasServerId.cs" />
<Compile Include="Dto\IHasSyncInfo.cs" />
<Compile Include="Dto\ItemLayout.cs" />
<Compile Include="Dto\MetadataEditorInfo.cs" />
<Compile Include="Dto\NameIdPair.cs" />
<Compile Include="Dto\NameValuePair.cs" />

View File

@ -384,7 +384,6 @@ namespace MediaBrowser.Providers.Manager
else
{
currentImage.DateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
currentImage.Length = ((FileInfo) image.FileInfo).Length;
}
}
else

View File

@ -482,6 +482,11 @@ namespace MediaBrowser.Providers.Manager
protected virtual bool IsFullLocalMetadata(TItemType item)
{
if (string.IsNullOrWhiteSpace(item.Name))
{
return false;
}
return true;
}

View File

@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo
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);
@ -358,17 +358,21 @@ namespace MediaBrowser.Providers.MediaInfo
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 (!string.IsNullOrWhiteSpace(data.OfficialRating))
if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
{
video.OfficialRating = data.OfficialRating;
}
}
if (!video.LockedFields.Contains(MetadataFields.Cast))
{
if (video.People.Count == 0 || isFullRefresh)
{
video.People.Clear();
@ -382,8 +386,11 @@ namespace MediaBrowser.Providers.MediaInfo
});
}
}
}
if (!video.LockedFields.Contains(MetadataFields.Genres))
{
if (video.Genres.Count == 0 || isFullRefresh)
{
video.Genres.Clear();
@ -392,8 +399,11 @@ namespace MediaBrowser.Providers.MediaInfo
video.AddGenre(genre);
}
}
}
if (!video.LockedFields.Contains(MetadataFields.Studios))
{
if (video.Studios.Count == 0 || isFullRefresh)
{
video.Studios.Clear();
@ -402,23 +412,36 @@ namespace MediaBrowser.Providers.MediaInfo
video.AddStudio(studio);
}
}
}
if (data.ProductionYear.HasValue)
{
if (!video.ProductionYear.HasValue || isFullRefresh)
{
video.ProductionYear = data.ProductionYear;
}
}
if (data.PremiereDate.HasValue)
{
if (!video.PremiereDate.HasValue || isFullRefresh)
{
video.PremiereDate = data.PremiereDate;
}
}
if (data.IndexNumber.HasValue)
{
if (!video.IndexNumber.HasValue || isFullRefresh)
{
video.IndexNumber = data.IndexNumber;
}
}
if (data.ParentIndexNumber.HasValue)
{
if (!video.ParentIndexNumber.HasValue || isFullRefresh)
{
video.ParentIndexNumber = data.ParentIndexNumber;
}
}
// If we don't have a ProductionYear try and get it from PremiereDate
if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)
@ -428,7 +451,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (!video.LockedFields.Contains(MetadataFields.Overview))
{
if (!string.IsNullOrWhiteSpace(data.Overview))
if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
{
video.Overview = data.Overview;
}

View File

@ -36,10 +36,6 @@ namespace MediaBrowser.Providers.Movies
protected override bool IsFullLocalMetadata(Movie item)
{
if (string.IsNullOrWhiteSpace(item.Name))
{
return false;
}
if (string.IsNullOrWhiteSpace(item.Overview))
{
return false;

View File

@ -77,10 +77,6 @@ namespace MediaBrowser.Providers.TV
protected override bool IsFullLocalMetadata(Series item)
{
if (string.IsNullOrWhiteSpace(item.Name))
{
return false;
}
if (string.IsNullOrWhiteSpace(item.Overview))
{
return false;

View File

@ -243,7 +243,15 @@ namespace MediaBrowser.Providers.TV
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()

View File

@ -828,14 +828,11 @@ namespace MediaBrowser.Server.Implementations.Dto
if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
{
var file = new FileInfo(chapterInfo.ImagePath);
dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo
{
Path = chapterInfo.ImagePath,
Type = ImageType.Chapter,
DateModified = _fileSystem.GetLastWriteTimeUtc(file),
Length = file.Length
DateModified = _fileSystem.GetLastWriteTimeUtc(chapterInfo.ImagePath)
});
}

View File

@ -1,5 +1,4 @@
using System.Linq;
using MediaBrowser.Controller;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
@ -139,48 +138,18 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
// 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
List<Mapping> currentMappings = null;
try
{
currentMappings = device.GetAllMappings().ToList();
}
catch (NotSupportedException)
{
}
var address = device.LocalAddress.ToString();
if (!_createdRules.Contains(address))
{
_createdRules.Add(address);
CreatePortMap(device, currentMappings, _appHost.HttpPort, _config.Configuration.PublicPort);
CreatePortMap(device, currentMappings, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
}
}
private void CreatePortMap(INatDevice device, List<Mapping> currentMappings, int privatePort, int publicPort)
{
var hasMapping = false;
if (currentMappings != null)
{
hasMapping = currentMappings.Any(i => i.PublicPort == publicPort && i.PrivatePort == privatePort);
}
else
{
try
{
var mapping = device.GetSpecificMapping(Protocol.Tcp, publicPort);
hasMapping = mapping != null;
}
catch (NotSupportedException)
{
}
}
if (!hasMapping)
private void CreatePortMap(INatDevice device, int privatePort, int publicPort)
{
_logger.Debug("Creating port map on port {0}", privatePort);
device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
@ -188,7 +157,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
Description = _appHost.Name
});
}
}
// As I said before, this method will be never invoked. You can remove it.
void NatUtility_DeviceLost(object sender, DeviceEventArgs e)

View File

@ -1602,7 +1602,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
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);
}
@ -1662,6 +1662,7 @@ namespace MediaBrowser.Server.Implementations.Library
string parentId,
string viewType,
string sortName,
string uniqueId,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(parentId))
@ -1669,7 +1670,7 @@ namespace MediaBrowser.Server.Implementations.Library
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,
@ -1677,6 +1678,7 @@ namespace MediaBrowser.Server.Implementations.Library
string parentId,
string viewType,
string sortName,
string uniqueId,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(name))
@ -1684,7 +1686,13 @@ namespace MediaBrowser.Server.Implementations.Library
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"));
@ -1723,7 +1731,7 @@ namespace MediaBrowser.Server.Implementations.Library
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)
{

View File

@ -97,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.Library
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))
@ -105,7 +105,7 @@ namespace MediaBrowser.Server.Implementations.Library
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))
@ -113,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.Library
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))
@ -121,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.Library
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))
@ -129,7 +129,7 @@ namespace MediaBrowser.Server.Implementations.Library
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))
@ -137,12 +137,12 @@ namespace MediaBrowser.Server.Implementations.Library
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)
{
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)
@ -169,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Library
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 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)
{
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)
@ -200,16 +202,27 @@ namespace MediaBrowser.Server.Implementations.Library
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)))
{
var name = parents[0].Name;
if (!string.IsNullOrWhiteSpace(parents[0].Name))
{
name = parents[0].Name;
}
var parentId = parents[0].Id;
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;
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);
}
else
{
var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
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)
{
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)

View File

@ -293,7 +293,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
ProgramName = info.ProgramName,
SourceType = info.SourceType,
Status = info.Status,
ChannelName = channelName
ChannelName = channelName,
Url = info.Url
};
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)
{
var inputPaths = new[] { mediaSource.Path };
var originalRuntime = mediaSource.RunTimeTicks;
var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
@ -131,8 +131,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false
}, cancellationToken)
.ConfigureAwait(false);
}, cancellationToken).ConfigureAwait(false);
mediaSource.Bitrate = info.Bitrate;
mediaSource.Container = info.Container;
@ -146,6 +145,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
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);
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.",
"HeaderViewStyles": "View Styles",
"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)
.Where(i => i != null)
.ToList();
_logger.Debug("Returning {0} ready sync items for targetId {1}", readyItems.Count, targetId);
return readyItems;
}
public async Task<SyncDataResponse> SyncData(SyncDataRequest request)

View File

@ -470,6 +470,7 @@ namespace MediaBrowser.WebDashboard.Api
"notificationlist.js",
"notificationsetting.js",
"notificationsettings.js",
"photos.js",
"playlists.js",
"playlistedit.js",

View File

@ -132,6 +132,9 @@
<Content Include="dashboard-ui\mysyncjob.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\photos.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\dashboardhosting.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@ -147,6 +150,9 @@
<Content Include="dashboard-ui\scripts\livetvitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\photos.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\selectserver.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.620</version>
<version>3.0.621</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<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>
<copyright>Copyright © Emby 2013</copyright>
<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="SimpleInjector" version="2.7.0" />
</dependencies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.620</version>
<version>3.0.621</version>
<title>MediaBrowser.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Model.Signed</id>
<version>3.0.620</version>
<version>3.0.621</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.620</version>
<version>3.0.621</version>
<title>Media Browser.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<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" />
</dependencies>
</metadata>

View File

@ -1,4 +1,4 @@
using System.Reflection;
//[assembly: AssemblyVersion("3.0.*")]
[assembly: AssemblyVersion("3.0.5582.4")]
[assembly: AssemblyVersion("3.0.5588.0")]