aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs150
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs5
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs2
-rw-r--r--MediaBrowser.Api/Sync/SyncHelper.cs13
-rw-r--r--MediaBrowser.Api/Sync/SyncService.cs3
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs22
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs1
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs15
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs7
-rw-r--r--MediaBrowser.Dlna/Profiles/DefaultProfile.cs1
-rw-r--r--MediaBrowser.Dlna/Profiles/KodiProfile.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs118
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs16
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs3
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs3
-rw-r--r--MediaBrowser.Model/Dlna/AudioOptions.cs6
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs3
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs48
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs4
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs3
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoProvider.cs9
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs40
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs5
-rw-r--r--MediaBrowser.Server.Mono/Native/BaseMonoApp.cs29
-rw-r--r--MediaBrowser.Server.Startup.Common/ApplicationHost.cs2
-rw-r--r--MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj2
-rw-r--r--MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs40
-rw-r--r--MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs40
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj3
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
42 files changed, 319 insertions, 316 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index f52505c04..6f4b6323d 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -298,7 +298,8 @@ namespace MediaBrowser.Api.Playback
// Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
if (state.VideoType == VideoType.VideoFile)
{
- var hwType = ApiEntryPoint.Instance.GetEncodingOptions().HardwareAccelerationType;
+ var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
+ var hwType = encodingOptions.HardwareAccelerationType;
if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) ||
string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase))
@@ -314,6 +315,10 @@ namespace MediaBrowser.Api.Playback
{
return GetAvailableEncoder("h264_omx", defaultEncoder);
}
+ if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice))
+ {
+ return GetAvailableEncoder("h264_vaapi", defaultEncoder);
+ }
}
return defaultEncoder;
@@ -427,7 +432,8 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Profile))
{
- if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase))
+ if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
// not supported by h264_omx
param += " -profile:v " + state.VideoRequest.Profile;
@@ -482,7 +488,8 @@ namespace MediaBrowser.Api.Playback
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
}
@@ -548,59 +555,97 @@ namespace MediaBrowser.Api.Playback
var filters = new List<string>();
- if (state.DeInterlace)
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+ {
+ filters.Add("format=nv12|vaapi");
+ filters.Add("hwupload");
+ }
+ else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
filters.Add("yadif=0:-1:0");
}
- // If fixed dimensions were supplied
- if (request.Width.HasValue && request.Height.HasValue)
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
- var widthParam = request.Width.Value.ToString(UsCulture);
- var heightParam = request.Height.Value.ToString(UsCulture);
+ // Work around vaapi's reduced scaling features
+ var scaler = "scale_vaapi";
- filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
- }
+ // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
+ // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
+ // output dimensions. Output dimensions are guaranteed to be even.
+ decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
+ decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
+ decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
+ decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
+ decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
+ decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
- // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
- else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
- {
- var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
- var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+ if (outputWidth > maximumWidth || outputHeight > maximumHeight)
+ {
+ var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
+ outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
+ outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
+ }
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
- }
+ outputWidth = 2 * Math.Truncate(outputWidth / 2);
+ outputHeight = 2 * Math.Truncate(outputHeight / 2);
- // If a fixed width was requested
- else if (request.Width.HasValue)
+ if (outputWidth != inputWidth || outputHeight != inputHeight)
+ {
+ filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
+ }
+ }
+ else
{
- var widthParam = request.Width.Value.ToString(UsCulture);
+ // If fixed dimensions were supplied
+ if (request.Width.HasValue && request.Height.HasValue)
+ {
+ var widthParam = request.Width.Value.ToString(UsCulture);
+ var heightParam = request.Height.Value.ToString(UsCulture);
- filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
- }
+ filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
+ }
- // If a fixed height was requested
- else if (request.Height.HasValue)
- {
- var heightParam = request.Height.Value.ToString(UsCulture);
+ // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
+ else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
+ {
+ var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+ var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
- filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
- }
+ filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
+ }
- // If a max width was requested
- else if (request.MaxWidth.HasValue)
- {
- var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+ // If a fixed width was requested
+ else if (request.Width.HasValue)
+ {
+ var widthParam = request.Width.Value.ToString(UsCulture);
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
- }
+ filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
+ }
- // If a max height was requested
- else if (request.MaxHeight.HasValue)
- {
- var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+ // If a fixed height was requested
+ else if (request.Height.HasValue)
+ {
+ var heightParam = request.Height.Value.ToString(UsCulture);
- filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
+ filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
+ }
+
+ // If a max width was requested
+ else if (request.MaxWidth.HasValue)
+ {
+ var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+
+ filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
+ }
+
+ // If a max height was requested
+ else if (request.MaxHeight.HasValue)
+ {
+ var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+
+ filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
+ }
}
var output = string.Empty;
@@ -935,6 +980,17 @@ namespace MediaBrowser.Api.Playback
}
}
+ if (state.VideoRequest != null)
+ {
+ var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
+ if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
+ }
+ }
+
+ arg += string.Format(" -ss {0}", MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(1).Ticks));
+
return arg.Trim();
}
@@ -1590,13 +1646,6 @@ namespace MediaBrowser.Api.Playback
}
else if (i == 25)
{
- if (videoRequest != null)
- {
- videoRequest.ForceLiveStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
- }
- }
- else if (i == 26)
- {
if (!string.IsNullOrWhiteSpace(val) && videoRequest != null)
{
SubtitleDeliveryMethod method;
@@ -1606,18 +1655,18 @@ namespace MediaBrowser.Api.Playback
}
}
}
- else if (i == 27)
+ else if (i == 26)
{
request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture);
}
- else if (i == 28)
+ else if (i == 27)
{
if (videoRequest != null)
{
videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
}
- else if (i == 29)
+ else if (i == 28)
{
request.Tag = val;
}
@@ -2218,7 +2267,6 @@ namespace MediaBrowser.Api.Playback
if (state.VideoRequest != null)
{
state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
- state.VideoRequest.ForceLiveStream = transcodingProfile.ForceLiveStream;
state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
}
}
@@ -2244,7 +2292,7 @@ namespace MediaBrowser.Api.Playback
return Task.FromResult(true);
}
- if (!string.Equals(MediaEncoder.EncoderLocationType, "Default", StringComparison.OrdinalIgnoreCase))
+ if (!MediaEncoder.IsDefaultEncoderPath)
{
return Task.FromResult(true);
}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 4ec14c96c..a0ac96b9d 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -281,11 +281,6 @@ namespace MediaBrowser.Api.Playback.Hls
{
var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
- if (state.VideoRequest.ForceLiveStream)
- {
- return true;
- }
-
return isLiveStream;
}
}
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index e1a577f52..8cdf846ed 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -193,8 +193,6 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool CopyTimestamps { get; set; }
- public bool ForceLiveStream { get; set; }
-
public bool EnableSubtitlesInManifest { get; set; }
public VideoStreamRequest()
diff --git a/MediaBrowser.Api/Sync/SyncHelper.cs b/MediaBrowser.Api/Sync/SyncHelper.cs
index 2f857000c..60df2bb1e 100644
--- a/MediaBrowser.Api/Sync/SyncHelper.cs
+++ b/MediaBrowser.Api/Sync/SyncHelper.cs
@@ -24,6 +24,19 @@ namespace MediaBrowser.Api.Sync
}
break;
}
+ if (item.IsAudio)
+ {
+ options.Add(SyncJobOption.Quality);
+ options.Add(SyncJobOption.Profile);
+ break;
+ }
+ if (item.IsMusicGenre || item.IsArtist|| item.IsType("musicalbum"))
+ {
+ options.Add(SyncJobOption.Quality);
+ options.Add(SyncJobOption.Profile);
+ options.Add(SyncJobOption.ItemLimit);
+ break;
+ }
if (item.IsFolderItem && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
{
options.Add(SyncJobOption.Quality);
diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs
index b9544d71b..821591dc4 100644
--- a/MediaBrowser.Api/Sync/SyncService.cs
+++ b/MediaBrowser.Api/Sync/SyncService.cs
@@ -291,7 +291,8 @@ namespace MediaBrowser.Api.Sync
{
Fields = new List<ItemFields>
{
- ItemFields.SyncInfo
+ ItemFields.SyncInfo,
+ ItemFields.BasicSyncInfo
}
};
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index cbbb9a89a..984374a49 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -281,6 +281,20 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public Task UpdateIsOffline(bool newValue)
+ {
+ var item = this;
+
+ if (item.IsOffline != newValue)
+ {
+ item.IsOffline = newValue;
+ // this is creating too many repeated db updates
+ //return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
+ }
+
+ return Task.FromResult(true);
+ }
+
/// <summary>
/// Gets or sets the type of the location.
/// </summary>
@@ -290,10 +304,10 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (IsOffline)
- {
- return LocationType.Offline;
- }
+ //if (IsOffline)
+ //{
+ // return LocationType.Offline;
+ //}
if (string.IsNullOrWhiteSpace(Path))
{
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index e120f2e23..597ecf973 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -106,6 +106,7 @@ namespace MediaBrowser.Controller.Entities
{
LibraryOptions[path] = options;
+ options.SchemaVersion = 1;
XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
}
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index b5c76c0eb..bea648a3d 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -375,7 +375,7 @@ namespace MediaBrowser.Controller.Entities
if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child))
{
- await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
+ await currentChild.UpdateIsOffline(false).ConfigureAwait(false);
validChildren.Add(currentChild);
continue;
@@ -404,7 +404,7 @@ namespace MediaBrowser.Controller.Entities
else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
{
- await UpdateIsOffline(item, true).ConfigureAwait(false);
+ await item.UpdateIsOffline(true).ConfigureAwait(false);
}
else
{
@@ -461,17 +461,6 @@ namespace MediaBrowser.Controller.Entities
progress.Report(100);
}
- private Task UpdateIsOffline(BaseItem item, bool newValue)
- {
- if (item.IsOffline != newValue)
- {
- item.IsOffline = newValue;
- return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
- }
-
- return Task.FromResult(true);
- }
-
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
{
var children = ActualChildren.ToList();
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 1fef8bead..7fdbf020c 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -134,5 +134,6 @@ namespace MediaBrowser.Controller.MediaEncoding
Task UpdateEncoderPath(string path, string pathType);
bool SupportsEncoder(string encoder);
+ bool IsDefaultEncoderPath { get; }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
index 66a9fa60b..96a3753e1 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -36,8 +36,13 @@ namespace MediaBrowser.Controller.MediaEncoding
return new[] {videoPath};
}
- public static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames)
+ private static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, List<string> filenames)
{
+ if (filenames.Count == 0)
+ {
+ return new List<string>();
+ }
+
var allFiles = fileSystem
.GetFilePaths(rootPath, true)
.ToList();
diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
index e4f6d337f..5f60e8306 100644
--- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
@@ -33,7 +33,6 @@ namespace MediaBrowser.Dlna.Profiles
MaxStreamingBitrate = 20000000;
MaxStaticBitrate = 20000000;
MusicStreamingTranscodingBitrate = 192000;
- MusicSyncBitrate = 192000;
EnableAlbumArtInDidl = false;
diff --git a/MediaBrowser.Dlna/Profiles/KodiProfile.cs b/MediaBrowser.Dlna/Profiles/KodiProfile.cs
index 184923b70..5e1ac5760 100644
--- a/MediaBrowser.Dlna/Profiles/KodiProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/KodiProfile.cs
@@ -11,9 +11,7 @@ namespace MediaBrowser.Dlna.Profiles
Name = "Kodi";
MaxStreamingBitrate = 100000000;
- MaxStaticBitrate = 100000000;
MusicStreamingTranscodingBitrate = 1280000;
- MusicSyncBitrate = 1280000;
TimelineOffsetSeconds = 5;
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index 32cd950af..aaa5593b4 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
@@ -680,7 +680,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrEmpty(state.Options.Profile))
{
- if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase))
+ if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
// not supported by h264_omx
param += " -profile:v " + state.Options.Profile;
@@ -737,7 +738,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
}
@@ -887,66 +889,96 @@ namespace MediaBrowser.MediaEncoding.Encoder
var filters = new List<string>();
- if (state.DeInterlace)
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+ {
+ filters.Add("format=nv12|vaapi");
+ filters.Add("hwupload");
+ }
+ else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
filters.Add("yadif=0:-1:0");
}
- // If fixed dimensions were supplied
- if (request.Width.HasValue && request.Height.HasValue)
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
- var widthParam = request.Width.Value.ToString(UsCulture);
- var heightParam = request.Height.Value.ToString(UsCulture);
+ // Work around vaapi's reduced scaling features
+ var scaler = "scale_vaapi";
- filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
- }
+ // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
+ // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
+ // output dimensions. Output dimensions are guaranteed to be even.
+ decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
+ decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
+ decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
+ decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
+ decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
+ decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
- // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
- else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
- {
- var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
- var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+ if (outputWidth > maximumWidth || outputHeight > maximumHeight)
+ {
+ var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
+ outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
+ outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
+ }
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
- }
+ outputWidth = 2 * Math.Truncate(outputWidth / 2);
+ outputHeight = 2 * Math.Truncate(outputHeight / 2);
- // If a fixed width was requested
- else if (request.Width.HasValue)
+ if (outputWidth != inputWidth || outputHeight != inputHeight)
+ {
+ filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
+ }
+ }
+ else
{
- var widthParam = request.Width.Value.ToString(UsCulture);
+ // If fixed dimensions were supplied
+ if (request.Width.HasValue && request.Height.HasValue)
+ {
+ var widthParam = request.Width.Value.ToString(UsCulture);
+ var heightParam = request.Height.Value.ToString(UsCulture);
- filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
- }
+ filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
+ }
- // If a fixed height was requested
- else if (request.Height.HasValue)
- {
- var heightParam = request.Height.Value.ToString(UsCulture);
+ // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
+ else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
+ {
+ var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+ var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
- filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
- }
+ filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
+ }
- // If a max width was requested
- else if (request.MaxWidth.HasValue)
- {
- var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+ // If a fixed width was requested
+ else if (request.Width.HasValue)
+ {
+ var widthParam = request.Width.Value.ToString(UsCulture);
- filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
- }
+ filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
+ }
- // If a max height was requested
- else if (request.MaxHeight.HasValue)
- {
- var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+ // If a fixed height was requested
+ else if (request.Height.HasValue)
+ {
+ var heightParam = request.Height.Value.ToString(UsCulture);
- filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
- }
+ filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
+ }
- if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
- {
- if (filters.Count > 1)
+ // If a max width was requested
+ else if (request.MaxWidth.HasValue)
+ {
+ var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+
+ filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
+ }
+
+ // If a max height was requested
+ else if (request.MaxHeight.HasValue)
{
- //filters[filters.Count - 1] += ":flags=fast_bilinear";
+ var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+
+ filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
index ba7b14950..c72532669 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
@@ -586,6 +586,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
return GetAvailableEncoder(mediaEncoder, "h264_omx", defaultEncoder);
}
+ if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(options.VaapiDevice))
+ {
+ return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder);
+ }
}
return defaultEncoder;
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index f488be11a..ad84ffee8 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -123,20 +123,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
return "System";
}
- if (IsDefaultPath(FFMpegPath))
- {
- return "Default";
- }
-
return "Custom";
}
}
- private bool IsDefaultPath(string path)
+ public bool IsDefaultEncoderPath
{
- var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
+ get
+ {
+ var path = FFMpegPath;
+
+ var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
- return FileSystem.ContainsSubPath(parentPath, path);
+ return FileSystem.ContainsSubPath(parentPath, path);
+ }
}
private bool IsSystemInstalledPath(string path)
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 91d28a296..3c03dc12a 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Configuration
public int ThrottleDelaySeconds { get; set; }
public string HardwareAccelerationType { get; set; }
public string EncoderAppPath { get; set; }
+ public string VaapiDevice { get; set; }
public EncodingOptions()
{
@@ -17,6 +18,7 @@ namespace MediaBrowser.Model.Configuration
EnableThrottling = true;
ThrottleDelaySeconds = 180;
EncodingThreadCount = -1;
+ VaapiDevice = "/dev/dri/card0";
}
}
}
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index e15df37c1..3fe694553 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -4,10 +4,13 @@
{
public bool EnableArchiveMediaFiles { get; set; }
public bool EnablePhotos { get; set; }
+ public bool EnableRealtimeMonitor { get; set; }
+ public int SchemaVersion { get; set; }
public LibraryOptions()
{
EnablePhotos = true;
+ EnableRealtimeMonitor = true;
}
}
}
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 69dc23b21..313c5243c 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -27,8 +27,6 @@ namespace MediaBrowser.Model.Configuration
public bool DisplayMissingEpisodes { get; set; }
public bool DisplayUnairedEpisodes { get; set; }
- public bool GroupMoviesIntoBoxSets { get; set; }
-
public string[] ExcludeFoldersFromGrouping { get; set; }
public string[] GroupedFolders { get; set; }
@@ -48,7 +46,6 @@ namespace MediaBrowser.Model.Configuration
public bool RememberAudioSelections { get; set; }
public bool RememberSubtitleSelections { get; set; }
public bool EnableNextEpisodeAutoPlay { get; set; }
- public bool DisplayFoldersView { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="UserConfiguration" /> class.
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs
index c208e8ab0..f3b6df861 100644
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ b/MediaBrowser.Model/Dlna/AudioOptions.cs
@@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Dlna
/// Gets the maximum bitrate.
/// </summary>
/// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
- public int? GetMaxBitrate()
+ public int? GetMaxBitrate(bool isAudio)
{
if (MaxBitrate.HasValue)
{
@@ -70,6 +70,10 @@ namespace MediaBrowser.Model.Dlna
{
if (Context == EncodingContext.Static)
{
+ if (isAudio && Profile.MaxStaticMusicBitrate.HasValue)
+ {
+ return Profile.MaxStaticMusicBitrate;
+ }
return Profile.MaxStaticBitrate;
}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 423928f62..d6a322322 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Dlna
public int? MaxStaticBitrate { get; set; }
public int? MusicStreamingTranscodingBitrate { get; set; }
- public int? MusicSyncBitrate { get; set; }
+ public int? MaxStaticMusicBitrate { get; set; }
/// <summary>
/// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
@@ -115,7 +115,6 @@ namespace MediaBrowser.Model.Dlna
MaxStreamingBitrate = 8000000;
MaxStaticBitrate = 8000000;
MusicStreamingTranscodingBitrate = 128000;
- MusicSyncBitrate = 128000;
}
public List<string> GetSupportedMediaTypes()
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 0710417c8..d042125b9 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Dlna
stream.DeviceProfileId = options.Profile.Id;
}
- return GetOptimalStream(streams, options.GetMaxBitrate());
+ return GetOptimalStream(streams, options.GetMaxBitrate(true));
}
public StreamInfo BuildVideoItem(VideoOptions options)
@@ -88,7 +88,7 @@ namespace MediaBrowser.Model.Dlna
stream.DeviceProfileId = options.Profile.Id;
}
- return GetOptimalStream(streams, options.GetMaxBitrate());
+ return GetOptimalStream(streams, options.GetMaxBitrate(false));
}
private StreamInfo GetOptimalStream(List<StreamInfo> streams, int? maxBitrate)
@@ -275,24 +275,32 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
}
- int configuredBitrate = options.AudioTranscodingBitrate ??
- (options.Context == EncodingContext.Static ? options.Profile.MusicSyncBitrate : options.Profile.MusicStreamingTranscodingBitrate) ??
+ int transcodingBitrate = options.AudioTranscodingBitrate ??
+ options.Profile.MusicStreamingTranscodingBitrate ??
128000;
- playlistItem.AudioBitrate = Math.Min(configuredBitrate, playlistItem.AudioBitrate ?? configuredBitrate);
+ int? configuredBitrate = options.GetMaxBitrate(true);
+
+ if (configuredBitrate.HasValue)
+ {
+ transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
+ }
+
+ playlistItem.AudioBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
+
}
return playlistItem;
}
- private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options)
+ private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio)
{
if (item.Protocol == MediaProtocol.File)
{
return options.Profile.MaxStaticBitrate;
}
- return options.GetMaxBitrate();
+ return options.GetMaxBitrate(isAudio);
}
private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
@@ -312,7 +320,7 @@ namespace MediaBrowser.Model.Dlna
if (directPlayProfile != null)
{
// While options takes the network and other factors into account. Only applies to direct stream
- if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate()) && options.EnableDirectStream)
+ if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true)) && options.EnableDirectStream)
{
playMethods.Add(PlayMethod.DirectStream);
}
@@ -320,7 +328,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 (item.SupportsDirectPlay &&
- IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)) && options.EnableDirectPlay)
+ IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true)) && options.EnableDirectPlay)
{
playMethods.Add(PlayMethod.DirectPlay);
}
@@ -403,8 +411,8 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream = item.VideoStream;
// TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
- bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay));
- bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream));
+ bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay));
+ bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), subtitleStream, options, PlayMethod.DirectStream));
_logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
options.Profile.Name ?? "Unknown Profile",
@@ -469,7 +477,6 @@ namespace MediaBrowser.Model.Dlna
playlistItem.VideoCodec = transcodingProfile.VideoCodec;
playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps;
- playlistItem.ForceLiveStream = transcodingProfile.ForceLiveStream;
playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels))
@@ -570,10 +577,10 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
}
- int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
+ int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
- int? maxBitrateSetting = options.GetMaxBitrate();
+ int? maxBitrateSetting = options.GetMaxBitrate(false);
// Honor max rate
if (maxBitrateSetting.HasValue)
{
@@ -595,19 +602,16 @@ namespace MediaBrowser.Model.Dlna
private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
{
- var defaultBitrate = 128000;
- if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
- {
- defaultBitrate = 192000;
- }
- if (!string.IsNullOrEmpty(targetAudioCodec) && audioStream != null && StringHelper.EqualsIgnoreCase(audioStream.Codec, targetAudioCodec))
+ var defaultBitrate = audioStream.BitRate ?? 192000;
+ // Reduce the bitrate if we're downmixing
+ if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
{
- defaultBitrate = audioStream.BitRate ?? defaultBitrate;
+ defaultBitrate = StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3") ? 192000 : 128000;
}
if (targetAudioChannels.HasValue)
{
- if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1500000)
+ if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1200000)
{
if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
{
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 02239aa48..ac024f00b 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -36,7 +36,6 @@ namespace MediaBrowser.Model.Dlna
public string VideoProfile { get; set; }
public bool CopyTimestamps { get; set; }
- public bool ForceLiveStream { get; set; }
public bool EnableSubtitlesInManifest { get; set; }
public string[] AudioCodecs { get; set; }
@@ -216,7 +215,7 @@ namespace MediaBrowser.Model.Dlna
list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty));
list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty));
- if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !item.ForceLiveStream)
+ if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"))
{
list.Add(new NameValuePair("StartTimeTicks", string.Empty));
}
@@ -246,7 +245,6 @@ namespace MediaBrowser.Model.Dlna
}
list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower()));
- list.Add(new NameValuePair("ForceLiveStream", item.ForceLiveStream.ToString().ToLower()));
list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty));
diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
index 19caf85eb..beb83b053 100644
--- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
@@ -35,9 +35,6 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("context")]
public EncodingContext Context { get; set; }
- [XmlAttribute("forceLiveStream")]
- public bool ForceLiveStream { get; set; }
-
[XmlAttribute("enableSubtitlesInManifest")]
public bool EnableSubtitlesInManifest { get; set; }
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index a13814538..8bf0703be 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -241,7 +241,7 @@ namespace MediaBrowser.Model.Net
}
if (StringHelper.EqualsIgnoreCase(ext, ".opus"))
{
- return "audio/opus";
+ return "audio/ogg";
}
// Playlists
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index 6c7918988..027341ee6 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -167,10 +167,13 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{
- var file = directoryService.GetFile(item.Path);
- if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
{
- return true;
+ var file = directoryService.GetFile(item.Path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ return true;
+ }
}
return false;
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 0df8b6c4b..d255110fb 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -171,7 +171,7 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{
- if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path))
+ if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
{
var file = directoryService.GetFile(item.Path);
if (file != null && file.LastWriteTimeUtc != item.DateModified)
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index 280e92beb..2ad02da2e 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -194,7 +194,7 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{
- if (item.EnableRefreshOnDateModifiedChange)
+ if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
{
var file = directoryService.GetFile(item.Path);
if (file != null && file.LastWriteTimeUtc != item.DateModified)
diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs
index 619b72636..c48c3d09b 100644
--- a/MediaBrowser.Providers/Photos/PhotoProvider.cs
+++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs
@@ -154,10 +154,13 @@ namespace MediaBrowser.Providers.Photos
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{
- var file = directoryService.GetFile(item.Path);
- if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
{
- return true;
+ var file = directoryService.GetFile(item.Path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ return true;
+ }
}
return false;
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index ea9e58ee4..7ed4dc71e 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -172,27 +172,29 @@ namespace MediaBrowser.Server.Implementations.IO
}
}
- public void Start()
+ private bool IsLibraryMonitorEnabaled(BaseItem item)
{
- if (EnableLibraryMonitor)
+ var options = LibraryManager.GetLibraryOptions(item);
+
+ if (options != null && options.SchemaVersion >= 1)
{
- StartInternal();
+ return options.EnableRealtimeMonitor;
}
+
+ return EnableLibraryMonitor;
}
- /// <summary>
- /// Starts this instance.
- /// </summary>
- private void StartInternal()
+ public void Start()
{
LibraryManager.ItemAdded += LibraryManager_ItemAdded;
LibraryManager.ItemRemoved += LibraryManager_ItemRemoved;
- var pathsToWatch = new List<string> { LibraryManager.RootFolder.Path };
+ var pathsToWatch = new List<string> { };
var paths = LibraryManager
.RootFolder
.Children
+ .Where(IsLibraryMonitorEnabaled)
.OfType<Folder>()
.SelectMany(f => f.PhysicalLocations)
.Distinct(StringComparer.OrdinalIgnoreCase)
@@ -213,6 +215,14 @@ namespace MediaBrowser.Server.Implementations.IO
}
}
+ private void StartWatching(BaseItem item)
+ {
+ if (IsLibraryMonitorEnabaled(item))
+ {
+ StartWatchingPath(item.Path);
+ }
+ }
+
/// <summary>
/// Handles the ItemRemoved event of the LibraryManager control.
/// </summary>
@@ -235,7 +245,7 @@ namespace MediaBrowser.Server.Implementations.IO
{
if (e.Item.GetParent() is AggregateFolder)
{
- StartWatchingPath(e.Item.Path);
+ StartWatching(e.Item);
}
}
@@ -382,14 +392,6 @@ namespace MediaBrowser.Server.Implementations.IO
Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex);
DisposeWatcher(dw);
-
- if (ConfigurationManager.Configuration.EnableLibraryMonitor == AutoOnOff.Auto)
- {
- Logger.Info("Disabling realtime monitor to prevent future instability");
-
- ConfigurationManager.Configuration.EnableLibraryMonitor = AutoOnOff.Disabled;
- Stop();
- }
}
/// <summary>
@@ -420,8 +422,8 @@ namespace MediaBrowser.Server.Implementations.IO
var filename = Path.GetFileName(path);
- var monitorPath = !string.IsNullOrEmpty(filename) &&
- !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
+ var monitorPath = !string.IsNullOrEmpty(filename) &&
+ !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
!_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase);
// Ignore certain files
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
index 7f709d084..46ba7d2e7 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -123,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
{
if (extractImages)
{
- if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay)
+ if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
{
continue;
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
index bf2afb5ac..c1394ee1c 100644
--- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
@@ -313,8 +313,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (Folder.IsPathOffline(path))
{
- libraryItem.IsOffline = true;
- await libraryItem.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
+ await libraryItem.UpdateIsOffline(true).ConfigureAwait(false);
continue;
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index b9befb531..5c94d589d 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -211,7 +211,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.AddColumn(Logger, "TypedBaseItems", "ProductionYear", "INT");
_connection.AddColumn(Logger, "TypedBaseItems", "ParentId", "GUID");
_connection.AddColumn(Logger, "TypedBaseItems", "Genres", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ParentalRatingValue", "INT");
_connection.AddColumn(Logger, "TypedBaseItems", "SchemaVersion", "INT");
_connection.AddColumn(Logger, "TypedBaseItems", "SortName", "Text");
_connection.AddColumn(Logger, "TypedBaseItems", "RunTimeTicks", "BIGINT");
@@ -488,7 +487,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
"ProductionYear",
"ParentId",
"Genres",
- "ParentalRatingValue",
"InheritedParentalRatingValue",
"SchemaVersion",
"SortName",
@@ -795,7 +793,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray());
- _saveItemCommand.GetParameter(index++).Value = item.GetParentalRatingValue() ?? 0;
_saveItemCommand.GetParameter(index++).Value = item.GetInheritedParentalRatingValue() ?? 0;
_saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion;
diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
index 03485012f..408ec717e 100644
--- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
+++ b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
@@ -85,6 +85,11 @@ namespace MediaBrowser.Server.Implementations.Sync
{
Name = "Low",
Id = "low"
+ },
+ new SyncQualityOption
+ {
+ Name = "Custom",
+ Id = "custom"
}
};
}
diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
index 4011fa3de..faf3ba37e 100644
--- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
+++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
@@ -132,7 +132,7 @@ namespace MediaBrowser.Server.Mono.Native
{
get
{
- return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
+ return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
}
}
@@ -187,7 +187,7 @@ namespace MediaBrowser.Server.Mono.Native
{
info.SystemArchitecture = Architecture.X64;
}
- else
+ else
{
info.SystemArchitecture = Architecture.X86;
}
@@ -273,32 +273,11 @@ namespace MediaBrowser.Server.Mono.Native
break;
}
- info.DownloadUrls = GetDownloadUrls(environment);
+ // No version available - user requirement
+ info.DownloadUrls = new string[] { };
return info;
}
-
- private static string[] GetDownloadUrls(NativeEnvironment environment)
- {
- switch (environment.OperatingSystem)
- {
- case OperatingSystem.Linux:
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
- };
- }
- break;
- }
-
- // No version available
- return new string[] { };
- }
-
}
public class NullPowerManagement : IPowerManagement
diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
index 8516e54ee..9c5015b0e 100644
--- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
+++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
@@ -386,8 +386,6 @@ namespace MediaBrowser.Server.Startup.Common
new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
new DbMigration(ServerConfigurationManager, TaskManager),
new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename),
- new FolderViewSettingMigration(ServerConfigurationManager, UserManager),
- new CollectionGroupingMigration(ServerConfigurationManager, UserManager),
new CollectionsViewMigration(ServerConfigurationManager, UserManager)
};
diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
index 979a3a357..5ee7d49e8 100644
--- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
+++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
@@ -70,9 +70,7 @@
<Compile Include="FFMpeg\FFMpegInfo.cs" />
<Compile Include="INativeApp.cs" />
<Compile Include="MbLinkShortcutHandler.cs" />
- <Compile Include="Migrations\CollectionGroupingMigration.cs" />
<Compile Include="Migrations\CollectionsViewMigration.cs" />
- <Compile Include="Migrations\FolderViewSettingMigration.cs" />
<Compile Include="Migrations\IVersionMigration.cs" />
<Compile Include="Migrations\DbMigration.cs" />
<Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
diff --git a/MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs
deleted file mode 100644
index 5041c49b8..000000000
--- a/MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Linq;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
- public class CollectionGroupingMigration : IVersionMigration
- {
- private readonly IServerConfigurationManager _config;
- private readonly IUserManager _userManager;
-
- public CollectionGroupingMigration(IServerConfigurationManager config, IUserManager userManager)
- {
- _config = config;
- _userManager = userManager;
- }
-
- public void Run()
- {
- var migrationKey = this.GetType().Name;
- var migrationKeyList = _config.Configuration.Migrations.ToList();
-
- if (!migrationKeyList.Contains(migrationKey))
- {
- if (_config.Configuration.IsStartupWizardCompleted)
- {
- if (_userManager.Users.Any(i => i.Configuration.GroupMoviesIntoBoxSets))
- {
- _config.Configuration.EnableGroupingIntoCollections = true;
- }
- }
-
- migrationKeyList.Add(migrationKey);
- _config.Configuration.Migrations = migrationKeyList.ToArray();
- _config.SaveConfiguration();
- }
-
- }
- }
-}
diff --git a/MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs
deleted file mode 100644
index 12054864b..000000000
--- a/MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Linq;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
- public class FolderViewSettingMigration : IVersionMigration
- {
- private readonly IServerConfigurationManager _config;
- private readonly IUserManager _userManager;
-
- public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager)
- {
- _config = config;
- _userManager = userManager;
- }
-
- public void Run()
- {
- var migrationKey = this.GetType().Name;
- var migrationKeyList = _config.Configuration.Migrations.ToList();
-
- if (!migrationKeyList.Contains(migrationKey))
- {
- if (_config.Configuration.IsStartupWizardCompleted)
- {
- if (_userManager.Users.Any(i => i.Configuration.DisplayFoldersView))
- {
- _config.Configuration.EnableFolderView = true;
- }
- }
-
- migrationKeyList.Add(migrationKey);
- _config.Configuration.Migrations = migrationKeyList.ToArray();
- _config.SaveConfiguration();
- }
-
- }
- }
-}
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index b2eb34526..5b66c27f4 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -1001,9 +1001,6 @@
<Content Include="dashboard-ui\scripts\livetvsuggested.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\sync.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\tvupcoming.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index e96a77e64..d1c4c0b44 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.654</version>
+ <version>3.0.655</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.654" />
+ <dependency id="MediaBrowser.Common" version="3.0.655" />
<dependency id="NLog" version="4.3.6" />
<dependency id="SimpleInjector" version="3.2.0" />
</dependencies>
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index ada11fd45..ffee84de2 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.654</version>
+ <version>3.0.655</version>
<title>MediaBrowser.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index f4a79f454..73c51ca35 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.654</version>
+ <version>3.0.655</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.654" />
+ <dependency id="MediaBrowser.Common" version="3.0.655" />
<dependency id="Interfaces.IO" version="1.0.0.5" />
</dependencies>
</metadata>