public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("{0} is in series folder, unpacking check", localEpisode.Path);
                return true;
            }

            foreach (var workingFolder in _configService.DownloadClientWorkingFolders.Split('|'))
            {
                if (Directory.GetParent(localEpisode.Path).Name.StartsWith(workingFolder))
                {
                    if (OsInfo.IsMono)
                    {
                        _logger.Debug("{0} is still being unpacked", localEpisode.Path);
                        return false;
                    }

                    if (_diskProvider.FileGetLastWriteUtc(localEpisode.Path) > DateTime.UtcNow.AddMinutes(-5))
                    {
                        _logger.Debug("{0} appears to be unpacking still", localEpisode.Path);
                        return false;
                    }
                }
            }

            return true;
        }
        private ImportDecision GetDecision(LocalEpisode localEpisode)
        {
            var reasons = _specifications.Select(c => EvaluateSpec(c, localEpisode))
                .Where(c => !string.IsNullOrWhiteSpace(c));

            return new ImportDecision(localEpisode, reasons.ToArray());
        }
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            try
            {
                if (localEpisode.ExistingFile)
                {
                    _logger.Trace("Skipping free space check for existing episode");
                    return true;
                }

                var path = Directory.GetParent(localEpisode.Series.Path);
                var freeSpace = _diskProvider.GetAvailableSpace(path.FullName);

                if (!freeSpace.HasValue)
                {
                    _logger.Trace("Free space check returned an invalid result for: {0}", path);
                    return true;
                }

                if (freeSpace < localEpisode.Size + 100.Megabytes())
                {
                    _logger.Warn("Not enough free space to import: {0}", localEpisode);
                    return false;
                }
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Unable to check free disk space while importing: " + localEpisode.Path, ex);
            }

            return true;
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("{0} is in series folder, skipping unpacking check", localEpisode.Path);
                return Decision.Accept();
            }

            foreach (var workingFolder in _configService.DownloadClientWorkingFolders.Split('|'))
            {
                DirectoryInfo parent = Directory.GetParent(localEpisode.Path);
                while (parent != null)
                {
                    if (parent.Name.StartsWith(workingFolder))
                    {
                        if (OsInfo.IsNotWindows)
                        {
                            _logger.Debug("{0} is still being unpacked", localEpisode.Path);
                            return Decision.Reject("File is still being unpacked");
                        }

                        if (_diskProvider.FileGetLastWrite(localEpisode.Path) > DateTime.UtcNow.AddMinutes(-5))
                        {
                            _logger.Debug("{0} appears to be unpacking still", localEpisode.Path);
                            return Decision.Reject("File is still being unpacked");
                        }
                    }

                    parent = parent.Parent;
                }
            }

            return Decision.Accept();
        }
        public EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
        {
            var moveFileResult = new EpisodeFileMoveResult();
            var existingFiles = localEpisode.Episodes
                                            .Where(e => e.EpisodeFileId > 0)
                                            .Select(e => e.EpisodeFile.Value)
                                            .GroupBy(e => e.Id);

            foreach (var existingFile in existingFiles)
            {
                var file = existingFile.First();

                if (_diskProvider.FileExists(file.Path))
                {
                    _logger.Debug("Removing existing episode file: {0}", file);
                    _recycleBinProvider.DeleteFile(file.Path);
                }

                moveFileResult.OldFiles.Add(file);
                _mediaFileService.Delete(file, true);
            }

            moveFileResult.EpisodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);

            return moveFileResult;
        }
        private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource)
        {
            foreach (var file in videoFiles)
            {
                ImportDecision decision = null;

                try
                {
                    var parsedEpisode = _parsingService.GetEpisodes(file, series, sceneSource);
                    
                    if (parsedEpisode != null)
                    {
                        parsedEpisode.Size = _diskProvider.GetFileSize(file);
                        decision = GetDecision(parsedEpisode);
                    }

                    else
                    {
                        parsedEpisode = new LocalEpisode();
                        parsedEpisode.Path = file;

                        decision = new ImportDecision(parsedEpisode, "Unable to parse file");
                    }
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Couldn't import file." + file, e);
                }

                if (decision != null)
                {
                    yield return decision;
                }
            }
        }
 public string MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
 {
     var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
     var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
     MoveFile(episodeFile, localEpisode.Series, filePath);
     
     return filePath;
 }
Example #8
0
 public EpisodeImportedEvent(LocalEpisode episodeInfo, EpisodeFile importedEpisode, bool newDownload, string downloadClient, string downloadClientId)
 {
     EpisodeInfo = episodeInfo;
     ImportedEpisode = importedEpisode;
     NewDownload = newDownload;
     DownloadClient = downloadClient;
     DownloadClientId = downloadClientId;
 }
 public EpisodeImportedEvent(LocalEpisode episodeInfo, EpisodeFile importedEpisode, bool newDownload, string downloadClient, string downloadId, bool isReadOnly)
 {
     EpisodeInfo = episodeInfo;
     ImportedEpisode = importedEpisode;
     NewDownload = newDownload;
     DownloadClient = downloadClient;
     DownloadId = downloadId;
     IsReadOnly = isReadOnly;
 }
Example #10
0
        public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
        {
            var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
            var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));

            _logger.Debug("Copying episode file: {0} to {1}", episodeFile, filePath);

            return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, true);
        }
        public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
        {
            var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
            var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));

            _logger.Trace("Moving episode file: {0} to {1}", episodeFile, filePath);
            
            return MoveFile(episodeFile, localEpisode.Series, filePath);
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ParsedEpisodeInfo.FullSeason)
            {
                _logger.Debug("Single episode file detected as containing all episodes in the season");
                return Decision.Reject("Single episode file contains all episodes in seasons");
            }

            return Decision.Accept();
        }
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && e.EpisodeFile.Value.Quality > localEpisode.Quality))
            {
                _logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
                return false;
            }

            return true;
        }
Example #14
0
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ParsedEpisodeInfo.FullSeason)
            {
                _logger.Debug("Single episode file detected as containing all episodes in the season");
                return false;
            }

            return true;
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (_sameEpisodesSpecification.IsSatisfiedBy(localEpisode.Episodes))
            {
                return Decision.Accept();
            }

            _logger.Debug("Episode file on disk contains more episodes than this file contains");
            return Decision.Reject("Episode file on disk contains more episodes than this file contains");
        }
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Trace("Existing file, skipping sample check");
                return true;
            }

            if (localEpisode.Series.SeriesType == SeriesTypes.Daily)
            {
                _logger.Trace("Daily Series, skipping sample check");
                return true;
            }

            if (localEpisode.SeasonNumber == 0)
            {
                _logger.Trace("Special, skipping sample check");
                return true;
            }

            var extension = Path.GetExtension(localEpisode.Path);

            if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase))
            {
                _logger.Trace("Skipping sample check for .flv file");
                return true;
            }

            try
            {
                var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path);

                if (runTime.TotalMinutes.Equals(0))
                {
                    _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", localEpisode);
                    return false;
                }

                if (runTime.TotalSeconds < 90)
                {
                    _logger.Trace("[{0}] appears to be a sample. Size: {1} Runtime: {2}", localEpisode.Path, localEpisode.Size, runTime);
                    return false;
                }
            }

            catch (DllNotFoundException)
            {
                _logger.Trace("Falling back to file size detection");

                return CheckSize(localEpisode);
            }

            _logger.Trace("Runtime is over 90 seconds");
            return true;
        }
        public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
        {
            var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
            var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));

            EnsureEpisodeFolder(episodeFile, localEpisode, filePath);

            _logger.Debug("Moving episode file: {0} to {1}", episodeFile.Path, filePath);
            
            return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move);
        }
Example #18
0
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile);
            if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0))
            {
                _logger.Debug("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
                return Decision.Reject("Not an upgrade for existing episode file(s)");
            }

            return Decision.Accept();
        }
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile);
            if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0))
            {
                _logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
                return false;
            }

            return true;
        }
Example #20
0
        public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
        {
            var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
            var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));

            if (_configService.CopyUsingHardlinks)
            {
                _logger.Debug("Hardlinking episode file: {0} to {1}", episodeFile.Path, filePath);
                return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.HardLinkOrCopy);
            }

            _logger.Debug("Copying episode file: {0} to {1}", episodeFile.Path, filePath);
            return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Copy);
        }
Example #21
0
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("Existing file, skipping sample check");
                return true;
            }

            return !_sampleService.IsSample(localEpisode.Series,
                                            localEpisode.Quality,
                                            localEpisode.Path,
                                            localEpisode.Size,
                                            localEpisode.SeasonNumber);
        }
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Trace("{0} is in series folder, skipping in use check", localEpisode.Path);
                return true;
            }

            if (_diskProvider.IsFileLocked(localEpisode.Path))
            {
                _logger.Trace("{0} is in use");
                return false;
            }

            return true;
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("Skipping scene numbering check for existing episode");
                return Decision.Accept();
            }

            if (localEpisode.Episodes.Any(v => v.UnverifiedSceneNumbering))
            {
                _logger.Debug("This file uses unverified scene numbers, will not auto-import until numbering is confirmed on TheXEM. Skipping {0}", localEpisode.Path);
                return Decision.Reject("This show has individual episode mappings on TheXEM but the mapping for this episode has not been confirmed yet by their administrators. TheXEM needs manual input.");
            }

            return Decision.Accept();
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                return Decision.Accept();
            }

            var dirInfo = new FileInfo(localEpisode.Path).Directory;

            if (dirInfo == null)
            {
                return Decision.Accept();
            }

            var folderInfo = Parser.Parser.ParseTitle(dirInfo.Name);

            if (folderInfo == null)
            {
                return Decision.Accept();
            }

            if (!folderInfo.EpisodeNumbers.Any())
            {
                return Decision.Accept();
            }

            if (folderInfo.FullSeason)
            {
                return Decision.Accept();
            }

            var unexpected = localEpisode.ParsedEpisodeInfo.EpisodeNumbers.Where(f => !folderInfo.EpisodeNumbers.Contains(f)).ToList();

            if (unexpected.Any())
            {
                _logger.Debug("Unexpected episode number(s) in file: {0}", string.Join(", ", unexpected));

                if (unexpected.Count == 1)
                {
                    return Decision.Reject("Episode Number {0} was unexpected considering the {1} folder name", unexpected.First(), dirInfo.Name);
                }

                return Decision.Reject("Episode Numbers {0} were unexpected considering the {1} folder name", string.Join(", ", unexpected), dirInfo.Name);
            }

            return Decision.Accept();
        }
        private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series, bool sceneSource, QualityModel quality = null)
        {
            foreach (var file in videoFiles)
            {
                ImportDecision decision = null;

                try
                {
                    var parsedEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource);
                    
                    if (parsedEpisode != null)
                    {
                        if (quality != null && new QualityModelComparer(parsedEpisode.Series.QualityProfile).Compare(quality, parsedEpisode.Quality) > 0)
                        {
                            _logger.Trace("Using quality from folder: {0}", quality);
                            parsedEpisode.Quality = quality;
                        }

                        parsedEpisode.Size = _diskProvider.GetFileSize(file);
                        _logger.Trace("Size: {0}", parsedEpisode.Size);

                        decision = GetDecision(parsedEpisode);
                    }

                    else
                    {
                        parsedEpisode = new LocalEpisode();
                        parsedEpisode.Path = file;

                        decision = new ImportDecision(parsedEpisode, "Unable to parse file");
                    }
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Couldn't import file." + file, e);
                }

                if (decision != null)
                {
                    yield return decision;
                }
            }
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (_configService.SkipFreeSpaceCheckWhenImporting)
            {
                _logger.Debug("Skipping free space check when importing");
                return Decision.Accept();
            }

            try
            {
                if (localEpisode.ExistingFile)
                {
                    _logger.Debug("Skipping free space check for existing episode");
                    return Decision.Accept();
                }

                var path = Directory.GetParent(localEpisode.Series.Path);
                var freeSpace = _diskProvider.GetAvailableSpace(path.FullName);

                if (!freeSpace.HasValue)
                {
                    _logger.Debug("Free space check returned an invalid result for: {0}", path);
                    return Decision.Accept();
                }

                if (freeSpace < localEpisode.Size + 100.Megabytes())
                {
                    _logger.Warn("Not enough free space ({0}) to import: {1} ({2})", freeSpace, localEpisode, localEpisode.Size);
                    return Decision.Reject("Not enough free space");
                }
            }
            catch (DirectoryNotFoundException ex)
            {
                _logger.Error("Unable to check free disk space while importing. " + ex.Message);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Unable to check free disk space while importing: " + localEpisode.Path);
            }

            return Decision.Accept();
        }
        public bool IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.Series.SeriesType == SeriesTypes.Daily)
            {
                _logger.Trace("Daily Series, skipping sample check");
                return true;
            }

            if (localEpisode.SeasonNumber == 0)
            {
                _logger.Trace("Special, skipping sample check");
                return true;
            }

            if (Path.GetExtension(localEpisode.Path).Equals(".flv", StringComparison.InvariantCultureIgnoreCase))
            {
                _logger.Trace("Skipping smaple check for .flv file");
                return true;
            }

            if (localEpisode.Size > SampleSizeLimit)
            {
                return true;
            }

            var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path);

            if (runTime.TotalMinutes.Equals(0))
            {
                _logger.Error("[{0}] has a runtime of 0, is it a valid video file?", localEpisode);
                return false;
            }

            if (runTime.TotalMinutes < 3)
            {
                _logger.Trace("[{0}] appears to be a sample. Size: {1} Runtime: {2}", localEpisode.Path, localEpisode.Size, runTime);
                return false;
            }

            return true;
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("Existing file, skipping sample check");
                return Decision.Accept();
            }

            var sample = _detectSample.IsSample(localEpisode.Series,
                                                localEpisode.Quality,
                                                localEpisode.Path,
                                                localEpisode.Size,
                                                localEpisode.IsSpecial);

            if (sample)
            {
                return Decision.Reject("Sample");
            }

            return Decision.Accept();
        }
        public string UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
        {
            var existingFiles = localEpisode.Episodes
                                            .Where(e => e.EpisodeFileId > 0)
                                            .Select(e => e.EpisodeFile.Value)
                                            .GroupBy(e => e.Id);

            foreach (var existingFile in existingFiles)
            {
                var file = existingFile.First();

                if (_diskProvider.FileExists(file.Path))
                {
                    _logger.Trace("Removing existing episode file: {0}", file);
                    _recycleBinProvider.DeleteFile(file.Path);
                }

                _mediaFileService.Delete(file, true);
            }

            _logger.Trace("Moving episode file: {0}", episodeFile);
            return _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);
        }
Example #30
0
        private string GetSceneName(DownloadClientItem downloadClientItem, LocalEpisode localEpisode)
        {
            if (downloadClientItem != null)
            {
                var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title);

                var parsedTitle = Parser.Parser.ParseTitle(title);

                if (parsedTitle != null && !parsedTitle.FullSeason)
                {
                    return title;
                }
            }

            var fileName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());

            if (SceneChecker.IsSceneTitle(fileName))
            {
                return fileName;
            }

            return null;
        }