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 @@ namespace NzbDrone.Common
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 @@ namespace NzbDrone.Core.Providers
}
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 @@ namespace NzbDrone.Core.Providers.Core
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 @@ namespace NzbDrone.Core.Providers.Metadata
{
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 @@ namespace NzbDrone.Core.Providers
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 @@ namespace NzbDrone.Core.Providers
}
- 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 @@ namespace NzbDrone.Core.Providers
}
}
- 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 @@ namespace NzbDrone.Core.Providers
}
}
- public virtual void OnGrab(string message)
+ public virtual void CreateForSeries(Series series)
{
- foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable))
- {
- notifier.OnGrab(message);
- }
- }
+ var tvDbSeries = _tvDbProvider.GetSeries(series.SeriesId, false, true);
- public virtual void OnDownload(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.OnDownload(message, series);
+ provider.ForSeries(series, tvDbSeries);
}
}
- public virtual void OnRename(string message, Series series)
+ public virtual void CreateForEpisodeFile(EpisodeFile episodeFile)
{
- foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable))
- {
- notifier.OnRename(message, series);
- }
- }
+ var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true);
- public virtual void AfterRename(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.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 @@ namespace NzbDrone.Core.Providers
}
}
-
- 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 @@ namespace NzbDrone.Core.Providers
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; }