public EpisodeFile MapIt(EpisodeFile episodeFile, Series series, Episode episode)
        {
            // Terminating call.  Since we can return null from this function
            // we need to be ready for PetaPoco to callback later with null
            // parameters
            if (episodeFile == null)
                return _current;

            // Is this the same EpisodeFile as the current one we're processing
            if (_current != null && _current.EpisodeFileId == episodeFile.EpisodeFileId)
            {
                // Yes, just add this post to the current EpisodeFiles's collection of Episodes
                _current.Episodes.Add(episode);

                // Return null to indicate we're not done with this EpisodeFiles yet
                return null;
            }

            // This is a different EpisodeFile to the current one, or this is the 
            // first time through and we don't have an EpisodeFile yet

            // Save the current EpisodeFile
            var prev = _current;

            // Setup the new current EpisodeFile
            _current = episodeFile;
            _current.Episodes = new List<Episode>();
            _current.Episodes.Add(episode);
            _current.Series = series;

            // Return the now populated previous EpisodeFile (or null if first time through)
            return prev;
        }
        public void Setup()
        {
            Mocker.Resolve<QualityUpgradeSpecification>();
            _upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();

            firstFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true };
            secondFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true };

            var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = null } };
            var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = secondFile }, new Episode { EpisodeFile = null } };

            var fakeSeries = Builder<Series>.CreateNew()
                         .With(c => c.QualityProfile = new QualityProfile { Cutoff = QualityTypes.Bluray1080p })
                         .Build();

            parseResultMulti = new EpisodeParseResult
            {
                Series = fakeSeries,
                Quality = new QualityModel(QualityTypes.DVD, true),
                EpisodeNumbers = new List<int> { 3, 4 },
                SeasonNumber = 12,
                Episodes = doubleEpisodeList
            };

            parseResultSingle = new EpisodeParseResult
            {
                Series = fakeSeries,
                Quality = new QualityModel(QualityTypes.DVD, true),
                EpisodeNumbers = new List<int> { 3 },
                SeasonNumber = 12,
                Episodes = singleEpisodeList
            };
        }
        public void Setup()
        {
            WithTempAsAppPath();

            series = Builder<Series>
                    .CreateNew()
                    .With(s => s.SeriesId == 79488)
                    .With(s => s.Title == "30 Rock")
                    .Build();

            episodeFile = Builder<EpisodeFile>.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<TvdbEpisode>.CreateListOfSize(2)
                    .All()
                    .With(e => e.SeriesId = 79488)
                    .With(e => e.SeasonNumber = 1)
                    .With(e => e.Directors = new List<string>{ "Fake Director" })
                    .With(e => e.Writer = new List<string>{ "Fake Writer" })
                    .With(e => e.GuestStars = new List<string> { "Guest Star 1", "Guest Star 2", "Guest Star 3", "" })
                    .Build();

            var seasonBanners = Builder<TvdbSeasonBanner>
                    .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<TvdbActor>
                    .CreateListOfSize(5)
                    .All()
                    .With(a => a.ActorImage = Builder<TvdbActorBanner>.CreateNew().Build())
                    .Build();

            tvdbSeries = Builder<TvdbSeries>
                    .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);
        }
Exemple #4
0
 public EpisodeFile(EpisodeFile source)
 {
     EpisodeFileId = source.EpisodeFileId;
     SeriesId = source.SeriesId;
     SeasonNumber = source.SeasonNumber;
     Path = source.Path;
     Quality = source.Quality;
     Proper = source.Proper;
     Size = source.Size;
 }
Exemple #5
0
        public void Start(ProgressNotification notification, dynamic options)
        {
            if (options == null || options.SeriesId <= 0)
                throw new ArgumentException("options");

            if (options.SeasonNumber < 0)
                throw new ArgumentException("options.SeasonNumber");

            var series = _seriesProvider.GetSeries(options.SeriesId);

            notification.CurrentMessage = String.Format("Renaming episodes for {0} Season {1}", series.Title, options.SeasonNumber);

            logger.Debug("Getting episodes from database for series: {0} and season: {1}", options.SeriesId, options.SeasonNumber);
            IList<EpisodeFile> episodeFiles = _mediaFileProvider.GetSeasonFiles(options.SeriesId, options.SeasonNumber);

            if (episodeFiles == null || !episodeFiles.Any())
            {
                logger.Warn("No episodes in database found for series: {0} and season: {1}.", options.SeriesId, options.SeasonNumber);
                return;
            }

            var newEpisodeFiles = new List<EpisodeFile>();
            var oldEpisodeFiles = new List<EpisodeFile>();

            foreach (var episodeFile in episodeFiles)
            {
                try
                {
                    var oldFile = new EpisodeFile(episodeFile);
                    var newFile = _diskScanProvider.MoveEpisodeFile(episodeFile);

                    if (newFile != null)
                    {
                        newEpisodeFiles.Add(newFile);
                        oldEpisodeFiles.Add(oldFile);
                    }
                }

                catch (Exception e)
                {
                    logger.WarnException("An error has occurred while renaming file", e);
                }
            }

            //Remove & Create Metadata for episode files
            _metadataProvider.RemoveForEpisodeFiles(oldEpisodeFiles);
            _metadataProvider.CreateForEpisodeFiles(newEpisodeFiles);

            //Start AfterRename

            var message = String.Format("Renamed: Series {0}, Season: {1}", series.Title, options.SeasonNumber);
            _externalNotificationProvider.AfterRename(message, series);

            notification.CurrentMessage = String.Format("Rename completed for {0} Season {1}", series.Title, options.SeasonNumber);
        }
        public void SetUp()
        {
            _series = Builder<Series>.CreateNew()
                    .Build();

            _episodeFile = Builder<EpisodeFile>.CreateNew()
                    .With(f => f.Quality = QualityTypes.SDTV)
                    .Build();

            _episode = Builder<Episode>.CreateNew()
                    .With(e => e.EpisodeFileId = 0)
                    .With(e => e.SeriesId = _series.SeriesId)
                    .With(e => e.Series = _series)
                    .With(e => e.EpisodeFileId = _episodeFile.EpisodeFileId)
                    .With(e => e.EpisodeFile = _episodeFile)
                    .Build();
        }
        public Season MapIt(Season season, Episode episode, EpisodeFile episodeFile)
        {
            // Terminating call. Since we can return null from this function
            // we need to be ready for PetaPoco to callback later with null
            // parameters
            if (season == null)
                return _current;

            //Todo: Find a Query that doesn't require this check
            //Map EpisodeFile to Episode (Map to null if 0, because PetaPoco is returning a POCO when it should be null)
            episode.EpisodeFile = (episode.EpisodeFileId == 0 ?  null : episodeFile);

            // Is this the same season as the current one we're processing
            if (_current != null && _current.SeasonId == season.SeasonId)
            {
                // Yes, just add this post to the current author's collection of posts
                _current.Episodes.Add(episode);

                // Return null to indicate we're not done with this author yet
                return null;
            }

            // This is season different author to the current one, or this is the 
            // first time through and we don't have an season yet

            // Save the current author
            var prev = _current;

            // Setup the new current season
            _current = season;
            _current.Episodes = new List<Episode>();
            _current.Episodes.Add(episode);

            // Return the now populated previous season (or null if first time through)
            return prev;
        }
 public virtual void RemoveForEpisodeFile(EpisodeFile episodeFile)
 {
     foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable))
     {
         provider.RemoveForEpisodeFile(episodeFile);
     }
 }
 public virtual void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries)
 {
     foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable))
     {
         provider.CreateForEpisodeFile(episodeFile, tvDbSeries);
     }
 }
Exemple #10
0
        public virtual void CreateForEpisodeFile(EpisodeFile episodeFile)
        {
            var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true);

            CreateForEpisodeFile(episodeFile, tvDbSeries);
        }
        public void Start(ProgressNotification notification, dynamic options)
        {
            List<Series> seriesToRename;

            if (options == null || options.SeriesId <= 0)
            {
                seriesToRename = _seriesProvider.GetAllSeries().ToList();
            }

            else
            {
                seriesToRename = new List<Series>{  _seriesProvider.GetSeries(options.SeriesId) };
            }

            foreach(var series in seriesToRename)
            {
                notification.CurrentMessage = String.Format("Renaming episodes for '{0}'", series.Title);

                Logger.Debug("Getting episodes from database for series: {0}", series.SeriesId);
                var episodeFiles = _mediaFileProvider.GetSeriesFiles(series.SeriesId);

                if (episodeFiles == null || episodeFiles.Count == 0)
                {
                    Logger.Warn("No episodes in database found for series: {0}", series.SeriesId);
                    return;
                }

                var newEpisodeFiles = new List<EpisodeFile>();
                var oldEpisodeFiles = new List<EpisodeFile>();

                foreach (var episodeFile in episodeFiles)
                {
                    try
                    {
                        var oldFile = new EpisodeFile(episodeFile);
                        var newFile = _diskScanProvider.MoveEpisodeFile(episodeFile);

                        if (newFile != null)
                        {
                            newEpisodeFiles.Add(newFile);
                            oldEpisodeFiles.Add(oldFile);
                        }
                    }

                    catch (Exception e)
                    {
                        Logger.WarnException("An error has occurred while renaming file", e);
                    }
                }

                //Remove & Create Metadata for episode files
                _metadataProvider.RemoveForEpisodeFiles(oldEpisodeFiles);
                _metadataProvider.CreateForEpisodeFiles(newEpisodeFiles);

                //Start AfterRename

                var message = String.Format("Renamed: Series {0}", series.Title);
                _externalNotificationProvider.AfterRename(message, series);

                notification.CurrentMessage = String.Format("Rename completed for {0}", series.Title);
            }
        }
Exemple #12
0
 /// <summary>
 ///   Creates metadata for the episode file
 /// </summary>
 /// <param name = "episodeFile">The episode file to create the metadata</param>
 /// <param name = "tvDbSeries">Series information from TheTvDb</param>
 public abstract void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries);
        private static void VerifyFileImport(EpisodeFile result, AutoMoqer Mocker, Episode fakeEpisode, int size)
        {
            Mocker.VerifyAllMocks();
            result.Should().NotBeNull();
            result.SeriesId.Should().Be(fakeEpisode.SeriesId);
            result.Size.Should().Be(size);
            result.DateAdded.Should().HaveDay(DateTime.Now.Day);
            Mocker.GetMock<MediaFileProvider>().Verify(p => p.Add(It.IsAny<EpisodeFile>()), Times.Once());

            //Get the count of episodes linked
            var count = Mocker.GetMock<EpisodeProvider>().Object.GetEpisodesByParseResult(null).Count;

            Mocker.GetMock<EpisodeProvider>().Verify(p => p.UpdateEpisode(It.Is<Episode>(e => e.EpisodeFileId == result.EpisodeFileId)), Times.Exactly(count));
        }
Exemple #14
0
        public virtual EpisodeFile ImportFile(Series series, string filePath)
        {
            Logger.Trace("Importing file to database [{0}]", filePath);

            if (_mediaFileProvider.Exists(filePath))
            {
                Logger.Trace("[{0}] already exists in the database. skipping.", filePath);
                return null;
            }

            long size = _diskProvider.GetSize(filePath);

            //Skip any file under 40MB - New samples don't even have sample in the name...
            if (size < Constants.IgnoreFileSize)
            {
                Logger.Trace("[{0}] appears to be a sample. skipping.", filePath);
                return null;
            }

            var parseResult = Parser.ParsePath(filePath);

            if (parseResult == null)
                return null;

            if (!_diskProvider.IsChildOfPath(filePath, series.Path))
                parseResult.SceneSource = true;

            parseResult.SeriesTitle = series.Title; //replaces the nasty path as title to help with logging
            parseResult.Series = series;

            var episodes = _episodeProvider.GetEpisodesByParseResult(parseResult);

            if (episodes.Count <= 0)
            {
                Logger.Debug("Can't find any matching episodes in the database. Skipping {0}", filePath);
                return null;
            }

            //Make sure this file is an upgrade for ALL episodes already on disk
            if (episodes.All(e => e.EpisodeFile == null || e.EpisodeFile.QualityWrapper <= parseResult.Quality))
            {
                Logger.Debug("Deleting the existing file(s) on disk to upgrade to: {0}", filePath);
                //Do the delete for files where there is already an episode on disk
                episodes.Where(e => e.EpisodeFile != null).Select(e => e.EpisodeFile.Path).Distinct().ToList().ForEach(p => _recycleBinProvider.DeleteFile(p));
            }

            else
            {
                //Skip this file because its not an upgrade
                Logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", filePath);
                return null;
            }

            var episodeFile = new EpisodeFile();
            episodeFile.DateAdded = DateTime.Now;
            episodeFile.SeriesId = series.SeriesId;
            episodeFile.Path = filePath.NormalizePath();
            episodeFile.Size = size;
            episodeFile.Quality = parseResult.Quality.Quality;
            episodeFile.Proper = parseResult.Quality.Proper;
            episodeFile.SeasonNumber = parseResult.SeasonNumber;
            episodeFile.SceneName = Path.GetFileNameWithoutExtension(filePath.NormalizePath());
            episodeFile.ReleaseGroup = parseResult.ReleaseGroup;
            var fileId = _mediaFileProvider.Add(episodeFile);

            //Link file to all episodes
            foreach (var ep in episodes)
            {
                ep.EpisodeFileId = fileId;
                ep.PostDownloadStatus = PostDownloadStatusType.NoError;
                _episodeProvider.UpdateEpisode(ep);
                Logger.Debug("Linking [{0}] > [{1}]", filePath, ep);
            }


            return episodeFile;
        }
 public virtual void Update(EpisodeFile episodeFile)
 {
     _database.Update(episodeFile);
 }
Exemple #16
0
        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"));
        }
 public virtual int Add(EpisodeFile episodeFile)
 {
     return Convert.ToInt32(_database.Insert(episodeFile));
 }
        public virtual string GetNewFilename(IList<Episode> episodes, string seriesTitle, QualityTypes quality, bool proper, EpisodeFile episodeFile)
        {
            if (_configProvider.SortingUseSceneName)
            {
                Logger.Trace("Attempting to use scene name");
                if (String.IsNullOrWhiteSpace(episodeFile.SceneName))
                {
                    var name = Path.GetFileNameWithoutExtension(episodeFile.Path);
                    Logger.Trace("Unable to use scene name, because it is null, sticking with current name: {0}", name);

                    return name;
                }

                return episodeFile.SceneName;
            }

            var sortedEpisodes = episodes.OrderBy(e => e.EpisodeNumber);

            var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SortingSeparatorStyle);
            var numberStyle = EpisodeSortingHelper.GetNumberStyle(_configProvider.SortingNumberStyle);

            var episodeNames = new List<String>();

            episodeNames.Add(Parser.CleanupEpisodeTitle(sortedEpisodes.First().Title));

            string result = String.Empty;

            if (_configProvider.SortingIncludeSeriesName)
            {
                result += seriesTitle + separatorStyle.Pattern;
            }

            result += numberStyle.Pattern.Replace("%0e", String.Format("{0:00}", sortedEpisodes.First().EpisodeNumber));

            if (episodes.Count > 1)
            {
                var multiEpisodeStyle = EpisodeSortingHelper.GetMultiEpisodeStyle(_configProvider.SortingMultiEpisodeStyle);

                foreach (var episode in sortedEpisodes.Skip(1))
                {
                    if (multiEpisodeStyle.Name == "Duplicate")
                    {
                        result += separatorStyle.Pattern + numberStyle.Pattern;
                    }
                    else
                    {
                        result += multiEpisodeStyle.Pattern;
                    }

                    result = result.Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber));
                    episodeNames.Add(Parser.CleanupEpisodeTitle(episode.Title));
                }
            }

            result = result
                .Replace("%s", String.Format("{0}", episodes.First().SeasonNumber))
                .Replace("%0s", String.Format("{0:00}", episodes.First().SeasonNumber))
                .Replace("%x", numberStyle.EpisodeSeparator)
                .Replace("%p", separatorStyle.Pattern);

            if (_configProvider.SortingIncludeEpisodeTitle)
            {
                if (episodeNames.Distinct().Count() == 1)
                    result += separatorStyle.Pattern + episodeNames.First();

                else
                    result += separatorStyle.Pattern + String.Join(" + ", episodeNames.Distinct());
            }

            if (_configProvider.SortingAppendQuality)
            {
                result += String.Format(" [{0}]", quality);

                if (proper)
                    result += " [Proper]";
            }

            if (_configProvider.SortingReplaceSpaces)
                result = result.Replace(' ', '.');

            Logger.Trace("New File Name is: [{0}]", result.Trim());
            return CleanFilename(result.Trim());
        }
Exemple #19
0
 /// <summary>
 ///   Removes metadata for the episode file
 /// </summary>
 /// <param name = "episodeFile">The episode file to create the metadata</param>
 public abstract void RemoveForEpisodeFile(EpisodeFile episodeFile);
        public void null_string_as_arg_should_not_fail()
        {
            var epFile = new EpisodeFile();
            Logger.Trace("File {0} no longer exists on disk. removing from database.", epFile.Path);

            epFile.Path.Should().BeNull();
        }
Exemple #21
0
        public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false)
        {
            if (episodeFile == null)
                throw new ArgumentNullException("episodeFile");

            var series = _seriesProvider.GetSeries(episodeFile.SeriesId);
            var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId);
            string newFileName = _mediaFileProvider.GetNewFilename(episodes, series.Title, episodeFile.Quality, episodeFile.Proper, episodeFile);
            var newFile = _mediaFileProvider.CalculateFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));

            //Only rename if existing and new filenames don't match
            if (DiskProvider.PathEquals(episodeFile.Path, newFile.FullName))
            {
                Logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
                return null;
            }

            _diskProvider.CreateDirectory(newFile.DirectoryName);

            Logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, newFile.FullName);
            _diskProvider.MoveFile(episodeFile.Path, newFile.FullName);

            //Wrapped in Try/Catch to prevent this from causing issues with remote NAS boxes, the move worked, which is more important.
            try
            {
                _diskProvider.InheritFolderPermissions(newFile.FullName);
            }
            catch (UnauthorizedAccessException ex)
            {
                Logger.Debug("Unable to apply folder permissions to: ", newFile.FullName);
                Logger.TraceException(ex.Message, ex);
            }

            episodeFile.Path = newFile.FullName;
            _mediaFileProvider.Update(episodeFile);

            var parseResult = Parser.ParsePath(episodeFile.Path);
            parseResult.Series = series;
            parseResult.Quality = new QualityModel{ Quality = episodeFile.Quality, Proper = episodeFile.Proper };
            parseResult.Episodes = episodes;

            var message = _downloadProvider.GetDownloadTitle(parseResult);

            if (newDownload)
            {
                _externalNotificationProvider.OnDownload(message, series);
                
                foreach(var episode in episodes)
                    _signalRProvider.UpdateEpisodeStatus(episode.EpisodeId, EpisodeStatusType.Ready, parseResult.Quality);
            }
            else
            {
                _externalNotificationProvider.OnRename(message, series);
            }

            return episodeFile;
        }
        public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false)
        {
            if (episodeFile == null)
                throw new ArgumentNullException("episodeFile");

            var series = _seriesProvider.GetSeries(episodeFile.SeriesId);
            var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId);
            string newFileName = _mediaFileProvider.GetNewFilename(episodes, series.Title, episodeFile.Quality, episodeFile.Proper);
            var newFile = _mediaFileProvider.CalculateFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));

            //Only rename if existing and new filenames don't match
            if (DiskProvider.PathEquals(episodeFile.Path, newFile.FullName))
            {
                Logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
                return false;
            }

            _diskProvider.CreateDirectory(newFile.DirectoryName);

            Logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, newFile.FullName);
            _diskProvider.MoveFile(episodeFile.Path, newFile.FullName);

            _diskProvider.InheritFolderPermissions(newFile.FullName);

            episodeFile.Path = newFile.FullName;
            _mediaFileProvider.Update(episodeFile);

            var parseResult = Parser.ParsePath(episodeFile.Path);
            parseResult.Series = series;

            var message = _downloadProvider.GetDownloadTitle(parseResult);

            if (newDownload)
            {
                _externalNotificationProvider.OnDownload(message, series);
                
                foreach(var episode in episodes)
                    _signalRProvider.UpdateEpisodeStatus(episode.EpisodeId, EpisodeStatusType.Ready);
            }
            else
            {
                _externalNotificationProvider.OnRename(message, series);
            }

            return true;
        }
 private static void VerifySkipImport(EpisodeFile result, AutoMoqer Mocker)
 {
     Mocker.VerifyAllMocks();
     result.Should().BeNull();
     Mocker.GetMock<MediaFileProvider>().Verify(p => p.Add(It.IsAny<EpisodeFile>()), Times.Never());
     Mocker.GetMock<EpisodeProvider>().Verify(p => p.UpdateEpisode(It.IsAny<Episode>()), Times.Never());
     Mocker.GetMock<DiskProvider>().Verify(p => p.DeleteFile(It.IsAny<string>()), Times.Never());
 }
Exemple #24
0
        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.ToString("yyyy-MM-dd")));
                    details.Add(new XElement("plot", tvdbEpisode.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", tvdbEpisode.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", "http://www.thetvdb.com/banners/" + actor.ActorImage.BannerPath)
                                            ));
                    }

                    doc.Add(details);
                    doc.Save(xw);

                    xmlResult += doc.ToString();
                    xmlResult += Environment.NewLine;
                }       
            }
            var filename = episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo");
            _logger.Debug("Saving episodedetails to: {0}", filename);
            _diskProvider.WriteAllText(filename, xmlResult.Trim(EnvironmentProvider.NewLineChars));
        }
        public virtual EpisodeFile ImportFile(Series series, string filePath)
        {
            Logger.Trace("Importing file to database [{0}]", filePath);

            if (_mediaFileProvider.Exists(filePath))
            {
                Logger.Trace("[{0}] already exists in the database. skipping.", filePath);
                return null;
            }

            long size = _diskProvider.GetSize(filePath);

            //If Size is less than 40MB and contains sample. Check for Size to ensure its not an episode with sample in the title
            if (size < Constants.IgnoreFileSize && filePath.ToLower().Contains("sample"))
            {
                Logger.Trace("[{0}] appears to be a sample. skipping.", filePath);
                return null;
            }

            var parseResult = Parser.ParsePath(filePath);

            if (parseResult == null)
                return null;

            parseResult.SeriesTitle = series.Title; //replaces the nasty path as title to help with logging
            parseResult.Series = series;

            var episodes = _episodeProvider.GetEpisodesByParseResult(parseResult);

            if (episodes.Count <= 0)
            {
                Logger.Debug("Can't find any matching episodes in the database. Skipping {0}", filePath);
                return null;
            }

            //Make sure this file is an upgrade for ALL episodes already on disk
            if (episodes.All(e => e.EpisodeFile == null || e.EpisodeFile.QualityWrapper <= parseResult.Quality))
            {
                Logger.Debug("Deleting the existing file(s) on disk to upgrade to: {0}", filePath);
                //Do the delete for files where there is already an episode on disk
                episodes.Where(e => e.EpisodeFile != null).Select(e => e.EpisodeFile.Path).Distinct().ToList().ForEach(p => _diskProvider.DeleteFile(p));
            }

            else
            {
                //Skip this file because its not an upgrade
                Logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", filePath);
                return null;
            }

            var episodeFile = new EpisodeFile();
            episodeFile.DateAdded = DateTime.Now;
            episodeFile.SeriesId = series.SeriesId;
            episodeFile.Path = filePath.NormalizePath();
            episodeFile.Size = size;
            episodeFile.Quality = parseResult.Quality.QualityType;
            episodeFile.Proper = parseResult.Quality.Proper;
            episodeFile.SeasonNumber = parseResult.SeasonNumber;
            var fileId = _mediaFileProvider.Add(episodeFile);

            //Link file to all episodes
            foreach (var ep in episodes)
            {
                ep.EpisodeFileId = fileId;
                ep.PostDownloadStatus = PostDownloadStatusType.NoError;
                _episodeProvider.UpdateEpisode(ep);
                Logger.Debug("Linking [{0}] > [{1}]", filePath, ep);
            }


            return episodeFile;
        }