diff options
Diffstat (limited to 'Emby.Server.Implementations/Devices/DeviceManager.cs')
| -rw-r--r-- | Emby.Server.Implementations/Devices/DeviceManager.cs | 380 |
1 files changed, 292 insertions, 88 deletions
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index ee4c4bb26..0fac886ef 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -18,114 +18,150 @@ using System.Linq; using System.Threading.Tasks; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Controller.Security; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Common.Extensions; namespace Emby.Server.Implementations.Devices { public class DeviceManager : IDeviceManager { - private readonly IDeviceRepository _repo; + private readonly IJsonSerializer _json; private readonly IUserManager _userManager; private readonly IFileSystem _fileSystem; private readonly ILibraryMonitor _libraryMonitor; private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly INetworkManager _network; + private readonly ILibraryManager _libraryManager; + private readonly ILocalizationManager _localizationManager; + private readonly IAuthenticationRepository _authRepo; + + public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated; public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded; - /// <summary> - /// Occurs when [device options updated]. - /// </summary> - public event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated; + private readonly object _cameraUploadSyncLock = new object(); + private readonly object _capabilitiesSyncLock = new object(); - public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network) + public DeviceManager(IAuthenticationRepository authRepo, IJsonSerializer json, ILibraryManager libraryManager, ILocalizationManager localizationManager, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network) { - _repo = repo; + _json = json; _userManager = userManager; _fileSystem = fileSystem; _libraryMonitor = libraryMonitor; _config = config; _logger = logger; _network = network; + _libraryManager = libraryManager; + _localizationManager = localizationManager; + _authRepo = authRepo; } - public DeviceInfo RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId) + + private Dictionary<string, ClientCapabilities> _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase); + public void SaveCapabilities(string deviceId, ClientCapabilities capabilities) { - if (string.IsNullOrWhiteSpace(reportedId)) - { - throw new ArgumentNullException("reportedId"); - } + var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json"); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); - var device = GetDevice(reportedId) ?? new DeviceInfo + lock (_capabilitiesSyncLock) { - Id = reportedId - }; + _capabilitiesCache[deviceId] = capabilities; - device.ReportedName = name; - device.AppName = appName; - device.AppVersion = appVersion; + _json.SerializeToFile(capabilities, path); + } + } - if (!string.IsNullOrWhiteSpace(usedByUserId)) - { - var user = _userManager.GetUserById(usedByUserId); + public void UpdateDeviceOptions(string deviceId, DeviceOptions options) + { + _authRepo.UpdateDeviceOptions(deviceId, options); - device.LastUserId = user.Id.ToString("N"); - device.LastUserName = user.Name; + if (DeviceOptionsUpdated != null) + { + DeviceOptionsUpdated(this, new GenericEventArgs<Tuple<string, DeviceOptions>>() + { + Argument = new Tuple<string, DeviceOptions>(deviceId, options) + }); } + } - device.DateLastModified = DateTime.UtcNow; + public DeviceOptions GetDeviceOptions(string deviceId) + { + return _authRepo.GetDeviceOptions(deviceId); + } - device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName; + public ClientCapabilities GetCapabilities(string id) + { + lock (_capabilitiesSyncLock) + { + ClientCapabilities result; + if (_capabilitiesCache.TryGetValue(id, out result)) + { + return result; + } - _repo.SaveDevice(device); + var path = Path.Combine(GetDevicePath(id), "capabilities.json"); + try + { + return _json.DeserializeFromFile<ClientCapabilities>(path) ?? new ClientCapabilities(); + } + catch + { + } + } - return device; + return new ClientCapabilities(); } - public void SaveCapabilities(string reportedId, ClientCapabilities capabilities) + public DeviceInfo GetDevice(string id) { - _repo.SaveCapabilities(reportedId, capabilities); + return GetDevice(id, true); } - public ClientCapabilities GetCapabilities(string reportedId) + private DeviceInfo GetDevice(string id, bool includeCapabilities) { - return _repo.GetCapabilities(reportedId); - } + var session = _authRepo.Get(new AuthenticationInfoQuery + { + DeviceId = id - public DeviceInfo GetDevice(string id) - { - return _repo.GetDevice(id); + }).Items.FirstOrDefault(); + + var device = session == null ? null : ToDeviceInfo(session); + + return device; } public QueryResult<DeviceInfo> GetDevices(DeviceQuery query) { - IEnumerable<DeviceInfo> devices = _repo.GetDevices(); + var sessions = _authRepo.Get(new AuthenticationInfoQuery + { + //UserId = query.UserId + HasUser = true + + }).Items; if (query.SupportsSync.HasValue) { var val = query.SupportsSync.Value; - devices = devices.Where(i => i.Capabilities.SupportsSync == val); + sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == val).ToArray(); } - if (query.SupportsPersistentIdentifier.HasValue) + if (!query.UserId.Equals(Guid.Empty)) { - var val = query.SupportsPersistentIdentifier.Value; + var user = _userManager.GetUserById(query.UserId); - devices = devices.Where(i => - { - var deviceVal = i.Capabilities.SupportsPersistentIdentifier; - return deviceVal == val; - }); + sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)).ToArray(); } - if (!string.IsNullOrWhiteSpace(query.UserId)) - { - devices = devices.Where(i => CanAccessDevice(query.UserId, i.Id)); - } + var array = sessions.Select(ToDeviceInfo).ToArray(); - var array = devices.ToArray(); return new QueryResult<DeviceInfo> { Items = array, @@ -133,20 +169,59 @@ namespace Emby.Server.Implementations.Devices }; } - public void DeleteDevice(string id) + private DeviceInfo ToDeviceInfo(AuthenticationInfo authInfo) + { + var caps = GetCapabilities(authInfo.DeviceId); + + return new DeviceInfo + { + AppName = authInfo.AppName, + AppVersion = authInfo.AppVersion, + Id = authInfo.DeviceId, + LastUserId = authInfo.UserId, + LastUserName = authInfo.UserName, + Name = authInfo.DeviceName, + DateLastActivity = authInfo.DateLastActivity, + IconUrl = caps == null ? null : caps.IconUrl + }; + } + + private string GetDevicesPath() + { + return Path.Combine(_config.ApplicationPaths.DataPath, "devices"); + } + + private string GetDevicePath(string id) { - _repo.DeleteDevice(id); + return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N")); } public ContentUploadHistory GetCameraUploadHistory(string deviceId) { - return _repo.GetCameraUploadHistory(deviceId); + var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); + + lock (_cameraUploadSyncLock) + { + try + { + return _json.DeserializeFromFile<ContentUploadHistory>(path); + } + catch (IOException) + { + return new ContentUploadHistory + { + DeviceId = deviceId + }; + } + } } public async Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file) { - var device = GetDevice(deviceId); - var path = GetUploadPath(device); + var device = GetDevice(deviceId, false); + var uploadPathInfo = GetUploadPath(device); + + var path = uploadPathInfo.Item1; if (!string.IsNullOrWhiteSpace(file.Album)) { @@ -156,10 +231,12 @@ namespace Emby.Server.Implementations.Devices path = Path.Combine(path, file.Name); path = Path.ChangeExtension(path, MimeTypes.ToExtension(file.MimeType) ?? "jpg"); - _libraryMonitor.ReportFileSystemChangeBeginning(path); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); + await EnsureLibraryFolder(uploadPathInfo.Item2, uploadPathInfo.Item3).ConfigureAwait(false); + + _libraryMonitor.ReportFileSystemChangeBeginning(path); + try { using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) @@ -167,7 +244,7 @@ namespace Emby.Server.Implementations.Devices await stream.CopyToAsync(fs).ConfigureAwait(false); } - _repo.AddCameraUpload(deviceId, file); + AddCameraUpload(deviceId, file); } finally { @@ -187,65 +264,118 @@ namespace Emby.Server.Implementations.Devices } } - private string GetUploadPath(DeviceInfo device) + private void AddCameraUpload(string deviceId, LocalFileInfo file) { - if (!string.IsNullOrWhiteSpace(device.CameraUploadPath)) + var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); + _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); + + lock (_cameraUploadSyncLock) { - return device.CameraUploadPath; + ContentUploadHistory history; + + try + { + history = _json.DeserializeFromFile<ContentUploadHistory>(path); + } + catch (IOException) + { + history = new ContentUploadHistory + { + DeviceId = deviceId + }; + } + + history.DeviceId = deviceId; + + var list = history.FilesUploaded.ToList(); + list.Add(file); + history.FilesUploaded = list.ToArray(list.Count); + + _json.SerializeToFile(history, path); } + } + internal Task EnsureLibraryFolder(string path, string name) + { + var existingFolders = _libraryManager + .RootFolder + .Children + .OfType<Folder>() + .Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path)) + .ToList(); + + if (existingFolders.Count > 0) + { + return Task.CompletedTask; + } + + _fileSystem.CreateDirectory(path); + + var libraryOptions = new LibraryOptions + { + PathInfos = new[] { new MediaPathInfo { Path = path } }, + EnablePhotos = true, + EnableRealtimeMonitor = false, + SaveLocalMetadata = true + }; + + if (string.IsNullOrWhiteSpace(name)) + { + name = _localizationManager.GetLocalizedString("HeaderCameraUploads"); + } + + return _libraryManager.AddVirtualFolder(name, CollectionType.HomeVideos, libraryOptions, true); + } + + private Tuple<string, string, string> GetUploadPath(DeviceInfo device) + { var config = _config.GetUploadOptions(); var path = config.CameraUploadPath; + if (string.IsNullOrWhiteSpace(path)) { path = DefaultCameraUploadsPath; } + var topLibraryPath = path; + if (config.EnableCameraUploadSubfolders) { path = Path.Combine(path, _fileSystem.GetValidFilename(device.Name)); } - return path; - } - - private string DefaultCameraUploadsPath - { - get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); } + return new Tuple<string, string, string>(path, topLibraryPath, null); } - public void UpdateDeviceInfo(string id, DeviceOptions options) + internal string GetUploadsPath() { - var device = GetDevice(id); - - device.CustomName = options.CustomName; - device.CameraUploadPath = options.CameraUploadPath; + var config = _config.GetUploadOptions(); + var path = config.CameraUploadPath; - device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName; + if (string.IsNullOrWhiteSpace(path)) + { + path = DefaultCameraUploadsPath; + } - _repo.SaveDevice(device); + return path; + } - EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs<DeviceInfo>(device), _logger); + private string DefaultCameraUploadsPath + { + get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); } } - public bool CanAccessDevice(string userId, string deviceId) + public bool CanAccessDevice(User user, string deviceId) { - if (string.IsNullOrWhiteSpace(userId)) + if (user == null) { - throw new ArgumentNullException("userId"); + throw new ArgumentException("user not found"); } - if (string.IsNullOrWhiteSpace(deviceId)) + if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException("deviceId"); } - var user = _userManager.GetUserById(userId); - - if (user == null) - { - throw new ArgumentException("user not found"); - } - if (!CanAccessDevice(user.Policy, deviceId)) { var capabilities = GetCapabilities(deviceId); @@ -271,15 +401,89 @@ namespace Emby.Server.Implementations.Devices return true; } - return ListHelper.ContainsIgnoreCase(policy.EnabledDevices, id); + return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase); + } + } + + public class DeviceManagerEntryPoint : IServerEntryPoint + { + private readonly DeviceManager _deviceManager; + private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; + private ILogger _logger; + + public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger) + { + _deviceManager = (DeviceManager)deviceManager; + _config = config; + _fileSystem = fileSystem; + _logger = logger; + } + + public async void Run() + { + if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted) + { + var path = _deviceManager.GetUploadsPath(); + + if (_fileSystem.DirectoryExists(path)) + { + try + { + await _deviceManager.EnsureLibraryFolder(path, null).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error creating camera uploads library", ex); + } + + _config.Configuration.CameraUploadUpgraded = true; + _config.SaveConfiguration(); + } + } + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects). + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~DeviceManagerEntryPoint() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); } + #endregion } public class DevicesConfigStore : IConfigurationFactory { public IEnumerable<ConfigurationStore> GetConfigurations() { - return new List<ConfigurationStore> + return new ConfigurationStore[] { new ConfigurationStore { |
