From cc5b2c3e40793e4be1d7739b1531d6a5c44696fb Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 7 Jul 2012 16:52:04 -0700 Subject: [PATCH 1/6] Initial commit for Metadata --- .../Providers/Metadata/MetadataBase.cs | 52 ++++++++ NzbDrone.Core/Providers/MetadataProvider.cs | 121 ++++++++++++++++++ .../Repository/MetabaseDefinition.cs | 17 +++ 3 files changed, 190 insertions(+) create mode 100644 NzbDrone.Core/Providers/Metadata/MetadataBase.cs create mode 100644 NzbDrone.Core/Providers/MetadataProvider.cs create mode 100644 NzbDrone.Core/Repository/MetabaseDefinition.cs diff --git a/NzbDrone.Core/Providers/Metadata/MetadataBase.cs b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs new file mode 100644 index 000000000..c312f4c0f --- /dev/null +++ b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs @@ -0,0 +1,52 @@ +using System; +using NLog; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Providers.Metadata +{ + public abstract class MetadataBase + { + protected readonly Logger _logger; + protected readonly ConfigProvider _configProvider; + + protected MetadataBase(ConfigProvider configProvider) + { + _configProvider = configProvider; + _logger = LogManager.GetLogger(GetType().ToString()); + } + + /// + /// Gets the name for the notification provider + /// + public abstract string Name { get; } + + /// + /// Performs the on grab action + /// + /// The message to send to the receiver + public abstract void OnGrab(string message); + + /// + /// Performs the on download action + /// + /// The message to send to the receiver + /// The Series for the new download + public abstract void OnDownload(string message, Series series); + + /// + /// Performs the on rename action + /// + /// The message to send to the receiver + /// The Series for the new download + public abstract void OnRename(string message, Series series); + + /// + /// Performs the after rename action, this will be handled after all renaming for episode/season/series + /// + /// The message to send to the receiver + /// The Series for the new download + public abstract void AfterRename(string message, Series series); + } +} diff --git a/NzbDrone.Core/Providers/MetadataProvider.cs b/NzbDrone.Core/Providers/MetadataProvider.cs new file mode 100644 index 000000000..b5200167c --- /dev/null +++ b/NzbDrone.Core/Providers/MetadataProvider.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ninject; +using NLog; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.ExternalNotification; +using NzbDrone.Core.Providers.Metadata; +using NzbDrone.Core.Repository; +using PetaPoco; + +namespace NzbDrone.Core.Providers +{ + public class MetadataProvider + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private readonly IDatabase _database; + + private IEnumerable _metadataBases; + + [Inject] + public MetadataProvider(IDatabase database, IEnumerable metadataBases) + { + _database = database; + _metadataBases = metadataBases; + } + + public MetadataProvider() + { + + } + + public virtual List All() + { + return _database.Fetch(); + } + + public virtual void SaveSettings(MetabaseDefinition settings) + { + if (settings.Id == 0) + { + Logger.Debug("Adding Metabase definition for {0}", settings.Name); + _database.Insert(settings); + } + + else + { + Logger.Debug("Updating Metabase definition for {0}", settings.Name); + _database.Update(settings); + } + } + + public virtual MetabaseDefinition GetSettings(Type type) + { + return _database.SingleOrDefault("WHERE MetadataProviderType = @0", type.ToString()); + } + + public virtual IList GetEnabledExternalNotifiers() + { + var all = All(); + return _metadataBases.Where(i => all.Exists(c => c.MetadataProviderType == i.GetType().ToString() && c.Enable)).ToList(); + } + + public virtual void InitializeNotifiers(IList notifiers) + { + Logger.Debug("Initializing notifiers. Count {0}", notifiers.Count); + + _metadataBases = notifiers; + + var currentNotifiers = All(); + + foreach (var notificationProvider in notifiers) + { + MetadataBase metadataProviderLocal = notificationProvider; + if (!currentNotifiers.Exists(c => c.MetadataProviderType == metadataProviderLocal.GetType().ToString())) + { + var settings = new MetabaseDefinition + { + Enable = false, + MetadataProviderType = metadataProviderLocal.GetType().ToString(), + Name = metadataProviderLocal.Name + }; + + SaveSettings(settings); + } + } + } + + public virtual void OnGrab(string message) + { + foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnGrab(message); + } + } + + public virtual void OnDownload(string message, Series series) + { + foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnDownload(message, series); + } + } + + public virtual void OnRename(string message, Series series) + { + foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnRename(message, series); + } + } + + public virtual void AfterRename(string message, Series series) + { + foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.AfterRename(message, series); + } + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Repository/MetabaseDefinition.cs b/NzbDrone.Core/Repository/MetabaseDefinition.cs new file mode 100644 index 000000000..fce0e67b2 --- /dev/null +++ b/NzbDrone.Core/Repository/MetabaseDefinition.cs @@ -0,0 +1,17 @@ +using PetaPoco; + +namespace NzbDrone.Core.Repository +{ + [TableName("MetabaseDefinitions")] + [PrimaryKey("Id", autoIncrement = true)] + public class MetabaseDefinition + { + public int Id { get; set; } + + public bool Enable { get; set; } + + public string MetadataProviderType { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file From b50e16a4560e475a67393ac050ca3dd044263a71 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 9 Jul 2012 21:37:24 -0700 Subject: [PATCH 2/6] Metadata coming together for XBMC --- NzbDrone.Common/DiskProvider.cs | 4 + .../Datastore/Migrations/Migration20120707.cs | 21 ++ NzbDrone.Core/Model/MisnamedEpisodeModel.cs | 2 +- NzbDrone.Core/Providers/BannerProvider.cs | 17 ++ .../Providers/Core/ConfigProvider.cs | 21 ++ .../Providers/Metadata/MetadataBase.cs | 45 ++-- NzbDrone.Core/Providers/Metadata/Xbmc.cs | 199 ++++++++++++++++++ NzbDrone.Core/Providers/MetadataProvider.cs | 62 +++--- NzbDrone.Core/Providers/TvDbProvider.cs | 8 +- ...aseDefinition.cs => MetadataDefinition.cs} | 4 +- 10 files changed, 317 insertions(+), 66 deletions(-) create mode 100644 NzbDrone.Core/Datastore/Migrations/Migration20120707.cs create mode 100644 NzbDrone.Core/Providers/Metadata/Xbmc.cs rename NzbDrone.Core/Repository/{MetabaseDefinition.cs => MetadataDefinition.cs} (79%) diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 4322f3d0d..66b7414bf 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -200,6 +200,10 @@ public virtual string ReadAllText(string filePath) return File.ReadAllText(filePath); } + public virtual void WriteAllText(string filename, string contents) + { + File.WriteAllText(filename, contents); + } public static bool PathEquals(string firstPath, string secondPath) { diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120707.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120707.cs new file mode 100644 index 000000000..e93c4446d --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120707.cs @@ -0,0 +1,21 @@ +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + + [Migration(20120707)] + public class Migration20120707 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddTable("MetadataDefinitions", new[] + { + new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("Enable", DbType.Boolean, ColumnProperty.NotNull), + new Column("MetadataProviderType", DbType.String, ColumnProperty.NotNull), + new Column("Name", DbType.String, ColumnProperty.NotNull) + }); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Model/MisnamedEpisodeModel.cs b/NzbDrone.Core/Model/MisnamedEpisodeModel.cs index 8697f6754..a7445d609 100644 --- a/NzbDrone.Core/Model/MisnamedEpisodeModel.cs +++ b/NzbDrone.Core/Model/MisnamedEpisodeModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace NzbDrone.Core.Model +namespace NzbDrone.Core.Model.Metadata { public class MisnamedEpisodeModel { diff --git a/NzbDrone.Core/Providers/BannerProvider.cs b/NzbDrone.Core/Providers/BannerProvider.cs index 2176e9d7c..16f091a43 100644 --- a/NzbDrone.Core/Providers/BannerProvider.cs +++ b/NzbDrone.Core/Providers/BannerProvider.cs @@ -81,5 +81,22 @@ public virtual bool Delete(int seriesId) } return true; } + + public virtual void Download(string remotePath, string filename) + { + var url = BANNER_URL_PREFIX + remotePath; + + try + { + _httpProvider.DownloadFile(url, filename); + logger.Trace("Successfully download banner from '{0}' to '{1}'", url, filename); + } + catch (Exception ex) + { + var message = String.Format("Failed to download Banner from '{0}' to '{1}'", url, filename); + logger.DebugException(message, ex); + throw; + } + } } } diff --git a/NzbDrone.Core/Providers/Core/ConfigProvider.cs b/NzbDrone.Core/Providers/Core/ConfigProvider.cs index 2cdc6be0e..76ad96d42 100644 --- a/NzbDrone.Core/Providers/Core/ConfigProvider.cs +++ b/NzbDrone.Core/Providers/Core/ConfigProvider.cs @@ -501,6 +501,27 @@ public virtual string PlexPassword set { SetValue("PlexPassword", value); } } + public virtual Boolean MetadataEnabled + { + get { return GetValueBoolean("MetadataEnabled"); } + + set { SetValue("MetadataEnabled", value); } + } + + public virtual Boolean MetadataXbmcEnabled + { + get { return GetValueBoolean("MetadataXbmcEnabled"); } + + set { SetValue("MetadataXbmcEnabled", value); } + } + + public virtual Boolean MetadataUseBanners + { + get { return GetValueBoolean("MetadataUseBanners"); } + + set { SetValue("MetadataUseBanners", value); } + } + private string GetValue(string key) { return GetValue(key, String.Empty); diff --git a/NzbDrone.Core/Providers/Metadata/MetadataBase.cs b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs index c312f4c0f..dc2f84ee2 100644 --- a/NzbDrone.Core/Providers/Metadata/MetadataBase.cs +++ b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs @@ -1,8 +1,10 @@ using System; using NLog; +using NzbDrone.Common; using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; +using TvdbLib.Data; namespace NzbDrone.Core.Providers.Metadata { @@ -10,43 +12,42 @@ public abstract class MetadataBase { protected readonly Logger _logger; protected readonly ConfigProvider _configProvider; + protected readonly DiskProvider _diskProvider; + protected readonly BannerProvider _bannerProvider; + protected readonly EpisodeProvider _episodeProvider; - protected MetadataBase(ConfigProvider configProvider) + protected MetadataBase(ConfigProvider configProvider, DiskProvider diskProvider, + BannerProvider bannerProvider, EpisodeProvider episodeProvider) { _configProvider = configProvider; + _diskProvider = diskProvider; + _bannerProvider = bannerProvider; + _episodeProvider = episodeProvider; _logger = LogManager.GetLogger(GetType().ToString()); } /// - /// Gets the name for the notification provider + /// Gets the name for the metabase provider /// public abstract string Name { get; } /// - /// Performs the on grab action + /// Creates metadata for a series /// - /// The message to send to the receiver - public abstract void OnGrab(string message); + /// The series to create the metadata for + /// Series information from TheTvDb + public abstract void ForSeries(Series series, TvdbSeries tvDbSeries); /// - /// Performs the on download action + /// Creates metadata for the episode file /// - /// The message to send to the receiver - /// The Series for the new download - public abstract void OnDownload(string message, Series series); + /// The episode file to create the metadata + /// Series information from TheTvDb + public abstract void ForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries); - /// - /// Performs the on rename action - /// - /// The message to send to the receiver - /// The Series for the new download - public abstract void OnRename(string message, Series series); - - /// - /// Performs the after rename action, this will be handled after all renaming for episode/season/series - /// - /// The message to send to the receiver - /// The Series for the new download - public abstract void AfterRename(string message, Series series); + public virtual string GetEpisodeGuideUrl(int seriesId) + { + return String.Format("http://www.thetvdb.com/api/{0}/series/{1}/all/en.zip", TvDbProvider.TVDB_APIKEY, seriesId); + } } } diff --git a/NzbDrone.Core/Providers/Metadata/Xbmc.cs b/NzbDrone.Core/Providers/Metadata/Xbmc.cs new file mode 100644 index 000000000..46b9f6f01 --- /dev/null +++ b/NzbDrone.Core/Providers/Metadata/Xbmc.cs @@ -0,0 +1,199 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using NLog; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using TvdbLib.Data; +using TvdbLib.Data.Banner; + +namespace NzbDrone.Core.Providers.Metadata +{ + public abstract class Xbmc : MetadataBase + { + protected readonly Logger _logger; + + public Xbmc(ConfigProvider configProvider, DiskProvider diskProvider, BannerProvider bannerProvider, EpisodeProvider episodeProvider) + : base(configProvider, diskProvider, bannerProvider, episodeProvider) + { + } + + public override string Name + { + get { return "XBMC"; } + } + + public override void ForSeries(Series series, TvdbSeries tvDbSeries) + { + //Create tvshow.nfo, fanart.jpg, folder.jpg and searon##.tbn + var episodeGuideUrl = GetEpisodeGuideUrl(series.SeriesId); + + _logger.Debug("Generating tvshow.nfo for: {0}", series.Title); + var sb = new StringBuilder(); + var xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = false; + xws.Indent = false; + + using (var xw = XmlWriter.Create(sb, xws)) + { + var tvShow = new XElement("tvshow"); + tvShow.Add(new XElement("title", tvDbSeries.SeriesName)); + tvShow.Add(new XElement("rating", tvDbSeries.Rating)); + tvShow.Add(new XElement("plot", tvDbSeries.Overview)); + tvShow.Add(new XElement("episodeguide", new XElement("url"), episodeGuideUrl)); + tvShow.Add(new XElement("episodeguideurl", episodeGuideUrl)); + tvShow.Add(new XElement("mpaa", tvDbSeries.ContentRating)); + tvShow.Add(new XElement("genre", tvDbSeries.GenreString)); + tvShow.Add(new XElement("premiered", tvDbSeries.FirstAired.ToString("yyyy-MM-dd"))); + tvShow.Add(new XElement("studio", tvDbSeries.Network)); + + foreach(var actor in tvDbSeries.TvdbActors) + { + tvShow.Add(new XElement("actor", + new XElement("name", actor.Name), + new XElement("role", actor.Role), + new XElement("thumb", actor.ActorImage) + )); + } + + var doc = new XDocument(tvShow); + doc.Save(xw); + } + + _logger.Debug("Saving tvshow.nfo for {0}", series.Title); + _diskProvider.WriteAllText(Path.Combine(series.Path, "tvshow.nfo"), sb.ToString()); + + _logger.Debug("Downloading fanart for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")); + + if (!_configProvider.MetadataUseBanners) + { + _logger.Debug("Downloading series thumbnail for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.PosterPath, "folder.jpg"); + + _logger.Debug("Downloading Season posters for {0}", series.Title); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.season); + } + + else + { + _logger.Debug("Downloading series banner for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.BannerPath, "folder.jpg"); + + _logger.Debug("Downloading Season banners for {0}", series.Title); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.seasonwide); + } + } + + public override void ForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries) + { + //Download filename.tbn and filename.nfo + //Use BannerPath for Thumbnail + var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); + + if (!episodes.Any()) + { + _logger.Debug("No episodes where found for this episode file: {0}", episodeFile.EpisodeFileId); + return; + } + + var episodeFileThumbnail = tvDbSeries.Episodes.FirstOrDefault( + e => + e.SeasonNumber == episodeFile.SeasonNumber && + e.EpisodeNumber == episodes.First().EpisodeNumber); + + if (episodeFileThumbnail == null || String.IsNullOrWhiteSpace(episodeFileThumbnail.BannerPath)) + { + _logger.Debug("No thumbnail is available for this episode"); + return; + } + + _logger.Debug("Downloading episode thumbnail for: {0}", episodeFile.EpisodeFileId); + _bannerProvider.Download(episodeFileThumbnail.BannerPath, "folder.jpg"); + + _logger.Debug("Generating filename.nfo for: {0}", episodeFile.EpisodeFileId); + var sb = new StringBuilder(); + var xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = false; + xws.Indent = false; + + using (var xw = XmlWriter.Create(sb, xws)) + { + var doc = new XDocument(); + + foreach (var episode in episodes) + { + var tvdbEpisode = + tvDbSeries.Episodes.FirstOrDefault( + e => + e.SeasonNumber == episode.SeasonNumber && + e.EpisodeNumber == episode.EpisodeNumber); + + if (tvdbEpisode == null) + { + _logger.Debug("Unable to find episode from TvDb - skipping"); + return; + } + + var details = new XElement("episodedetails"); + details.Add(new XElement("title", tvdbEpisode.EpisodeName)); + details.Add(new XElement("season", tvdbEpisode.SeasonNumber)); + details.Add(new XElement("episode", tvdbEpisode.EpisodeNumber)); + details.Add(new XElement("aired", tvdbEpisode.FirstAired)); + details.Add(new XElement("plot", tvDbSeries.Overview)); + details.Add(new XElement("displayseason")); + details.Add(new XElement("displayepisode")); + details.Add(new XElement("thumb", "http://www.thetvdb.com/banners/" + tvdbEpisode.BannerPath)); + details.Add(new XElement("watched", "false")); + details.Add(new XElement("credits", tvdbEpisode.Writer.First())); + details.Add(new XElement("director", tvdbEpisode.Directors.First())); + details.Add(new XElement("rating", tvDbSeries.Rating)); + + foreach(var actor in tvdbEpisode.GuestStars) + { + if (!String.IsNullOrWhiteSpace(actor)) + continue; + + details.Add(new XElement("actor", + new XElement("name", actor) + )); + } + + foreach(var actor in tvDbSeries.TvdbActors) + { + details.Add(new XElement("actor", + new XElement("name", actor.Name), + new XElement("role", actor.Role), + new XElement("thumb", actor.ActorImage) + )); + } + + doc.Add(details); + doc.Save(xw); + } + } + + var filename = Path.GetFileNameWithoutExtension(episodeFile.Path) + ".nfo"; + _logger.Debug("Saving episodedetails to: {0}", filename); + _diskProvider.WriteAllText(filename, sb.ToString()); + } + + private void DownloadSeasonThumbnails(Series series, TvdbSeries tvDbSeries, TvdbSeasonBanner.Type bannerType) + { + var seasons = tvDbSeries.SeasonBanners.Where(s => s.BannerType == bannerType).Select(s => s.Season); + + foreach (var season in seasons) + { + var banner = tvDbSeries.SeasonBanners.FirstOrDefault(b => b.BannerType == bannerType && b.Season == season); + _logger.Debug("Downloading banner for Season: {0} Series: {1}", season, series.Title); + _bannerProvider.Download(banner.BannerPath, + Path.Combine(series.Path, String.Format("season{0:00}.tbn", season))); + } + } + } +} diff --git a/NzbDrone.Core/Providers/MetadataProvider.cs b/NzbDrone.Core/Providers/MetadataProvider.cs index b5200167c..eb0f11d5e 100644 --- a/NzbDrone.Core/Providers/MetadataProvider.cs +++ b/NzbDrone.Core/Providers/MetadataProvider.cs @@ -16,13 +16,15 @@ public class MetadataProvider private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly IDatabase _database; - private IEnumerable _metadataBases; + private IEnumerable _metadataProviders; + private readonly TvDbProvider _tvDbProvider; [Inject] - public MetadataProvider(IDatabase database, IEnumerable metadataBases) + public MetadataProvider(IDatabase database, IEnumerable metadataProviders, TvDbProvider tvDbProvider) { _database = database; - _metadataBases = metadataBases; + _metadataProviders = metadataProviders; + _tvDbProvider = tvDbProvider; } public MetadataProvider() @@ -30,12 +32,12 @@ public MetadataProvider() } - public virtual List All() + public virtual List All() { - return _database.Fetch(); + return _database.Fetch(); } - public virtual void SaveSettings(MetabaseDefinition settings) + public virtual void SaveSettings(MetadataDefinition settings) { if (settings.Id == 0) { @@ -50,31 +52,31 @@ public virtual void SaveSettings(MetabaseDefinition settings) } } - public virtual MetabaseDefinition GetSettings(Type type) + public virtual MetadataDefinition GetSettings(Type type) { - return _database.SingleOrDefault("WHERE MetadataProviderType = @0", type.ToString()); + return _database.SingleOrDefault("WHERE MetadataProviderType = @0", type.ToString()); } - public virtual IList GetEnabledExternalNotifiers() + public virtual IList GetEnabledMetabaseProviders() { var all = All(); - return _metadataBases.Where(i => all.Exists(c => c.MetadataProviderType == i.GetType().ToString() && c.Enable)).ToList(); + return _metadataProviders.Where(i => all.Exists(c => c.MetadataProviderType == i.GetType().ToString() && c.Enable)).ToList(); } - public virtual void InitializeNotifiers(IList notifiers) + public virtual void Initialize(IList metabaseProviders) { - Logger.Debug("Initializing notifiers. Count {0}", notifiers.Count); + Logger.Debug("Initializing metabases. Count {0}", metabaseProviders.Count); - _metadataBases = notifiers; + _metadataProviders = metabaseProviders; var currentNotifiers = All(); - foreach (var notificationProvider in notifiers) + foreach (var notificationProvider in metabaseProviders) { MetadataBase metadataProviderLocal = notificationProvider; if (!currentNotifiers.Exists(c => c.MetadataProviderType == metadataProviderLocal.GetType().ToString())) { - var settings = new MetabaseDefinition + var settings = new MetadataDefinition { Enable = false, MetadataProviderType = metadataProviderLocal.GetType().ToString(), @@ -86,35 +88,23 @@ public virtual void InitializeNotifiers(IList notifiers) } } - public virtual void OnGrab(string message) + public virtual void CreateForSeries(Series series) { - foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) + var tvDbSeries = _tvDbProvider.GetSeries(series.SeriesId, false, true); + + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) { - notifier.OnGrab(message); + provider.ForSeries(series, tvDbSeries); } } - public virtual void OnDownload(string message, Series series) + public virtual void CreateForEpisodeFile(EpisodeFile episodeFile) { - foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) - { - notifier.OnDownload(message, series); - } - } + var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true); - public virtual void OnRename(string message, Series series) - { - foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) { - notifier.OnRename(message, series); - } - } - - public virtual void AfterRename(string message, Series series) - { - foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) - { - notifier.AfterRename(message, series); + provider.ForEpisodeFile(episodeFile, tvDbSeries); } } } diff --git a/NzbDrone.Core/Providers/TvDbProvider.cs b/NzbDrone.Core/Providers/TvDbProvider.cs index c6e26be7a..0bbd29b69 100644 --- a/NzbDrone.Core/Providers/TvDbProvider.cs +++ b/NzbDrone.Core/Providers/TvDbProvider.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Providers public class TvDbProvider { private readonly EnvironmentProvider _environmentProvider; - private const string TVDB_APIKEY = "5D2D188E86E07F4F"; + public const string TVDB_APIKEY = "5D2D188E86E07F4F"; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly TvdbHandler _handler; @@ -44,13 +44,12 @@ public virtual IList SearchSeries(string title) } } - - public virtual TvdbSeries GetSeries(int id, bool loadEpisodes) + public virtual TvdbSeries GetSeries(int id, bool loadEpisodes, bool loadActors = false) { lock (_handler) { Logger.Debug("Fetching SeriesId'{0}' from tvdb", id); - var result = _handler.GetSeries(id, TvdbLanguage.DefaultLanguage, loadEpisodes, false, true, true); + var result = _handler.GetSeries(id, TvdbLanguage.DefaultLanguage, loadEpisodes, loadActors, true, true); //Fix American Dad's scene gongshow if (result != null && result.Id == 73141) @@ -86,6 +85,5 @@ public virtual TvdbSeries GetSeries(int id, bool loadEpisodes) return result; } } - } } \ No newline at end of file diff --git a/NzbDrone.Core/Repository/MetabaseDefinition.cs b/NzbDrone.Core/Repository/MetadataDefinition.cs similarity index 79% rename from NzbDrone.Core/Repository/MetabaseDefinition.cs rename to NzbDrone.Core/Repository/MetadataDefinition.cs index fce0e67b2..9147d4f02 100644 --- a/NzbDrone.Core/Repository/MetabaseDefinition.cs +++ b/NzbDrone.Core/Repository/MetadataDefinition.cs @@ -2,9 +2,9 @@ namespace NzbDrone.Core.Repository { - [TableName("MetabaseDefinitions")] + [TableName("MetadataDefinitions")] [PrimaryKey("Id", autoIncrement = true)] - public class MetabaseDefinition + public class MetadataDefinition { public int Id { get; set; } From eee24aff1dce8291614adc64e9db332b37715ab7 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 12 Jul 2012 11:30:43 -0700 Subject: [PATCH 3/6] Tests added Series metadata will be created when series info is refreshed (and on add) --- .../NzbDrone.App.Test.ncrunchproject | 19 +++ .../NzbDrone.Common.Test.ncrunchproject | 19 +++ .../NzbDrone.Common.ncrunchproject | 19 +++ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 2 + .../NzbDrone.Core.Test.ncrunchproject | 19 +++ .../ProviderTests/EpisodeProviderTest.cs | 32 ++-- .../Metadata/Xbmc_ForEpisodeFile_Fixture.cs | 153 ++++++++++++++++++ .../Metadata/Xbmc_ForSeries_Fixture.cs | 128 +++++++++++++++ NzbDrone.Core/Model/MisnamedEpisodeModel.cs | 2 +- NzbDrone.Core/NzbDrone.Core.csproj | 5 + NzbDrone.Core/NzbDrone.Core.ncrunchproject | 19 +++ NzbDrone.Core/Providers/Metadata/Xbmc.cs | 66 ++++---- NzbDrone.Core/Providers/MetadataProvider.cs | 6 + NzbDrone.Core/Providers/SeriesProvider.cs | 8 +- .../NzbDrone.Services.Service.ncrunchproject | 19 +++ .../NzbDrone.Services.Tests.ncrunchproject | 19 +++ .../NzbDrone.Test.Common.ncrunchproject | 19 +++ .../NzbDrone.Test.Dummy.ncrunchproject | 19 +++ .../NzbDrone.Update.Test.ncrunchproject | 19 +++ .../NzbDrone.Update.ncrunchproject | 19 +++ .../NzbDrone.Web.UI.Automation.ncrunchproject | 19 +++ NzbDrone.Web/NzbDrone.Web.ncrunchproject | 19 +++ NzbDrone.ncrunchsolution | 6 +- NzbDrone/NzbDrone.ncrunchproject | 19 +++ 24 files changed, 624 insertions(+), 50 deletions(-) create mode 100644 NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject create mode 100644 NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject create mode 100644 NzbDrone.Common/NzbDrone.Common.ncrunchproject create mode 100644 NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject create mode 100644 NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs create mode 100644 NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs create mode 100644 NzbDrone.Core/NzbDrone.Core.ncrunchproject create mode 100644 NzbDrone.Services/NzbDrone.Services.Service/NzbDrone.Services.Service.ncrunchproject create mode 100644 NzbDrone.Services/NzbDrone.Services.Tests/NzbDrone.Services.Tests.ncrunchproject create mode 100644 NzbDrone.Test.Common/NzbDrone.Test.Common.ncrunchproject create mode 100644 NzbDrone.Test.Dummy/NzbDrone.Test.Dummy.ncrunchproject create mode 100644 NzbDrone.Update.Test/NzbDrone.Update.Test.ncrunchproject create mode 100644 NzbDrone.Update/NzbDrone.Update.ncrunchproject create mode 100644 NzbDrone.Web.UI.Test/NzbDrone.Web.UI.Automation.ncrunchproject create mode 100644 NzbDrone.Web/NzbDrone.Web.ncrunchproject create mode 100644 NzbDrone/NzbDrone.ncrunchproject diff --git a/NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject b/NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Common/NzbDrone.Common.ncrunchproject b/NzbDrone.Common/NzbDrone.Common.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Common/NzbDrone.Common.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index c4938453d..cfaef9e0f 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -114,6 +114,8 @@ + + diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject b/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs index 7798d6695..28229bb67 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs @@ -172,7 +172,7 @@ public void RefreshEpisodeInfo_emptyRepo() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); //Act @@ -207,7 +207,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -248,7 +248,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null_for_existing_e Db.Insert(fakeEpisode); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeTvDbEpisodes); //Act @@ -285,7 +285,7 @@ public void RefreshEpisodeInfo_ignore_episode_zero() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -324,7 +324,7 @@ public void RefreshEpisodeInfo_should_skip_future_episodes_with_no_title() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -362,7 +362,7 @@ public void RefreshEpisodeInfo_should_skip_older_than_1900_year_episodes_with_no Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -395,7 +395,7 @@ public void RefreshEpisodeInfo_should_add_future_episodes_with_title() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -428,7 +428,7 @@ public void RefreshEpisodeInfo_should_add_old_episodes_with_no_title() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -463,7 +463,7 @@ public void RefreshEpisodeInfo_ignore_season_zero() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); Mocker.GetMock() @@ -493,7 +493,7 @@ public void new_episodes_only_calls_Insert() var currentEpisodes = new List(); Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -528,7 +528,7 @@ public void existing_episodes_only_calls_Update() } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -565,7 +565,7 @@ public void should_try_to_get_existing_episode_using_tvdbid_first() .Returns(fakeEpisodeList); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeTvDbResult); //Act @@ -602,7 +602,7 @@ public void should_try_to_get_existing_episode_using_tvdbid_first_then_season_ep var fakeSeries = Builder.CreateNew().With(c => c.SeriesId = seriesId).Build(); Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -634,7 +634,7 @@ public void existing_episodes_keep_their_episodeId_file_id() } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); var updatedEpisodes = new List(); @@ -695,7 +695,7 @@ public void RefreshEpisodeInfo_should_ignore_new_episode_for_ignored_season() Db.Insert(fakeEpisode); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -1407,7 +1407,7 @@ public void RefreshEpisodeInfo_should_ignore_episode_zero_except_if_season_one() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); //Act diff --git a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs new file mode 100644 index 000000000..53f3df7f4 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.Metadata; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Data.Banner; + +namespace NzbDrone.Core.Test.ProviderTests.Metadata +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class Xbmc_ForEpisoddeFile_Fixture : CoreTest + { + private Series series; + private EpisodeFile episodeFile; + private TvdbSeries tvdbSeries; + + [SetUp] + public void Setup() + { + WithTempAsAppPath(); + + series = Builder + .CreateNew() + .With(s => s.SeriesId == 79488) + .With(s => s.Title == "30 Rock") + .Build(); + + episodeFile = Builder.CreateNew() + .With(f => f.SeriesId = 79488) + .With(f => f.SeasonNumber = 1) + .With(f => f.Path = @"C:\Test\30 Rock\Season 01\30 Rock - S01E01 - Pilot.avi") + .Build(); + + var tvdbEpisodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeriesId = 79488) + .With(e => e.SeasonNumber = 1) + .With(e => e.Directors = new List{ "Fake Director" }) + .With(e => e.Writer = new List{ "Fake Writer" }) + .With(e => e.GuestStars = new List { "Guest Star 1", "Guest Star 2", "Guest Star 3", "" }) + .Build(); + + var seasonBanners = Builder + .CreateListOfSize(4) + .TheFirst(2) + .With(b => b.Season = 1) + .TheLast(2) + .With(b => b.Season = 2) + .TheFirst(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-1-1.jpg") + .TheNext(2) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) + .With(b => b.BannerPath = "banners/seasons/79488-test.jpg") + .TheLast(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-2-1.jpg") + .Build(); + + var seriesActors = Builder + .CreateListOfSize(5) + .Build(); + + tvdbSeries = Builder + .CreateNew() + .With(s => s.Id = 79488) + .With(s => s.SeriesName = "30 Rock") + .With(s => s.TvdbActors = seriesActors.ToList()) + .With(s => s.Episodes = tvdbEpisodes.ToList()) + .Build(); + + tvdbSeries.Banners.AddRange(seasonBanners); + } + + private void WithUseBanners() + { + Mocker.GetMock().SetupGet(s => s.MetadataUseBanners).Returns(true); + } + + private void WithSingleEpisodeFile() + { + var episode = Builder.CreateNew() + .With(e => e.SeasonNumber = 1) + .With(e => e.SeriesId = 79488) + .With(e => e.EpisodeNumber = 1) + .Build(); + + Mocker.GetMock() + .Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) + .Returns(new List { episode }); + } + + private void WithMultiEpisodeFile() + { + var episodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeriesId = 79488) + .With(e => e.SeasonNumber = 1) + .Build(); + + Mocker.GetMock() + .Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) + .Returns(episodes.ToList()); + } + + [Test] + public void should_not_blowup() + { + WithSingleEpisodeFile(); + Mocker.Resolve().ForEpisodeFile(episodeFile, tvdbSeries); + } + + [Test] + public void should_call_diskprovider_writeAllText_once_for_single_episode() + { + WithSingleEpisodeFile(); + Mocker.Resolve().ForEpisodeFile(episodeFile, tvdbSeries); + Mocker.GetMock().Verify(v => v.WriteAllText(episodeFile.Path.Replace("avi", "nfo"), It.IsAny()), Times.Once()); + } + + [Test] + public void should_call_diskprovider_writeAllText_once_for_multi_episode() + { + WithMultiEpisodeFile(); + Mocker.Resolve().ForEpisodeFile(episodeFile, tvdbSeries); + Mocker.GetMock().Verify(v => v.WriteAllText(episodeFile.Path.Replace("avi", "nfo"), It.IsAny()), Times.Once()); + } + + [Test] + public void should_download_thumbnail_when_thumbnail_path_is_not_null() + { + WithSingleEpisodeFile(); + Mocker.Resolve().ForEpisodeFile(episodeFile, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Episodes.First().BannerPath, episodeFile.Path.Replace("avi", "tbn")), Times.Once()); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs new file mode 100644 index 000000000..4e468b501 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.Metadata; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Data.Banner; + +namespace NzbDrone.Core.Test.ProviderTests.Metadata +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class Xbmc_ForSeries_Fixture : CoreTest + { + private Series series; + private TvdbSeries tvdbSeries; + + [SetUp] + public void Setup() + { + WithTempAsAppPath(); + + series = Builder + .CreateNew() + .With(s => s.SeriesId == 79488) + .With(s => s.Title == "30 Rock") + .Build(); + + var seasonBanners = Builder + .CreateListOfSize(4) + .TheFirst(2) + .With(b => b.Season = 1) + .TheLast(2) + .With(b => b.Season = 2) + .TheFirst(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-1-1.jpg") + .TheNext(2) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) + .With(b => b.BannerPath = "banners/seasons/79488-test.jpg") + .TheLast(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-2-1.jpg") + .Build(); + + var seriesActors = Builder + .CreateListOfSize(5) + .Build(); + + tvdbSeries = Builder + .CreateNew() + .With(s => s.Id = 79488) + .With(s => s.SeriesName = "30 Rock") + .With(s => s.TvdbActors = seriesActors.ToList()) + .Build(); + + tvdbSeries.Banners.AddRange(seasonBanners); + } + + private void WithUseBanners() + { + Mocker.GetMock().SetupGet(s => s.MetadataUseBanners).Returns(true); + } + + [Test] + public void should_not_blowup() + { + Mocker.Resolve().ForSeries(series, tvdbSeries); + } + + [Test] + public void should_call_diskprovider_writeAllText() + { + Mocker.Resolve().ForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.WriteAllText(Path.Combine(series.Path, "tvshow.nfo"), It.IsAny()), Times.Once()); + } + + [Test] + public void should_download_fanart() + { + Mocker.Resolve().ForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")), Times.Once()); + } + + [Test] + public void should_download_poster_when_useBanners_is_false() + { + Mocker.Resolve().ForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")), Times.Once()); + } + + [Test] + public void should_download_banner_when_useBanners_is_true() + { + WithUseBanners(); + Mocker.Resolve().ForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")), Times.Once()); + } + + [Test] + public void should_download_season_poster_when_useBanners_is_false() + { + Mocker.Resolve().ForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(It.Is(s => !s.Contains("banners")), It.IsRegex(@"season\d{2}\.tbn")), Times.Exactly(2)); + } + + [Test] + public void should_download_season_banner_when_useBanners_is_true() + { + WithUseBanners(); + Mocker.Resolve().ForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(It.Is(s => s.Contains("banners")), It.IsRegex(@"season\d{2}\.tbn")), Times.Exactly(2)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Model/MisnamedEpisodeModel.cs b/NzbDrone.Core/Model/MisnamedEpisodeModel.cs index a7445d609..8697f6754 100644 --- a/NzbDrone.Core/Model/MisnamedEpisodeModel.cs +++ b/NzbDrone.Core/Model/MisnamedEpisodeModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace NzbDrone.Core.Model.Metadata +namespace NzbDrone.Core.Model { public class MisnamedEpisodeModel { diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index b51d80ed1..1e69555c1 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -239,6 +239,7 @@ + @@ -284,6 +285,9 @@ + + + @@ -313,6 +317,7 @@ Code + diff --git a/NzbDrone.Core/NzbDrone.Core.ncrunchproject b/NzbDrone.Core/NzbDrone.Core.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Core/NzbDrone.Core.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Metadata/Xbmc.cs b/NzbDrone.Core/Providers/Metadata/Xbmc.cs index 46b9f6f01..fe2e16d89 100644 --- a/NzbDrone.Core/Providers/Metadata/Xbmc.cs +++ b/NzbDrone.Core/Providers/Metadata/Xbmc.cs @@ -14,9 +14,9 @@ namespace NzbDrone.Core.Providers.Metadata { - public abstract class Xbmc : MetadataBase + public class Xbmc : MetadataBase { - protected readonly Logger _logger; + protected readonly Logger _logger = LogManager.GetCurrentClassLogger(); public Xbmc(ConfigProvider configProvider, DiskProvider diskProvider, BannerProvider bannerProvider, EpisodeProvider episodeProvider) : base(configProvider, diskProvider, bannerProvider, episodeProvider) @@ -71,22 +71,22 @@ public override void ForSeries(Series series, TvdbSeries tvDbSeries) _logger.Debug("Downloading fanart for: {0}", series.Title); _bannerProvider.Download(tvDbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")); - if (!_configProvider.MetadataUseBanners) + if (_configProvider.MetadataUseBanners) { - _logger.Debug("Downloading series thumbnail for: {0}", series.Title); - _bannerProvider.Download(tvDbSeries.PosterPath, "folder.jpg"); + _logger.Debug("Downloading series banner for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")); - _logger.Debug("Downloading Season posters for {0}", series.Title); - DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.season); + _logger.Debug("Downloading Season banners for {0}", series.Title); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.seasonwide); } else { - _logger.Debug("Downloading series banner for: {0}", series.Title); - _bannerProvider.Download(tvDbSeries.BannerPath, "folder.jpg"); + _logger.Debug("Downloading series thumbnail for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")); - _logger.Debug("Downloading Season banners for {0}", series.Title); - DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.seasonwide); + _logger.Debug("Downloading Season posters for {0}", series.Title); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.season); } } @@ -114,25 +114,33 @@ public override void ForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeri } _logger.Debug("Downloading episode thumbnail for: {0}", episodeFile.EpisodeFileId); - _bannerProvider.Download(episodeFileThumbnail.BannerPath, "folder.jpg"); + _bannerProvider.Download(episodeFileThumbnail.BannerPath, episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn")); _logger.Debug("Generating filename.nfo for: {0}", episodeFile.EpisodeFileId); - var sb = new StringBuilder(); - var xws = new XmlWriterSettings(); - xws.OmitXmlDeclaration = false; - xws.Indent = false; - using (var xw = XmlWriter.Create(sb, xws)) + var xmlResult = String.Empty; + foreach (var episode in episodes) { - var doc = new XDocument(); + var sb = new StringBuilder(); + var xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = false; + xws.Indent = false; - foreach (var episode in episodes) + using (var xw = XmlWriter.Create(sb, xws)) { - var tvdbEpisode = - tvDbSeries.Episodes.FirstOrDefault( - e => - e.SeasonNumber == episode.SeasonNumber && - e.EpisodeNumber == episode.EpisodeNumber); + var doc = new XDocument(); + var tvdbEpisode = tvDbSeries.Episodes.FirstOrDefault( + e => + e.Id == episode.TvDbEpisodeId); + + if (tvdbEpisode == null) + { + _logger.Debug("Looking up by TvDbEpisodeId failed, trying to match via season/episode number combination"); + tvdbEpisode = tvDbSeries.Episodes.FirstOrDefault( + e => + e.SeasonNumber == episode.SeasonNumber && + e.EpisodeNumber == episode.EpisodeNumber); + } if (tvdbEpisode == null) { @@ -161,7 +169,7 @@ public override void ForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeri details.Add(new XElement("actor", new XElement("name", actor) - )); + )); } foreach(var actor in tvDbSeries.TvdbActors) @@ -170,17 +178,17 @@ public override void ForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeri new XElement("name", actor.Name), new XElement("role", actor.Role), new XElement("thumb", actor.ActorImage) - )); + )); } doc.Add(details); doc.Save(xw); } + xmlResult += sb.ToString(); } - - var filename = Path.GetFileNameWithoutExtension(episodeFile.Path) + ".nfo"; + var filename = episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo"); _logger.Debug("Saving episodedetails to: {0}", filename); - _diskProvider.WriteAllText(filename, sb.ToString()); + _diskProvider.WriteAllText(filename, xmlResult); } private void DownloadSeasonThumbnails(Series series, TvdbSeries tvDbSeries, TvdbSeasonBanner.Type bannerType) diff --git a/NzbDrone.Core/Providers/MetadataProvider.cs b/NzbDrone.Core/Providers/MetadataProvider.cs index eb0f11d5e..91084d974 100644 --- a/NzbDrone.Core/Providers/MetadataProvider.cs +++ b/NzbDrone.Core/Providers/MetadataProvider.cs @@ -8,6 +8,7 @@ using NzbDrone.Core.Providers.Metadata; using NzbDrone.Core.Repository; using PetaPoco; +using TvdbLib.Data; namespace NzbDrone.Core.Providers { @@ -92,6 +93,11 @@ public virtual void CreateForSeries(Series series) { var tvDbSeries = _tvDbProvider.GetSeries(series.SeriesId, false, true); + CreateForSeries(series, tvDbSeries); + } + + public virtual void CreateForSeries(Series series, TvdbSeries tvDbSeries) + { foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) { provider.ForSeries(series, tvDbSeries); diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 0a5efdbac..aee5b150d 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -20,17 +20,19 @@ public class SeriesProvider private readonly IDatabase _database; private readonly SceneMappingProvider _sceneNameMappingProvider; private readonly BannerProvider _bannerProvider; + private readonly MetadataProvider _metadataProvider; private static readonly Regex TimeRegex = new Regex(@"^(?