2
0
Эх сурвалжийг харах

Re-designed item identity providers

Thomas Gillen 9 жил өмнө
parent
commit
556b34d000

+ 1 - 5
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -38,7 +38,6 @@ namespace MediaBrowser.Controller.Entities
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             LockedFields = new List<MetadataFields>();
             ImageInfos = new List<ItemImageInfo>();
-            Identities = new List<IItemIdentity>();
         }
 
         /// <summary>
@@ -336,10 +335,7 @@ namespace MediaBrowser.Controller.Entities
         public bool IsLocked { get; set; }
 
         public bool IsUnidentified { get; set; }
-
-        [IgnoreDataMember]
-        public List<IItemIdentity> Identities { get; set; }
-
+        
         /// <summary>
         /// Gets or sets the locked fields.
         /// </summary>

+ 1 - 6
MediaBrowser.Controller/Entities/IHasMetadata.cs

@@ -49,12 +49,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <value><c>true</c> if this instance is unidentified; otherwise, <c>false</c>.</value>
         bool IsUnidentified { get; set; }
-
-        /// <summary>
-        /// Gets the item identities.
-        /// </summary>
-        List<IItemIdentity> Identities { get; set; }
-
+        
         /// <summary>
         /// Afters the metadata refresh.
         /// </summary>

+ 0 - 5
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -268,7 +268,6 @@
     <Compile Include="Providers\DirectoryService.cs" />
     <Compile Include="Providers\DynamicImageInfo.cs" />
     <Compile Include="Providers\DynamicImageResponse.cs" />
-    <Compile Include="Providers\EpisodeIdentity.cs" />
     <Compile Include="Providers\EpisodeInfo.cs" />
     <Compile Include="Providers\ExtraInfo.cs" />
     <Compile Include="Providers\ExtraSource.cs" />
@@ -282,14 +281,12 @@
     <Compile Include="Providers\IForcedProvider.cs" />
     <Compile Include="Providers\IHasChangeMonitor.cs" />
     <Compile Include="Entities\IHasMetadata.cs" />
-    <Compile Include="Providers\IHasIdentities.cs" />
     <Compile Include="Providers\IHasItemChangeMonitor.cs" />
     <Compile Include="Providers\IHasLookupInfo.cs" />
     <Compile Include="Providers\IHasOrder.cs" />
     <Compile Include="Providers\IImageFileSaver.cs" />
     <Compile Include="Providers\IImageProvider.cs" />
     <Compile Include="Providers\IImageSaver.cs" />
-    <Compile Include="Providers\IItemIdentity.cs" />
     <Compile Include="Providers\IItemIdentityConverter.cs" />
     <Compile Include="Providers\IItemIdentityProvider.cs" />
     <Compile Include="Providers\ILocalImageFileProvider.cs" />
@@ -314,9 +311,7 @@
     <Compile Include="Providers\MusicVideoInfo.cs" />
     <Compile Include="Providers\PersonLookupInfo.cs" />
     <Compile Include="Providers\RemoteSearchQuery.cs" />
-    <Compile Include="Providers\SeasonIdentity.cs" />
     <Compile Include="Providers\SeasonInfo.cs" />
-    <Compile Include="Providers\SeriesIdentity.cs" />
     <Compile Include="Providers\SeriesInfo.cs" />
     <Compile Include="Providers\SeriesOrderTypes.cs" />
     <Compile Include="Providers\SongInfo.cs" />

+ 0 - 12
MediaBrowser.Controller/Providers/EpisodeIdentity.cs

@@ -1,12 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
-    public class EpisodeIdentity : IItemIdentity
-    {
-        public string Type { get; set; }
-
-        public string SeriesId { get; set; }
-        public int? SeasonIndex { get; set; }
-        public int IndexNumber { get; set; }
-        public int? IndexNumberEnd { get; set; }
-    }
-}

+ 1 - 17
MediaBrowser.Controller/Providers/EpisodeInfo.cs

@@ -1,15 +1,10 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Providers
 {
-    public class EpisodeInfo : ItemLookupInfo, IHasIdentities<EpisodeIdentity>
+    public class EpisodeInfo : ItemLookupInfo
     {
-        private List<EpisodeIdentity> _identities = new List<EpisodeIdentity>();
-
         public Dictionary<string, string> SeriesProviderIds { get; set; }
 
         public int? IndexNumberEnd { get; set; }
@@ -19,16 +14,5 @@ namespace MediaBrowser.Controller.Providers
         {
             SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
-
-        public IEnumerable<EpisodeIdentity> Identities
-        {
-            get { return _identities; }
-        }
-
-        public async Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken)
-        {
-            var identifier = new ItemIdentifier<EpisodeInfo, EpisodeIdentity>();
-            _identities = (await identifier.FindIdentities(this, providerManager, cancellationToken)).ToList();
-        }
     }
 }

+ 0 - 14
MediaBrowser.Controller/Providers/IHasIdentities.cs

@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Providers
-{
-    public interface IHasIdentities<out TIdentity>
-        where TIdentity : IItemIdentity
-    {
-        IEnumerable<TIdentity> Identities { get; }
-
-        Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken);
-    }
-}

+ 0 - 7
MediaBrowser.Controller/Providers/IItemIdentity.cs

@@ -1,7 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
-    public interface IItemIdentity
-    {
-        string Type { get; }
-    }
-}

+ 1 - 1
MediaBrowser.Controller/Providers/IItemIdentityConverter.cs

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

+ 1 - 1
MediaBrowser.Controller/Providers/IItemIdentityProvider.cs

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

+ 5 - 7
MediaBrowser.Controller/Providers/IProviderManager.cs

@@ -195,18 +195,16 @@ namespace MediaBrowser.Controller.Providers
         /// Gets the item identity providers.
         /// </summary>
         /// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam>
-        /// <typeparam name="TIdentity">The type of the t identity.</typeparam>
         /// <returns>IEnumerable&lt;IItemIdentityProvider&lt;TLookupInfo, TIdentity&gt;&gt;.</returns>
-        IEnumerable<IItemIdentityProvider<TLookupInfo, TIdentity>> GetItemIdentityProviders<TLookupInfo, TIdentity>()
-            where TLookupInfo : ItemLookupInfo
-            where TIdentity : IItemIdentity;
+        IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>()
+            where TLookupInfo : ItemLookupInfo;
 
         /// <summary>
         /// Gets the item identity converters.
         /// </summary>
-        /// <typeparam name="TIdentity">The type of the t identity.</typeparam>
+        /// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam>
         /// <returns>IEnumerable&lt;IItemIdentityConverter&lt;TIdentity&gt;&gt;.</returns>
-        IEnumerable<IItemIdentityConverter<TIdentity>> GetItemIdentityConverters<TIdentity>()
-            where TIdentity : IItemIdentity;
+        IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>()
+            where TLookupInfo : ItemLookupInfo;
     }
 }

+ 13 - 50
MediaBrowser.Controller/Providers/ItemIdentifier.cs

@@ -1,73 +1,36 @@
-using System.Collections.Generic;
-using System.Linq;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Providers
 {
-    public class ItemIdentifier<TLookupInfo, TIdentity>
+    public static class ItemIdentifier<TLookupInfo>
         where TLookupInfo : ItemLookupInfo
-        where TIdentity : IItemIdentity
     {
-        public async Task<IEnumerable<TIdentity>> FindIdentities(TLookupInfo item, IProviderManager providerManager, CancellationToken cancellationToken)
+        public static async Task FindIdentities(TLookupInfo item, IProviderManager providerManager, CancellationToken cancellationToken)
         {
-            var providers = providerManager.GetItemIdentityProviders<TLookupInfo, TIdentity>();
-            var converters = providerManager.GetItemIdentityConverters<TIdentity>();
-
-            var identities = new List<IdentityPair>();
-
+            var providers = providerManager.GetItemIdentityProviders<TLookupInfo>();
+            var converters = providerManager.GetItemIdentityConverters<TLookupInfo>().ToList();
+            
             foreach (var provider in providers)
             {
-                var result = new IdentityPair
-                {
-                    Identity = await provider.FindIdentity(item),
-                    Order = provider.Order
-                };
-
-                if (!Equals(result.Identity, default(TIdentity)))
-                {
-                    identities.Add(result);
-                }
+                await provider.Identify(item);
             }
 
-            var convertersAvailable = new List<IItemIdentityConverter<TIdentity>>(converters);
-            bool changesMade;
+            bool changesMade = true;
 
-            do
+            while (changesMade)
             {
                 changesMade = false;
 
-                for (int i = convertersAvailable.Count - 1; i >= 0; i--)
+                foreach (var converter in converters)
                 {
-                    var converter = convertersAvailable[i];
-                    var input = identities.FirstOrDefault(id => id.Identity.Type == converter.SourceType);
-                    var existing = identities.Where(id => id.Identity.Type == converter.ResultType);
-
-                    if (input != null && !existing.Any(id => id.Order <= converter.Order))
+                    if (await converter.Convert(item))
                     {
-                        var result = new IdentityPair
-                        {
-                            Identity = await converter.Convert(input.Identity).ConfigureAwait(false),
-                            Order = converter.Order
-                        };
-
-                        if (!Equals(result.Identity, default(TIdentity)))
-                        {
-                            identities.Add(result);
-                            convertersAvailable.RemoveAt(i);
-                            changesMade = true;
-                        }
+                        changesMade = true;
                     }
                 }
-            } while (changesMade);
-
-            return identities.OrderBy(id => id.Order).Select(id => id.Identity);
-        }
-
-        private class IdentityPair
-        {
-            public TIdentity Identity;
-            public int Order;
+            }
         }
     }
 }

+ 5 - 10
MediaBrowser.Controller/Providers/ItemIdentities.cs

@@ -2,20 +2,15 @@
 
 namespace MediaBrowser.Controller.Providers
 {
-    public interface IItemIdentityProvider<in TLookupInfo, TIdentity> : IItemIdentityProvider
+    public interface IItemIdentityProvider<in TLookupInfo> : IItemIdentityProvider
         where TLookupInfo : ItemLookupInfo
-        where TIdentity : IItemIdentity
     {
-        Task<TIdentity> FindIdentity(TLookupInfo info);
+        Task Identify(TLookupInfo info);
     }
 
-    public interface IItemIdentityConverter<TIdentity> : IItemIdentityConverter
-        where TIdentity : IItemIdentity
+    public interface IItemIdentityConverter<in TLookupInfo> : IItemIdentityConverter
+        where TLookupInfo : ItemLookupInfo
     {
-        Task<TIdentity> Convert(TIdentity identity);
-
-        string SourceType { get; }
-
-        string ResultType { get; }
+        Task<bool> Convert(TLookupInfo info);
     }
 }

+ 0 - 11
MediaBrowser.Controller/Providers/SeasonIdentity.cs

@@ -1,11 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
-    public class SeasonIdentity : IItemIdentity
-    {
-        public string Type { get; set; }
-
-        public string SeriesId { get; set; }
-
-        public int SeasonIndex { get; set; }
-    }
-}

+ 1 - 17
MediaBrowser.Controller/Providers/SeasonInfo.cs

@@ -1,15 +1,10 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Providers
 {
-    public class SeasonInfo : ItemLookupInfo, IHasIdentities<SeasonIdentity>
+    public class SeasonInfo : ItemLookupInfo
     {
-        private List<SeasonIdentity> _identities = new List<SeasonIdentity>();
-
         public Dictionary<string, string> SeriesProviderIds { get; set; }
         public int? AnimeSeriesIndex { get; set; }
 
@@ -17,16 +12,5 @@ namespace MediaBrowser.Controller.Providers
         {
             SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
-
-        public IEnumerable<SeasonIdentity> Identities
-        {
-            get { return _identities; }
-        }
-
-        public async Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken)
-        {
-            var identifier = new ItemIdentifier<SeasonInfo, SeasonIdentity>();
-            _identities = (await identifier.FindIdentities(this, providerManager, cancellationToken)).ToList();
-        }
     }
 }

+ 0 - 9
MediaBrowser.Controller/Providers/SeriesIdentity.cs

@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.Providers
-{
-    public class SeriesIdentity : IItemIdentity
-    {
-        public string Type { get; set; }
-
-        public string Id { get; set; }
-    }
-}

+ 1 - 19
MediaBrowser.Controller/Providers/SeriesInfo.cs

@@ -1,25 +1,7 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
 namespace MediaBrowser.Controller.Providers
 {
-    public class SeriesInfo : ItemLookupInfo, IHasIdentities<SeriesIdentity>
+    public class SeriesInfo : ItemLookupInfo
     {
-        private List<SeriesIdentity> _identities = new List<SeriesIdentity>();
-
         public int? AnimeSeriesIndex { get; set; }
-
-        public IEnumerable<SeriesIdentity> Identities
-        {
-            get { return _identities; }
-        }
-
-        public async Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken)
-        {
-            var identifier = new ItemIdentifier<SeriesInfo, SeriesIdentity>();
-            _identities = (await identifier.FindIdentities(this, providerManager, cancellationToken)).ToList();
-        }
     }
 }

+ 3 - 33
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -141,7 +141,8 @@ namespace MediaBrowser.Providers.Manager
 
                 if (providers.Count > 0)
                 {
-                    var id = await CreateInitialLookupInfo(itemOfType, cancellationToken).ConfigureAwait(false);
+                    var id = itemOfType.GetLookupInfo();
+                    await ItemIdentifier<TIdType>.FindIdentities(id, ProviderManager, cancellationToken);
 
                     var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
 
@@ -155,8 +156,6 @@ namespace MediaBrowser.Providers.Manager
                     {
                         refreshResult.SetDateLastMetadataRefresh(null);
                     }
-
-                    MergeIdentities(itemOfType, id);
                 }
             }
 
@@ -223,16 +222,7 @@ namespace MediaBrowser.Providers.Manager
             item.AfterMetadataRefresh();
             return _cachedTask;
         }
-
-        private void MergeIdentities(TItemType item, TIdType id)
-        {
-            var hasIdentity = id as IHasIdentities<IItemIdentity>;
-            if (hasIdentity != null)
-            {
-                item.Identities = hasIdentity.Identities.ToList();
-            }
-        }
-
+        
         private readonly Task<ItemUpdateType> _cachedResult = Task.FromResult(ItemUpdateType.None);
         /// <summary>
         /// Befores the save.
@@ -623,26 +613,6 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private async Task<TIdType> CreateInitialLookupInfo(TItemType item, CancellationToken cancellationToken)
-        {
-            var info = item.GetLookupInfo();
-
-            var hasIdentity = info as IHasIdentities<IItemIdentity>;
-            if (hasIdentity != null)
-            {
-                try
-                {
-                    await hasIdentity.FindIdentities(ProviderManager, cancellationToken).ConfigureAwait(false);
-                }
-                catch (Exception ex)
-                {
-                    Logger.ErrorException("Error in identity providers", ex);
-                }
-            }
-
-            return info;
-        }
-
         private void MergeNewData(TItemType source, TIdType lookupInfo)
         {
             // Copy new provider id's that may have been obtained

+ 5 - 6
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -298,17 +298,16 @@ namespace MediaBrowser.Providers.Manager
                 .ThenBy(GetDefaultOrder);
         }
 
-        public IEnumerable<IItemIdentityProvider<TLookupInfo, TIdentity>> GetItemIdentityProviders<TLookupInfo, TIdentity>()
+        public IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>()
             where TLookupInfo : ItemLookupInfo
-            where TIdentity : IItemIdentity
         {
-            return _identityProviders.OfType<IItemIdentityProvider<TLookupInfo, TIdentity>>();
+            return _identityProviders.OfType<IItemIdentityProvider<TLookupInfo>>();
         }
 
-        public IEnumerable<IItemIdentityConverter<TIdentity>> GetItemIdentityConverters<TIdentity>()
-            where TIdentity : IItemIdentity
+        public IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>()
+            where TLookupInfo : ItemLookupInfo
         {
-            return _identityConverters.OfType<IItemIdentityConverter<TIdentity>>();
+            return _identityConverters.OfType<IItemIdentityConverter<TLookupInfo>>();
         }
 
         private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasImages item, bool includeDisabled)

+ 95 - 22
MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs

@@ -24,8 +24,11 @@ namespace MediaBrowser.Providers.TV
     /// <summary>
     /// Class RemoteEpisodeProvider
     /// </summary>
-    class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo, EpisodeIdentity>, IHasChangeMonitor
+    class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo>, IHasChangeMonitor
     {
+        private const string FullIdFormat = "{0}:{1}:{2}"; // seriesId:seasonIndex:episodeNumbers
+        private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full";
+
         internal static TvdbEpisodeProvider Current;
         private readonly IFileSystem _fileSystem;
         private readonly IServerConfigurationManager _config;
@@ -45,18 +48,24 @@ namespace MediaBrowser.Providers.TV
         {
             var list = new List<RemoteSearchResult>();
 
-            var identity = searchInfo.Identities.FirstOrDefault(id => id.Type == MetadataProviders.Tvdb.ToString()) ?? await FindIdentity(searchInfo).ConfigureAwait(false);
+            var identity = ParseIdentity(searchInfo.GetProviderId(FullIdKey));
+
+            if (identity == null)
+            {
+                await Identify(searchInfo).ConfigureAwait(false);
+                identity = ParseIdentity(searchInfo.GetProviderId(FullIdKey));
+            }
 
             if (identity != null)
             {
-                await TvdbSeriesProvider.Current.EnsureSeriesInfo(identity.SeriesId, searchInfo.MetadataLanguage,
+                await TvdbSeriesProvider.Current.EnsureSeriesInfo(identity.Value.SeriesId, searchInfo.MetadataLanguage,
                         cancellationToken).ConfigureAwait(false);
 
-                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.SeriesId);
+                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.Value.SeriesId);
 
                 try
                 {
-                    var metadataResult = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
+                    var metadataResult = FetchEpisodeData(searchInfo, identity.Value, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
 
                     if (metadataResult.HasMetadata)
                     {
@@ -95,17 +104,23 @@ namespace MediaBrowser.Providers.TV
 
         public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken)
         {
-            var identity = searchInfo.Identities.FirstOrDefault(id => id.Type == MetadataProviders.Tvdb.ToString()) ?? await FindIdentity(searchInfo).ConfigureAwait(false);
+            var identity = ParseIdentity(searchInfo.GetProviderId(FullIdKey));
+
+            if (identity == null)
+            {
+                await Identify(searchInfo).ConfigureAwait(false);
+                identity = ParseIdentity(searchInfo.GetProviderId(FullIdKey));
+            }
 
             var result = new MetadataResult<Episode>();
 
             if (identity != null)
             {
-                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.SeriesId);
+                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.Value.SeriesId);
 
                 try
                 {
-                    result = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
+                    result = FetchEpisodeData(searchInfo, identity.Value, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
                 }
                 catch (FileNotFoundException)
                 {
@@ -231,9 +246,9 @@ namespace MediaBrowser.Providers.TV
         /// <param name="seriesProviderIds">The series provider ids.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
+        private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, Identity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
         {
-            var episodeNumber = identity.IndexNumber;
+            var episodeNumber = identity.EpisodeNumber;
             var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(seriesProviderIds) ?? 0;
             var seasonNumber = identity.SeasonIndex + seasonOffset;
             
@@ -278,7 +293,7 @@ namespace MediaBrowser.Providers.TV
                 usingAbsoluteData = true;
             }
 
-            var end = identity.IndexNumberEnd ?? episodeNumber;
+            var end = identity.EpisodeNumberEnd ?? episodeNumber;
             episodeNumber++;
 
             while (episodeNumber <= end)
@@ -753,28 +768,86 @@ namespace MediaBrowser.Providers.TV
             });
         }
 
-        public Task<EpisodeIdentity> FindIdentity(EpisodeInfo info)
+        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<EpisodeIdentity>(null);
+                return Task.FromResult<object>(null);
             }
-            
-            var id = new EpisodeIdentity
-            {
-                Type = MetadataProviders.Tvdb.ToString(),
-                SeriesId = seriesTvdbId,
-                SeasonIndex = info.ParentIndexNumber,
-                IndexNumber = info.IndexNumber.Value,
-                IndexNumberEnd = info.IndexNumberEnd
-            };
+
+            var number = info.IndexNumber.Value.ToString();
+            if (info.IndexNumberEnd != null)
+                number += "-" + info.IndexNumberEnd;
+
+            var id = string.Format(
+                FullIdFormat,
+                seriesTvdbId,
+                info.ParentIndexNumber.HasValue ? info.ParentIndexNumber.Value.ToString() : "A",
+                number);
+
+            info.SetProviderId(FullIdKey, FullIdFormat);
 
             return Task.FromResult(id);
         }
 
+        private 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;
+            }
+        }
+
         public int Order { get { return 0; } }
+
+        private 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 seriesId, int? seasonIndex, int episodeNumber, int? episodeNumberEnd)
+            {
+                SeriesId = seriesId;
+                SeasonIndex = seasonIndex;
+                EpisodeNumber = episodeNumber;
+                EpisodeNumberEnd = episodeNumberEnd;
+            }
+        }
     }
 }

+ 34 - 10
MediaBrowser.Providers/TV/TvdbSeasonIdentityProvider.cs

@@ -4,26 +4,50 @@ using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Providers.TV
 {
-    public class TvdbSeasonIdentityProvider : IItemIdentityProvider<SeasonInfo, SeasonIdentity>
+    public class TvdbSeasonIdentityProvider : IItemIdentityProvider<SeasonInfo>
     {
-        public Task<SeasonIdentity> FindIdentity(SeasonInfo info)
+        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<SeasonIdentity>(null);
+                return Task.FromResult<object>(null);
             }
 
-            var result = new SeasonIdentity
+            if (string.IsNullOrEmpty(info.GetProviderId(FullIdKey)))
             {
-                Type = MetadataProviders.Tvdb.ToString(),
-                SeriesId = tvdbSeriesId,
-                SeasonIndex = info.IndexNumber.Value
-            };
+                var id = string.Format("{0}:{1}", tvdbSeriesId, info.IndexNumber.Value);
+                info.SetProviderId(FullIdKey, id);
+            }
+            
+            return Task.FromResult<object>(null);
+        }
 
-            return Task.FromResult(result);
+        public static TvdbSeasonIdentity? ParseIdentity(string id)
+        {
+            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 int Order { get { return 0; } }
+        public TvdbSeasonIdentity(string seriesId, int index)
+        {
+            SeriesId = seriesId;
+            Index = index;
+        }
     }
 }

+ 10 - 13
MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs

@@ -65,26 +65,23 @@ namespace MediaBrowser.Providers.TV
             var season = (Season)item;
             var series = season.Series;
 
-            var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tvdb) : null;
+            var identity = TvdbSeasonIdentityProvider.ParseIdentity(season.GetProviderId(TvdbSeasonIdentityProvider.FullIdKey));
+            if (identity == null && series != null && season.IndexNumber.HasValue)
+            {
+                identity = new TvdbSeasonIdentity(series.GetProviderId(MetadataProviders.Tvdb), season.IndexNumber.Value);
+            }
 
-            if (!string.IsNullOrEmpty(seriesId) && season.IndexNumber.HasValue)
+            if (identity != null && series != null)
             {
-                await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesId, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
+                var id = identity.Value;
+                await TvdbSeriesProvider.Current.EnsureSeriesInfo(id.SeriesId, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
 
                 // Process images
-                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesId);
+                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, id.SeriesId);
 
                 var path = Path.Combine(seriesDataPath, "banners.xml");
 
-                var identity = season.Identities.OfType<SeasonIdentity>()
-                    .FirstOrDefault(id => id.Type == MetadataProviders.Tvdb.ToString());
-
-                var seasonNumber = season.IndexNumber.Value;
-
-                if (identity != null)
-                {
-                    seasonNumber = AdjustForSeriesOffset(series, identity.SeasonIndex);
-                }
+                var seasonNumber = AdjustForSeriesOffset(series, id.Index);
                 
                 try
                 {

+ 9 - 30
MediaBrowser.Providers/TV/TvdbSeriesProvider.cs

@@ -25,7 +25,7 @@ using System.Xml;
 
 namespace MediaBrowser.Providers.TV
 {
-    public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IItemIdentityProvider<SeriesInfo, SeriesIdentity>, IHasOrder
+    public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IItemIdentityProvider<SeriesInfo>, IHasOrder
     {
         private const string TvdbSeriesOffset = "TvdbSeriesOffset";
         private const string TvdbSeriesOffsetFormat = "{0}-{1}";
@@ -94,24 +94,10 @@ namespace MediaBrowser.Providers.TV
 
             if (string.IsNullOrWhiteSpace(seriesId))
             {
-                seriesId = itemId.Identities
-                                 .Where(id => id.Type == MetadataProviders.Tvdb.ToString())
-                                 .Select(id => id.Id)
-                                 .FirstOrDefault();
-
-                if (string.IsNullOrWhiteSpace(seriesId))
-                {
-                    var srch = await GetSearchResults(itemId, cancellationToken).ConfigureAwait(false);
-
-                    var entry = srch.FirstOrDefault();
-
-                    if (entry != null)
-                    {
-                        seriesId = entry.GetProviderId(MetadataProviders.Tvdb);
-                    }
-                }
+                await Identify(itemId).ConfigureAwait(false);
+                seriesId = itemId.GetProviderId(MetadataProviders.Tvdb);
             }
-
+            
             cancellationToken.ThrowIfCancellationRequested();
 
             if (!string.IsNullOrWhiteSpace(seriesId))
@@ -1239,27 +1225,20 @@ namespace MediaBrowser.Providers.TV
             get { return "TheTVDB"; }
         }
 
-        public async Task<SeriesIdentity> FindIdentity(SeriesInfo info)
+        public async Task Identify(SeriesInfo info)
         {
-            string tvdbId;
-            if (!info.ProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out tvdbId))
+            if (string.IsNullOrEmpty(info.GetProviderId(MetadataProviders.Tvdb)))
             {
-                var srch = await GetSearchResults(info, CancellationToken.None).ConfigureAwait(false);
+                var srch = await FindSeries(info.Name, info.MetadataLanguage, CancellationToken.None).ConfigureAwait(false);
 
                 var entry = srch.FirstOrDefault();
 
                 if (entry != null)
                 {
-                    tvdbId = entry.GetProviderId(MetadataProviders.Tvdb);
+                    var id = entry.GetProviderId(MetadataProviders.Tvdb);
+                    info.SetProviderId(MetadataProviders.Tvdb, id);
                 }
             }
-
-            if (!string.IsNullOrWhiteSpace(tvdbId))
-            {
-                return new SeriesIdentity { Type = MetadataProviders.Tvdb.ToString(), Id = tvdbId };
-            }
-
-            return null;
         }
 
         public int Order