Metadata coming together for XBMC

This commit is contained in:
Mark McDowall 2012-07-09 21:37:24 -07:00
parent 8263ab1d0f
commit b50e16a456
10 changed files with 317 additions and 66 deletions

View File

@ -200,6 +200,10 @@ namespace NzbDrone.Common
return File.ReadAllText(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) public static bool PathEquals(string firstPath, string secondPath)
{ {

View File

@ -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)
});
}
}
}

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace NzbDrone.Core.Model namespace NzbDrone.Core.Model.Metadata
{ {
public class MisnamedEpisodeModel public class MisnamedEpisodeModel
{ {

View File

@ -81,5 +81,22 @@ namespace NzbDrone.Core.Providers
} }
return true; 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;
}
}
} }
} }

View File

@ -501,6 +501,27 @@ namespace NzbDrone.Core.Providers.Core
set { SetValue("PlexPassword", value); } 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) private string GetValue(string key)
{ {
return GetValue(key, String.Empty); return GetValue(key, String.Empty);

View File

@ -1,8 +1,10 @@
using System; using System;
using NLog; using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using TvdbLib.Data;
namespace NzbDrone.Core.Providers.Metadata namespace NzbDrone.Core.Providers.Metadata
{ {
@ -10,43 +12,42 @@ namespace NzbDrone.Core.Providers.Metadata
{ {
protected readonly Logger _logger; protected readonly Logger _logger;
protected readonly ConfigProvider _configProvider; 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; _configProvider = configProvider;
_diskProvider = diskProvider;
_bannerProvider = bannerProvider;
_episodeProvider = episodeProvider;
_logger = LogManager.GetLogger(GetType().ToString()); _logger = LogManager.GetLogger(GetType().ToString());
} }
/// <summary> /// <summary>
/// Gets the name for the notification provider /// Gets the name for the metabase provider
/// </summary> /// </summary>
public abstract string Name { get; } public abstract string Name { get; }
/// <summary> /// <summary>
/// Performs the on grab action /// Creates metadata for a series
/// </summary> /// </summary>
/// <param name = "message">The message to send to the receiver</param> /// <param name = "series">The series to create the metadata for</param>
public abstract void OnGrab(string message); /// <param name = "tvDbSeries">Series information from TheTvDb</param>
public abstract void ForSeries(Series series, TvdbSeries tvDbSeries);
/// <summary> /// <summary>
/// Performs the on download action /// Creates metadata for the episode file
/// </summary> /// </summary>
/// <param name = "message">The message to send to the receiver</param> /// <param name = "episodeFile">The episode file to create the metadata</param>
/// <param name = "series">The Series for the new download</param> /// <param name = "tvDbSeries">Series information from TheTvDb</param>
public abstract void OnDownload(string message, Series series); public abstract void ForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries);
/// <summary> public virtual string GetEpisodeGuideUrl(int seriesId)
/// Performs the on rename action {
/// </summary> return String.Format("http://www.thetvdb.com/api/{0}/series/{1}/all/en.zip", TvDbProvider.TVDB_APIKEY, seriesId);
/// <param name = "message">The message to send to the receiver</param> }
/// <param name = "series">The Series for the new download</param>
public abstract void OnRename(string message, Series series);
/// <summary>
/// Performs the after rename action, this will be handled after all renaming for episode/season/series
/// </summary>
/// <param name = "message">The message to send to the receiver</param>
/// <param name = "series">The Series for the new download</param>
public abstract void AfterRename(string message, Series series);
} }
} }

View File

@ -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)));
}
}
}
}

View File

@ -16,13 +16,15 @@ namespace NzbDrone.Core.Providers
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IDatabase _database; private readonly IDatabase _database;
private IEnumerable<MetadataBase> _metadataBases; private IEnumerable<MetadataBase> _metadataProviders;
private readonly TvDbProvider _tvDbProvider;
[Inject] [Inject]
public MetadataProvider(IDatabase database, IEnumerable<MetadataBase> metadataBases) public MetadataProvider(IDatabase database, IEnumerable<MetadataBase> metadataProviders, TvDbProvider tvDbProvider)
{ {
_database = database; _database = database;
_metadataBases = metadataBases; _metadataProviders = metadataProviders;
_tvDbProvider = tvDbProvider;
} }
public MetadataProvider() public MetadataProvider()
@ -30,12 +32,12 @@ namespace NzbDrone.Core.Providers
} }
public virtual List<MetabaseDefinition> All() public virtual List<MetadataDefinition> All()
{ {
return _database.Fetch<MetabaseDefinition>(); return _database.Fetch<MetadataDefinition>();
} }
public virtual void SaveSettings(MetabaseDefinition settings) public virtual void SaveSettings(MetadataDefinition settings)
{ {
if (settings.Id == 0) 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<MetabaseDefinition>("WHERE MetadataProviderType = @0", type.ToString()); return _database.SingleOrDefault<MetadataDefinition>("WHERE MetadataProviderType = @0", type.ToString());
} }
public virtual IList<MetadataBase> GetEnabledExternalNotifiers() public virtual IList<MetadataBase> GetEnabledMetabaseProviders()
{ {
var all = All(); 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<MetadataBase> notifiers) public virtual void Initialize(IList<MetadataBase> metabaseProviders)
{ {
Logger.Debug("Initializing notifiers. Count {0}", notifiers.Count); Logger.Debug("Initializing metabases. Count {0}", metabaseProviders.Count);
_metadataBases = notifiers; _metadataProviders = metabaseProviders;
var currentNotifiers = All(); var currentNotifiers = All();
foreach (var notificationProvider in notifiers) foreach (var notificationProvider in metabaseProviders)
{ {
MetadataBase metadataProviderLocal = notificationProvider; MetadataBase metadataProviderLocal = notificationProvider;
if (!currentNotifiers.Exists(c => c.MetadataProviderType == metadataProviderLocal.GetType().ToString())) if (!currentNotifiers.Exists(c => c.MetadataProviderType == metadataProviderLocal.GetType().ToString()))
{ {
var settings = new MetabaseDefinition var settings = new MetadataDefinition
{ {
Enable = false, Enable = false,
MetadataProviderType = metadataProviderLocal.GetType().ToString(), 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)) 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)) var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true);
{
notifier.OnDownload(message, series);
}
}
public virtual void OnRename(string message, Series series) foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable))
{ {
foreach (var notifier in _metadataBases.Where(i => GetSettings(i.GetType()).Enable)) provider.ForEpisodeFile(episodeFile, tvDbSeries);
{
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);
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace NzbDrone.Core.Providers
public class TvDbProvider public class TvDbProvider
{ {
private readonly EnvironmentProvider _environmentProvider; private readonly EnvironmentProvider _environmentProvider;
private const string TVDB_APIKEY = "5D2D188E86E07F4F"; public const string TVDB_APIKEY = "5D2D188E86E07F4F";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly TvdbHandler _handler; private readonly TvdbHandler _handler;
@ -44,13 +44,12 @@ namespace NzbDrone.Core.Providers
} }
} }
public virtual TvdbSeries GetSeries(int id, bool loadEpisodes, bool loadActors = false)
public virtual TvdbSeries GetSeries(int id, bool loadEpisodes)
{ {
lock (_handler) lock (_handler)
{ {
Logger.Debug("Fetching SeriesId'{0}' from tvdb", id); 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 //Fix American Dad's scene gongshow
if (result != null && result.Id == 73141) if (result != null && result.Id == 73141)
@ -86,6 +85,5 @@ namespace NzbDrone.Core.Providers
return result; return result;
} }
} }
} }
} }

View File

@ -2,9 +2,9 @@
namespace NzbDrone.Core.Repository namespace NzbDrone.Core.Repository
{ {
[TableName("MetabaseDefinitions")] [TableName("MetadataDefinitions")]
[PrimaryKey("Id", autoIncrement = true)] [PrimaryKey("Id", autoIncrement = true)]
public class MetabaseDefinition public class MetadataDefinition
{ {
public int Id { get; set; } public int Id { get; set; }