瀏覽代碼

add search methods to remote metadata providers

Luke Pulverenti 11 年之前
父節點
當前提交
13e4b2a6a7
共有 45 個文件被更改,包括 520 次插入162 次删除
  1. 0 26
      MediaBrowser.Controller/Entities/BaseItem.cs
  2. 0 3
      MediaBrowser.Controller/Entities/Folder.cs
  3. 8 0
      MediaBrowser.Controller/Library/DeleteOptions.cs
  4. 14 2
      MediaBrowser.Controller/Library/ILibraryManager.cs
  5. 1 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  6. 7 0
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  7. 14 0
      MediaBrowser.Controller/Providers/IProviderManager.cs
  8. 13 13
      MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
  9. 3 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  10. 3 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  11. 1 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  12. 39 0
      MediaBrowser.Model/Providers/RemoteSearchResult.cs
  13. 11 0
      MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
  14. 2 2
      MediaBrowser.Providers/Manager/ProviderManager.cs
  15. 13 2
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  16. 14 1
      MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs
  17. 14 3
      MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
  18. 11 0
      MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
  19. 11 0
      MediaBrowser.Providers/Music/LastfmAlbumProvider.cs
  20. 12 0
      MediaBrowser.Providers/Music/LastfmArtistProvider.cs
  21. 14 2
      MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs
  22. 12 0
      MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
  23. 14 1
      MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
  24. 22 1
      MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
  25. 16 16
      MediaBrowser.Providers/People/MovieDbPersonProvider.cs
  26. 19 9
      MediaBrowser.Providers/TV/FanartSeriesProvider.cs
  27. 6 3
      MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
  28. 12 1
      MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs
  29. 1 1
      MediaBrowser.Providers/TV/SeriesPostScanTask.cs
  30. 52 8
      MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
  31. 11 0
      MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
  32. 31 22
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  33. 8 6
      MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
  34. 5 3
      MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
  35. 6 4
      MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
  36. 2 1
      MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
  37. 0 1
      MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
  38. 11 6
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  39. 31 4
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  40. 46 13
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  41. 1 1
      MediaBrowser.ServerApplication/ApplicationHost.cs
  42. 4 2
      MediaBrowser.ServerApplication/Native/Autorun.cs
  43. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  44. 1 1
      Nuget/MediaBrowser.Common.nuspec
  45. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 0 - 26
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -912,32 +912,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Determine if we have changed vs the passed in copy
-        /// </summary>
-        /// <param name="copy">The copy.</param>
-        /// <returns><c>true</c> if the specified copy has changed; otherwise, <c>false</c>.</returns>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public virtual bool HasChanged(BaseItem copy)
-        {
-            if (copy == null)
-            {
-                throw new ArgumentNullException();
-            }
-            if (IsInMixedFolder != copy.IsInMixedFolder)
-            {
-                Logger.Debug(Name + " changed due to different value for IsInMixedFolder.");
-                return true;
-            }
-
-            var changed = copy.DateModified != DateModified;
-            if (changed)
-            {
-                Logger.Debug(Name + " changed - original creation: " + DateCreated + " new creation: " + copy.DateCreated + " original modified: " + DateModified + " new modified: " + copy.DateModified);
-            }
-            return changed;
-        }
-
         public virtual string GetClientTypeName()
         {
             return GetType().Name;

+ 0 - 3
MediaBrowser.Controller/Entities/Folder.cs

@@ -181,8 +181,6 @@ namespace MediaBrowser.Controller.Entities
 
             item.Parent = null;
 
-            LibraryManager.ReportItemRemoved(item);
-
             return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
         }
 
@@ -220,7 +218,6 @@ namespace MediaBrowser.Controller.Entities
                 {LocalizedStrings.Instance.GetString("GenreDispPref")},
                 {LocalizedStrings.Instance.GetString("DirectorDispPref")},
                 {LocalizedStrings.Instance.GetString("YearDispPref")},
-                //{LocalizedStrings.Instance.GetString("OfficialRatingDispPref"), null},
                 {LocalizedStrings.Instance.GetString("StudioDispPref")}
             };
 

+ 8 - 0
MediaBrowser.Controller/Library/DeleteOptions.cs

@@ -0,0 +1,8 @@
+
+namespace MediaBrowser.Controller.Library
+{
+    public class DeleteOptions
+    {
+        public bool DeleteFileLocation { get; set; }
+    }
+}

+ 14 - 2
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -41,7 +41,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <returns>BaseItem.</returns>
         BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null);
-        
+
         /// <summary>
         /// Resolves a set of files into a list of BaseItem
         /// </summary>
@@ -335,7 +335,19 @@ namespace MediaBrowser.Controller.Library
         /// Deletes the item.
         /// </summary>
         /// <param name="item">The item.</param>
+        /// <param name="options">The options.</param>
         /// <returns>Task.</returns>
-        Task DeleteItem(BaseItem item);
+        Task DeleteItem(BaseItem item, DeleteOptions options);
+    }
+
+    public static class LibraryManagerExtensions
+    {
+        public static Task DeleteItem(this ILibraryManager manager, BaseItem item)
+        {
+            return manager.DeleteItem(item, new DeleteOptions
+            {
+                DeleteFileLocation = true
+            });
+        }
     }
 }

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

@@ -111,6 +111,7 @@
     <Compile Include="Entities\IHasAwards.cs" />
     <Compile Include="Entities\Photo.cs" />
     <Compile Include="FileOrganization\IFileOrganizationService.cs" />
+    <Compile Include="Library\DeleteOptions.cs" />
     <Compile Include="Library\ILibraryPostScanTask.cs" />
     <Compile Include="Library\IMetadataSaver.cs" />
     <Compile Include="Library\ItemUpdateType.cs" />

+ 7 - 0
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -95,6 +95,13 @@ namespace MediaBrowser.Controller.Persistence
         /// <returns>IEnumerable{ChildDefinition}.</returns>
         IEnumerable<Guid> GetChildren(Guid parentId);
 
+        /// <summary>
+        /// Gets the type of the items of.
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <returns>IEnumerable{Guid}.</returns>
+        IEnumerable<BaseItem> GetItemsOfType(Type type);
+        
         /// <summary>
         /// Saves the children.
         /// </summary>

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

@@ -96,5 +96,19 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="item">The item.</param>
         /// <returns>MetadataOptions.</returns>
         MetadataOptions GetMetadataOptions(IHasImages item);
+
+        /// <summary>
+        /// Gets the remote search results.
+        /// </summary>
+        /// <typeparam name="TItemType">The type of the t item type.</typeparam>
+        /// <typeparam name="TLookupType">The type of the t lookup type.</typeparam>
+        /// <param name="searchInfo">The search information.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{IEnumerable{SearchResult{``1}}}.</returns>
+        Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(
+            RemoteSearchQuery<TLookupType> searchInfo,
+            CancellationToken cancellationToken)
+            where TItemType : BaseItem, new()
+            where TLookupType : ItemLookupInfo;
     }
 }

+ 13 - 13
MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Providers;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
@@ -9,29 +11,27 @@ namespace MediaBrowser.Controller.Providers
     {
     }
 
-    public interface IRemoteMetadataProvider<TItemType, TLookupInfoType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider
+    public interface IRemoteMetadataProvider<TItemType, in TLookupInfoType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider, IRemoteSearchProvider<TLookupInfoType>
         where TItemType : IHasMetadata, IHasLookupInfo<TLookupInfoType>
         where TLookupInfoType : ItemLookupInfo, new()
     {
         Task<MetadataResult<TItemType>> GetMetadata(TLookupInfoType info, CancellationToken cancellationToken);
     }
 
-    public interface IRemoteSearchProvider<TLookupInfoType>
+    public interface IRemoteSearchProvider<in TLookupInfoType> : IMetadataProvider
         where TLookupInfoType : ItemLookupInfo
     {
-        string Name { get; }
+        Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken);
 
-        Task<IEnumerable<SearchResult<TLookupInfoType>>> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken);
+        /// <summary>
+        /// Gets the image response.
+        /// </summary>
+        /// <param name="url">The URL.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{HttpResponseInfo}.</returns>
+        Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken);
     }
     
-    public class SearchResult<T>
-        where T : ItemLookupInfo
-    {
-        public T Item { get; set; }
-
-        public string ImageUrl { get; set; }
-    }
-
     public class RemoteSearchQuery<T>
         where T : ItemLookupInfo
     {

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -338,6 +338,9 @@
     <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageResult.cs">
       <Link>Providers\RemoteImageResult.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Providers\RemoteSearchResult.cs">
+      <Link>Providers\RemoteSearchResult.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Querying\ArtistsQuery.cs">
       <Link>Querying\ArtistsQuery.cs</Link>
     </Compile>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -325,6 +325,9 @@
     <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageResult.cs">
       <Link>Providers\RemoteImageResult.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Providers\RemoteSearchResult.cs">
+      <Link>Providers\RemoteSearchResult.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Querying\ArtistsQuery.cs">
       <Link>Querying\ArtistsQuery.cs</Link>
     </Compile>

+ 1 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -112,6 +112,7 @@
     <Compile Include="Notifications\NotificationResult.cs" />
     <Compile Include="Notifications\NotificationsSummary.cs" />
     <Compile Include="Providers\RemoteImageResult.cs" />
+    <Compile Include="Providers\RemoteSearchResult.cs" />
     <Compile Include="Querying\ArtistsQuery.cs" />
     <Compile Include="Querying\EpisodeQuery.cs" />
     <Compile Include="Querying\ItemCountsQuery.cs" />

+ 39 - 0
MediaBrowser.Model/Providers/RemoteSearchResult.cs

@@ -0,0 +1,39 @@
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.Providers
+{
+    public class RemoteSearchResult : IHasProviderIds
+    {
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public string Name { get; set; }
+        /// <summary>
+        /// Gets or sets the provider ids.
+        /// </summary>
+        /// <value>The provider ids.</value>
+        public Dictionary<string, string> ProviderIds { get; set; }
+        /// <summary>
+        /// Gets or sets the year.
+        /// </summary>
+        /// <value>The year.</value>
+        public int? ProductionYear { get; set; }
+        public int? IndexNumber { get; set; }
+        public int? IndexNumberEnd { get; set; }
+        public int? ParentIndexNumber { get; set; }
+
+        public DateTime? PremiereDate { get; set; }
+
+        public string ImageUrl { get; set; }
+
+        public string SearchProviderName { get; set; }
+
+        public RemoteSearchResult()
+        {
+            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        }
+    }
+}

+ 11 - 0
MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs

@@ -7,6 +7,7 @@ using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Providers.Movies;
 using System;
@@ -42,6 +43,11 @@ namespace MediaBrowser.Providers.BoxSets
             Current = this;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken)
         {
             var tmdbId = id.GetProviderId(MetadataProviders.Tmdb);
@@ -282,5 +288,10 @@ namespace MediaBrowser.Providers.BoxSets
             public List<Part> parts { get; set; }
             public Images images { get; set; }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 2 - 2
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -586,7 +586,7 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        public async Task<IEnumerable<SearchResult<TLookupType>>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo,
+        public async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo,
             CancellationToken cancellationToken)
             where TItemType : BaseItem, new()
             where TLookupType : ItemLookupInfo
@@ -623,7 +623,7 @@ namespace MediaBrowser.Providers.Manager
             }
 
             // Nothing found
-            return new List<SearchResult<TLookupType>>();
+            return new List<RemoteSearchResult>();
         }
     }
 }

+ 13 - 2
MediaBrowser.Providers/Movies/MovieDbProvider.cs

@@ -1,5 +1,4 @@
-using System.Linq;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
@@ -9,10 +8,12 @@ using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -45,6 +46,11 @@ namespace MediaBrowser.Providers.Movies
             Current = this;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken)
         {
             return GetItemMetadata<Movie>(info, cancellationToken);
@@ -549,5 +555,10 @@ namespace MediaBrowser.Providers.Movies
                 return 1;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 14 - 1
MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs

@@ -1,6 +1,9 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Providers;
 using System;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -13,6 +16,11 @@ namespace MediaBrowser.Providers.Movies
             return MovieDbProvider.Current.GetItemMetadata<Trailer>(info, cancellationToken);
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public string Name
         {
             get { return MovieDbProvider.Current.Name; }
@@ -31,5 +39,10 @@ namespace MediaBrowser.Providers.Movies
                 return 1;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 14 - 3
MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
@@ -15,7 +16,7 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Providers.Music
 {
-    public class AudioDbAlbumProvider : IRemoteMetadataProvider<MusicAlbum,AlbumInfo>, IHasOrder
+    public class AudioDbAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder
     {
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
@@ -36,6 +37,11 @@ namespace MediaBrowser.Providers.Music
             Current = this;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<MusicAlbum>();
@@ -81,7 +87,7 @@ namespace MediaBrowser.Providers.Music
             item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
             item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, result.strMusicBrainzID);
         }
-        
+
         public string Name
         {
             get { return "TheAudioDB"; }
@@ -112,7 +118,7 @@ namespace MediaBrowser.Providers.Music
             var url = AudioDbArtistProvider.BaseUrl + "/album-mb.php?i=" + musicBrainzReleaseGroupId;
 
             var path = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId);
-            
+
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
             using (var response = await _httpClient.Get(new HttpRequestOptions
@@ -206,5 +212,10 @@ namespace MediaBrowser.Providers.Music
         {
             public List<Album> album { get; set; }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 11 - 0
MediaBrowser.Providers/Music/AudioDbArtistProvider.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
@@ -36,6 +37,11 @@ namespace MediaBrowser.Providers.Music
             Current = this;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<MusicArtist>();
@@ -213,5 +219,10 @@ namespace MediaBrowser.Providers.Music
                 return 1;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 11 - 0
MediaBrowser.Providers/Music/LastfmAlbumProvider.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using MoreLinq;
 using System;
@@ -32,6 +33,11 @@ namespace MediaBrowser.Providers.Music
             _logger = logger;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<MusicAlbum>();
@@ -204,6 +210,11 @@ namespace MediaBrowser.Providers.Music
                 return 2;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 
     #region Result Objects

+ 12 - 0
MediaBrowser.Providers/Music/LastfmArtistProvider.cs

@@ -5,8 +5,10 @@ using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -38,6 +40,11 @@ namespace MediaBrowser.Providers.Music
             _logger = logger;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<MusicArtist>();
@@ -170,5 +177,10 @@ namespace MediaBrowser.Providers.Music
                 return 2;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 14 - 2
MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs

@@ -1,8 +1,10 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Providers.Movies;
 using System;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -15,6 +17,11 @@ namespace MediaBrowser.Providers.Music
             return MovieDbProvider.Current.GetItemMetadata<MusicVideo>(info, cancellationToken);
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MusicVideoInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public string Name
         {
             get { return MovieDbProvider.Current.Name; }
@@ -24,5 +31,10 @@ namespace MediaBrowser.Providers.Music
         {
             return MovieDbProvider.Current.HasChanged(item, date);
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 12 - 0
MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs

@@ -3,7 +3,9 @@ using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Net;
 using System.Text;
@@ -27,6 +29,11 @@ namespace MediaBrowser.Providers.Music
             Current = this;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken)
         {
             var releaseId = id.GetReleaseId();
@@ -232,5 +239,10 @@ namespace MediaBrowser.Providers.Music
         {
             get { return 0; }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 14 - 1
MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs

@@ -1,7 +1,10 @@
-using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Net;
@@ -14,6 +17,11 @@ namespace MediaBrowser.Providers.Music
 {
     public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>
     {
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<MusicArtist>();
@@ -115,5 +123,10 @@ namespace MediaBrowser.Providers.Music
         {
             get { return "MusicBrainz"; }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 22 - 1
MediaBrowser.Providers/Omdb/OmdbItemProvider.cs

@@ -5,11 +5,12 @@ using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Providers.Movies;
 using MediaBrowser.Providers.TV;
 using System;
-using System.Globalization;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -29,6 +30,21 @@ namespace MediaBrowser.Providers.Omdb
             _logger = logger;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public string Name
         {
             get { return "IMDb via The Open Movie Database"; }
@@ -139,5 +155,10 @@ namespace MediaBrowser.Providers.Omdb
 
             return new Tuple<string, string>(imdb, tvdb);
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 16 - 16
MediaBrowser.Providers/People/MovieDbPersonProvider.cs

@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Providers.Movies;
 using System;
@@ -42,7 +43,7 @@ namespace MediaBrowser.Providers.People
             get { return "TheMovieDb"; }
         }
 
-        public async Task<IEnumerable<SearchResult<PersonLookupInfo>>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken)
         {
             var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb);
 
@@ -59,18 +60,15 @@ namespace MediaBrowser.Providers.People
 
                 var images = (info.images ?? new Images()).profiles ?? new List<Profile>();
 
-                var result = new SearchResult<PersonLookupInfo>
+                var result = new RemoteSearchResult
                 {
-                    Item = new PersonLookupInfo
-                    {
-                        Name = info.name
-                    },
+                    Name = info.name,
 
                     ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].file_path)
                 };
 
-                result.Item.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture));
-                result.Item.SetProviderId(MetadataProviders.Imdb, info.imdb_id.ToString(_usCulture));
+                result.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture));
+                result.SetProviderId(MetadataProviders.Imdb, info.imdb_id.ToString(_usCulture));
 
                 return new[] { result };
             }
@@ -92,19 +90,16 @@ namespace MediaBrowser.Providers.People
             }
         }
 
-        private SearchResult<PersonLookupInfo> GetSearchResult(PersonSearchResult i, string baseImageUrl)
+        private RemoteSearchResult GetSearchResult(PersonSearchResult i, string baseImageUrl)
         {
-            var result = new SearchResult<PersonLookupInfo>
+            var result = new RemoteSearchResult
             {
-                Item = new PersonLookupInfo
-                {
-                    Name = i.Name
-                },
+                Name = i.Name,
 
                 ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : (baseImageUrl + i.Profile_Path)
             };
 
-            result.Item.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture));
+            result.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture));
 
             return result;
         }
@@ -175,7 +170,7 @@ namespace MediaBrowser.Providers.People
         {
             var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
 
-            return results.Select(i => i.Item.GetProviderId(MetadataProviders.Tmdb)).FirstOrDefault();
+            return results.Select(i => i.GetProviderId(MetadataProviders.Tmdb)).FirstOrDefault();
         }
 
         internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken)
@@ -351,5 +346,10 @@ namespace MediaBrowser.Providers.People
         }
 
         #endregion
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 19 - 9
MediaBrowser.Providers/TV/FanartSeriesProvider.cs

@@ -367,22 +367,32 @@ namespace MediaBrowser.Providers.TV
             return Path.Combine(dataPath, "fanart.xml");
         }
 
-        private readonly Task _cachedTask = Task.FromResult(true);
-        internal Task EnsureSeriesXml(string tvdbId, CancellationToken cancellationToken)
+        private readonly SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1, 1);
+        internal async Task EnsureSeriesXml(string tvdbId, CancellationToken cancellationToken)
         {
-            var xmlPath = GetSeriesDataPath(_config.ApplicationPaths, tvdbId);
+            var xmlPath = GetFanartXmlPath(tvdbId);
 
-            var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath);
+            // Only allow one thread in here at a time since every season will be calling this method, possibly concurrently
+            await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 
-            if (fileInfo.Exists)
+            try
             {
-                if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
+                var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath);
+
+                if (fileInfo.Exists)
                 {
-                    return _cachedTask;
+                    if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
+                    {
+                        return;
+                    }
                 }
-            }
 
-            return DownloadSeriesXml(tvdbId, cancellationToken);
+                await DownloadSeriesXml(tvdbId, cancellationToken).ConfigureAwait(false);
+            }
+            finally
+            {
+                _ensureSemaphore.Release();
+            }
         }
 
         /// <summary>

+ 6 - 3
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
@@ -20,13 +21,15 @@ namespace MediaBrowser.Providers.TV
     {
         private readonly IServerConfigurationManager _config;
         private readonly ILogger _logger;
+        private readonly ILibraryManager _libraryManager;
 
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
-        public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config)
+        public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager)
         {
             _logger = logger;
             _config = config;
+            _libraryManager = libraryManager;
         }
 
         public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken)
@@ -268,7 +271,7 @@ namespace MediaBrowser.Providers.TV
             {
                 _logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber);
 
-                await episodeToRemove.Parent.RemoveChild(episodeToRemove, cancellationToken).ConfigureAwait(false);
+                await _libraryManager.DeleteItem(episodeToRemove).ConfigureAwait(false);
 
                 hasChanges = true;
             }
@@ -327,7 +330,7 @@ namespace MediaBrowser.Providers.TV
             {
                 _logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
 
-                await seasonToRemove.Parent.RemoveChild(seasonToRemove, cancellationToken).ConfigureAwait(false);
+                await _libraryManager.DeleteItem(seasonToRemove).ConfigureAwait(false);
 
                 hasChanges = true;
             }

+ 12 - 1
MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs

@@ -8,15 +8,16 @@ using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Providers.Movies;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using System.Linq;
 
 namespace MediaBrowser.Providers.TV
 {
@@ -48,6 +49,11 @@ namespace MediaBrowser.Providers.TV
             get { return "TheMovieDb"; }
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<Series>();
@@ -453,5 +459,10 @@ namespace MediaBrowser.Providers.TV
                 return 2;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 1 - 1
MediaBrowser.Providers/TV/SeriesPostScanTask.cs

@@ -45,7 +45,7 @@ namespace MediaBrowser.Providers.TV
                                group series by tvdbId into g
                                select g;
 
-            await new MissingEpisodeProvider(_logger, _config).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
+            await new MissingEpisodeProvider(_logger, _config, _libraryManager).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
 
             var numComplete = 0;
 

+ 52 - 8
MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs

@@ -1,9 +1,11 @@
 using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -34,17 +36,54 @@ namespace MediaBrowser.Providers.TV
             Current = this;
         }
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
+        {
+            var list = new List<RemoteSearchResult>();
+
+            string seriesTvdbId;
+            searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesTvdbId);
+
+            if (!string.IsNullOrEmpty(seriesTvdbId))
+            {
+                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesTvdbId);
+
+                try
+                {
+                    var item = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
+
+                    if (item != null)
+                    {
+                        list.Add(new RemoteSearchResult
+                        {
+                            IndexNumber = item.IndexNumber,
+                            Name = item.Name,
+                            ParentIndexNumber = item.ParentIndexNumber,
+                            PremiereDate = item.PremiereDate,
+                            ProductionYear = item.ProductionYear,
+                            ProviderIds = item.ProviderIds,
+                            SearchProviderName = Name,
+                            IndexNumberEnd = item.IndexNumberEnd
+                        });
+                    }
+                }
+                catch (FileNotFoundException)
+                {
+                    // Don't fail the provider because this will just keep on going and going.
+                }
+            }
+
+            return list;
+        }
+
         public string Name
         {
             get { return "TheTVDB"; }
         }
 
-        public Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo id, CancellationToken cancellationToken)
+        public Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken)
         {
-            var episodeId = id;
-
             string seriesTvdbId;
-            episodeId.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesTvdbId);
+            searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesTvdbId);
 
             var result = new MetadataResult<Episode>();
 
@@ -54,7 +93,7 @@ namespace MediaBrowser.Providers.TV
 
                 try
                 {
-                    result.Item = FetchEpisodeData(episodeId, seriesDataPath, cancellationToken);
+                    result.Item = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
                     result.HasMetadata = result.Item != null;
                 }
                 catch (FileNotFoundException)
@@ -192,9 +231,9 @@ namespace MediaBrowser.Providers.TV
 
             var episode = new Episode
             {
-                 IndexNumber = id.IndexNumber,
-                 ParentIndexNumber = id.ParentIndexNumber,
-                 IndexNumberEnd = id.IndexNumberEnd
+                IndexNumber = id.IndexNumber,
+                ParentIndexNumber = id.ParentIndexNumber,
+                IndexNumberEnd = id.IndexNumberEnd
             };
 
             try
@@ -674,5 +713,10 @@ namespace MediaBrowser.Providers.TV
                 }
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 11 - 0
MediaBrowser.Providers/TV/TvdbSeriesProvider.cs

@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -47,6 +48,11 @@ namespace MediaBrowser.Providers.TV
         private const string SeriesQuery = "GetSeries.php?seriesname={0}";
         private const string SeriesGetZip = "http://www.thetvdb.com/api/{0}/series/{1}/all/{2}.zip";
 
+        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
+        {
+            return new List<RemoteSearchResult>();
+        }
+
         public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo itemId, CancellationToken cancellationToken)
         {
             var result = new MetadataResult<Series>();
@@ -1081,5 +1087,10 @@ namespace MediaBrowser.Providers.TV
                 return 1;
             }
         }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 31 - 22
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -2,7 +2,6 @@
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
@@ -28,10 +27,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         private readonly IFileSystem _fileSystem;
         private readonly IFileOrganizationService _organizationService;
         private readonly IServerConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor)
+        public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager)
         {
             _organizationService = organizationService;
             _config = config;
@@ -39,9 +39,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             _logger = logger;
             _libraryManager = libraryManager;
             _libraryMonitor = libraryMonitor;
+            _providerManager = providerManager;
         }
 
-        public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting)
+        public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting, CancellationToken cancellationToken)
         {
             _logger.Info("Sorting file {0}", path);
 
@@ -77,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
                         result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
 
-                        OrganizeEpisode(path, seriesName, season.Value, episode.Value, endingEpisodeNumber, options, overwriteExisting, result);
+                        await OrganizeEpisode(path, seriesName, season.Value, episode.Value, endingEpisodeNumber, options, overwriteExisting, result, cancellationToken).ConfigureAwait(false);
                     }
                     else
                     {
@@ -119,20 +120,20 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             return result;
         }
 
-        public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, TvFileOrganizationOptions options)
+        public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, TvFileOrganizationOptions options, CancellationToken cancellationToken)
         {
             var result = _organizationService.GetResult(request.ResultId);
 
             var series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
 
-            OrganizeEpisode(result.OriginalPath, series, request.SeasonNumber, request.EpisodeNumber, request.EndingEpisodeNumber, _config.Configuration.TvFileOrganizationOptions, true, result);
+            await OrganizeEpisode(result.OriginalPath, series, request.SeasonNumber, request.EpisodeNumber, request.EndingEpisodeNumber, _config.Configuration.TvFileOrganizationOptions, true, result, cancellationToken).ConfigureAwait(false);
 
             await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
 
             return result;
         }
 
-        private void OrganizeEpisode(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result)
+        private Task OrganizeEpisode(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
         {
             var series = GetMatchingSeries(seriesName, result);
 
@@ -142,18 +143,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 result.Status = FileSortingStatus.Failure;
                 result.StatusMessage = msg;
                 _logger.Warn(msg);
-                return;
+                return Task.FromResult(true);
             }
 
-            OrganizeEpisode(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, overwriteExisting, result);
+            return OrganizeEpisode(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, overwriteExisting, result, cancellationToken);
         }
 
-        private void OrganizeEpisode(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result)
+        private async Task OrganizeEpisode(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
         {
             _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
 
             // Proceed to sort the file
-            var newPath = GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options);
+            var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, cancellationToken).ConfigureAwait(false);
 
             if (string.IsNullOrEmpty(newPath))
             {
@@ -326,25 +327,33 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         /// <param name="endingEpisodeNumber">The ending episode number.</param>
         /// <param name="options">The options.</param>
         /// <returns>System.String.</returns>
-        private string GetNewPath(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, TvFileOrganizationOptions options)
+        private async Task<string> GetNewPath(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, TvFileOrganizationOptions options, CancellationToken cancellationToken)
         {
-            // If season and episode numbers match
-            var currentEpisodes = series.RecursiveChildren.OfType<Episode>()
-                .Where(i => i.IndexNumber.HasValue &&
-                            i.IndexNumber.Value == episodeNumber &&
-                            i.ParentIndexNumber.HasValue &&
-                            i.ParentIndexNumber.Value == seasonNumber)
-                .ToList();
+            var episodeInfo = new EpisodeInfo
+            {
+                IndexNumber = episodeNumber,
+                IndexNumberEnd = endingEpisodeNumber,
+                MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
+                MetadataLanguage = series.GetPreferredMetadataLanguage(),
+                ParentIndexNumber = seasonNumber,
+                SeriesProviderIds = series.ProviderIds
+            };
 
-            if (currentEpisodes.Count == 0)
+            var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
+            {
+                SearchInfo = episodeInfo
+
+            }, cancellationToken).ConfigureAwait(false);
+
+            var episode = searchResults.FirstOrDefault();
+
+            if (episode == null)
             {
                 return null;
             }
 
             var newPath = GetSeasonFolderPath(series, seasonNumber, options);
 
-            var episode = currentEpisodes.First();
-
             var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber, episodeNumber, endingEpisodeNumber, episode.Name, options);
 
             newPath = Path.Combine(newPath, episodeFileName);

+ 8 - 6
MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs

@@ -3,9 +3,9 @@ using MediaBrowser.Common.IO;
 using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
@@ -25,8 +25,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         private readonly ILibraryManager _libraryManager;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
+        private readonly IProviderManager _providerManager;
 
-        public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem)
+        public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
         {
             _taskManager = taskManager;
             _repo = repo;
@@ -35,6 +36,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             _libraryManager = libraryManager;
             _config = config;
             _fileSystem = fileSystem;
+            _providerManager = providerManager;
         }
 
         public void BeginProcessNewFiles()
@@ -103,9 +105,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
 
             var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
-                _libraryMonitor);
+                _libraryMonitor, _providerManager);
 
-            await organizer.OrganizeEpisodeFile(result.OriginalPath, _config.Configuration.TvFileOrganizationOptions, true)
+            await organizer.OrganizeEpisodeFile(result.OriginalPath, _config.Configuration.TvFileOrganizationOptions, true, CancellationToken.None)
                     .ConfigureAwait(false);
         }
 
@@ -117,9 +119,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
         {
             var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
-                _libraryMonitor);
+                _libraryMonitor, _providerManager);
 
-            await organizer.OrganizeWithCorrection(request, _config.Configuration.TvFileOrganizationOptions).ConfigureAwait(false);
+            await organizer.OrganizeWithCorrection(request, _config.Configuration.TvFileOrganizationOptions, CancellationToken.None).ConfigureAwait(false);
         }
     }
 }

+ 5 - 3
MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs

@@ -2,8 +2,8 @@
 using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -20,8 +20,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         private readonly IFileSystem _fileSystem;
         private readonly IServerConfigurationManager _config;
         private readonly IFileOrganizationService _organizationService;
+        private readonly IProviderManager _providerManager;
 
-        public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService)
+        public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService, IProviderManager providerManager)
         {
             _libraryMonitor = libraryMonitor;
             _libraryManager = libraryManager;
@@ -29,6 +30,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             _fileSystem = fileSystem;
             _config = config;
             _organizationService = organizationService;
+            _providerManager = providerManager;
         }
 
         public string Name
@@ -48,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
         public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         {
-            return new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config)
+            return new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config, _providerManager)
                 .Organize(_config.Configuration.TvFileOrganizationOptions, cancellationToken, progress);
         }
 

+ 6 - 4
MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs

@@ -1,8 +1,8 @@
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.FileOrganization;
@@ -24,8 +24,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         private readonly IFileSystem _fileSystem;
         private readonly IFileOrganizationService _organizationService;
         private readonly IServerConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config)
+        public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config, IProviderManager providerManager)
         {
             _libraryManager = libraryManager;
             _logger = logger;
@@ -33,6 +34,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             _libraryMonitor = libraryMonitor;
             _organizationService = organizationService;
             _config = config;
+            _providerManager = providerManager;
         }
 
         public async Task Organize(TvFileOrganizationOptions options, CancellationToken cancellationToken, IProgress<double> progress)
@@ -57,9 +59,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 foreach (var file in eligibleFiles)
                 {
                     var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager,
-                        _libraryMonitor);
+                        _libraryMonitor, _providerManager);
 
-                    var result = await organizer.OrganizeEpisodeFile(file.FullName, options, false).ConfigureAwait(false);
+                    var result = await organizer.OrganizeEpisodeFile(file.FullName, options, false, cancellationToken).ConfigureAwait(false);
 
                     if (result.Status == FileSortingStatus.Success)
                     {

+ 2 - 1
MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -1,4 +1,5 @@
-using Funq;
+using System.Net.Sockets;
+using Funq;
 using MediaBrowser.Common;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;

+ 0 - 1
MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs

@@ -3,7 +3,6 @@ using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Server.Implementations.ScheduledTasks;
 using Microsoft.Win32;

+ 11 - 6
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -408,8 +408,14 @@ namespace MediaBrowser.Server.Implementations.Library
             LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
         }
 
-        public async Task DeleteItem(BaseItem item)
+        public async Task DeleteItem(BaseItem item, DeleteOptions options)
         {
+            _logger.Debug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                item.GetType().Name,
+                item.Name,
+                item.Path ?? string.Empty,
+                item.Id);
+
             var parent = item.Parent;
 
             var locationType = item.LocationType;
@@ -436,7 +442,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 }
             }
 
-            if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
+            if (options.DeleteFileLocation && (locationType == LocationType.FileSystem || locationType == LocationType.Offline))
             {
                 foreach (var path in item.GetDeletePaths().ToList())
                 {
@@ -462,15 +468,14 @@ namespace MediaBrowser.Server.Implementations.Library
             {
                 await parent.RemoveChild(item, CancellationToken.None).ConfigureAwait(false);
             }
-            else
-            {
-                throw new InvalidOperationException("Don't know how to delete " + item.Name);
-            }
 
+            await ItemRepository.DeleteItem(item.Id, CancellationToken.None).ConfigureAwait(false);
             foreach (var child in children)
             {
                 await ItemRepository.DeleteItem(child.Id, CancellationToken.None).ConfigureAwait(false);
             }
+
+            ReportItemRemoved(item);
         }
 
         private IEnumerable<string> GetMetadataPaths(BaseItem item, IEnumerable<BaseItem> children)

+ 31 - 4
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -726,7 +726,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             foreach (var channelInfo in allChannelsList)
             {
                 cancellationToken.ThrowIfCancellationRequested();
-                
+
                 try
                 {
                     var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, cancellationToken).ConfigureAwait(false);
@@ -764,7 +764,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             foreach (var item in list)
             {
                 cancellationToken.ThrowIfCancellationRequested();
-                
+
                 // Avoid implicitly captured closure
                 var currentChannel = item;
 
@@ -793,17 +793,44 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 double percent = numComplete;
                 percent /= allChannelsList.Count;
 
-                progress.Report(80 * percent + 10);
+                progress.Report(70 * percent + 10);
             }
 
             _programs = programs.ToDictionary(i => i.Id);
+            progress.Report(80);
 
             // Load these now which will prefetch metadata
             await GetRecordings(new RecordingQuery(), cancellationToken).ConfigureAwait(false);
-            
+            progress.Report(85);
+
+            await DeleteOldPrograms(_programs.Keys.ToList(), progress, cancellationToken).ConfigureAwait(false);
+
             progress.Report(100);
         }
 
+        private async Task DeleteOldPrograms(List<Guid> currentIdList, IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var list = _itemRepo.GetItemsOfType(typeof(LiveTvProgram)).ToList();
+
+            var numComplete = 0;
+
+            foreach (var program in list)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (!currentIdList.Contains(program.Id))
+                {
+                    await _libraryManager.DeleteItem(program).ConfigureAwait(false);
+                }
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= list.Count;
+
+                progress.Report(15 * percent + 85);
+            }
+        }
+
         private double GetGuideDays(int channelCount)
         {
             if (_config.Configuration.LiveTvOptions.GuideDays.HasValue)

+ 46 - 13
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -281,25 +281,30 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 {
                     if (reader.Read())
                     {
-                        var typeString = reader.GetString(0);
+                        return GetItem(reader);
+                    }
+                }
+                return null;
+            }
+        }
 
-                        var type = _typeMapper.GetType(typeString);
+        private BaseItem GetItem(IDataReader reader)
+        {
+            var typeString = reader.GetString(0);
 
-                        if (type == null)
-                        {
-                            _logger.Debug("Unknown type {0}", typeString);
+            var type = _typeMapper.GetType(typeString);
 
-                            return null;
-                        }
+            if (type == null)
+            {
+                _logger.Debug("Unknown type {0}", typeString);
 
-                        using (var stream = reader.GetMemoryStream(1))
-                        {
-                            return _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
-                        }
-                    }
-                }
                 return null;
             }
+
+            using (var stream = reader.GetMemoryStream(1))
+            {
+                return _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
+            }
         }
 
         /// <summary>
@@ -468,6 +473,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
         }
 
+        public IEnumerable<BaseItem> GetItemsOfType(Type type)
+        {
+            if (type == null)
+            {
+                throw new ArgumentNullException("type");
+            }
+
+            using (var cmd = _connection.CreateCommand())
+            {
+                cmd.CommandText = "select type,data from TypedBaseItems where type = @type";
+
+                cmd.Parameters.Add(cmd, "@type", DbType.String).Value = type.FullName;
+
+                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+                {
+                    while (reader.Read())
+                    {
+                        var item = GetItem(reader);
+
+                        if (item != null)
+                        {
+                            yield return item;
+                        }
+                    }
+                }
+            }
+        }
+
         public async Task DeleteItem(Guid id, CancellationToken cancellationToken)
         {
             if (id == Guid.Empty)

+ 1 - 1
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -363,7 +363,7 @@ namespace MediaBrowser.ServerApplication
             var newsService = new Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
             RegisterSingleInstance<INewsService>(newsService);
 
-            var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, Logger, LibraryMonitor, LibraryManager, ServerConfigurationManager, FileSystemManager);
+            var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, Logger, LibraryMonitor, LibraryManager, ServerConfigurationManager, FileSystemManager, ProviderManager);
             RegisterSingleInstance<IFileOrganizationService>(fileOrganizationService);
 
             progress.Report(15);

+ 4 - 2
MediaBrowser.ServerApplication/Native/Autorun.cs

@@ -16,15 +16,17 @@ namespace MediaBrowser.ServerApplication.Native
         {
             var shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk");
 
+            var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
+
             if (autorun)
             {
                 //Copy our shortut into the startup folder for this user
-                File.Copy(shortcutPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"), true);
+                File.Copy(shortcutPath, Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"), true);
             }
             else
             {
                 //Remove our shortcut from the startup folder for this user
-                File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"));
+                File.Delete(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"));
             }
         }
     }

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.329</version>
+        <version>3.0.330</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.329" />
+            <dependency id="MediaBrowser.Common" version="3.0.330" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="SimpleInjector" version="2.4.1" />
             <dependency id="sharpcompress" version="0.10.2" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.329</version>
+        <version>3.0.330</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.329</version>
+        <version>3.0.330</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.329" />
+            <dependency id="MediaBrowser.Common" version="3.0.330" />
         </dependencies>
     </metadata>
     <files>