using System; using System.IO; using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; 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 class Xbmc : MetadataBase { 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 CreateForSeries(Series series, TvdbSeries tvDbSeries) { //Create tvshow.nfo, fanart.jpg, folder.jpg and season##.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 = true; 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.BannerPath) )); } 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"), doc.ToString()); } if (!_diskProvider.FileExists(Path.Combine(series.Path, "fanart.jpg"))) { _logger.Debug("Downloading fanart for: {0}", series.Title); _bannerProvider.Download(tvDbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")); } if (!_diskProvider.FileExists(Path.Combine(series.Path, "folder.jpg"))) { if(_configProvider.MetadataUseBanners) { _logger.Debug("Downloading series banner for: {0}", series.Title); _bannerProvider.Download(tvDbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")); _logger.Debug("Downloading Season banners for {0}", series.Title); DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.seasonwide); } else { _logger.Debug("Downloading series thumbnail for: {0}", series.Title); _bannerProvider.Download(tvDbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")); _logger.Debug("Downloading Season posters for {0}", series.Title); DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.season); } } } public override void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries) { //Create filename.tbn and filename.nfo 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; } if (!_diskProvider.FileExists(episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn"))) { _logger.Debug("Downloading episode thumbnail for: {0}", episodeFile.EpisodeFileId); _bannerProvider.Download(episodeFileThumbnail.BannerPath, episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn")); } _logger.Debug("Generating filename.nfo for: {0}", episodeFile.EpisodeFileId); var xmlResult = String.Empty; foreach (var episode in episodes) { var sb = new StringBuilder(); var xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; xws.Indent = false; using (var xw = XmlWriter.Create(sb, xws)) { 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) { _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.FirstOrDefault())); details.Add(new XElement("director", tvdbEpisode.Directors.FirstOrDefault())); 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.BannerPath) )); } doc.Add(details); doc.Save(xw); xmlResult += doc.ToString(); } } var filename = episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo"); _logger.Debug("Saving episodedetails to: {0}", filename); _diskProvider.WriteAllText(filename, xmlResult); } public override void RemoveForSeries(Series series) { //Remove tvshow.nfo, fanart.jpg, folder.jpg and season##.tbn _logger.Debug("Deleting series metadata for: ", series.Title); _diskProvider.DeleteFile(Path.Combine(series.Path, "tvshow.nfo")); _diskProvider.DeleteFile(Path.Combine(series.Path, "fanart.jpg")); _diskProvider.DeleteFile(Path.Combine(series.Path, "fanart.jpg")); foreach (var file in _diskProvider.GetFiles(series.Path, SearchOption.TopDirectoryOnly)) { if (Path.GetExtension(file) != ".tbn") continue; if (!Path.GetFileName(file).StartsWith("season")) continue; _logger.Debug("Deleting season thumbnail: {0}", file); _diskProvider.DeleteFile(file); } } public override void RemoveForEpisodeFile(EpisodeFile episodeFile) { //Remove filename.tbn and filename.nfo _logger.Debug("Deleting episode metadata for: {0}", episodeFile); _diskProvider.DeleteFile(episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo")); _diskProvider.DeleteFile(episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn")); } 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); if (season == 0) { if (!_diskProvider.FileExists(Path.Combine(series.Path, "season-specials.tbn"))) { _bannerProvider.Download(banner.BannerPath, Path.Combine(series.Path, "season-specials.tbn")); } } else { if (!_diskProvider.FileExists(Path.Combine(series.Path, String.Format("season{0:00}.tbn", season)))) { _bannerProvider.Download(banner.BannerPath, Path.Combine(series.Path, String.Format("season{0:00}.tbn", season))); } } } } } }