commit
a9eb446288
|
@ -290,8 +290,6 @@
|
||||||
<Compile Include="Providers\IImageFileSaver.cs" />
|
<Compile Include="Providers\IImageFileSaver.cs" />
|
||||||
<Compile Include="Providers\IImageProvider.cs" />
|
<Compile Include="Providers\IImageProvider.cs" />
|
||||||
<Compile Include="Providers\IImageSaver.cs" />
|
<Compile Include="Providers\IImageSaver.cs" />
|
||||||
<Compile Include="Providers\IItemIdentityConverter.cs" />
|
|
||||||
<Compile Include="Providers\IItemIdentityProvider.cs" />
|
|
||||||
<Compile Include="Providers\ILocalImageFileProvider.cs" />
|
<Compile Include="Providers\ILocalImageFileProvider.cs" />
|
||||||
<Compile Include="Providers\ILocalMetadataProvider.cs" />
|
<Compile Include="Providers\ILocalMetadataProvider.cs" />
|
||||||
<Compile Include="Providers\ImageRefreshMode.cs" />
|
<Compile Include="Providers\ImageRefreshMode.cs" />
|
||||||
|
@ -329,8 +327,6 @@
|
||||||
<Compile Include="Sorting\SortHelper.cs" />
|
<Compile Include="Sorting\SortHelper.cs" />
|
||||||
<Compile Include="Subtitles\ISubtitleManager.cs" />
|
<Compile Include="Subtitles\ISubtitleManager.cs" />
|
||||||
<Compile Include="Subtitles\ISubtitleProvider.cs" />
|
<Compile Include="Subtitles\ISubtitleProvider.cs" />
|
||||||
<Compile Include="Providers\ItemIdentifier.cs" />
|
|
||||||
<Compile Include="Providers\ItemIdentities.cs" />
|
|
||||||
<Compile Include="Providers\ItemLookupInfo.cs" />
|
<Compile Include="Providers\ItemLookupInfo.cs" />
|
||||||
<Compile Include="Providers\MetadataRefreshOptions.cs" />
|
<Compile Include="Providers\MetadataRefreshOptions.cs" />
|
||||||
<Compile Include="Providers\ISeriesOrderManager.cs" />
|
<Compile Include="Providers\ISeriesOrderManager.cs" />
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
namespace MediaBrowser.Controller.Providers
|
|
||||||
{
|
|
||||||
public interface IItemIdentityConverter { }
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
namespace MediaBrowser.Controller.Providers
|
|
||||||
{
|
|
||||||
public interface IItemIdentityProvider { }
|
|
||||||
}
|
|
|
@ -97,13 +97,11 @@ namespace MediaBrowser.Controller.Providers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="imageProviders">The image providers.</param>
|
/// <param name="imageProviders">The image providers.</param>
|
||||||
/// <param name="metadataServices">The metadata services.</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="metadataProviders">The metadata providers.</param>
|
||||||
/// <param name="savers">The savers.</param>
|
/// <param name="savers">The savers.</param>
|
||||||
/// <param name="imageSavers">The image savers.</param>
|
/// <param name="imageSavers">The image savers.</param>
|
||||||
/// <param name="externalIds">The external ids.</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<IMetadataSaver> savers,
|
||||||
IEnumerable<IImageSaver> imageSavers,
|
IEnumerable<IImageSaver> imageSavers,
|
||||||
IEnumerable<IExternalId> externalIds);
|
IEnumerable<IExternalId> externalIds);
|
||||||
|
@ -190,21 +188,5 @@ namespace MediaBrowser.Controller.Providers
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{HttpResponseInfo}.</returns>
|
/// <returns>Task{HttpResponseInfo}.</returns>
|
||||||
Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken);
|
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<IItemIdentityProvider<TLookupInfo, TIdentity>>.</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<IItemIdentityConverter<TIdentity>>.</returns>
|
|
||||||
IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>()
|
|
||||||
where TLookupInfo : ItemLookupInfo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -179,18 +179,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
lookupInfo.Year = result.ProductionYear;
|
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)
|
private DateTime GetLastRefreshDate(IHasMetadata item)
|
||||||
{
|
{
|
||||||
return item.DateLastRefreshed;
|
return item.DateLastRefreshed;
|
||||||
|
|
|
@ -55,8 +55,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
private IMetadataService[] _metadataServices = { };
|
private IMetadataService[] _metadataServices = { };
|
||||||
private IItemIdentityProvider[] _identityProviders = { };
|
|
||||||
private IItemIdentityConverter[] _identityConverters = { };
|
|
||||||
private IMetadataProvider[] _metadataProviders = { };
|
private IMetadataProvider[] _metadataProviders = { };
|
||||||
private IEnumerable<IMetadataSaver> _savers;
|
private IEnumerable<IMetadataSaver> _savers;
|
||||||
private IImageSaver[] _imageSavers;
|
private IImageSaver[] _imageSavers;
|
||||||
|
@ -92,22 +90,17 @@ namespace MediaBrowser.Providers.Manager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="imageProviders">The image providers.</param>
|
/// <param name="imageProviders">The image providers.</param>
|
||||||
/// <param name="metadataServices">The metadata services.</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="metadataProviders">The metadata providers.</param>
|
||||||
/// <param name="metadataSavers">The metadata savers.</param>
|
/// <param name="metadataSavers">The metadata savers.</param>
|
||||||
/// <param name="imageSavers">The image savers.</param>
|
/// <param name="imageSavers">The image savers.</param>
|
||||||
/// <param name="externalIds">The external ids.</param>
|
/// <param name="externalIds">The external ids.</param>
|
||||||
public void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices,
|
public void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices,
|
||||||
IEnumerable<IItemIdentityProvider> identityProviders, IEnumerable<IItemIdentityConverter> identityConverters,
|
|
||||||
IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers,
|
IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers,
|
||||||
IEnumerable<IImageSaver> imageSavers, IEnumerable<IExternalId> externalIds)
|
IEnumerable<IImageSaver> imageSavers, IEnumerable<IExternalId> externalIds)
|
||||||
{
|
{
|
||||||
ImageProviders = imageProviders.ToArray();
|
ImageProviders = imageProviders.ToArray();
|
||||||
|
|
||||||
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
|
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
|
||||||
_identityProviders = identityProviders.ToArray();
|
|
||||||
_identityConverters = identityConverters.ToArray();
|
|
||||||
_metadataProviders = metadataProviders.ToArray();
|
_metadataProviders = metadataProviders.ToArray();
|
||||||
_imageSavers = imageSavers.ToArray();
|
_imageSavers = imageSavers.ToArray();
|
||||||
_externalIds = externalIds.OrderBy(i => i.Name).ToArray();
|
_externalIds = externalIds.OrderBy(i => i.Name).ToArray();
|
||||||
|
@ -301,18 +294,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
.ThenBy(GetDefaultOrder);
|
.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)
|
private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasImages item, bool includeDisabled)
|
||||||
{
|
{
|
||||||
var options = GetMetadataOptions(item);
|
var options = GetMetadataOptions(item);
|
||||||
|
|
|
@ -177,7 +177,6 @@
|
||||||
<Compile Include="TV\SeriesMetadataService.cs" />
|
<Compile Include="TV\SeriesMetadataService.cs" />
|
||||||
<Compile Include="TV\TheTVDB\TvdbEpisodeImageProvider.cs" />
|
<Compile Include="TV\TheTVDB\TvdbEpisodeImageProvider.cs" />
|
||||||
<Compile Include="People\TvdbPersonImageProvider.cs" />
|
<Compile Include="People\TvdbPersonImageProvider.cs" />
|
||||||
<Compile Include="TV\TheTVDB\TvdbSeasonIdentityProvider.cs" />
|
|
||||||
<Compile Include="TV\TheTVDB\TvdbSeasonImageProvider.cs" />
|
<Compile Include="TV\TheTVDB\TvdbSeasonImageProvider.cs" />
|
||||||
<Compile Include="TV\TheTVDB\TvdbSeriesImageProvider.cs" />
|
<Compile Include="TV\TheTVDB\TvdbSeriesImageProvider.cs" />
|
||||||
<Compile Include="TV\SeasonMetadataService.cs" />
|
<Compile Include="TV\SeasonMetadataService.cs" />
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class RemoteEpisodeProvider
|
/// Class RemoteEpisodeProvider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo>
|
class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>
|
||||||
{
|
{
|
||||||
private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full";
|
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 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -70,21 +70,6 @@ namespace MediaBrowser.Providers.TV
|
||||||
var seriesProviderIds = series.ProviderIds;
|
var seriesProviderIds = series.ProviderIds;
|
||||||
var seasonNumber = season.IndexNumber.Value;
|
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);
|
var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(seriesDataPath))
|
if (!string.IsNullOrWhiteSpace(seriesDataPath))
|
||||||
|
|
|
@ -25,7 +25,7 @@ using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.TV
|
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 TvdbSeriesOffset = "TvdbSeriesOffset";
|
||||||
private const string TvdbSeriesOffsetFormat = "{0}-{1}";
|
private const string TvdbSeriesOffsetFormat = "{0}-{1}";
|
||||||
|
|
241
MediaBrowser.Server.Implementations/IO/FileRefresher.cs
Normal file
241
MediaBrowser.Server.Implementations/IO/FileRefresher.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -929,10 +929,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
|
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
|
||||||
recordPath = EnsureFileUnique(recordPath, timer.Id);
|
recordPath = EnsureFileUnique(recordPath, timer.Id);
|
||||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
|
|
||||||
activeRecordingInfo.Path = recordPath;
|
|
||||||
|
|
||||||
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
|
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
|
||||||
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
|
||||||
|
activeRecordingInfo.Path = recordPath;
|
||||||
|
|
||||||
var duration = recordingEndDate - DateTime.UtcNow;
|
var duration = recordingEndDate - DateTime.UtcNow;
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,7 @@
|
||||||
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
|
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
|
||||||
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
|
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
|
||||||
<Compile Include="Intros\DefaultIntroProvider.cs" />
|
<Compile Include="Intros\DefaultIntroProvider.cs" />
|
||||||
|
<Compile Include="IO\FileRefresher.cs" />
|
||||||
<Compile Include="IO\LibraryMonitor.cs" />
|
<Compile Include="IO\LibraryMonitor.cs" />
|
||||||
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
|
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
|
||||||
<Compile Include="Library\LibraryManager.cs" />
|
<Compile Include="Library\LibraryManager.cs" />
|
||||||
|
|
|
@ -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_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
|
||||||
"create index if not exists idx_Type on TypedBaseItems(Type)",
|
"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);
|
_connection.RunQueries(postQueries, Logger);
|
||||||
|
@ -1673,7 +1674,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
var slowThreshold = 1000;
|
var slowThreshold = 1000;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
slowThreshold = 200;
|
slowThreshold = 100;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (elapsed >= slowThreshold)
|
if (elapsed >= slowThreshold)
|
||||||
|
|
|
@ -804,8 +804,6 @@ namespace MediaBrowser.Server.Startup.Common
|
||||||
|
|
||||||
ProviderManager.AddParts(GetExports<IImageProvider>(),
|
ProviderManager.AddParts(GetExports<IImageProvider>(),
|
||||||
GetExports<IMetadataService>(),
|
GetExports<IMetadataService>(),
|
||||||
GetExports<IItemIdentityProvider>(),
|
|
||||||
GetExports<IItemIdentityConverter>(),
|
|
||||||
GetExports<IMetadataProvider>(),
|
GetExports<IMetadataProvider>(),
|
||||||
GetExports<IMetadataSaver>(),
|
GetExports<IMetadataSaver>(),
|
||||||
GetExports<IImageSaver>(),
|
GetExports<IImageSaver>(),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user