From c583d49541f297a103956d6d3bdc20cf1d681b8e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 13 Oct 2017 14:32:58 -0400 Subject: [PATCH 1/7] live tv subtitle fixes --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 29 +++++++++++------------- MediaBrowser.Model/Dlna/StreamInfo.cs | 4 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 95a80c34c0..a11c886974 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -686,7 +686,7 @@ namespace MediaBrowser.Model.Dlna if (subtitleStream != null) { - SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, null, null); + SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, null, null); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; @@ -728,7 +728,7 @@ namespace MediaBrowser.Model.Dlna if (subtitleStream != null) { - SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Protocol, transcodingProfile.Container); + SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Protocol, transcodingProfile.Container); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; @@ -1054,15 +1054,6 @@ namespace MediaBrowser.Model.Dlna string videoCodec = videoStream == null ? null : videoStream.Codec; - if (string.IsNullOrEmpty(videoCodec)) - { - _logger.Info("Profile: {0}, DirectPlay=false. Reason=Unknown video codec. Path: {1}", - profile.Name ?? "Unknown Profile", - mediaSource.Path ?? "Unknown path"); - - return new Tuple>(null, new List { TranscodeReason.UnknownVideoStreamInfo }); - } - conditions = new List(); foreach (CodecProfile i in profile.CodecProfiles) { @@ -1189,7 +1180,7 @@ namespace MediaBrowser.Model.Dlna { if (subtitleStream != null) { - SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, playMethod, _transcoderSupport, null, null); + SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, playMethod, _transcoderSupport, null, null); if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) { @@ -1208,7 +1199,7 @@ namespace MediaBrowser.Model.Dlna return new Tuple(result, TranscodeReason.ContainerBitrateExceedsLimit); } - public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, string transcodingSubProtocol, string transcodingContainer) + public static SubtitleProfile GetSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, string transcodingSubProtocol, string transcodingContainer) { if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || !string.Equals(transcodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))) { @@ -1262,8 +1253,8 @@ namespace MediaBrowser.Model.Dlna } // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion - return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ?? - GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ?? + return GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ?? + GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ?? new SubtitleProfile { Method = SubtitleDeliveryMethod.Encode, @@ -1299,7 +1290,7 @@ namespace MediaBrowser.Model.Dlna return false; } - private static SubtitleProfile GetExternalSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion) + private static SubtitleProfile GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion) { foreach (SubtitleProfile profile in subtitleProfiles) { @@ -1338,6 +1329,12 @@ namespace MediaBrowser.Model.Dlna continue; } + // TODO: Build this into subtitleStream.SupportsExternalStream + if (mediaSource.IsInfiniteStream) + { + continue; + } + if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsExternalStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format)) { return profile; diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 5a059e91d2..93a46aaf40 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -466,7 +466,7 @@ namespace MediaBrowser.Model.Dlna private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) { - SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, PlayMethod, transcoderSupport, SubProtocol, Container); + SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, SubProtocol, Container); SubtitleStreamInfo info = new SubtitleStreamInfo { IsForced = stream.IsForced, @@ -480,7 +480,7 @@ namespace MediaBrowser.Model.Dlna if (info.DeliveryMethod == SubtitleDeliveryMethod.External) { - if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format)) + if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal) { info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl, From 9fdaa039c4b1e95842a219499f80ac963bbdb91e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 13 Oct 2017 15:18:05 -0400 Subject: [PATCH 2/7] rework device repository --- .../ApplicationHost.cs | 4 +- .../Devices/DeviceManager.cs | 27 +- .../Devices/DeviceRepository.cs | 212 --------- .../Devices/SqliteDeviceRepository.cs | 441 ++++++++++++++++++ .../Emby.Server.Implementations.csproj | 2 +- .../Session/SessionManager.cs | 12 +- MediaBrowser.Api/Devices/DeviceService.cs | 8 +- .../Devices/IDeviceManager.cs | 13 +- .../Devices/IDeviceRepository.cs | 13 +- MediaBrowser.Model/Devices/DeviceInfo.cs | 12 +- 10 files changed, 477 insertions(+), 267 deletions(-) delete mode 100644 Emby.Server.Implementations/Devices/DeviceRepository.cs create mode 100644 Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 57c509923a..96c87b6a50 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -938,7 +938,9 @@ namespace Emby.Server.Implementations ConnectManager = CreateConnectManager(); RegisterSingleInstance(ConnectManager); - DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); + var deviceRepo = new SqliteDeviceRepository(LogManager.GetLogger("DeviceManager"), ServerConfigurationManager, FileSystemManager, JsonSerializer); + deviceRepo.Initialize(); + DeviceManager = new DeviceManager(deviceRepo, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); RegisterSingleInstance(DeviceManager); var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 027a55516c..ee4c4bb265 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Devices _network = network; } - public async Task RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId) + public DeviceInfo RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId) { if (string.IsNullOrWhiteSpace(reportedId)) { @@ -76,14 +76,16 @@ namespace Emby.Server.Implementations.Devices device.DateLastModified = DateTime.UtcNow; - await _repo.SaveDevice(device).ConfigureAwait(false); + device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName; + + _repo.SaveDevice(device); return device; } - public Task SaveCapabilities(string reportedId, ClientCapabilities capabilities) + public void SaveCapabilities(string reportedId, ClientCapabilities capabilities) { - return _repo.SaveCapabilities(reportedId, capabilities); + _repo.SaveCapabilities(reportedId, capabilities); } public ClientCapabilities GetCapabilities(string reportedId) @@ -98,13 +100,13 @@ namespace Emby.Server.Implementations.Devices public QueryResult GetDevices(DeviceQuery query) { - IEnumerable devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified); + IEnumerable devices = _repo.GetDevices(); if (query.SupportsSync.HasValue) { var val = query.SupportsSync.Value; - devices = devices.Where(i => GetCapabilities(i.Id).SupportsSync == val); + devices = devices.Where(i => i.Capabilities.SupportsSync == val); } if (query.SupportsPersistentIdentifier.HasValue) @@ -113,8 +115,7 @@ namespace Emby.Server.Implementations.Devices devices = devices.Where(i => { - var caps = GetCapabilities(i.Id); - var deviceVal = caps.SupportsPersistentIdentifier; + var deviceVal = i.Capabilities.SupportsPersistentIdentifier; return deviceVal == val; }); } @@ -132,9 +133,9 @@ namespace Emby.Server.Implementations.Devices }; } - public Task DeleteDevice(string id) + public void DeleteDevice(string id) { - return _repo.DeleteDevice(id); + _repo.DeleteDevice(id); } public ContentUploadHistory GetCameraUploadHistory(string deviceId) @@ -213,14 +214,16 @@ namespace Emby.Server.Implementations.Devices get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); } } - public async Task UpdateDeviceInfo(string id, DeviceOptions options) + public void UpdateDeviceInfo(string id, DeviceOptions options) { var device = GetDevice(id); device.CustomName = options.CustomName; device.CameraUploadPath = options.CameraUploadPath; - await _repo.SaveDevice(device).ConfigureAwait(false); + device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName; + + _repo.SaveDevice(device); EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs(device), _logger); } diff --git a/Emby.Server.Implementations/Devices/DeviceRepository.cs b/Emby.Server.Implementations/Devices/DeviceRepository.cs deleted file mode 100644 index b286a3bb07..0000000000 --- a/Emby.Server.Implementations/Devices/DeviceRepository.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Model.Devices; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Session; -using MediaBrowser.Model.Extensions; - -namespace Emby.Server.Implementations.Devices -{ - public class DeviceRepository : IDeviceRepository - { - private readonly object _syncLock = new object(); - - private readonly IApplicationPaths _appPaths; - private readonly IJsonSerializer _json; - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - - private Dictionary _devices; - - public DeviceRepository(IApplicationPaths appPaths, IJsonSerializer json, ILogger logger, IFileSystem fileSystem) - { - _appPaths = appPaths; - _json = json; - _logger = logger; - _fileSystem = fileSystem; - } - - private string GetDevicesPath() - { - return Path.Combine(_appPaths.DataPath, "devices"); - } - - private string GetDevicePath(string id) - { - return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N")); - } - - public Task SaveDevice(DeviceInfo device) - { - var path = Path.Combine(GetDevicePath(device.Id), "device.json"); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); - - lock (_syncLock) - { - _json.SerializeToFile(device, path); - _devices[device.Id] = device; - } - return Task.FromResult(true); - } - - public Task SaveCapabilities(string reportedId, ClientCapabilities capabilities) - { - var device = GetDevice(reportedId); - - if (device == null) - { - throw new ArgumentException("No device has been registed with id " + reportedId); - } - - device.Capabilities = capabilities; - SaveDevice(device); - - return Task.FromResult(true); - } - - public ClientCapabilities GetCapabilities(string reportedId) - { - var device = GetDevice(reportedId); - - return device == null ? null : device.Capabilities; - } - - public DeviceInfo GetDevice(string id) - { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentNullException("id"); - } - - return GetDevices() - .FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); - } - - public IEnumerable GetDevices() - { - lock (_syncLock) - { - if (_devices == null) - { - _devices = new Dictionary(StringComparer.OrdinalIgnoreCase); - - var devices = LoadDevices().ToList(); - foreach (var device in devices) - { - _devices[device.Id] = device; - } - } - return _devices.Values.ToList(); - } - } - - private IEnumerable LoadDevices() - { - var path = GetDevicesPath(); - - try - { - return _fileSystem - .GetFilePaths(path, true) - .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase)) - .ToList() - .Select(i => - { - try - { - return _json.DeserializeFromFile(i); - } - catch (Exception ex) - { - _logger.ErrorException("Error reading {0}", ex, i); - return null; - } - }) - .Where(i => i != null); - } - catch (IOException) - { - return new List(); - } - } - - public Task DeleteDevice(string id) - { - var path = GetDevicePath(id); - - lock (_syncLock) - { - try - { - _fileSystem.DeleteDirectory(path, true); - } - catch (IOException) - { - } - - _devices = null; - } - - return Task.FromResult(true); - } - - public ContentUploadHistory GetCameraUploadHistory(string deviceId) - { - var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); - - lock (_syncLock) - { - try - { - return _json.DeserializeFromFile(path); - } - catch (IOException) - { - return new ContentUploadHistory - { - DeviceId = deviceId - }; - } - } - } - - public void AddCameraUpload(string deviceId, LocalFileInfo file) - { - var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); - - lock (_syncLock) - { - ContentUploadHistory history; - - try - { - history = _json.DeserializeFromFile(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); - } - } - } -} diff --git a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs new file mode 100644 index 0000000000..e8b7466a58 --- /dev/null +++ b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs @@ -0,0 +1,441 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using Emby.Server.Implementations.Data; +using MediaBrowser.Controller; +using MediaBrowser.Model.Logging; +using SQLitePCL.pretty; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.IO; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Model.Devices; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Session; +using MediaBrowser.Controller.Configuration; + +namespace Emby.Server.Implementations.Devices +{ + public class SqliteDeviceRepository : BaseSqliteRepository, IDeviceRepository + { + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + protected IFileSystem FileSystem { get; private set; } + private readonly object _syncLock = new object(); + private readonly IJsonSerializer _json; + private IServerApplicationPaths _appPaths; + + public SqliteDeviceRepository(ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IJsonSerializer json) + : base(logger) + { + var appPaths = config.ApplicationPaths; + + DbFilePath = Path.Combine(appPaths.DataPath, "devices.db"); + FileSystem = fileSystem; + _json = json; + _appPaths = appPaths; + } + + public void Initialize() + { + try + { + InitializeInternal(); + } + catch (Exception ex) + { + Logger.ErrorException("Error loading database file. Will reset and retry.", ex); + + FileSystem.DeleteFile(DbFilePath); + + InitializeInternal(); + } + } + + private void InitializeInternal() + { + using (var connection = CreateConnection()) + { + RunDefaultInitialization(connection); + + string[] queries = { + "create table if not exists Devices (Id TEXT PRIMARY KEY, Name TEXT, ReportedName TEXT, CustomName TEXT, CameraUploadPath TEXT, LastUserName TEXT, AppName TEXT, AppVersion TEXT, LastUserId TEXT, DateLastModified DATETIME, Capabilities TEXT)", + "create index if not exists idx_id on Devices(Id)" + }; + + connection.RunQueries(queries); + + MigrateDevices(); + } + } + + private void MigrateDevices() + { + var files = FileSystem + .GetFilePaths(GetDevicesPath(), true) + .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase)) + .ToList(); + + foreach (var file in files) + { + try + { + var device = _json.DeserializeFromFile(file); + + SaveDevice(device); + } + catch (Exception ex) + { + Logger.ErrorException("Error reading {0}", ex, file); + } + finally + { + try + { + FileSystem.DeleteFile(file); + } + catch (IOException) + { + try + { + FileSystem.MoveFile(file, Path.ChangeExtension(file, ".old")); + } + catch (IOException) + { + } + } + } + } + } + + private const string BaseSelectText = "select Id, Name, ReportedName, CustomName, CameraUploadPath, LastUserName, AppName, AppVersion, LastUserId, DateLastModified, Capabilities from Devices"; + + public void SaveCapabilities(string deviceId, ClientCapabilities capabilities) + { + using (WriteLock.Write()) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + using (var statement = db.PrepareStatement("update devices set Capabilities=@Capabilities where Id=@Id")) + { + statement.TryBind("@Id", deviceId); + + if (capabilities == null) + { + statement.TryBindNull("@Capabilities"); + } + else + { + statement.TryBind("@Capabilities", _json.SerializeToString(capabilities)); + } + + statement.MoveNext(); + } + }, TransactionMode); + } + } + } + + public void SaveDevice(DeviceInfo entry) + { + if (entry == null) + { + throw new ArgumentNullException("entry"); + } + + using (WriteLock.Write()) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + using (var statement = db.PrepareStatement("replace into Devices (Id, Name, ReportedName, CustomName, CameraUploadPath, LastUserName, AppName, AppVersion, LastUserId, DateLastModified, Capabilities) values (@Id, @Name, @ReportedName, @CustomName, @CameraUploadPath, @LastUserName, @AppName, @AppVersion, @LastUserId, @DateLastModified, @Capabilities)")) + { + statement.TryBind("@Id", entry.Id); + statement.TryBind("@Name", entry.Name); + statement.TryBind("@ReportedName", entry.ReportedName); + statement.TryBind("@CustomName", entry.CustomName); + statement.TryBind("@CameraUploadPath", entry.CameraUploadPath); + statement.TryBind("@LastUserName", entry.LastUserName); + statement.TryBind("@AppName", entry.AppName); + statement.TryBind("@AppVersion", entry.AppVersion); + statement.TryBind("@DateLastModified", entry.DateLastModified); + + if (entry.Capabilities == null) + { + statement.TryBindNull("@Capabilities"); + } + else + { + statement.TryBind("@Capabilities", _json.SerializeToString(entry.Capabilities)); + } + + statement.MoveNext(); + } + }, TransactionMode); + } + } + } + + public DeviceInfo GetDevice(string id) + { + using (WriteLock.Read()) + { + using (var connection = CreateConnection(true)) + { + var statementTexts = new List(); + statementTexts.Add(BaseSelectText + " where Id=@Id"); + + return connection.RunInTransaction(db => + { + var statements = PrepareAllSafe(db, statementTexts).ToList(); + + using (var statement = statements[0]) + { + statement.TryBind("@Id", id); + + foreach (var row in statement.ExecuteQuery()) + { + return GetEntry(row); + } + } + + return null; + + }, ReadTransactionMode); + } + } + } + + public List GetDevices() + { + using (WriteLock.Read()) + { + using (var connection = CreateConnection(true)) + { + var statementTexts = new List(); + statementTexts.Add(BaseSelectText + " order by DateLastModified desc"); + + return connection.RunInTransaction(db => + { + var list = new List(); + + var statements = PrepareAllSafe(db, statementTexts).ToList(); + + using (var statement = statements[0]) + { + foreach (var row in statement.ExecuteQuery()) + { + list.Add(GetEntry(row)); + } + } + + return list; + + }, ReadTransactionMode); + } + } + } + + public ClientCapabilities GetCapabilities(string id) + { + using (WriteLock.Read()) + { + using (var connection = CreateConnection(true)) + { + var statementTexts = new List(); + statementTexts.Add("Select Capabilities from Devices where Id=@Id"); + + return connection.RunInTransaction(db => + { + var statements = PrepareAllSafe(db, statementTexts).ToList(); + + using (var statement = statements[0]) + { + statement.TryBind("@Id", id); + + foreach (var row in statement.ExecuteQuery()) + { + if (row[0].SQLiteType != SQLiteType.Null) + { + return _json.DeserializeFromString(row.GetString(0)); + } + } + } + + return null; + + }, ReadTransactionMode); + } + } + } + + private DeviceInfo GetEntry(IReadOnlyList reader) + { + var index = 0; + + var info = new DeviceInfo + { + Id = reader.GetString(index) + }; + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.Name = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.ReportedName = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.CustomName = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.CameraUploadPath = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.LastUserName = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.AppName = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.AppVersion = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.LastUserId = reader.GetString(index); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.DateLastModified = reader[index].ReadDateTime(); + } + + index++; + if (reader[index].SQLiteType != SQLiteType.Null) + { + info.Capabilities = _json.DeserializeFromString(reader.GetString(index)); + } + + return info; + } + + private string GetDevicesPath() + { + return Path.Combine(_appPaths.DataPath, "devices"); + } + + private string GetDevicePath(string id) + { + return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N")); + } + + public ContentUploadHistory GetCameraUploadHistory(string deviceId) + { + var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); + + lock (_syncLock) + { + try + { + return _json.DeserializeFromFile(path); + } + catch (IOException) + { + return new ContentUploadHistory + { + DeviceId = deviceId + }; + } + } + } + + public void AddCameraUpload(string deviceId, LocalFileInfo file) + { + var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path)); + + lock (_syncLock) + { + ContentUploadHistory history; + + try + { + history = _json.DeserializeFromFile(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); + } + } + + public void DeleteDevice(string id) + { + using (WriteLock.Write()) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + using (var statement = db.PrepareStatement("delete from devices where Id=@Id")) + { + statement.TryBind("@Id", id); + + statement.MoveNext(); + } + }, TransactionMode); + } + } + + var path = GetDevicePath(id); + + lock (_syncLock) + { + try + { + FileSystem.DeleteDirectory(path, true); + } + catch (IOException) + { + } + } + } + } +} diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 4f5471bc31..083a147889 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -67,7 +67,7 @@ - + diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 97506cdefd..2c15351656 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -437,7 +437,7 @@ namespace Emby.Server.Implementations.Session if (!string.IsNullOrEmpty(deviceId)) { var userIdString = userId.HasValue ? userId.Value.ToString("N") : null; - device = await _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString).ConfigureAwait(false); + device = _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString); } } @@ -446,7 +446,7 @@ namespace Emby.Server.Implementations.Session if (device == null) { var userIdString = userId.HasValue ? userId.Value.ToString("N") : null; - device = await _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString).ConfigureAwait(false); + device = _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString); } if (device != null) @@ -1567,7 +1567,7 @@ namespace Emby.Server.Implementations.Session ReportCapabilities(session, capabilities, true); } - private async void ReportCapabilities(SessionInfo session, + private void ReportCapabilities(SessionInfo session, ClientCapabilities capabilities, bool saveCapabilities) { @@ -1593,7 +1593,7 @@ namespace Emby.Server.Implementations.Session { try { - await SaveCapabilities(session.DeviceId, capabilities).ConfigureAwait(false); + SaveCapabilities(session.DeviceId, capabilities); } catch (Exception ex) { @@ -1607,9 +1607,9 @@ namespace Emby.Server.Implementations.Session return _deviceManager.GetCapabilities(deviceId); } - private Task SaveCapabilities(string deviceId, ClientCapabilities capabilities) + private void SaveCapabilities(string deviceId, ClientCapabilities capabilities) { - return _deviceManager.SaveCapabilities(deviceId, capabilities); + _deviceManager.SaveCapabilities(deviceId, capabilities); } public SessionInfoDto GetSessionInfoDto(SessionInfo session) diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index 012f0ddb27..c375e272a7 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -85,13 +85,11 @@ namespace MediaBrowser.Api.Devices public void Post(PostDeviceOptions request) { - var task = _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions + _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions { CustomName = request.CustomName, CameraUploadPath = request.CameraUploadPath }); - - Task.WaitAll(task); } public object Get(GetDeviceInfo request) @@ -116,9 +114,7 @@ namespace MediaBrowser.Api.Devices public void Delete(DeleteDevice request) { - var task = _deviceManager.DeleteDevice(request.Id); - - Task.WaitAll(task); + _deviceManager.DeleteDevice(request.Id); } public void Post(PostCameraUpload request) diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 2846bcfc64..676db09aa7 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Devices /// The application version. /// The used by user identifier. /// Task. - Task RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId); + DeviceInfo RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId); /// /// Saves the capabilities. @@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.Devices /// The reported identifier. /// The capabilities. /// Task. - Task SaveCapabilities(string reportedId, ClientCapabilities capabilities); + void SaveCapabilities(string reportedId, ClientCapabilities capabilities); /// /// Gets the capabilities. @@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Devices /// The identifier. /// The options. /// Task. - Task UpdateDeviceInfo(string id, DeviceOptions options); + void UpdateDeviceInfo(string id, DeviceOptions options); /// /// Gets the devices. @@ -67,12 +67,7 @@ namespace MediaBrowser.Controller.Devices /// IEnumerable<DeviceInfo>. QueryResult GetDevices(DeviceQuery query); - /// - /// Deletes the device. - /// - /// The identifier. - /// Task. - Task DeleteDevice(string id); + void DeleteDevice(string id); /// /// Gets the upload history. diff --git a/MediaBrowser.Controller/Devices/IDeviceRepository.cs b/MediaBrowser.Controller/Devices/IDeviceRepository.cs index 736504da30..b9ebbb6c71 100644 --- a/MediaBrowser.Controller/Devices/IDeviceRepository.cs +++ b/MediaBrowser.Controller/Devices/IDeviceRepository.cs @@ -1,7 +1,6 @@ using MediaBrowser.Model.Devices; using MediaBrowser.Model.Session; using System.Collections.Generic; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Devices { @@ -12,7 +11,7 @@ namespace MediaBrowser.Controller.Devices /// /// The device. /// Task. - Task SaveDevice(DeviceInfo device); + void SaveDevice(DeviceInfo device); /// /// Saves the capabilities. @@ -20,7 +19,7 @@ namespace MediaBrowser.Controller.Devices /// The identifier. /// The capabilities. /// Task. - Task SaveCapabilities(string id, ClientCapabilities capabilities); + void SaveCapabilities(string id, ClientCapabilities capabilities); /// /// Gets the capabilities. @@ -36,18 +35,14 @@ namespace MediaBrowser.Controller.Devices /// DeviceInfo. DeviceInfo GetDevice(string id); - /// - /// Gets the devices. - /// - /// IEnumerable<DeviceInfo>. - IEnumerable GetDevices(); + List GetDevices(); /// /// Deletes the device. /// /// The identifier. /// Task. - Task DeleteDevice(string id); + void DeleteDevice(string id); /// /// Gets the upload history. diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs index d8358977e7..d2ee3fdcc6 100644 --- a/MediaBrowser.Model/Devices/DeviceInfo.cs +++ b/MediaBrowser.Model/Devices/DeviceInfo.cs @@ -21,17 +21,7 @@ namespace MediaBrowser.Model.Devices /// The camera upload path. public string CameraUploadPath { get; set; } - /// - /// Gets the name. - /// - /// The name. - public string Name - { - get - { - return string.IsNullOrEmpty(CustomName) ? ReportedName : CustomName; - } - } + public string Name { get; set; } /// /// Gets or sets the identifier. From 0bc3cdfab7d15a42dadb0101d037cbf12631362b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 13 Oct 2017 15:21:43 -0400 Subject: [PATCH 3/7] update suggestions --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index bc4ab8315f..a6c7150daa 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3038,8 +3038,8 @@ namespace Emby.Server.Implementations.Data { if (orderBy.Count == 0) { - orderBy.Add(new Tuple(ItemSortBy.Random, SortOrder.Ascending)); orderBy.Add(new Tuple("SimilarityScore", SortOrder.Descending)); + orderBy.Add(new Tuple(ItemSortBy.Random, SortOrder.Ascending)); //orderBy.Add(new Tuple(ItemSortBy.Random, SortOrder.Ascending)); } } diff --git a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs index e8b7466a58..ca0552d98f 100644 --- a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs +++ b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs @@ -83,6 +83,8 @@ namespace Emby.Server.Implementations.Devices { var device = _json.DeserializeFromFile(file); + device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName; + SaveDevice(device); } catch (Exception ex) From 0351c968c303da53db03aa2b99051d69c45f3ab8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 13 Oct 2017 15:22:24 -0400 Subject: [PATCH 4/7] reduce file checks during library scan --- Emby.Photos/PhotoProvider.cs | 16 +---------- .../Library/MediaSourceManager.cs | 16 ++++------- MediaBrowser.Controller/Entities/AudioBook.cs | 6 ----- MediaBrowser.Controller/Entities/Book.cs | 6 ----- MediaBrowser.Controller/Entities/Game.cs | 6 ----- .../LiveTv/LiveTvChannel.cs | 3 ++- .../MediaBrowser.Controller.csproj | 1 - .../Providers/IHasChangeMonitor.cs | 17 ------------ .../MediaInfo/AudioImageProvider.cs | 16 +---------- .../MediaInfo/FFProbeProvider.cs | 27 ------------------- .../MediaInfo/VideoImageProvider.cs | 16 +---------- 11 files changed, 10 insertions(+), 120 deletions(-) delete mode 100644 MediaBrowser.Controller/Providers/IHasChangeMonitor.cs diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs index b2d6ecdcb3..3a29b86a50 100644 --- a/Emby.Photos/PhotoProvider.cs +++ b/Emby.Photos/PhotoProvider.cs @@ -17,7 +17,7 @@ using TagLib.IFD.Tags; namespace Emby.Photos { - public class PhotoProvider : ICustomMetadataProvider, IHasItemChangeMonitor, IForcedProvider + public class PhotoProvider : ICustomMetadataProvider, IForcedProvider { private readonly ILogger _logger; private readonly IFileSystem _fileSystem; @@ -177,19 +177,5 @@ namespace Emby.Photos { get { return "Embedded Information"; } } - - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) - { - if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem) - { - var file = directoryService.GetFile(item.Path); - if (file != null && file.LastWriteTimeUtc != item.DateModified) - { - return true; - } - } - - return false; - } } } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index d60a043533..688da57649 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -96,23 +96,17 @@ namespace Emby.Server.Implementations.Library return GetMediaStreamsForItem(list); } - private List GetMediaStreamsForItem(IEnumerable streams) + private List GetMediaStreamsForItem(List streams) { - var list = streams.ToList(); - - var subtitleStreams = list - .Where(i => i.Type == MediaStreamType.Subtitle) - .ToList(); - - if (subtitleStreams.Count > 0) + foreach (var stream in streams) { - foreach (var subStream in subtitleStreams) + if (stream.Type == MediaStreamType.Subtitle) { - subStream.SupportsExternalStream = StreamSupportsExternalStream(subStream); + stream.SupportsExternalStream = StreamSupportsExternalStream(stream); } } - return list; + return streams; } public async Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs index 78fb102536..374bb21f74 100644 --- a/MediaBrowser.Controller/Entities/AudioBook.cs +++ b/MediaBrowser.Controller/Entities/AudioBook.cs @@ -51,12 +51,6 @@ namespace MediaBrowser.Controller.Entities return null; } - [IgnoreDataMember] - public override bool EnableRefreshOnDateModifiedChange - { - get { return true; } - } - public Guid? FindSeriesId() { return SeriesId; diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index 9b1a52f836..45e3915ce2 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -38,12 +38,6 @@ namespace MediaBrowser.Controller.Entities return SeriesPresentationUniqueKey; } - [IgnoreDataMember] - public override bool EnableRefreshOnDateModifiedChange - { - get { return true; } - } - public Guid? FindSeriesId() { return SeriesId; diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index a99058925f..fd45f7c823 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -28,12 +28,6 @@ namespace MediaBrowser.Controller.Entities locationType != LocationType.Virtual; } - [IgnoreDataMember] - public override bool EnableRefreshOnDateModifiedChange - { - get { return true; } - } - [IgnoreDataMember] public override bool SupportsThemeMedia { diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 4a93d0399a..16010b7f5a 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -136,7 +136,8 @@ namespace MediaBrowser.Controller.LiveTv Name = Name, Path = Path, RunTimeTicks = RunTimeTicks, - Type = MediaSourceType.Placeholder + Type = MediaSourceType.Placeholder, + IsInfiniteStream = RunTimeTicks == null }; list.Add(info); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 960ff0aa7d..dafca05985 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -220,7 +220,6 @@ - diff --git a/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs b/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs deleted file mode 100644 index aa0b0e3c97..0000000000 --- a/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MediaBrowser.Controller.Entities; -using System; - -namespace MediaBrowser.Controller.Providers -{ - public interface IHasChangeMonitor - { - /// - /// Determines whether the specified item has changed. - /// - /// The item. - /// The directory service. - /// The date. - /// true if the specified item has changed; otherwise, false. - bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date); - } -} diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index a4f2053a9d..3499d5d3f8 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.MediaInfo /// /// Uses ffmpeg to create video images /// - public class AudioImageProvider : IDynamicImageProvider, IHasItemChangeMonitor + public class AudioImageProvider : IDynamicImageProvider { private readonly IMediaEncoder _mediaEncoder; private readonly IServerConfigurationManager _config; @@ -134,19 +134,5 @@ namespace MediaBrowser.Providers.MediaInfo return item.LocationType == LocationType.FileSystem && audio != null; } - - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) - { - if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem) - { - var file = directoryService.GetFile(item.Path); - if (file != null && file.LastWriteTimeUtc != item.DateModified) - { - return true; - } - } - - return false; - } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index a9aa71bfa4..bce4219019 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -34,7 +34,6 @@ namespace MediaBrowser.Providers.MediaInfo ICustomMetadataProvider