Merge pull request #2952 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2017-10-14 02:55:15 -04:00 committed by GitHub
commit c8244f4687
34 changed files with 618 additions and 461 deletions

View File

@ -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;
}
} }
} }

View File

@ -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);

View File

@ -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));
} }
} }

View File

@ -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);
} }

View File

@ -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);
}
}
}
}

View 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)
{
}
}
}
}
}

View File

@ -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" />

View File

@ -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; }
} }
} }

View File

@ -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)

View File

@ -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);
}); });
} }

View File

@ -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;
} }

View File

@ -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);
}
} }
} }

View File

@ -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)

View File

@ -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)

View File

@ -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
}; };

View File

@ -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)

View File

@ -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&lt;DeviceInfo&gt;.</returns> /// <returns>IEnumerable&lt;DeviceInfo&gt;.</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.

View File

@ -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&lt;DeviceInfo&gt;.</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.

View File

@ -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;

View File

@ -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;

View File

@ -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
{ {

View File

@ -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);

View File

@ -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" />

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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;
}
} }
} }

View File

@ -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

View File

@ -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;
}
} }
} }

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,3 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("3.2.33.7")] [assembly: AssemblyVersion("3.2.33.8")]