aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations
diff options
context:
space:
mode:
authorjluce50 <jeremyluce@gmail.com>2015-03-03 09:42:17 -0600
committerjluce50 <jeremyluce@gmail.com>2015-03-03 09:42:17 -0600
commit6c2e01830c7234438b879af633caaf415f560a5a (patch)
treeafd595af2f85888544f437b2a367e241ade607dc /MediaBrowser.Server.Implementations
parent7fd26410a9c49e84a146dfd77a2732b2330c3834 (diff)
parent2fc0686c308e74654f4f7ef9ea6cf56fb61b5ff5 (diff)
Merge pull request #1 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.Server.Implementations')
-rw-r--r--MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs6
-rw-r--r--MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs47
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs5
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs12
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs18
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs8
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs12
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs55
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs11
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs47
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json17
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj9
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs11
-rw-r--r--MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs14
-rw-r--r--MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs27
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs70
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs1
-rw-r--r--MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs118
-rw-r--r--MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs59
-rw-r--r--MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs31
-rw-r--r--MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs143
-rw-r--r--MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Sync/MediaSync.cs225
-rw-r--r--MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs69
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncManager.cs32
-rw-r--r--MediaBrowser.Server.Implementations/packages.config2
31 files changed, 913 insertions, 198 deletions
diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
index 2fe5d8f74..566f4c5f4 100644
--- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
+++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
+using System;
using System.IO;
using System.Linq;
@@ -14,6 +15,11 @@ namespace MediaBrowser.Server.Implementations.Devices
public override bool IsVisible(User user)
{
+ if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
return GetChildren(user, true).Any() &&
base.IsVisible(user);
}
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs
index 81d4a786a..6287d0bb8 100644
--- a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs
+++ b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs
@@ -62,8 +62,9 @@ namespace MediaBrowser.Server.Implementations.Drawing
logger.Info("Failed to read image header for {0}. Doing it the slow way.", path);
}
- using (var wand = new MagickWand(path))
+ using (var wand = new MagickWand())
{
+ wand.PingImage(path);
var img = wand.CurrentImage;
return new ImageSize
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
index 85eadd73c..180faa6bb 100644
--- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
+++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
@@ -350,9 +350,9 @@ namespace MediaBrowser.Server.Implementations.Drawing
}
/// <summary>
- /// Increment this when indicator drawings change
+ /// Increment this when there's a change requiring caches to be invalidated
/// </summary>
- private const string IndicatorVersion = "2";
+ private const string Version = "3";
/// <summary>
/// Gets the cache file path based on a set of parameters
@@ -371,29 +371,19 @@ namespace MediaBrowser.Server.Implementations.Drawing
filename += "f=" + format;
- var hasIndicator = false;
-
if (addPlayedIndicator)
{
filename += "pl=true";
- hasIndicator = true;
}
if (percentPlayed > 0)
{
filename += "p=" + percentPlayed;
- hasIndicator = true;
}
if (unwatchedCount.HasValue)
{
filename += "p=" + unwatchedCount.Value;
- hasIndicator = true;
- }
-
- if (hasIndicator)
- {
- filename += "iv=" + IndicatorVersion;
}
if (!string.IsNullOrEmpty(backgroundColor))
@@ -401,6 +391,8 @@ namespace MediaBrowser.Server.Implementations.Drawing
filename += "b=" + backgroundColor;
}
+ filename += "v=" + Version;
+
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower());
}
@@ -414,6 +406,11 @@ namespace MediaBrowser.Server.Implementations.Drawing
return GetImageSize(path, File.GetLastWriteTimeUtc(path));
}
+ public ImageSize GetImageSize(ItemImageInfo info)
+ {
+ return GetImageSize(info.Path, info.DateModified);
+ }
+
/// <summary>
/// Gets the size of the image.
/// </summary>
@@ -421,7 +418,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="imageDateModified">The image date modified.</param>
/// <returns>ImageSize.</returns>
/// <exception cref="System.ArgumentNullException">path</exception>
- public ImageSize GetImageSize(string path, DateTime imageDateModified)
+ private ImageSize GetImageSize(string path, DateTime imageDateModified)
{
if (string.IsNullOrEmpty(path))
{
@@ -666,30 +663,6 @@ namespace MediaBrowser.Server.Implementations.Drawing
return enhancedImagePath;
}
- private ImageFormat GetFormat(string path)
- {
- var extension = Path.GetExtension(path);
-
- if (string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase))
- {
- return ImageFormat.Png;
- }
- if (string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase))
- {
- return ImageFormat.Gif;
- }
- if (string.Equals(extension, ".webp", StringComparison.OrdinalIgnoreCase))
- {
- return ImageFormat.Webp;
- }
- if (string.Equals(extension, ".bmp", StringComparison.OrdinalIgnoreCase))
- {
- return ImageFormat.Bmp;
- }
-
- return ImageFormat.Jpg;
- }
-
/// <summary>
/// Executes the image enhancers.
/// </summary>
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index b15809738..75037159c 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -1598,14 +1598,11 @@ namespace MediaBrowser.Server.Implementations.Dto
var path = imageInfo.Path;
- // See if we can avoid a file system lookup by looking for the file in ResolveArgs
- var dateModified = imageInfo.DateModified;
-
ImageSize size;
try
{
- size = _imageProcessor.GetImageSize(path, dateModified);
+ size = _imageProcessor.GetImageSize(imageInfo);
}
catch (FileNotFoundException)
{
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs
index 0b0661321..28883e9a2 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs
@@ -86,6 +86,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_userManager.UserPasswordChanged += _userManager_UserPasswordChanged;
_userManager.UserDeleted += _userManager_UserDeleted;
_userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
+ _userManager.UserLockedOut += _userManager_UserLockedOut;
//_config.ConfigurationUpdated += _config_ConfigurationUpdated;
//_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@@ -95,6 +96,16 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
}
+ void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
+ {
+ CreateLogEntry(new ActivityLogEntry
+ {
+ Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
+ Type = "UserLockedOut",
+ UserId = e.Argument.Id.ToString("N")
+ });
+ }
+
void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
{
CreateLogEntry(new ActivityLogEntry
@@ -482,6 +493,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_userManager.UserPasswordChanged -= _userManager_UserPasswordChanged;
_userManager.UserDeleted -= _userManager_UserDeleted;
_userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
+ _userManager.UserLockedOut -= _userManager_UserLockedOut;
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
_config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated;
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs
index 37bca4ddb..f6a35973b 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs
@@ -78,6 +78,22 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
_deviceManager.CameraImageUploaded +=_deviceManager_CameraImageUploaded;
+
+ _userManager.UserLockedOut += _userManager_UserLockedOut;
+ }
+
+ async void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
+ {
+ var type = NotificationType.UserLockedOut.ToString();
+
+ var notification = new NotificationRequest
+ {
+ NotificationType = type
+ };
+
+ notification.Variables["UserName"] = e.Argument.Name;
+
+ await SendNotification(notification).ConfigureAwait(false);
}
async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
@@ -235,7 +251,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
return;
}
-
var notification = new NotificationRequest
{
NotificationType = type
@@ -471,6 +486,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
_appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
_deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded;
+ _userManager.UserLockedOut -= _userManager_UserLockedOut;
}
private void DisposeLibraryUpdateTimer()
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
index 681d3ac5e..ecf58e4a6 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -461,10 +461,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest)
{
- Throttle = options.Throttle,
- ThrottleLimit = options.ThrottleLimit,
- MinThrottlePosition = options.MinThrottlePosition,
- ThrottleCallback = options.ThrottleCallback,
OnComplete = options.OnComplete
};
}
@@ -480,10 +476,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
return new StreamWriter(stream, contentType, _logger)
{
- Throttle = options.Throttle,
- ThrottleLimit = options.ThrottleLimit,
- MinThrottlePosition = options.MinThrottlePosition,
- ThrottleCallback = options.ThrottleCallback,
OnComplete = options.OnComplete
};
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
index cdd4c6d7c..8c72f9e7e 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -24,10 +24,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
private long RangeLength { get; set; }
private long TotalContentLength { get; set; }
- public bool Throttle { get; set; }
- public long ThrottleLimit { get; set; }
- public long MinThrottlePosition;
- public Func<long, long, long> ThrottleCallback { get; set; }
public Action OnComplete { get; set; }
/// <summary>
@@ -165,14 +161,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream)
{
- if (Throttle)
- {
- responseStream = new ThrottledStream(responseStream, ThrottleLimit)
- {
- MinThrottlePosition = MinThrottlePosition,
- ThrottleCallback = ThrottleCallback
- };
- }
WriteToInternal(responseStream);
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
index 1db14e887..fe662542e 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
@@ -35,10 +35,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
get { return _options; }
}
- public bool Throttle { get; set; }
- public long ThrottleLimit { get; set; }
- public long MinThrottlePosition;
- public Func<long, long, long> ThrottleCallback { get; set; }
public Action OnComplete { get; set; }
/// <summary>
@@ -82,14 +78,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream)
{
- if (Throttle)
- {
- responseStream = new ThrottledStream(responseStream, ThrottleLimit)
- {
- MinThrottlePosition = MinThrottlePosition,
- ThrottleCallback = ThrottleCallback
- };
- }
WriteToInternal(responseStream);
}
diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
index a45757d13..6ce989b02 100644
--- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
+using System;
using System.Collections.Generic;
using System.Linq;
@@ -47,5 +48,59 @@ namespace MediaBrowser.Server.Implementations.Library
{
return true;
}
+
+ public IEnumerable<MediaStream> GetMediaStreams(string mediaSourceId)
+ {
+ var list = GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = new Guid(mediaSourceId)
+ });
+
+ return GetMediaStreamsForItem(list);
+ }
+
+ public IEnumerable<MediaStream> GetMediaStreams(Guid itemId)
+ {
+ var list = GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = itemId
+ });
+
+ return GetMediaStreamsForItem(list);
+ }
+
+ private IEnumerable<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams)
+ {
+ var list = streams.ToList();
+
+ var subtitleStreams = list
+ .Where(i => i.Type == MediaStreamType.Subtitle)
+ .ToList();
+
+ if (subtitleStreams.Count > 0)
+ {
+ var videoStream = list.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+
+ // This is abitrary but at some point it becomes too slow to extract subtitles on the fly
+ // We need to learn more about when this is the case vs. when it isn't
+ const int maxAllowedBitrateForExternalSubtitleStream = 10000000;
+
+ var videoBitrate = videoStream == null ? maxAllowedBitrateForExternalSubtitleStream : videoStream.BitRate ?? maxAllowedBitrateForExternalSubtitleStream;
+
+ foreach (var subStream in subtitleStreams)
+ {
+ var supportsExternalStream = StreamSupportsExternalStream(subStream);
+
+ if (supportsExternalStream && videoBitrate >= maxAllowedBitrateForExternalSubtitleStream)
+ {
+ supportsExternalStream = false;
+ }
+
+ subStream.SupportsExternalStream = supportsExternalStream;
+ }
+ }
+
+ return list;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 7371ca5a9..3551b71b7 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -1,19 +1,18 @@
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
-using System;
-using System.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.IO;
using MediaBrowser.Naming.TV;
using MediaBrowser.Server.Implementations.Logging;
-using EpisodeInfo = MediaBrowser.Controller.Providers.EpisodeInfo;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index bf8792461..00c674436 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -97,6 +97,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
public event EventHandler<GenericEventArgs<User>> UserUpdated;
public event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
+ public event EventHandler<GenericEventArgs<User>> UserLockedOut;
/// <summary>
/// Called when [user updated].
@@ -259,6 +260,11 @@ namespace MediaBrowser.Server.Implementations.Library
{
user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
await UpdateUser(user).ConfigureAwait(false);
+ await UpdateInvalidLoginAttemptCount(user, 0).ConfigureAwait(false);
+ }
+ else
+ {
+ await UpdateInvalidLoginAttemptCount(user, user.Policy.InvalidLoginAttemptCount + 1).ConfigureAwait(false);
}
_logger.Info("Authentication request for {0} {1}.", user.Name, (success ? "has succeeded" : "has been denied"));
@@ -266,6 +272,38 @@ namespace MediaBrowser.Server.Implementations.Library
return success;
}
+ private async Task UpdateInvalidLoginAttemptCount(User user, int newValue)
+ {
+ if (user.Policy.InvalidLoginAttemptCount != newValue || newValue > 0)
+ {
+ user.Policy.InvalidLoginAttemptCount = newValue;
+
+ var maxCount = user.Policy.IsAdministrator ?
+ 3 :
+ 5;
+
+ var fireLockout = false;
+
+ if (newValue >= maxCount)
+ {
+ _logger.Debug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture));
+ user.Policy.IsDisabled = true;
+
+ fireLockout = true;
+ }
+
+ await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
+
+ if (fireLockout)
+ {
+ if (UserLockedOut != null)
+ {
+ EventHelper.FireEventIfNotNull(UserLockedOut, this, new GenericEventArgs<User>(user), _logger);
+ }
+ }
+ }
+ }
+
private string GetPasswordHash(User user)
{
return string.IsNullOrEmpty(user.Password)
@@ -332,11 +370,6 @@ namespace MediaBrowser.Server.Implementations.Library
{
if (!user.Configuration.HasMigratedToPolicy)
{
- user.Policy.BlockUnratedItems = user.Configuration.BlockUnratedItems;
- user.Policy.EnableContentDeletion = user.Configuration.EnableContentDeletion;
- user.Policy.EnableLiveTvAccess = user.Configuration.EnableLiveTvAccess;
- user.Policy.EnableLiveTvManagement = user.Configuration.EnableLiveTvManagement;
- user.Policy.EnableMediaPlayback = user.Configuration.EnableMediaPlayback;
user.Policy.IsAdministrator = user.Configuration.IsAdministrator;
await UpdateUserPolicy(user, user.Policy, false);
@@ -915,10 +948,6 @@ namespace MediaBrowser.Server.Implementations.Library
}
user.Configuration.IsAdministrator = user.Policy.IsAdministrator;
- user.Configuration.EnableLiveTvManagement = user.Policy.EnableLiveTvManagement;
- user.Configuration.EnableLiveTvAccess = user.Policy.EnableLiveTvAccess;
- user.Configuration.EnableMediaPlayback = user.Policy.EnableMediaPlayback;
- user.Configuration.EnableContentDeletion = user.Policy.EnableContentDeletion;
await UpdateConfiguration(user, user.Configuration, true).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
index f1bb5c13a..6473d10d6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -442,7 +442,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return null;
}
- private const string InternalVersionNumber = "3";
+ private const string InternalVersionNumber = "4";
public Guid GetInternalChannelId(string serviceName, string externalId)
{
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index dc80778da..2f593efcd 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -48,8 +48,10 @@
"LabelDashboardSourcePathHelp": "If running the server from source, specify the path to the dashboard-ui folder. All web client files will be served from this location.",
"ButtonConvertMedia": "Convert media",
"ButtonOrganize": "Organize",
+ "LabelPinCode": "Pin code:",
"ButtonOk": "Ok",
"ButtonCancel": "Cancel",
+ "ButtonExit": "Exit",
"ButtonNew": "New",
"HeaderTV": "TV",
"HeaderAudio": "Audio",
@@ -57,6 +59,12 @@
"HeaderPaths": "Paths",
"CategorySync": "Sync",
"HeaderEasyPinCode": "Easy Pin Code",
+ "HeaderGrownupsOnly": "Grown-ups Only!",
+ "DividerOr": "-- or --",
+ "HeaderToAccessPleaseEnterEasyPinCode": "To access, please enter your easy pin code",
+ "KidsModeAdultInstruction": "Click the lock icon in the bottom right to configure or leave kids mode. Your pin code will be required.",
+ "ButtonConfigurePinCode": "Configure pin code",
+ "HeaderAdultsReadHere": "Adults Read Here!",
"RegisterWithPayPal": "Register with PayPal",
"HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership",
"HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial",
@@ -670,6 +678,7 @@
"NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionNewLibraryContentMultiple": "New content added (multiple)",
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
+ "NotificationOptionUserLockedOut": "User locked out",
"SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.",
"NotificationOptionServerRestartRequired": "Server restart required",
"LabelNotificationEnabled": "Enable this notification",
@@ -1061,6 +1070,7 @@
"OptionBox": "Box",
"OptionBoxRear": "Box rear",
"OptionDisc": "Disc",
+ "OptionIcon": "Icon",
"OptionLogo": "Logo",
"OptionMenu": "Menu",
"OptionScreenshot": "Screenshot",
@@ -1105,6 +1115,7 @@
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"LabelRunningTimeValue": "Running time: {0}",
"LabelIpAddressValue": "Ip address: {0}",
+ "UserLockedOutWithName": "User {0} has been locked out",
"UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
"UserCreatedWithName": "User {0} has been created",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
@@ -1114,7 +1125,7 @@
"MessageApplicationUpdated": "Media Browser Server has been updated",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
- "UserDownloadingItemWithValues": "{0} is downloading {1}",
+ "UserDownloadingItemWithValues": "{0} is downloading {1}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
"UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
"AppDeviceValues": "App: {0}, Device: {1}",
@@ -1369,5 +1380,7 @@
"TabJobs": "Jobs",
"TabSyncJobs": "Sync Jobs",
"LabelTagFilterMode": "Mode:",
- "LabelTagFilterAllowModeHelp": "If allowed tags are used as part of a deeply nested folder structure, content that is tagged will require parent folders to be tagged as well."
+ "LabelTagFilterAllowModeHelp": "If allowed tags are used as part of a deeply nested folder structure, content that is tagged will require parent folders to be tagged as well.",
+ "HeaderThisUserIsCurrentlyDisabled": "This user is currently disabled",
+ "MessageReenableUser": "See below to reenable"
}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 509a274ee..54df9a86d 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -47,7 +47,7 @@
<ItemGroup>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\ImageMagickSharp.1.0.0.2\lib\net45\ImageMagickSharp.dll</HintPath>
+ <HintPath>..\packages\ImageMagickSharp.1.0.0.6\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
<Reference Include="MediaBrowser.Naming, Version=1.0.5509.27636, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -278,6 +278,7 @@
<Compile Include="Sorting\CommunityRatingComparer.cs" />
<Compile Include="Sorting\CriticRatingComparer.cs" />
<Compile Include="Sorting\DateCreatedComparer.cs" />
+ <Compile Include="Sorting\DateLastMediaAddedComparer.cs" />
<Compile Include="Sorting\DatePlayedComparer.cs" />
<Compile Include="Sorting\GameSystemComparer.cs" />
<Compile Include="Sorting\IsFavoriteOrLikeComparer.cs" />
@@ -303,8 +304,12 @@
<Compile Include="Sorting\StudioComparer.cs" />
<Compile Include="Sorting\VideoBitRateComparer.cs" />
<Compile Include="Sync\AppSyncProvider.cs" />
- <Compile Include="Sync\CloudSyncProvider.cs" />
+ <Compile Include="Sync\FolderSync\FolderSyncDataProvider.cs" />
+ <Compile Include="Sync\FolderSync\FolderSyncProvider.cs" />
+ <Compile Include="Sync\CloudSyncProfile.cs" />
+ <Compile Include="Sync\IHasSyncProfile.cs" />
<Compile Include="Sync\MediaSync.cs" />
+ <Compile Include="Sync\MultiProviderSync.cs" />
<Compile Include="Sync\SyncRegistrationInfo.cs" />
<Compile Include="Sync\SyncConfig.cs" />
<Compile Include="Sync\SyncJobProcessor.cs" />
diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs
index d8acbe06c..a33fe2147 100644
--- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs
+++ b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs
@@ -143,6 +143,13 @@ namespace MediaBrowser.Server.Implementations.Notifications
Type = NotificationType.CameraImageUploaded.ToString(),
DefaultTitle = "A new camera image has been uploaded from {DeviceName}.",
Variables = new List<string>{"DeviceName"}
+ },
+
+ new NotificationTypeInfo
+ {
+ Type = NotificationType.UserLockedOut.ToString(),
+ DefaultTitle = "{UserName} has been locked out.",
+ Variables = new List<string>{"UserName"}
}
};
@@ -185,6 +192,10 @@ namespace MediaBrowser.Server.Implementations.Notifications
{
note.Category = _localization.GetLocalizedString("CategorySync");
}
+ else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ note.Category = _localization.GetLocalizedString("CategoryUser");
+ }
else
{
note.Category = _localization.GetLocalizedString("CategorySystem");
diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
index e1f98c659..40b85dad1 100644
--- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
@@ -108,7 +108,12 @@ namespace MediaBrowser.Server.Implementations.Photos
protected Task<Stream> GetThumbCollage(List<BaseItem> items)
{
- return DynamicImageHelpers.GetThumbCollage(items.Select(i => i.GetImagePath(ImageType.Primary) ?? i.GetImagePath(ImageType.Thumb)).ToList(),
+ var files = items
+ .Select(i => i.GetImagePath(ImageType.Primary) ?? i.GetImagePath(ImageType.Thumb))
+ .Where(i => !string.IsNullOrWhiteSpace(i))
+ .ToList();
+
+ return DynamicImageHelpers.GetThumbCollage(files,
FileSystem,
1600,
900,
@@ -117,7 +122,12 @@ namespace MediaBrowser.Server.Implementations.Photos
protected Task<Stream> GetSquareCollage(List<BaseItem> items)
{
- return DynamicImageHelpers.GetSquareCollage(items.Select(i => i.GetImagePath(ImageType.Primary) ?? i.GetImagePath(ImageType.Thumb)).ToList(),
+ var files = items
+ .Select(i => i.GetImagePath(ImageType.Primary) ?? i.GetImagePath(ImageType.Thumb))
+ .Where(i => !string.IsNullOrWhiteSpace(i))
+ .ToList();
+
+ return DynamicImageHelpers.GetSquareCollage(files,
FileSystem,
800, ApplicationPaths);
}
diff --git a/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs b/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs
index c2af9cdaf..e7cd2f4d2 100644
--- a/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs
+++ b/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Common.IO;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Photos
@@ -15,6 +16,11 @@ namespace MediaBrowser.Server.Implementations.Photos
int width,
int height, IApplicationPaths appPaths)
{
+ if (files.Any(string.IsNullOrWhiteSpace))
+ {
+ throw new ArgumentException("Empty file found in files list");
+ }
+
if (files.Count < 3)
{
return await GetSingleImage(files, fileSystem).ConfigureAwait(false);
@@ -27,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Photos
int cellHeight = height;
var index = 0;
- using (var wand = new MagickWand(width, height, "transparent"))
+ using (var wand = new MagickWand(width, height, new PixelWand(ColorName.None, 1)))
{
for (var row = 0; row < rows; row++)
{
@@ -57,6 +63,11 @@ namespace MediaBrowser.Server.Implementations.Photos
IFileSystem fileSystem,
int size, IApplicationPaths appPaths)
{
+ if (files.Any(string.IsNullOrWhiteSpace))
+ {
+ throw new ArgumentException("Empty file found in files list");
+ }
+
if (files.Count < 4)
{
return await GetSingleImage(files, fileSystem).ConfigureAwait(false);
@@ -68,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Photos
int singleSize = size / 2;
var index = 0;
- using (var wand = new MagickWand(size, size, "transparent"))
+ using (var wand = new MagickWand(size, size, new PixelWand(ColorName.None, 1)))
{
for (var row = 0; row < rows; row++)
{
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index d02ef9d27..8eef8536a 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -399,7 +399,7 @@ namespace MediaBrowser.Server.Implementations.Session
Client = clientType,
DeviceId = deviceId,
ApplicationVersion = appVersion,
- Id = Guid.NewGuid().ToString("N")
+ Id = key.GetMD5().ToString("N")
};
sessionInfo.DeviceName = deviceName;
@@ -798,6 +798,19 @@ namespace MediaBrowser.Server.Implementations.Session
return session;
}
+ private SessionInfo GetSessionToRemoteControl(string sessionId)
+ {
+ // Accept either device id or session id
+ var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
+
+ if (session == null)
+ {
+ throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
+ }
+
+ return session;
+ }
+
public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken)
{
var generalCommand = new GeneralCommand
@@ -818,7 +831,7 @@ namespace MediaBrowser.Server.Implementations.Session
public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken)
{
- var session = GetSession(sessionId);
+ var session = GetSessionToRemoteControl(sessionId);
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
@@ -828,7 +841,7 @@ namespace MediaBrowser.Server.Implementations.Session
public Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken)
{
- var session = GetSession(sessionId);
+ var session = GetSessionToRemoteControl(sessionId);
var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null;
@@ -955,7 +968,7 @@ namespace MediaBrowser.Server.Implementations.Session
public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken)
{
- var session = GetSession(sessionId);
+ var session = GetSessionToRemoteControl(sessionId);
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
@@ -1566,11 +1579,7 @@ namespace MediaBrowser.Server.Implementations.Session
if (!string.IsNullOrWhiteSpace(mediaSourceId))
{
- info.MediaStreams = _mediaSourceManager.GetMediaStreams(new MediaStreamQuery
- {
- ItemId = new Guid(mediaSourceId)
-
- }).ToList();
+ info.MediaStreams = _mediaSourceManager.GetMediaStreams(mediaSourceId).ToList();
}
return info;
diff --git a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
new file mode 100644
index 000000000..68cd44ec9
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
@@ -0,0 +1,70 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Querying;
+using System;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Sorting
+{
+ public class DateLastMediaAddedComparer : IUserBaseItemComparer
+ {
+ /// <summary>
+ /// Gets or sets the user.
+ /// </summary>
+ /// <value>The user.</value>
+ public User User { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user manager.
+ /// </summary>
+ /// <value>The user manager.</value>
+ public IUserManager UserManager { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user data repository.
+ /// </summary>
+ /// <value>The user data repository.</value>
+ public IUserDataManager UserDataRepository { get; set; }
+
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns>System.Int32.</returns>
+ public int Compare(BaseItem x, BaseItem y)
+ {
+ return GetDate(x).CompareTo(GetDate(y));
+ }
+
+ /// <summary>
+ /// Gets the date.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <returns>DateTime.</returns>
+ private DateTime GetDate(BaseItem x)
+ {
+ var folder = x as Folder;
+
+ if (folder != null)
+ {
+ return folder.GetRecursiveChildren(User, i => !i.IsFolder)
+ .Select(i => i.DateCreated)
+ .OrderByDescending(i => i)
+ .FirstOrDefault();
+ }
+
+ return x.DateCreated;
+ }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get { return ItemSortBy.DateLastContentAdded; }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs
index 7605a7a50..c881591be 100644
--- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs
+++ b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
index d35ff8fc4..2106fc12e 100644
--- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
+++ b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
@@ -8,7 +8,7 @@ using System.Linq;
namespace MediaBrowser.Server.Implementations.Sync
{
- public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds
+ public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncProfile
{
private readonly IDeviceManager _deviceManager;
@@ -42,5 +42,18 @@ namespace MediaBrowser.Server.Implementations.Sync
{
get { return "App Sync"; }
}
+
+ public IEnumerable<SyncTarget> GetAllSyncTargets()
+ {
+ return _deviceManager.GetDevices(new DeviceQuery
+ {
+ SupportsSync = true
+
+ }).Items.Select(i => new SyncTarget
+ {
+ Id = i.Id,
+ Name = i.Name
+ });
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
new file mode 100644
index 000000000..babdf3f80
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
@@ -0,0 +1,118 @@
+using MediaBrowser.Model.Dlna;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+ public class CloudSyncProfile : DeviceProfile
+ {
+ public CloudSyncProfile(bool supportsAc3, bool supportsDca)
+ {
+ Name = "Cloud Sync";
+
+ MaxStreamingBitrate = 20000000;
+ MaxStaticBitrate = 20000000;
+
+ var mkvAudio = "aac,mp3";
+ var mp4Audio = "aac";
+
+ if (supportsAc3)
+ {
+ mkvAudio += ",ac3";
+ mp4Audio += ",ac3";
+ }
+
+ if (supportsDca)
+ {
+ mkvAudio += ",dca";
+ }
+
+ DirectPlayProfiles = new[]
+ {
+ new DirectPlayProfile
+ {
+ Container = "mkv",
+ VideoCodec = "h264,mpeg4",
+ AudioCodec = mkvAudio,
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "mp4,mov,m4v",
+ VideoCodec = "h264,mpeg4",
+ AudioCodec = mp4Audio,
+ Type = DlnaProfileType.Video
+ }
+ };
+
+ ContainerProfiles = new ContainerProfile[] { };
+
+ CodecProfiles = new[]
+ {
+ new CodecProfile
+ {
+ Type = CodecType.Video,
+ Conditions = new []
+ {
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.VideoBitDepth,
+ Value = "8",
+ IsRequired = false
+ },
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.Height,
+ Value = "1080",
+ IsRequired = false
+ },
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.RefFrames,
+ Value = "12",
+ IsRequired = false
+ }
+ }
+ }
+ };
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.External
+ }
+ };
+
+ TranscodingProfiles = new[]
+ {
+ new TranscodingProfile
+ {
+ Container = "mp3",
+ AudioCodec = "mp3",
+ Type = DlnaProfileType.Audio,
+ Context = EncodingContext.Static
+ },
+
+ new TranscodingProfile
+ {
+ Container = "mp4",
+ Type = DlnaProfileType.Video,
+ AudioCodec = "aac",
+ VideoCodec = "h264",
+ Context = EncodingContext.Static
+ },
+
+ new TranscodingProfile
+ {
+ Container = "jpeg",
+ Type = DlnaProfileType.Photo,
+ Context = EncodingContext.Static
+ }
+ };
+
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs
deleted file mode 100644
index 37caa561e..000000000
--- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class CloudSyncProvider : IServerSyncProvider
- {
- private readonly ICloudSyncProvider[] _providers = {};
-
- public CloudSyncProvider(IApplicationHost appHost)
- {
- _providers = appHost.GetExports<ICloudSyncProvider>().ToArray();
- }
-
- public IEnumerable<SyncTarget> GetSyncTargets(string userId)
- {
- return _providers.SelectMany(i => i.GetSyncTargets(userId));
- }
-
- public DeviceProfile GetDeviceProfile(SyncTarget target)
- {
- return new DeviceProfile();
- }
-
- public string Name
- {
- get { return "Cloud Sync"; }
- }
-
- private ICloudSyncProvider GetProvider(SyncTarget target)
- {
- return null;
- }
-
- public Task<List<string>> GetServerItemIds(string serverId, SyncTarget target, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task DeleteItem(string serverId, string itemId, SyncTarget target, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task TransferItemFile(string serverId, string itemId, string inputFile, string[] pathParts, SyncTarget target, CancellationToken cancellationToken)
- {
- var provider = GetProvider(target);
-
- return provider.TransferItemFile(serverId, itemId, inputFile, pathParts, target, cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs
new file mode 100644
index 000000000..b9008d87e
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs
@@ -0,0 +1,31 @@
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Sync;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Sync.FolderSync
+{
+ public class FolderSyncDataProvider : ISyncDataProvider
+ {
+ public Task<List<string>> GetServerItemIds(SyncTarget target, string serverId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task AddOrUpdate(SyncTarget target, LocalItem item)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task Delete(SyncTarget target, string id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task<LocalItem> Get(SyncTarget target, string id)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs
new file mode 100644
index 000000000..3183816c8
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs
@@ -0,0 +1,143 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Sync;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Sync.FolderSync
+{
+ public class FolderSyncProvider : IServerSyncProvider
+ {
+ private readonly IApplicationPaths _appPaths;
+ private readonly IUserManager _userManager;
+
+ public FolderSyncProvider(IApplicationPaths appPaths, IUserManager userManager)
+ {
+ _appPaths = appPaths;
+ _userManager = userManager;
+ }
+
+ public Task SendFile(string inputFile, string path, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ return Task.Run(() => File.Copy(inputFile, path, true), cancellationToken);
+ }
+
+ public Task DeleteFile(string path, SyncTarget target, CancellationToken cancellationToken)
+ {
+ return Task.Run(() => File.Delete(path), cancellationToken);
+ }
+
+ public Task<Stream> GetFile(string path, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ return Task.FromResult((Stream)File.OpenRead(path));
+ }
+
+ public string GetFullPath(IEnumerable<string> paths, SyncTarget target)
+ {
+ var account = GetSyncAccounts()
+ .FirstOrDefault(i => string.Equals(i.Id, target.Id, StringComparison.OrdinalIgnoreCase));
+
+ if (account == null)
+ {
+ throw new ArgumentException("Invalid SyncTarget supplied.");
+ }
+
+ var list = paths.ToList();
+ list.Insert(0, account.Path);
+
+ return Path.Combine(list.ToArray());
+ }
+
+ public string GetParentDirectoryPath(string path, SyncTarget target)
+ {
+ return Path.GetDirectoryName(path);
+ }
+
+ public Task<List<DeviceFileInfo>> GetFileSystemEntries(string path, SyncTarget target)
+ {
+ List<FileInfo> files;
+
+ try
+ {
+ files = new DirectoryInfo(path).EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
+ }
+ catch (DirectoryNotFoundException)
+ {
+ files = new List<FileInfo>();
+ }
+
+ return Task.FromResult(files.Select(i => new DeviceFileInfo
+ {
+ Name = i.Name,
+ Path = i.FullName
+
+ }).ToList());
+ }
+
+ public ISyncDataProvider GetDataProvider()
+ {
+ // If single instances are needed, manage them here
+ return new FolderSyncDataProvider();
+ }
+
+ public string Name
+ {
+ get { return "Folder Sync"; }
+ }
+
+ public IEnumerable<SyncTarget> GetSyncTargets(string userId)
+ {
+ return GetSyncAccounts()
+ .Where(i => i.UserIds.Contains(userId, StringComparer.OrdinalIgnoreCase))
+ .Select(GetSyncTarget);
+ }
+
+ public IEnumerable<SyncTarget> GetAllSyncTargets()
+ {
+ return GetSyncAccounts().Select(GetSyncTarget);
+ }
+
+ private SyncTarget GetSyncTarget(SyncAccount account)
+ {
+ return new SyncTarget
+ {
+ Id = account.Id,
+ Name = account.Name
+ };
+ }
+
+ private IEnumerable<SyncAccount> GetSyncAccounts()
+ {
+ return new List<SyncAccount>();
+ // Dummy this up
+ return _userManager
+ .Users
+ .Select(i => new SyncAccount
+ {
+ Id = i.Id.ToString("N"),
+ UserIds = new List<string> { i.Id.ToString("N") },
+ Path = Path.Combine(_appPaths.DataPath, "foldersync", i.Id.ToString("N")),
+ Name = i.Name + "'s Folder Sync"
+ });
+ }
+
+ // An internal class to manage all configured Folder Sync accounts for differnet users
+ class SyncAccount
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Path { get; set; }
+ public List<string> UserIds { get; set; }
+
+ public SyncAccount()
+ {
+ UserIds = new List<string>();
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs
new file mode 100644
index 000000000..b7e9daf49
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs
@@ -0,0 +1,15 @@
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Sync;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+ public interface IHasSyncProfile
+ {
+ /// <summary>
+ /// Gets the device profile.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ /// <returns>DeviceProfile.</returns>
+ DeviceProfile GetDeviceProfile(SyncTarget target);
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
index 349e6aa1d..0407510a8 100644
--- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
+++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
@@ -1,10 +1,18 @@
-using MediaBrowser.Common.Progress;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Sync;
using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -15,22 +23,25 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly ISyncManager _syncManager;
private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
- public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost)
+ public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem)
{
_logger = logger;
_syncManager = syncManager;
_appHost = appHost;
+ _fileSystem = fileSystem;
}
- public async Task Sync(IServerSyncProvider provider,
+ public async Task Sync(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
SyncTarget target,
IProgress<double> progress,
CancellationToken cancellationToken)
{
var serverId = _appHost.SystemId;
- await SyncData(provider, serverId, target, cancellationToken).ConfigureAwait(false);
+ await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
progress.Report(3);
var innerProgress = new ActionableProgress<double>();
@@ -40,20 +51,21 @@ namespace MediaBrowser.Server.Implementations.Sync
totalProgress += 1;
progress.Report(totalProgress);
});
- await GetNewMedia(provider, target, serverId, innerProgress, cancellationToken);
+ await GetNewMedia(provider, dataProvider, target, serverId, innerProgress, cancellationToken);
// Do the data sync twice so the server knows what was removed from the device
- await SyncData(provider, serverId, target, cancellationToken).ConfigureAwait(false);
+ await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
progress.Report(100);
}
private async Task SyncData(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
string serverId,
SyncTarget target,
CancellationToken cancellationToken)
{
- var localIds = await provider.GetServerItemIds(serverId, target, cancellationToken).ConfigureAwait(false);
+ var localIds = await dataProvider.GetServerItemIds(target, serverId).ConfigureAwait(false);
var result = await _syncManager.SyncData(new SyncDataRequest
{
@@ -68,23 +80,24 @@ namespace MediaBrowser.Server.Implementations.Sync
{
try
{
- await RemoveItem(provider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false);
+ await RemoveItem(provider, dataProvider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
- _logger.ErrorException("Error deleting item from sync target. Id: {0}", ex, itemIdToRemove);
+ _logger.ErrorException("Error deleting item from device. Id: {0}", ex, itemIdToRemove);
}
}
}
private async Task GetNewMedia(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
SyncTarget target,
string serverId,
IProgress<double> progress,
CancellationToken cancellationToken)
{
- var jobItems = await _syncManager.GetReadySyncItems(target.Id).ConfigureAwait(false);
-
+ var jobItems = await _syncManager.GetReadySyncItems(target.Id).ConfigureAwait(false);
+
var numComplete = 0;
double startingPercent = 0;
double percentPerItem = 1;
@@ -106,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.Sync
progress.Report(totalProgress);
});
- await GetItem(provider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
+ await GetItem(provider, dataProvider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
numComplete++;
startingPercent = numComplete;
@@ -117,6 +130,7 @@ namespace MediaBrowser.Server.Implementations.Sync
}
private async Task GetItem(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
SyncTarget target,
string serverId,
SyncedItem jobItem,
@@ -129,6 +143,8 @@ namespace MediaBrowser.Server.Implementations.Sync
var fileTransferProgress = new ActionableProgress<double>();
fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
+ var localItem = CreateLocalItem(provider, target, libraryItem, serverId, jobItem.OriginalFileName);
+
await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id);
var transferSuccess = false;
@@ -136,10 +152,10 @@ namespace MediaBrowser.Server.Implementations.Sync
try
{
- string[] pathParts = GetPathParts(serverId, libraryItem);
+ await SendFile(provider, internalSyncJobItem.OutputPath, localItem, target, cancellationToken).ConfigureAwait(false);
- await provider.TransferItemFile(serverId, libraryItem.Id, internalSyncJobItem.OutputPath, pathParts, target, cancellationToken)
- .ConfigureAwait(false);
+ // Create db record
+ await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
progress.Report(92);
@@ -165,18 +181,189 @@ namespace MediaBrowser.Server.Implementations.Sync
}
}
- private Task RemoveItem(IServerSyncProvider provider,
+ private async Task RemoveItem(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
string serverId,
string itemId,
SyncTarget target,
CancellationToken cancellationToken)
{
- return provider.DeleteItem(serverId, itemId, target, cancellationToken);
+ var localId = GetLocalId(serverId, itemId);
+ var localItem = await dataProvider.Get(target, localId);
+
+ if (localItem == null)
+ {
+ return;
+ }
+
+ var files = await GetFiles(provider, localItem, target);
+
+ foreach (var file in files)
+ {
+ await provider.DeleteFile(file.Path, target, cancellationToken).ConfigureAwait(false);
+ }
+
+ await dataProvider.Delete(target, localId).ConfigureAwait(false);
+ }
+
+ private Task SendFile(IServerSyncProvider provider, string inputPath, LocalItem item, SyncTarget target, CancellationToken cancellationToken)
+ {
+ return provider.SendFile(inputPath, item.LocalPath, target, new Progress<double>(), cancellationToken);
+ }
+
+ private string GetLocalId(string serverId, string itemId)
+ {
+ var bytes = Encoding.UTF8.GetBytes(serverId + itemId);
+ bytes = CreateMD5(bytes);
+ return BitConverter.ToString(bytes, 0, bytes.Length).Replace("-", string.Empty);
+ }
+
+ private byte[] CreateMD5(byte[] value)
+ {
+ using (var provider = MD5.Create())
+ {
+ return provider.ComputeHash(value);
+ }
+ }
+
+ public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncTarget target, BaseItemDto libraryItem, string serverId, string originalFileName)
+ {
+ var path = GetDirectoryPath(provider, libraryItem, serverId);
+ path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
+
+ var localPath = provider.GetFullPath(path, target);
+
+ foreach (var mediaSource in libraryItem.MediaSources)
+ {
+ mediaSource.Path = localPath;
+ mediaSource.Protocol = MediaProtocol.File;
+ }
+
+ return new LocalItem
+ {
+ Item = libraryItem,
+ ItemId = libraryItem.Id,
+ ServerId = serverId,
+ LocalPath = localPath,
+ Id = GetLocalId(serverId, libraryItem.Id)
+ };
+ }
+
+ private List<string> GetDirectoryPath(IServerSyncProvider provider, BaseItemDto item, string serverId)
+ {
+ var parts = new List<string>
+ {
+ serverId
+ };
+
+ if (item.IsType("episode"))
+ {
+ parts.Add("TV");
+ parts.Add(item.SeriesName);
+
+ if (!string.IsNullOrWhiteSpace(item.SeasonName))
+ {
+ parts.Add(item.SeasonName);
+ }
+ }
+ else if (item.IsVideo)
+ {
+ parts.Add("Videos");
+ parts.Add(item.Name);
+ }
+ else if (item.IsAudio)
+ {
+ parts.Add("Music");
+
+ if (!string.IsNullOrWhiteSpace(item.AlbumArtist))
+ {
+ parts.Add(item.AlbumArtist);
+ }
+
+ if (!string.IsNullOrWhiteSpace(item.Album))
+ {
+ parts.Add(item.Album);
+ }
+ }
+ else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
+ {
+ parts.Add("Photos");
+
+ if (!string.IsNullOrWhiteSpace(item.Album))
+ {
+ parts.Add(item.Album);
+ }
+ }
+
+ return parts.Select(i => GetValidFilename(provider, i)).ToList();
+ }
+
+ private string GetLocalFileName(IServerSyncProvider provider, BaseItemDto item, string originalFileName)
+ {
+ var filename = originalFileName;
+
+ if (string.IsNullOrEmpty(filename))
+ {
+ filename = item.Name;
+ }
+
+ return GetValidFilename(provider, filename);
+ }
+
+ private string GetValidFilename(IServerSyncProvider provider, string filename)
+ {
+ // We can always add this method to the sync provider if it's really needed
+ return _fileSystem.GetValidFilename(filename);
}
- private string[] GetPathParts(string serverId, BaseItemDto item)
+ private async Task<List<ItemFileInfo>> GetFiles(IServerSyncProvider provider, LocalItem item, SyncTarget target)
{
- return null;
+ var path = item.LocalPath;
+ path = provider.GetParentDirectoryPath(path, target);
+
+ var list = await provider.GetFileSystemEntries(path, target).ConfigureAwait(false);
+
+ var itemFiles = new List<ItemFileInfo>();
+
+ var name = Path.GetFileNameWithoutExtension(item.LocalPath);
+
+ foreach (var file in list.Where(f => f.Name.Contains(name)))
+ {
+ var itemFile = new ItemFileInfo
+ {
+ Path = file.Path,
+ Name = file.Name
+ };
+
+ if (IsSubtitleFile(file.Name))
+ {
+ itemFile.Type = ItemFileType.Subtitles;
+ }
+ else if (!IsImageFile(file.Name))
+ {
+ itemFile.Type = ItemFileType.Media;
+ }
+
+ itemFiles.Add(itemFile);
+ }
+
+ return itemFiles;
+ }
+
+ private static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".webp" };
+ private bool IsImageFile(string path)
+ {
+ var ext = Path.GetExtension(path) ?? string.Empty;
+
+ return SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static readonly string[] SupportedSubtitleExtensions = { ".srt", ".vtt" };
+ private bool IsSubtitleFile(string path)
+ {
+ var ext = Path.GetExtension(path) ?? string.Empty;
+
+ return SupportedSubtitleExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs
new file mode 100644
index 000000000..cbfa82f1d
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs
@@ -0,0 +1,69 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Sync;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+ public class MultiProviderSync
+ {
+ private readonly ISyncManager _syncManager;
+ private readonly IServerApplicationHost _appHost;
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+
+ public MultiProviderSync(ISyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem)
+ {
+ _syncManager = syncManager;
+ _appHost = appHost;
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
+ public async Task Sync(IEnumerable<IServerSyncProvider> providers, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var targets = providers
+ .SelectMany(i => i.GetAllSyncTargets().Select(t => new Tuple<IServerSyncProvider, SyncTarget>(i, t)))
+ .ToList();
+
+ var numComplete = 0;
+ double startingPercent = 0;
+ double percentPerItem = 1;
+ if (targets.Count > 0)
+ {
+ percentPerItem /= targets.Count;
+ }
+
+ foreach (var target in targets)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var currentPercent = startingPercent;
+ var innerProgress = new ActionableProgress<double>();
+ innerProgress.RegisterAction(pct =>
+ {
+ var totalProgress = pct * percentPerItem;
+ totalProgress += currentPercent;
+ progress.Report(totalProgress);
+ });
+
+ await new MediaSync(_logger, _syncManager, _appHost, _fileSystem)
+ .Sync(target.Item1, target.Item1.GetDataProvider(), target.Item2, innerProgress, cancellationToken)
+ .ConfigureAwait(false);
+
+ numComplete++;
+ startingPercent = numComplete;
+ startingPercent /= targets.Count;
+ startingPercent *= 100;
+ progress.Report(startingPercent);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
index a2fd92bf5..8fc3651f7 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
@@ -407,6 +407,15 @@ namespace MediaBrowser.Server.Implementations.Sync
.OrderBy(i => i.Name);
}
+ private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider)
+ {
+ return provider.GetAllSyncTargets().Select(i => new SyncTarget
+ {
+ Name = i.Name,
+ Id = GetSyncTargetId(provider, i)
+ });
+ }
+
private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider, string userId)
{
return provider.GetSyncTargets(userId).Select(i => new SyncTarget
@@ -429,13 +438,6 @@ namespace MediaBrowser.Server.Implementations.Sync
return (providerId + "-" + target.Id).GetMD5().ToString("N");
}
- private ISyncProvider GetSyncProvider(SyncTarget target)
- {
- var providerId = target.Id.Split(new[] { '-' }, 2).First();
-
- return _providers.First(i => string.Equals(providerId, GetSyncProviderId(i)));
- }
-
private string GetSyncProviderId(ISyncProvider provider)
{
return (provider.GetType().Name).GetMD5().ToString("N");
@@ -543,11 +545,11 @@ namespace MediaBrowser.Server.Implementations.Sync
{
foreach (var provider in _providers)
{
- foreach (var target in GetSyncTargets(provider, null))
+ foreach (var target in GetSyncTargets(provider))
{
if (string.Equals(target.Id, targetId, StringComparison.OrdinalIgnoreCase))
{
- return provider.GetDeviceProfile(target);
+ return GetDeviceProfile(provider, target);
}
}
}
@@ -555,6 +557,18 @@ namespace MediaBrowser.Server.Implementations.Sync
return null;
}
+ public DeviceProfile GetDeviceProfile(ISyncProvider provider, SyncTarget target)
+ {
+ var hasProfile = provider as IHasSyncProfile;
+
+ if (hasProfile != null)
+ {
+ return hasProfile.GetDeviceProfile(target);
+ }
+
+ return new CloudSyncProfile(true, false);
+ }
+
public async Task ReportSyncJobItemTransferred(string id)
{
var jobItem = _repo.GetJobItem(id);
diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config
index 41df1b471..8c530e015 100644
--- a/MediaBrowser.Server.Implementations/packages.config
+++ b/MediaBrowser.Server.Implementations/packages.config
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="ImageMagickSharp" version="1.0.0.2" targetFramework="net45" />
+ <package id="ImageMagickSharp" version="1.0.0.6" targetFramework="net45" />
<package id="MediaBrowser.Naming" version="1.0.0.32" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.21.0" targetFramework="net45" />
<package id="morelinq" version="1.1.0" targetFramework="net45" />