Merge pull request #1781 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-05-26 15:24:04 -04:00
commit a9eb446288
18 changed files with 250 additions and 283 deletions

View File

@ -290,8 +290,6 @@
<Compile Include="Providers\IImageFileSaver.cs" />
<Compile Include="Providers\IImageProvider.cs" />
<Compile Include="Providers\IImageSaver.cs" />
<Compile Include="Providers\IItemIdentityConverter.cs" />
<Compile Include="Providers\IItemIdentityProvider.cs" />
<Compile Include="Providers\ILocalImageFileProvider.cs" />
<Compile Include="Providers\ILocalMetadataProvider.cs" />
<Compile Include="Providers\ImageRefreshMode.cs" />
@ -329,8 +327,6 @@
<Compile Include="Sorting\SortHelper.cs" />
<Compile Include="Subtitles\ISubtitleManager.cs" />
<Compile Include="Subtitles\ISubtitleProvider.cs" />
<Compile Include="Providers\ItemIdentifier.cs" />
<Compile Include="Providers\ItemIdentities.cs" />
<Compile Include="Providers\ItemLookupInfo.cs" />
<Compile Include="Providers\MetadataRefreshOptions.cs" />
<Compile Include="Providers\ISeriesOrderManager.cs" />

View File

@ -1,4 +0,0 @@
namespace MediaBrowser.Controller.Providers
{
public interface IItemIdentityConverter { }
}

View File

@ -1,4 +0,0 @@
namespace MediaBrowser.Controller.Providers
{
public interface IItemIdentityProvider { }
}

View File

@ -97,13 +97,11 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <param name="imageProviders">The image providers.</param>
/// <param name="metadataServices">The metadata services.</param>
/// <param name="identityProviders">The identity providers.</param>
/// <param name="identityConverters">The identity converters.</param>
/// <param name="metadataProviders">The metadata providers.</param>
/// <param name="savers">The savers.</param>
/// <param name="imageSavers">The image savers.</param>
/// <param name="externalIds">The external ids.</param>
void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IItemIdentityProvider> identityProviders, IEnumerable<IItemIdentityConverter> identityConverters, IEnumerable<IMetadataProvider> metadataProviders,
void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders,
IEnumerable<IMetadataSaver> savers,
IEnumerable<IImageSaver> imageSavers,
IEnumerable<IExternalId> externalIds);
@ -190,21 +188,5 @@ namespace MediaBrowser.Controller.Providers
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken);
/// <summary>
/// Gets the item identity providers.
/// </summary>
/// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam>
/// <returns>IEnumerable&lt;IItemIdentityProvider&lt;TLookupInfo, TIdentity&gt;&gt;.</returns>
IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>()
where TLookupInfo : ItemLookupInfo;
/// <summary>
/// Gets the item identity converters.
/// </summary>
/// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam>
/// <returns>IEnumerable&lt;IItemIdentityConverter&lt;TIdentity&gt;&gt;.</returns>
IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>()
where TLookupInfo : ItemLookupInfo;
}
}

View File

@ -1,36 +0,0 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
public static class ItemIdentifier<TLookupInfo>
where TLookupInfo : ItemLookupInfo
{
public static async Task FindIdentities(TLookupInfo item, IProviderManager providerManager, CancellationToken cancellationToken)
{
var providers = providerManager.GetItemIdentityProviders<TLookupInfo>();
var converters = providerManager.GetItemIdentityConverters<TLookupInfo>().ToList();
foreach (var provider in providers)
{
await provider.Identify(item);
}
bool changesMade = true;
while (changesMade)
{
changesMade = false;
foreach (var converter in converters)
{
if (await converter.Convert(item))
{
changesMade = true;
}
}
}
}
}
}

View File

@ -1,16 +0,0 @@
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
public interface IItemIdentityProvider<in TLookupInfo> : IItemIdentityProvider
where TLookupInfo : ItemLookupInfo
{
Task Identify(TLookupInfo info);
}
public interface IItemIdentityConverter<in TLookupInfo> : IItemIdentityConverter
where TLookupInfo : ItemLookupInfo
{
Task<bool> Convert(TLookupInfo info);
}
}

View File

@ -179,18 +179,6 @@ namespace MediaBrowser.Providers.Manager
lookupInfo.Year = result.ProductionYear;
}
private async Task FindIdentities(TIdType id, CancellationToken cancellationToken)
{
try
{
await ItemIdentifier<TIdType>.FindIdentities(id, ProviderManager, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error in FindIdentities", ex);
}
}
private DateTime GetLastRefreshDate(IHasMetadata item)
{
return item.DateLastRefreshed;

View File

@ -55,8 +55,6 @@ namespace MediaBrowser.Providers.Manager
private readonly IFileSystem _fileSystem;
private IMetadataService[] _metadataServices = { };
private IItemIdentityProvider[] _identityProviders = { };
private IItemIdentityConverter[] _identityConverters = { };
private IMetadataProvider[] _metadataProviders = { };
private IEnumerable<IMetadataSaver> _savers;
private IImageSaver[] _imageSavers;
@ -92,22 +90,17 @@ namespace MediaBrowser.Providers.Manager
/// </summary>
/// <param name="imageProviders">The image providers.</param>
/// <param name="metadataServices">The metadata services.</param>
/// <param name="identityProviders">The identity providers.</param>
/// <param name="identityConverters">The identity converters.</param>
/// <param name="metadataProviders">The metadata providers.</param>
/// <param name="metadataSavers">The metadata savers.</param>
/// <param name="imageSavers">The image savers.</param>
/// <param name="externalIds">The external ids.</param>
public void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices,
IEnumerable<IItemIdentityProvider> identityProviders, IEnumerable<IItemIdentityConverter> identityConverters,
IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers,
IEnumerable<IImageSaver> imageSavers, IEnumerable<IExternalId> externalIds)
{
ImageProviders = imageProviders.ToArray();
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
_identityProviders = identityProviders.ToArray();
_identityConverters = identityConverters.ToArray();
_metadataProviders = metadataProviders.ToArray();
_imageSavers = imageSavers.ToArray();
_externalIds = externalIds.OrderBy(i => i.Name).ToArray();
@ -301,18 +294,6 @@ namespace MediaBrowser.Providers.Manager
.ThenBy(GetDefaultOrder);
}
public IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>()
where TLookupInfo : ItemLookupInfo
{
return _identityProviders.OfType<IItemIdentityProvider<TLookupInfo>>();
}
public IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>()
where TLookupInfo : ItemLookupInfo
{
return _identityConverters.OfType<IItemIdentityConverter<TLookupInfo>>();
}
private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasImages item, bool includeDisabled)
{
var options = GetMetadataOptions(item);

View File

@ -177,7 +177,6 @@
<Compile Include="TV\SeriesMetadataService.cs" />
<Compile Include="TV\TheTVDB\TvdbEpisodeImageProvider.cs" />
<Compile Include="People\TvdbPersonImageProvider.cs" />
<Compile Include="TV\TheTVDB\TvdbSeasonIdentityProvider.cs" />
<Compile Include="TV\TheTVDB\TvdbSeasonImageProvider.cs" />
<Compile Include="TV\TheTVDB\TvdbSeriesImageProvider.cs" />
<Compile Include="TV\SeasonMetadataService.cs" />

View File

@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.TV
/// <summary>
/// Class RemoteEpisodeProvider
/// </summary>
class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo>
class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>
{
private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full";
@ -871,86 +871,6 @@ namespace MediaBrowser.Providers.TV
});
}
public Task Identify(EpisodeInfo info)
{
if (info.ProviderIds.ContainsKey(FullIdKey))
{
return Task.FromResult<object>(null);
}
string seriesTvdbId;
info.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesTvdbId);
if (string.IsNullOrEmpty(seriesTvdbId) || info.IndexNumber == null)
{
return Task.FromResult<object>(null);
}
var id = new Identity(seriesTvdbId, info.ParentIndexNumber, info.IndexNumber.Value, info.IndexNumberEnd);
info.SetProviderId(FullIdKey, id.ToString());
return Task.FromResult(id);
}
public int Order { get { return 0; } }
public struct Identity
{
public string SeriesId { get; private set; }
public int? SeasonIndex { get; private set; }
public int EpisodeNumber { get; private set; }
public int? EpisodeNumberEnd { get; private set; }
public Identity(string id)
: this()
{
this = ParseIdentity(id).Value;
}
public Identity(string seriesId, int? seasonIndex, int episodeNumber, int? episodeNumberEnd)
: this()
{
SeriesId = seriesId;
SeasonIndex = seasonIndex;
EpisodeNumber = episodeNumber;
EpisodeNumberEnd = episodeNumberEnd;
}
public override string ToString()
{
return string.Format("{0}:{1}:{2}",
SeriesId,
SeasonIndex != null ? SeasonIndex.Value.ToString() : "A",
EpisodeNumber + (EpisodeNumberEnd != null ? "-" + EpisodeNumberEnd.Value.ToString() : ""));
}
public static Identity? ParseIdentity(string id)
{
if (string.IsNullOrEmpty(id))
return null;
try {
var parts = id.Split(':');
var series = parts[0];
var season = parts[1] != "A" ? (int?)int.Parse(parts[1]) : null;
int index;
int? indexEnd;
if (parts[2].Contains("-")) {
var split = parts[2].IndexOf("-", StringComparison.OrdinalIgnoreCase);
index = int.Parse(parts[2].Substring(0, split));
indexEnd = int.Parse(parts[2].Substring(split + 1));
} else {
index = int.Parse(parts[2]);
indexEnd = null;
}
return new Identity(series, season, index, indexEnd);
} catch {
return null;
}
}
}
}
}

View File

@ -1,65 +0,0 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Providers.TV
{
public class TvdbSeasonIdentityProvider : IItemIdentityProvider<SeasonInfo>
{
public static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full";
public Task Identify(SeasonInfo info)
{
string tvdbSeriesId;
if (!info.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out tvdbSeriesId) || string.IsNullOrEmpty(tvdbSeriesId) || info.IndexNumber == null)
{
return Task.FromResult<object>(null);
}
if (string.IsNullOrEmpty(info.GetProviderId(FullIdKey)))
{
var id = string.Format("{0}:{1}", tvdbSeriesId, info.IndexNumber.Value);
info.SetProviderId(FullIdKey, id);
}
return Task.FromResult<object>(null);
}
public static TvdbSeasonIdentity? ParseIdentity(string id)
{
if (id == null)
{
return null;
}
try
{
var parts = id.Split(':');
return new TvdbSeasonIdentity(parts[0], int.Parse(parts[1]));
}
catch
{
return null;
}
}
}
public struct TvdbSeasonIdentity
{
public string SeriesId { get; private set; }
public int Index { get; private set; }
public TvdbSeasonIdentity(string id)
: this()
{
this = TvdbSeasonIdentityProvider.ParseIdentity(id).Value;
}
public TvdbSeasonIdentity(string seriesId, int index)
: this()
{
SeriesId = seriesId;
Index = index;
}
}
}

View File

@ -70,21 +70,6 @@ namespace MediaBrowser.Providers.TV
var seriesProviderIds = series.ProviderIds;
var seasonNumber = season.IndexNumber.Value;
var identity = TvdbSeasonIdentityProvider.ParseIdentity(season.GetProviderId(TvdbSeasonIdentityProvider.FullIdKey));
if (identity == null)
{
identity = new TvdbSeasonIdentity(series.GetProviderId(MetadataProviders.Tvdb), seasonNumber);
}
if (identity != null)
{
var id = identity.Value;
seasonNumber = AdjustForSeriesOffset(series, id.Index);
seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
seriesProviderIds[MetadataProviders.Tvdb.ToString()] = id.SeriesId;
}
var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(seriesDataPath))

View File

@ -25,7 +25,7 @@ using CommonIO;
namespace MediaBrowser.Providers.TV
{
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IItemIdentityProvider<SeriesInfo>, IHasOrder
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
private const string TvdbSeriesOffset = "TvdbSeriesOffset";
private const string TvdbSeriesOffsetFormat = "{0}-{1}";

View File

@ -0,0 +1,241 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.ScheduledTasks;
namespace MediaBrowser.Server.Implementations.IO
{
public class FileRefresher : IDisposable
{
private ILogger Logger { get; set; }
private ITaskManager TaskManager { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
private Timer _timer;
private readonly object _timerLock = new object();
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger)
{
_affectedPaths.Add(path);
_fileSystem = fileSystem;
ConfigurationManager = configurationManager;
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = logger;
}
private void RestartTimer()
{
lock (_timerLock)
{
if (_timer == null)
{
_timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
else
{
_timer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
}
}
private async void OnTimerCallback(object state)
{
// Extend the timer as long as any of the paths are still being written to.
if (_affectedPaths.Any(IsFileLocked))
{
Logger.Info("Timer extended.");
RestartTimer();
return;
}
Logger.Debug("Timer stopped.");
DisposeTimer();
try
{
await ProcessPathChanges(_affectedPaths).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error processing directory changes", ex);
}
}
private async Task ProcessPathChanges(List<string> paths)
{
var itemsToRefresh = paths
.Select(GetAffectedBaseItem)
.Where(item => item != null)
.Distinct()
.ToList();
foreach (var p in paths)
{
Logger.Info(p + " reports change.");
}
// If the root folder changed, run the library task so the user can see it
if (itemsToRefresh.Any(i => i is AggregateFolder))
{
TaskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
return;
}
foreach (var item in itemsToRefresh)
{
Logger.Info(item.Name + " (" + item.Path + ") will be refreshed.");
try
{
await item.ChangedExternally().ConfigureAwait(false);
}
catch (IOException ex)
{
// For now swallow and log.
// Research item: If an IOException occurs, the item may be in a disconnected state (media unavailable)
// Should we remove it from it's parent?
Logger.ErrorException("Error refreshing {0}", ex, item.Name);
}
catch (Exception ex)
{
Logger.ErrorException("Error refreshing {0}", ex, item.Name);
}
}
}
/// <summary>
/// Gets the affected base item.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BaseItem.</returns>
private BaseItem GetAffectedBaseItem(string path)
{
BaseItem item = null;
while (item == null && !string.IsNullOrEmpty(path))
{
item = LibraryManager.FindByPath(path, null);
path = Path.GetDirectoryName(path);
}
if (item != null)
{
// If the item has been deleted find the first valid parent that still exists
while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path))
{
item = item.GetParent();
if (item == null)
{
break;
}
}
}
return item;
}
private bool IsFileLocked(string path)
{
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
// Causing lockups on linux
return false;
}
try
{
var data = _fileSystem.GetFileSystemInfo(path);
if (!data.Exists
|| data.IsDirectory
// Opening a writable stream will fail with readonly files
|| data.Attributes.HasFlag(FileAttributes.ReadOnly))
{
return false;
}
}
catch (IOException)
{
return false;
}
catch (Exception ex)
{
Logger.ErrorException("Error getting file system info for: {0}", ex, path);
return false;
}
// In order to determine if the file is being written to, we have to request write access
// But if the server only has readonly access, this is going to cause this entire algorithm to fail
// So we'll take a best guess about our access level
var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
? FileAccess.ReadWrite
: FileAccess.Read;
try
{
using (_fileSystem.GetFileStream(path, FileMode.Open, requestedFileAccess, FileShare.ReadWrite))
{
//file is not locked
return false;
}
}
catch (DirectoryNotFoundException)
{
// File may have been deleted
return false;
}
catch (FileNotFoundException)
{
// File may have been deleted
return false;
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
Logger.Debug("{0} is locked.", path);
return true;
}
catch (Exception ex)
{
Logger.ErrorException("Error determining if file is locked: {0}", ex, path);
return false;
}
}
public void DisposeTimer()
{
lock (_timerLock)
{
if (_timer != null)
{
_timer.Dispose();
}
}
}
public void Dispose()
{
DisposeTimer();
}
}
}

View File

@ -929,10 +929,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
recordPath = EnsureFileUnique(recordPath, timer.Id);
_fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
activeRecordingInfo.Path = recordPath;
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
_fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
activeRecordingInfo.Path = recordPath;
var duration = recordingEndDate - DateTime.UtcNow;

View File

@ -180,6 +180,7 @@
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
<Compile Include="Intros\DefaultIntroProvider.cs" />
<Compile Include="IO\FileRefresher.cs" />
<Compile Include="IO\LibraryMonitor.cs" />
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
<Compile Include="Library\LibraryManager.cs" />

View File

@ -247,7 +247,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
"create index if not exists idx_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
"create index if not exists idx_Type on TypedBaseItems(Type)",
"create index if not exists idx_TopParentId on TypedBaseItems(TopParentId)"
"create index if not exists idx_TopParentId on TypedBaseItems(TopParentId)",
//"create index if not exists idx_TypeTopParentId on TypedBaseItems(Type,TopParentId)"
};
_connection.RunQueries(postQueries, Logger);
@ -1673,7 +1674,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
var slowThreshold = 1000;
#if DEBUG
slowThreshold = 200;
slowThreshold = 100;
#endif
if (elapsed >= slowThreshold)

View File

@ -804,8 +804,6 @@ namespace MediaBrowser.Server.Startup.Common
ProviderManager.AddParts(GetExports<IImageProvider>(),
GetExports<IMetadataService>(),
GetExports<IItemIdentityProvider>(),
GetExports<IItemIdentityConverter>(),
GetExports<IMetadataProvider>(),
GetExports<IMetadataSaver>(),
GetExports<IImageSaver>(),