mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-04 14:58:36 +01:00
Update to 3.5.2 and .net core 2.1
This commit is contained in:
@@ -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))
|
||||
var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
||||
|
||||
lock (_capabilitiesSyncLock)
|
||||
{
|
||||
throw new ArgumentNullException("reportedId");
|
||||
_capabilitiesCache[deviceId] = capabilities;
|
||||
|
||||
_json.SerializeToFile(capabilities, path);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateDeviceOptions(string deviceId, DeviceOptions options)
|
||||
{
|
||||
_authRepo.UpdateDeviceOptions(deviceId, options);
|
||||
|
||||
if (DeviceOptionsUpdated != null)
|
||||
{
|
||||
DeviceOptionsUpdated(this, new GenericEventArgs<Tuple<string, DeviceOptions>>()
|
||||
{
|
||||
Argument = new Tuple<string, DeviceOptions>(deviceId, options)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public DeviceOptions GetDeviceOptions(string deviceId)
|
||||
{
|
||||
return _authRepo.GetDeviceOptions(deviceId);
|
||||
}
|
||||
|
||||
public ClientCapabilities GetCapabilities(string id)
|
||||
{
|
||||
lock (_capabilitiesSyncLock)
|
||||
{
|
||||
ClientCapabilities result;
|
||||
if (_capabilitiesCache.TryGetValue(id, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var path = Path.Combine(GetDevicePath(id), "capabilities.json");
|
||||
try
|
||||
{
|
||||
return _json.DeserializeFromFile<ClientCapabilities>(path) ?? new ClientCapabilities();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
var device = GetDevice(reportedId) ?? new DeviceInfo
|
||||
{
|
||||
Id = reportedId
|
||||
};
|
||||
|
||||
device.ReportedName = name;
|
||||
device.AppName = appName;
|
||||
device.AppVersion = appVersion;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(usedByUserId))
|
||||
{
|
||||
var user = _userManager.GetUserById(usedByUserId);
|
||||
|
||||
device.LastUserId = user.Id.ToString("N");
|
||||
device.LastUserName = user.Name;
|
||||
}
|
||||
|
||||
device.DateLastModified = DateTime.UtcNow;
|
||||
|
||||
device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
|
||||
|
||||
_repo.SaveDevice(device);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
public void SaveCapabilities(string reportedId, ClientCapabilities capabilities)
|
||||
{
|
||||
_repo.SaveCapabilities(reportedId, capabilities);
|
||||
}
|
||||
|
||||
public ClientCapabilities GetCapabilities(string reportedId)
|
||||
{
|
||||
return _repo.GetCapabilities(reportedId);
|
||||
return new ClientCapabilities();
|
||||
}
|
||||
|
||||
public DeviceInfo GetDevice(string id)
|
||||
{
|
||||
return _repo.GetDevice(id);
|
||||
return GetDevice(id, true);
|
||||
}
|
||||
|
||||
private DeviceInfo GetDevice(string id, bool includeCapabilities)
|
||||
{
|
||||
var session = _authRepo.Get(new AuthenticationInfoQuery
|
||||
{
|
||||
DeviceId = 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)
|
||||
{
|
||||
_repo.DeleteDevice(id);
|
||||
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)
|
||||
{
|
||||
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,25 +264,99 @@ 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 new Tuple<string, string, string>(path, topLibraryPath, null);
|
||||
}
|
||||
|
||||
internal string GetUploadsPath()
|
||||
{
|
||||
var config = _config.GetUploadOptions();
|
||||
var path = config.CameraUploadPath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
path = DefaultCameraUploadsPath;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -214,37 +365,16 @@ namespace Emby.Server.Implementations.Devices
|
||||
get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); }
|
||||
}
|
||||
|
||||
public void UpdateDeviceInfo(string id, DeviceOptions options)
|
||||
public bool CanAccessDevice(User user, string deviceId)
|
||||
{
|
||||
var device = GetDevice(id);
|
||||
|
||||
device.CustomName = options.CustomName;
|
||||
device.CameraUploadPath = options.CameraUploadPath;
|
||||
|
||||
device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
|
||||
|
||||
_repo.SaveDevice(device);
|
||||
|
||||
EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs<DeviceInfo>(device), _logger);
|
||||
}
|
||||
|
||||
public bool CanAccessDevice(string userId, string deviceId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
throw new ArgumentNullException("userId");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(deviceId))
|
||||
{
|
||||
throw new ArgumentNullException("deviceId");
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentException("user not found");
|
||||
}
|
||||
if (string.IsNullOrEmpty(deviceId))
|
||||
{
|
||||
throw new ArgumentNullException("deviceId");
|
||||
}
|
||||
|
||||
if (!CanAccessDevice(user.Policy, 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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user