mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-19 11:59:02 -07:00
commit
c8244f4687
@ -17,7 +17,7 @@ using TagLib.IFD.Tags;
|
|||||||
|
|
||||||
namespace Emby.Photos
|
namespace Emby.Photos
|
||||||
{
|
{
|
||||||
public class PhotoProvider : ICustomMetadataProvider<Photo>, IHasItemChangeMonitor, IForcedProvider
|
public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
@ -177,19 +177,5 @@ namespace Emby.Photos
|
|||||||
{
|
{
|
||||||
get { return "Embedded Information"; }
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -792,6 +792,11 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
protected abstract IConnectManager CreateConnectManager();
|
protected abstract IConnectManager CreateConnectManager();
|
||||||
protected abstract ISyncManager CreateSyncManager();
|
protected abstract ISyncManager CreateSyncManager();
|
||||||
|
|
||||||
|
protected virtual IHttpClient CreateHttpClient()
|
||||||
|
{
|
||||||
|
return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers resources that classes will depend on
|
/// Registers resources that classes will depend on
|
||||||
@ -814,7 +819,7 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
RegisterSingleInstance(FileSystemManager);
|
RegisterSingleInstance(FileSystemManager);
|
||||||
|
|
||||||
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent);
|
HttpClient = CreateHttpClient();
|
||||||
RegisterSingleInstance(HttpClient);
|
RegisterSingleInstance(HttpClient);
|
||||||
|
|
||||||
RegisterSingleInstance(NetworkManager);
|
RegisterSingleInstance(NetworkManager);
|
||||||
@ -938,7 +943,9 @@ namespace Emby.Server.Implementations
|
|||||||
ConnectManager = CreateConnectManager();
|
ConnectManager = CreateConnectManager();
|
||||||
RegisterSingleInstance(ConnectManager);
|
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);
|
RegisterSingleInstance(DeviceManager);
|
||||||
|
|
||||||
var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
|
var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
|
||||||
@ -1116,7 +1123,7 @@ namespace Emby.Server.Implementations
|
|||||||
IsoManager.AddParts(list);
|
IsoManager.AddParts(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetDefaultUserAgent()
|
protected string GetDefaultUserAgent()
|
||||||
{
|
{
|
||||||
var name = FormatAttribute(Name);
|
var name = FormatAttribute(Name);
|
||||||
|
|
||||||
|
@ -3038,8 +3038,8 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
if (orderBy.Count == 0)
|
if (orderBy.Count == 0)
|
||||||
{
|
{
|
||||||
orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
|
|
||||||
orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
|
orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
|
||||||
|
orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
|
||||||
//orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
|
//orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
_network = network;
|
_network = network;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DeviceInfo> 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))
|
if (string.IsNullOrWhiteSpace(reportedId))
|
||||||
{
|
{
|
||||||
@ -76,14 +76,16 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
|
|
||||||
device.DateLastModified = DateTime.UtcNow;
|
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;
|
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)
|
public ClientCapabilities GetCapabilities(string reportedId)
|
||||||
@ -98,13 +100,13 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
|
|
||||||
public QueryResult<DeviceInfo> GetDevices(DeviceQuery query)
|
public QueryResult<DeviceInfo> GetDevices(DeviceQuery query)
|
||||||
{
|
{
|
||||||
IEnumerable<DeviceInfo> devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified);
|
IEnumerable<DeviceInfo> devices = _repo.GetDevices();
|
||||||
|
|
||||||
if (query.SupportsSync.HasValue)
|
if (query.SupportsSync.HasValue)
|
||||||
{
|
{
|
||||||
var val = query.SupportsSync.Value;
|
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)
|
if (query.SupportsPersistentIdentifier.HasValue)
|
||||||
@ -113,8 +115,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
|
|
||||||
devices = devices.Where(i =>
|
devices = devices.Where(i =>
|
||||||
{
|
{
|
||||||
var caps = GetCapabilities(i.Id);
|
var deviceVal = i.Capabilities.SupportsPersistentIdentifier;
|
||||||
var deviceVal = caps.SupportsPersistentIdentifier;
|
|
||||||
return deviceVal == val;
|
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)
|
public ContentUploadHistory GetCameraUploadHistory(string deviceId)
|
||||||
@ -213,14 +214,16 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); }
|
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);
|
var device = GetDevice(id);
|
||||||
|
|
||||||
device.CustomName = options.CustomName;
|
device.CustomName = options.CustomName;
|
||||||
device.CameraUploadPath = options.CameraUploadPath;
|
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<DeviceInfo>(device), _logger);
|
EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs<DeviceInfo>(device), _logger);
|
||||||
}
|
}
|
||||||
|
@ -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<string, DeviceInfo> _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<DeviceInfo> GetDevices()
|
|
||||||
{
|
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
if (_devices == null)
|
|
||||||
{
|
|
||||||
_devices = new Dictionary<string, DeviceInfo>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var devices = LoadDevices().ToList();
|
|
||||||
foreach (var device in devices)
|
|
||||||
{
|
|
||||||
_devices[device.Id] = device;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _devices.Values.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<DeviceInfo> 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<DeviceInfo>(i);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error reading {0}", ex, i);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(i => i != null);
|
|
||||||
}
|
|
||||||
catch (IOException)
|
|
||||||
{
|
|
||||||
return new List<DeviceInfo>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ContentUploadHistory>(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<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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
451
Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
Normal file
451
Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
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()
|
||||||
|
{
|
||||||
|
List<string> files;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
files = FileSystem
|
||||||
|
.GetFilePaths(GetDevicesPath(), true)
|
||||||
|
.Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var device = _json.DeserializeFromFile<DeviceInfo>(file);
|
||||||
|
|
||||||
|
device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
|
||||||
|
|
||||||
|
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<string>();
|
||||||
|
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<DeviceInfo> GetDevices()
|
||||||
|
{
|
||||||
|
using (WriteLock.Read())
|
||||||
|
{
|
||||||
|
using (var connection = CreateConnection(true))
|
||||||
|
{
|
||||||
|
var statementTexts = new List<string>();
|
||||||
|
statementTexts.Add(BaseSelectText + " order by DateLastModified desc");
|
||||||
|
|
||||||
|
return connection.RunInTransaction(db =>
|
||||||
|
{
|
||||||
|
var list = new List<DeviceInfo>();
|
||||||
|
|
||||||
|
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<string>();
|
||||||
|
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<ClientCapabilities>(row.GetString(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}, ReadTransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceInfo GetEntry(IReadOnlyList<IResultSetValue> 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<ClientCapabilities>(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<ContentUploadHistory>(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<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -67,7 +67,7 @@
|
|||||||
<Compile Include="Devices\CameraUploadsFolder.cs" />
|
<Compile Include="Devices\CameraUploadsFolder.cs" />
|
||||||
<Compile Include="Devices\DeviceId.cs" />
|
<Compile Include="Devices\DeviceId.cs" />
|
||||||
<Compile Include="Devices\DeviceManager.cs" />
|
<Compile Include="Devices\DeviceManager.cs" />
|
||||||
<Compile Include="Devices\DeviceRepository.cs" />
|
<Compile Include="Devices\SqliteDeviceRepository.cs" />
|
||||||
<Compile Include="Diagnostics\CommonProcess.cs" />
|
<Compile Include="Diagnostics\CommonProcess.cs" />
|
||||||
<Compile Include="Diagnostics\ProcessFactory.cs" />
|
<Compile Include="Diagnostics\ProcessFactory.cs" />
|
||||||
<Compile Include="Dto\DtoService.cs" />
|
<Compile Include="Dto\DtoService.cs" />
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.HttpClientManager
|
namespace Emby.Server.Implementations.HttpClientManager
|
||||||
{
|
{
|
||||||
@ -12,5 +13,6 @@ namespace Emby.Server.Implementations.HttpClientManager
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The last timeout.</value>
|
/// <value>The last timeout.</value>
|
||||||
public DateTime LastTimeout { get; set; }
|
public DateTime LastTimeout { get; set; }
|
||||||
|
public HttpClient HttpClient { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,23 +96,17 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return GetMediaStreamsForItem(list);
|
return GetMediaStreamsForItem(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams)
|
private List<MediaStream> GetMediaStreamsForItem(List<MediaStream> streams)
|
||||||
{
|
{
|
||||||
var list = streams.ToList();
|
foreach (var stream in streams)
|
||||||
|
|
||||||
var subtitleStreams = list
|
|
||||||
.Where(i => i.Type == MediaStreamType.Subtitle)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (subtitleStreams.Count > 0)
|
|
||||||
{
|
{
|
||||||
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<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken)
|
public async Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken)
|
||||||
|
@ -22,7 +22,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
|
||||||
private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
|
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
|
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
|
||||||
@ -35,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
|
|
||||||
protected override Task OpenInternal(CancellationToken openCancellationToken)
|
protected override Task OpenInternal(CancellationToken openCancellationToken)
|
||||||
{
|
{
|
||||||
_liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var mediaSource = OriginalMediaSource;
|
var mediaSource = OriginalMediaSource;
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
|
|
||||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
StartStreaming(url, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
|
StartStreaming(url, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||||
|
|
||||||
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||||
//OpenedMediaSource.Path = tempFile;
|
//OpenedMediaSource.Path = tempFile;
|
||||||
@ -65,12 +64,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
//await Task.Delay(5000).ConfigureAwait(false);
|
//await Task.Delay(5000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task Close()
|
public override async Task Close()
|
||||||
{
|
{
|
||||||
Logger.Info("Closing HDHR live stream");
|
Logger.Info("Closing HDHR live stream");
|
||||||
_liveStreamCancellationTokenSource.Cancel();
|
LiveStreamCancellationTokenSource.Cancel();
|
||||||
|
|
||||||
return _liveStreamTaskCompletionSource.Task;
|
await _liveStreamTaskCompletionSource.Task.ConfigureAwait(false);
|
||||||
|
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
private Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||||
@ -112,7 +112,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
}
|
}
|
||||||
|
|
||||||
_liveStreamTaskCompletionSource.TrySetResult(true);
|
_liveStreamTaskCompletionSource.TrySetResult(true);
|
||||||
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
private readonly ISocketFactory _socketFactory;
|
private readonly ISocketFactory _socketFactory;
|
||||||
|
|
||||||
private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
|
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
|
||||||
private readonly IHdHomerunChannelCommands _channelCommands;
|
private readonly IHdHomerunChannelCommands _channelCommands;
|
||||||
private readonly int _numTuners;
|
private readonly int _numTuners;
|
||||||
@ -45,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
|
|
||||||
protected override Task OpenInternal(CancellationToken openCancellationToken)
|
protected override Task OpenInternal(CancellationToken openCancellationToken)
|
||||||
{
|
{
|
||||||
_liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var mediaSource = OriginalMediaSource;
|
var mediaSource = OriginalMediaSource;
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
|
|
||||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
StartStreaming(uri.Host, localPort, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
|
StartStreaming(uri.Host, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||||
|
|
||||||
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||||
//OpenedMediaSource.Path = tempFile;
|
//OpenedMediaSource.Path = tempFile;
|
||||||
@ -76,7 +75,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
public override Task Close()
|
public override Task Close()
|
||||||
{
|
{
|
||||||
Logger.Info("Closing HDHR UDP live stream");
|
Logger.Info("Closing HDHR UDP live stream");
|
||||||
_liveStreamCancellationTokenSource.Cancel();
|
LiveStreamCancellationTokenSource.Cancel();
|
||||||
|
|
||||||
return _liveStreamTaskCompletionSource.Task;
|
return _liveStreamTaskCompletionSource.Task;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
|
|
||||||
protected readonly string TempFilePath;
|
protected readonly string TempFilePath;
|
||||||
protected readonly ILogger Logger;
|
protected readonly ILogger Logger;
|
||||||
|
protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
|
public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
|
||||||
{
|
{
|
||||||
@ -80,6 +81,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
FileSystem.DeleteFile(path);
|
FileSystem.DeleteFile(path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -96,6 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
|
|
||||||
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
|
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
|
||||||
|
|
||||||
var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
|
var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
|
||||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
||||||
|
|
||||||
@ -110,16 +121,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
|
private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
byte[] buffer = new byte[bufferSize];
|
byte[] buffer = new byte[bufferSize];
|
||||||
while (true)
|
|
||||||
|
var eofCount = 0;
|
||||||
|
var emptyReadLimit = 1000;
|
||||||
|
|
||||||
|
while (eofCount < emptyReadLimit)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var read = source.Read(buffer, 0, buffer.Length);
|
var bytesRead = source.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
if (read > 0)
|
if (bytesRead == 0)
|
||||||
{
|
{
|
||||||
|
eofCount++;
|
||||||
|
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eofCount = 0;
|
||||||
|
|
||||||
//await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
|
//await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
|
||||||
destination.Write(buffer, 0, read);
|
destination.Write(buffer, 0, bytesRead);
|
||||||
|
|
||||||
if (onStarted != null)
|
if (onStarted != null)
|
||||||
{
|
{
|
||||||
@ -127,10 +149,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
onStarted = null;
|
onStarted = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await Task.Delay(10).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +437,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
if (!string.IsNullOrEmpty(deviceId))
|
if (!string.IsNullOrEmpty(deviceId))
|
||||||
{
|
{
|
||||||
var userIdString = userId.HasValue ? userId.Value.ToString("N") : 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
if (device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
var userIdString = userId.HasValue ? userId.Value.ToString("N") : 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)
|
if (device != null)
|
||||||
@ -1567,7 +1567,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
ReportCapabilities(session, capabilities, true);
|
ReportCapabilities(session, capabilities, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ReportCapabilities(SessionInfo session,
|
private void ReportCapabilities(SessionInfo session,
|
||||||
ClientCapabilities capabilities,
|
ClientCapabilities capabilities,
|
||||||
bool saveCapabilities)
|
bool saveCapabilities)
|
||||||
{
|
{
|
||||||
@ -1593,7 +1593,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SaveCapabilities(session.DeviceId, capabilities).ConfigureAwait(false);
|
SaveCapabilities(session.DeviceId, capabilities);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -1607,9 +1607,9 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return _deviceManager.GetCapabilities(deviceId);
|
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)
|
public SessionInfoDto GetSessionInfoDto(SessionInfo session)
|
||||||
|
@ -85,13 +85,11 @@ namespace MediaBrowser.Api.Devices
|
|||||||
|
|
||||||
public void Post(PostDeviceOptions request)
|
public void Post(PostDeviceOptions request)
|
||||||
{
|
{
|
||||||
var task = _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions
|
_deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions
|
||||||
{
|
{
|
||||||
CustomName = request.CustomName,
|
CustomName = request.CustomName,
|
||||||
CameraUploadPath = request.CameraUploadPath
|
CameraUploadPath = request.CameraUploadPath
|
||||||
});
|
});
|
||||||
|
|
||||||
Task.WaitAll(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetDeviceInfo request)
|
public object Get(GetDeviceInfo request)
|
||||||
@ -116,9 +114,7 @@ namespace MediaBrowser.Api.Devices
|
|||||||
|
|
||||||
public void Delete(DeleteDevice request)
|
public void Delete(DeleteDevice request)
|
||||||
{
|
{
|
||||||
var task = _deviceManager.DeleteDevice(request.Id);
|
_deviceManager.DeleteDevice(request.Id);
|
||||||
|
|
||||||
Task.WaitAll(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Post(PostCameraUpload request)
|
public void Post(PostCameraUpload request)
|
||||||
|
@ -734,7 +734,7 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
|
|
||||||
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
|
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
|
||||||
|
|
||||||
return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment, CancellationToken.None)
|
return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment)
|
||||||
{
|
{
|
||||||
AllowEndOfFile = false
|
AllowEndOfFile = false
|
||||||
};
|
};
|
||||||
@ -753,7 +753,7 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
|
|
||||||
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
|
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
|
||||||
|
|
||||||
return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment, CancellationToken.None)
|
return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment)
|
||||||
{
|
{
|
||||||
AllowEndOfFile = false
|
AllowEndOfFile = false
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,6 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly string _path;
|
private readonly string _path;
|
||||||
private readonly CancellationToken _cancellationToken;
|
|
||||||
private readonly Dictionary<string, string> _outputHeaders;
|
private readonly Dictionary<string, string> _outputHeaders;
|
||||||
|
|
||||||
const int StreamCopyToBufferSize = 81920;
|
const int StreamCopyToBufferSize = 81920;
|
||||||
@ -28,22 +27,20 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
private readonly IDirectStreamProvider _directStreamProvider;
|
private readonly IDirectStreamProvider _directStreamProvider;
|
||||||
private readonly IEnvironmentInfo _environment;
|
private readonly IEnvironmentInfo _environment;
|
||||||
|
|
||||||
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
|
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment)
|
||||||
{
|
{
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_path = path;
|
_path = path;
|
||||||
_outputHeaders = outputHeaders;
|
_outputHeaders = outputHeaders;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_cancellationToken = cancellationToken;
|
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
|
public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment)
|
||||||
{
|
{
|
||||||
_directStreamProvider = directStreamProvider;
|
_directStreamProvider = directStreamProvider;
|
||||||
_outputHeaders = outputHeaders;
|
_outputHeaders = outputHeaders;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_cancellationToken = cancellationToken;
|
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,8 +66,6 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
|
|
||||||
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
|
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token;
|
|
||||||
|
|
||||||
if (_directStreamProvider != null)
|
if (_directStreamProvider != null)
|
||||||
{
|
{
|
||||||
await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
|
await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
|
||||||
@ -89,7 +84,9 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
inputStream.Position = StartPosition;
|
inputStream.Position = StartPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (eofCount < 20 || !AllowEndOfFile)
|
var emptyReadLimit = AllowEndOfFile ? 20 : 100;
|
||||||
|
|
||||||
|
while (eofCount < emptyReadLimit)
|
||||||
{
|
{
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
if (allowAsyncFileRead)
|
if (allowAsyncFileRead)
|
||||||
|
@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// <param name="appVersion">The application version.</param>
|
/// <param name="appVersion">The application version.</param>
|
||||||
/// <param name="usedByUserId">The used by user identifier.</param>
|
/// <param name="usedByUserId">The used by user identifier.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task<DeviceInfo> RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId);
|
DeviceInfo RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the capabilities.
|
/// Saves the capabilities.
|
||||||
@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// <param name="reportedId">The reported identifier.</param>
|
/// <param name="reportedId">The reported identifier.</param>
|
||||||
/// <param name="capabilities">The capabilities.</param>
|
/// <param name="capabilities">The capabilities.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SaveCapabilities(string reportedId, ClientCapabilities capabilities);
|
void SaveCapabilities(string reportedId, ClientCapabilities capabilities);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the capabilities.
|
/// Gets the capabilities.
|
||||||
@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// <param name="id">The identifier.</param>
|
/// <param name="id">The identifier.</param>
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task UpdateDeviceInfo(string id, DeviceOptions options);
|
void UpdateDeviceInfo(string id, DeviceOptions options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the devices.
|
/// Gets the devices.
|
||||||
@ -67,12 +67,7 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
||||||
QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
|
QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
|
||||||
|
|
||||||
/// <summary>
|
void DeleteDevice(string id);
|
||||||
/// Deletes the device.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The identifier.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
Task DeleteDevice(string id);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the upload history.
|
/// Gets the upload history.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using MediaBrowser.Model.Devices;
|
using MediaBrowser.Model.Devices;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Devices
|
namespace MediaBrowser.Controller.Devices
|
||||||
{
|
{
|
||||||
@ -12,7 +11,7 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="device">The device.</param>
|
/// <param name="device">The device.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SaveDevice(DeviceInfo device);
|
void SaveDevice(DeviceInfo device);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the capabilities.
|
/// Saves the capabilities.
|
||||||
@ -20,7 +19,7 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// <param name="id">The identifier.</param>
|
/// <param name="id">The identifier.</param>
|
||||||
/// <param name="capabilities">The capabilities.</param>
|
/// <param name="capabilities">The capabilities.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SaveCapabilities(string id, ClientCapabilities capabilities);
|
void SaveCapabilities(string id, ClientCapabilities capabilities);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the capabilities.
|
/// Gets the capabilities.
|
||||||
@ -36,18 +35,14 @@ namespace MediaBrowser.Controller.Devices
|
|||||||
/// <returns>DeviceInfo.</returns>
|
/// <returns>DeviceInfo.</returns>
|
||||||
DeviceInfo GetDevice(string id);
|
DeviceInfo GetDevice(string id);
|
||||||
|
|
||||||
/// <summary>
|
List<DeviceInfo> GetDevices();
|
||||||
/// Gets the devices.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
|
||||||
IEnumerable<DeviceInfo> GetDevices();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the device.
|
/// Deletes the device.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The identifier.</param>
|
/// <param name="id">The identifier.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task DeleteDevice(string id);
|
void DeleteDevice(string id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the upload history.
|
/// Gets the upload history.
|
||||||
|
@ -51,12 +51,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public override bool EnableRefreshOnDateModifiedChange
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid? FindSeriesId()
|
public Guid? FindSeriesId()
|
||||||
{
|
{
|
||||||
return SeriesId;
|
return SeriesId;
|
||||||
|
@ -38,12 +38,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return SeriesPresentationUniqueKey;
|
return SeriesPresentationUniqueKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public override bool EnableRefreshOnDateModifiedChange
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid? FindSeriesId()
|
public Guid? FindSeriesId()
|
||||||
{
|
{
|
||||||
return SeriesId;
|
return SeriesId;
|
||||||
|
@ -28,12 +28,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
locationType != LocationType.Virtual;
|
locationType != LocationType.Virtual;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public override bool EnableRefreshOnDateModifiedChange
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public override bool SupportsThemeMedia
|
public override bool SupportsThemeMedia
|
||||||
{
|
{
|
||||||
|
@ -136,7 +136,8 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
Name = Name,
|
Name = Name,
|
||||||
Path = Path,
|
Path = Path,
|
||||||
RunTimeTicks = RunTimeTicks,
|
RunTimeTicks = RunTimeTicks,
|
||||||
Type = MediaSourceType.Placeholder
|
Type = MediaSourceType.Placeholder,
|
||||||
|
IsInfiniteStream = RunTimeTicks == null
|
||||||
};
|
};
|
||||||
|
|
||||||
list.Add(info);
|
list.Add(info);
|
||||||
|
@ -220,7 +220,6 @@
|
|||||||
<Compile Include="Providers\IExternalId.cs" />
|
<Compile Include="Providers\IExternalId.cs" />
|
||||||
<Compile Include="Providers\IExtrasProvider.cs" />
|
<Compile Include="Providers\IExtrasProvider.cs" />
|
||||||
<Compile Include="Providers\IForcedProvider.cs" />
|
<Compile Include="Providers\IForcedProvider.cs" />
|
||||||
<Compile Include="Providers\IHasChangeMonitor.cs" />
|
|
||||||
<Compile Include="Entities\IHasMetadata.cs" />
|
<Compile Include="Entities\IHasMetadata.cs" />
|
||||||
<Compile Include="Providers\IHasItemChangeMonitor.cs" />
|
<Compile Include="Providers\IHasItemChangeMonitor.cs" />
|
||||||
<Compile Include="Providers\IHasLookupInfo.cs" />
|
<Compile Include="Providers\IHasLookupInfo.cs" />
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Providers
|
|
||||||
{
|
|
||||||
public interface IHasChangeMonitor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified item has changed.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <param name="directoryService">The directory service.</param>
|
|
||||||
/// <param name="date">The date.</param>
|
|
||||||
/// <returns><c>true</c> if the specified item has changed; otherwise, <c>false</c>.</returns>
|
|
||||||
bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date);
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,17 +21,7 @@ namespace MediaBrowser.Model.Devices
|
|||||||
/// <value>The camera upload path.</value>
|
/// <value>The camera upload path.</value>
|
||||||
public string CameraUploadPath { get; set; }
|
public string CameraUploadPath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
public string Name { get; set; }
|
||||||
/// Gets the name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The name.</value>
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return string.IsNullOrEmpty(CustomName) ? ReportedName : CustomName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the identifier.
|
/// Gets or sets the identifier.
|
||||||
|
@ -686,7 +686,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
if (subtitleStream != null)
|
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.SubtitleDeliveryMethod = subtitleProfile.Method;
|
||||||
playlistItem.SubtitleFormat = subtitleProfile.Format;
|
playlistItem.SubtitleFormat = subtitleProfile.Format;
|
||||||
@ -728,7 +728,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
if (subtitleStream != null)
|
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.SubtitleDeliveryMethod = subtitleProfile.Method;
|
||||||
playlistItem.SubtitleFormat = subtitleProfile.Format;
|
playlistItem.SubtitleFormat = subtitleProfile.Format;
|
||||||
@ -1054,15 +1054,6 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
string videoCodec = videoStream == null ? null : videoStream.Codec;
|
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<PlayMethod?, List<TranscodeReason>>(null, new List<TranscodeReason> { TranscodeReason.UnknownVideoStreamInfo });
|
|
||||||
}
|
|
||||||
|
|
||||||
conditions = new List<ProfileCondition>();
|
conditions = new List<ProfileCondition>();
|
||||||
foreach (CodecProfile i in profile.CodecProfiles)
|
foreach (CodecProfile i in profile.CodecProfiles)
|
||||||
{
|
{
|
||||||
@ -1189,7 +1180,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
{
|
{
|
||||||
if (subtitleStream != null)
|
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)
|
if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
|
||||||
{
|
{
|
||||||
@ -1208,7 +1199,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
return new Tuple<bool, TranscodeReason?>(result, TranscodeReason.ContainerBitrateExceedsLimit);
|
return new Tuple<bool, TranscodeReason?>(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)))
|
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
|
// 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) ??
|
return GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ??
|
||||||
GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ??
|
GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ??
|
||||||
new SubtitleProfile
|
new SubtitleProfile
|
||||||
{
|
{
|
||||||
Method = SubtitleDeliveryMethod.Encode,
|
Method = SubtitleDeliveryMethod.Encode,
|
||||||
@ -1299,7 +1290,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
return false;
|
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)
|
foreach (SubtitleProfile profile in subtitleProfiles)
|
||||||
{
|
{
|
||||||
@ -1338,6 +1329,12 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Build this into subtitleStream.SupportsExternalStream
|
||||||
|
if (mediaSource.IsInfiniteStream)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsExternalStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format))
|
if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsExternalStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format))
|
||||||
{
|
{
|
||||||
return profile;
|
return profile;
|
||||||
|
@ -466,7 +466,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
|
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
|
SubtitleStreamInfo info = new SubtitleStreamInfo
|
||||||
{
|
{
|
||||||
IsForced = stream.IsForced,
|
IsForced = stream.IsForced,
|
||||||
@ -480,7 +480,7 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
|
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}",
|
info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
|
||||||
baseUrl,
|
baseUrl,
|
||||||
|
@ -13,6 +13,7 @@ using System.Threading.Tasks;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Providers;
|
using MediaBrowser.Model.Providers;
|
||||||
|
using MediaBrowser.Providers.MediaInfo;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Manager
|
namespace MediaBrowser.Providers.Manager
|
||||||
{
|
{
|
||||||
@ -37,6 +38,28 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
LibraryManager = libraryManager;
|
LibraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool RequiresRefresh(IHasMetadata item, IDirectoryService directoryService)
|
||||||
|
{
|
||||||
|
if (item.RequiresRefresh())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.SupportsLocalMetadata)
|
||||||
|
{
|
||||||
|
var video = item as Video;
|
||||||
|
|
||||||
|
if (video != null && !video.IsPlaceHolder)
|
||||||
|
{
|
||||||
|
return !video.SubtitleFiles
|
||||||
|
.SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, FileSystem, false)
|
||||||
|
.OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
|
public async Task<ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var itemOfType = (TItemType)item;
|
var itemOfType = (TItemType)item;
|
||||||
@ -47,19 +70,35 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
|
|
||||||
var libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
|
var libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
|
||||||
|
|
||||||
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
|
DateTime? newDateModified = null;
|
||||||
|
if (item.LocationType == LocationType.FileSystem)
|
||||||
{
|
{
|
||||||
// TODO: If this returns true, should we instead just change metadata refresh mode to Full?
|
var file = refreshOptions.DirectoryService.GetFile(item.Path);
|
||||||
requiresRefresh = item.RequiresRefresh();
|
if (file != null)
|
||||||
|
{
|
||||||
|
newDateModified = file.LastWriteTimeUtc;
|
||||||
|
if (item.EnableRefreshOnDateModifiedChange)
|
||||||
|
{
|
||||||
|
if (newDateModified != item.DateModified)
|
||||||
|
{
|
||||||
|
Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, newDateModified, item.Id);
|
||||||
|
requiresRefresh = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requiresRefresh &&
|
if (!requiresRefresh && libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
|
||||||
libraryOptions.AutomaticRefreshIntervalDays > 0 &&
|
|
||||||
(DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
|
|
||||||
{
|
{
|
||||||
requiresRefresh = true;
|
requiresRefresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
|
||||||
|
{
|
||||||
|
// TODO: If this returns true, should we instead just change metadata refresh mode to Full?
|
||||||
|
requiresRefresh = RequiresRefresh(item, refreshOptions.DirectoryService);
|
||||||
|
}
|
||||||
|
|
||||||
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
|
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
|
||||||
var localImagesFailed = false;
|
var localImagesFailed = false;
|
||||||
|
|
||||||
@ -145,20 +184,9 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType);
|
var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType);
|
||||||
updateType = updateType | beforeSaveResult;
|
updateType = updateType | beforeSaveResult;
|
||||||
|
|
||||||
if (item.LocationType == LocationType.FileSystem)
|
if (newDateModified.HasValue)
|
||||||
{
|
{
|
||||||
var file = refreshOptions.DirectoryService.GetFile(item.Path);
|
item.DateModified = newDateModified.Value;
|
||||||
if (file != null)
|
|
||||||
{
|
|
||||||
var fileLastWriteTime = file.LastWriteTimeUtc;
|
|
||||||
if (item.EnableRefreshOnDateModifiedChange && fileLastWriteTime != item.DateModified)
|
|
||||||
{
|
|
||||||
Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, fileLastWriteTime, item.Id);
|
|
||||||
requiresRefresh = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.DateModified = fileLastWriteTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save if changes were made, or it's never been saved before
|
// Save if changes were made, or it's never been saved before
|
||||||
|
@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses ffmpeg to create video images
|
/// Uses ffmpeg to create video images
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AudioImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
|
public class AudioImageProvider : IDynamicImageProvider
|
||||||
{
|
{
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
@ -134,19 +134,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
|
|
||||||
return item.LocationType == LocationType.FileSystem && audio != null;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
ICustomMetadataProvider<Audio>,
|
ICustomMetadataProvider<Audio>,
|
||||||
ICustomMetadataProvider<AudioPodcast>,
|
ICustomMetadataProvider<AudioPodcast>,
|
||||||
ICustomMetadataProvider<AudioBook>,
|
ICustomMetadataProvider<AudioBook>,
|
||||||
IHasItemChangeMonitor,
|
|
||||||
IHasOrder,
|
IHasOrder,
|
||||||
IForcedProvider,
|
IForcedProvider,
|
||||||
IPreRefreshProvider
|
IPreRefreshProvider
|
||||||
@ -180,32 +179,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
return prober.Probe(item, cancellationToken);
|
return prober.Probe(item, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.SupportsLocalMetadata)
|
|
||||||
{
|
|
||||||
var video = item as Video;
|
|
||||||
|
|
||||||
if (video != null && !video.IsPlaceHolder)
|
|
||||||
{
|
|
||||||
return !video.SubtitleFiles
|
|
||||||
.SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, _fileSystem, false)
|
|
||||||
.OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Order
|
public int Order
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -16,7 +16,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MediaBrowser.Providers.MediaInfo
|
namespace MediaBrowser.Providers.MediaInfo
|
||||||
{
|
{
|
||||||
public class VideoImageProvider : IDynamicImageProvider, IHasItemChangeMonitor, IHasOrder
|
public class VideoImageProvider : IDynamicImageProvider, IHasOrder
|
||||||
{
|
{
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
@ -149,19 +149,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,12 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
name = name.Replace("!", " ");
|
name = name.Replace("!", " ");
|
||||||
name = name.Replace("?", " ");
|
name = name.Replace("?", " ");
|
||||||
|
|
||||||
|
var parenthIndex = name.IndexOf('(');
|
||||||
|
if (parenthIndex != -1)
|
||||||
|
{
|
||||||
|
name = name.Substring(0, parenthIndex);
|
||||||
|
}
|
||||||
|
|
||||||
name = name.Trim();
|
name = name.Trim();
|
||||||
|
|
||||||
// Search again if the new name is different
|
// Search again if the new name is different
|
||||||
|
@ -350,8 +350,6 @@ namespace MediaBrowser.Providers.TV
|
|||||||
|
|
||||||
foreach (var episodeToRemove in episodesToRemove.Select(e => e.Episode))
|
foreach (var episodeToRemove in episodesToRemove.Select(e => e.Episode))
|
||||||
{
|
{
|
||||||
_logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber);
|
|
||||||
|
|
||||||
await episodeToRemove.Delete(new DeleteOptions
|
await episodeToRemove.Delete(new DeleteOptions
|
||||||
{
|
{
|
||||||
DeleteFileLocation = true
|
DeleteFileLocation = true
|
||||||
@ -418,8 +416,6 @@ namespace MediaBrowser.Providers.TV
|
|||||||
|
|
||||||
foreach (var seasonToRemove in seasonsToRemove)
|
foreach (var seasonToRemove in seasonsToRemove)
|
||||||
{
|
{
|
||||||
_logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
|
|
||||||
|
|
||||||
await seasonToRemove.Delete(new DeleteOptions
|
await seasonToRemove.Delete(new DeleteOptions
|
||||||
{
|
{
|
||||||
DeleteFileLocation = true
|
DeleteFileLocation = true
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("3.2.33.7")]
|
[assembly: AssemblyVersion("3.2.33.8")]
|
||||||
|
Loading…
Reference in New Issue
Block a user