예제 #1
0
 protected override void Execute(EpisodeFile episodeFile)
 {
     if (Model.Files.Contains(episodeFile) && !episodeFile.InProcessing)
     {
         episodeFile.ShowSelector.DoProcessFile();
     }
 }
예제 #2
0
 protected override void Execute(EpisodeFile episodeFile)
 {
     if (Model.RunningFiles.Contains(episodeFile) && (episodeFile.RetryTimer != null))
     {
         episodeFile.Cancel();
         Model.RunningFiles.Remove(episodeFile);
     }
 }
예제 #3
0
 protected override void Execute(EpisodeFile episodeFile)
 {
     if (episodeFile.CanPerform)
     {
         Model.Files.Remove(episodeFile);
         Model.RunningFiles.Add(episodeFile);
     }
 }
예제 #4
0
 public EpisodeImportedEvent(LocalEpisode episodeInfo, EpisodeFile importedEpisode, bool newDownload, string downloadClient, string downloadClientId)
 {
     EpisodeInfo = episodeInfo;
     ImportedEpisode = importedEpisode;
     NewDownload = newDownload;
     DownloadClient = downloadClient;
     DownloadClientId = downloadClientId;
 }
예제 #5
0
 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;
 }
        public List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownload = false)
        {
            var qualifiedImports = GetQualifiedImports(decisions);
            var imported = new List<ImportDecision>();

            foreach (var importDecision in qualifiedImports)
            {
                var localEpisode = importDecision.LocalEpisode;

                try
                {
                    if (imported.SelectMany(r => r.LocalEpisode.Episodes)
                                         .Select(e => e.Id)
                                         .ToList()
                                         .Intersect(localEpisode.Episodes.Select(e => e.Id))
                                         .Any())
                    {
                        continue;
                    }

                    var episodeFile = new EpisodeFile();
                    episodeFile.DateAdded = DateTime.UtcNow;
                    episodeFile.SeriesId = localEpisode.Series.Id;
                    episodeFile.Path = localEpisode.Path.CleanFilePath();
                    episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
                    episodeFile.Quality = localEpisode.Quality;
                    episodeFile.SeasonNumber = localEpisode.SeasonNumber;
                    episodeFile.Episodes = localEpisode.Episodes;


                    if (newDownload)
                    {
                        episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
                        episodeFile.Path = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
                    }

                    _mediaFileService.Add(episodeFile);
                    imported.Add(importDecision);

                    if (newDownload)
                    {
                        _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile));
                        _eventAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode));
                    }
                }
                catch (Exception e)
                {
                    _logger.WarnException("Couldn't import episode " + localEpisode, e);
                }
            }

            return imported;
        }
예제 #7
0
 public void Setup()
 {
     _episodeFile = Builder <EpisodeFile>
                    .CreateNew()
                    .Build();
 }
예제 #8
0
 public override IEnumerable <ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile)
 {
     return(Enumerable.Empty <ExtraFile>());
 }
예제 #9
0
        public List <ImportResult> Import(List <ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto)
        {
            var qualifiedImports = decisions.Where(c => c.Approved)
                                   .GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
                                            .OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.QualityProfile))
                                            .ThenByDescending(c => c.LocalEpisode.Language, new LanguageComparer(s.First().LocalEpisode.Series.LanguageProfile))
                                            .ThenByDescending(c => c.LocalEpisode.Size))
                                   .SelectMany(c => c)
                                   .ToList();

            var importResults = new List <ImportResult>();

            foreach (var importDecision in qualifiedImports.OrderBy(e => e.LocalEpisode.Episodes.Select(episode => episode.EpisodeNumber).MinOrDefault())
                     .ThenByDescending(e => e.LocalEpisode.Size))
            {
                var localEpisode = importDecision.LocalEpisode;
                var oldFiles     = new List <EpisodeFile>();

                try
                {
                    //check if already imported
                    if (importResults.SelectMany(r => r.ImportDecision.LocalEpisode.Episodes)
                        .Select(e => e.Id)
                        .Intersect(localEpisode.Episodes.Select(e => e.Id))
                        .Any())
                    {
                        importResults.Add(new ImportResult(importDecision, "Episode has already been imported"));
                        continue;
                    }

                    var episodeFile = new EpisodeFile();
                    episodeFile.DateAdded    = DateTime.UtcNow;
                    episodeFile.SeriesId     = localEpisode.Series.Id;
                    episodeFile.Path         = localEpisode.Path.CleanFilePath();
                    episodeFile.Size         = _diskProvider.GetFileSize(localEpisode.Path);
                    episodeFile.Quality      = localEpisode.Quality;
                    episodeFile.MediaInfo    = localEpisode.MediaInfo;
                    episodeFile.SeasonNumber = localEpisode.SeasonNumber;
                    episodeFile.Episodes     = localEpisode.Episodes;
                    episodeFile.ReleaseGroup = localEpisode.ReleaseGroup;
                    episodeFile.Language     = localEpisode.Language;

                    bool copyOnly;
                    switch (importMode)
                    {
                    default:
                    case ImportMode.Auto:
                        copyOnly = downloadClientItem != null && !downloadClientItem.CanMoveFiles;
                        break;

                    case ImportMode.Move:
                        copyOnly = false;
                        break;

                    case ImportMode.Copy:
                        copyOnly = true;
                        break;
                    }

                    if (newDownload)
                    {
                        episodeFile.SceneName        = localEpisode.SceneName;
                        episodeFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localEpisode);

                        var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly);
                        oldFiles = moveResult.OldFiles;
                    }
                    else
                    {
                        episodeFile.RelativePath = localEpisode.Series.Path.GetRelativePath(episodeFile.Path);

                        // Delete existing files from the DB mapped to this path
                        var previousFiles = _mediaFileService.GetFilesWithRelativePath(localEpisode.Series.Id, episodeFile.RelativePath);

                        foreach (var previousFile in previousFiles)
                        {
                            _mediaFileService.Delete(previousFile, DeleteMediaFileReason.ManualOverride);
                        }
                    }

                    episodeFile = _mediaFileService.Add(episodeFile);
                    importResults.Add(new ImportResult(importDecision));

                    if (newDownload)
                    {
                        _extraService.ImportEpisode(localEpisode, episodeFile, copyOnly);
                    }

                    _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, oldFiles, newDownload, downloadClientItem));
                }
                catch (RootFolderNotFoundException e)
                {
                    _logger.Warn(e, "Couldn't import episode " + localEpisode);
                    _eventAggregator.PublishEvent(new EpisodeImportFailedEvent(e, localEpisode, newDownload, downloadClientItem));

                    importResults.Add(new ImportResult(importDecision, "Failed to import episode, Root folder missing."));
                }
                catch (DestinationAlreadyExistsException e)
                {
                    _logger.Warn(e, "Couldn't import episode " + localEpisode);
                    importResults.Add(new ImportResult(importDecision, "Failed to import episode, Destination already exists."));
                }
                catch (Exception e)
                {
                    _logger.Warn(e, "Couldn't import episode " + localEpisode);
                    importResults.Add(new ImportResult(importDecision, "Failed to import episode"));
                }
            }

            //Adding all the rejected decisions
            importResults.AddRange(decisions.Where(c => !c.Approved)
                                   .Select(d => new ImportResult(d, d.Rejections.Select(r => r.Reason).ToArray())));

            return(importResults);
        }
예제 #10
0
 public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile)
 {
     return(null);
 }
예제 #11
0
 protected virtual bool CanExecute(EpisodeFile episodeFile)
 {
     return true;
 }
예제 #12
0
 public EpisodeFileDeletedEvent(EpisodeFile episodeFile, DeleteMediaFileReason reason)
 {
     EpisodeFile = episodeFile;
     Reason = reason;
 }
예제 #13
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");
            }

            else
            {
                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));
        }
예제 #14
0
 public abstract List <ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile);
예제 #15
0
 public abstract MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile);
예제 #16
0
 public abstract ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly);
예제 #17
0
 public abstract IEnumerable <ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile);
예제 #18
0
        private void AddMediaInfoTokens(Dictionary <string, Func <TokenMatch, string> > tokenHandlers, EpisodeFile episodeFile)
        {
            if (episodeFile.MediaInfo == null)
            {
                return;
            }

            string videoCodec;

            switch (episodeFile.MediaInfo.VideoCodec)
            {
            case "AVC":
                if (episodeFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h264"))
                {
                    videoCodec = "h264";
                }
                else
                {
                    videoCodec = "x264";
                }
                break;

            case "V_MPEGH/ISO/HEVC":
                if (episodeFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h265"))
                {
                    videoCodec = "h265";
                }
                else
                {
                    videoCodec = "x265";
                }
                break;

            default:
                videoCodec = episodeFile.MediaInfo.VideoCodec;
                break;
            }

            string audioCodec;

            switch (episodeFile.MediaInfo.AudioFormat)
            {
            case "AC-3":
                audioCodec = "AC3";
                break;

            case "E-AC-3":
                audioCodec = "EAC3";
                break;

            case "MPEG Audio":
                if (episodeFile.MediaInfo.AudioProfile == "Layer 3")
                {
                    audioCodec = "MP3";
                }
                else
                {
                    audioCodec = episodeFile.MediaInfo.AudioFormat;
                }
                break;

            case "DTS":
                audioCodec = episodeFile.MediaInfo.AudioFormat;
                break;

            default:
                audioCodec = episodeFile.MediaInfo.AudioFormat;
                break;
            }

            var mediaInfoAudioLanguages = GetLanguagesToken(episodeFile.MediaInfo.AudioLanguages);

            if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
            {
                mediaInfoAudioLanguages = string.Format("[{0}]", mediaInfoAudioLanguages);
            }

            if (mediaInfoAudioLanguages == "[EN]")
            {
                mediaInfoAudioLanguages = string.Empty;
            }

            var mediaInfoSubtitleLanguages = GetLanguagesToken(episodeFile.MediaInfo.Subtitles);

            if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
            {
                mediaInfoSubtitleLanguages = string.Format("[{0}]", mediaInfoSubtitleLanguages);
            }

            var videoBitDepth = episodeFile.MediaInfo.VideoBitDepth > 0 ? episodeFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
            var audioChannels = episodeFile.MediaInfo.FormattedAudioChannels > 0 ?
                                episodeFile.MediaInfo.FormattedAudioChannels.ToString("F1", CultureInfo.InvariantCulture) :
                                string.Empty;

            tokenHandlers["{MediaInfo Video}"]         = m => videoCodec;
            tokenHandlers["{MediaInfo VideoCodec}"]    = m => videoCodec;
            tokenHandlers["{MediaInfo VideoBitDepth}"] = m => videoBitDepth;

            tokenHandlers["{MediaInfo Audio}"]         = m => audioCodec;
            tokenHandlers["{MediaInfo AudioCodec}"]    = m => audioCodec;
            tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannels;

            tokenHandlers["{MediaInfo Simple}"] = m => string.Format("{0} {1}", videoCodec, audioCodec);

            tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
        }
예제 #19
0
 public EpisodeImportedEvent(LocalEpisode episodeInfo, EpisodeFile importedEpisode, bool newDownload)
 {
     EpisodeInfo     = episodeInfo;
     ImportedEpisode = importedEpisode;
     NewDownload     = newDownload;
 }
예제 #20
0
 private void AddEpisodeFileTokens(Dictionary <String, Func <TokenMatch, String> > tokenHandlers, Series series, EpisodeFile episodeFile)
 {
     tokenHandlers["{Original Title}"]    = m => GetOriginalTitle(episodeFile);
     tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile);
     tokenHandlers["{Release Group}"]     = m => episodeFile.ReleaseGroup ?? "Sonarr";
 }
예제 #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, 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);
            }

            if (!_diskProvider.FileExists(episodeFile.Path))
            {
                Logger.Error("Episode file path does not exist, {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);
        }
예제 #22
0
        private void AddMediaInfoTokens(Dictionary <String, Func <TokenMatch, String> > tokenHandlers, EpisodeFile episodeFile)
        {
            if (episodeFile.MediaInfo == null)
            {
                return;
            }

            String mediaInfoVideo;

            switch (episodeFile.MediaInfo.VideoCodec)
            {
            case "AVC":
                // TODO: What to do if the original SceneName is hashed?
                if (!episodeFile.SceneName.IsNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h264"))
                {
                    mediaInfoVideo = "h264";
                }
                else
                {
                    mediaInfoVideo = "x264";
                }
                break;

            default:
                mediaInfoVideo = episodeFile.MediaInfo.VideoCodec;
                break;
            }

            String mediaInfoAudio;

            switch (episodeFile.MediaInfo.AudioFormat)
            {
            case "AC-3":
                mediaInfoAudio = "AC3";
                break;

            case "MPEG Audio":
                if (episodeFile.MediaInfo.AudioProfile == "Layer 3")
                {
                    mediaInfoAudio = "MP3";
                }
                else
                {
                    mediaInfoAudio = episodeFile.MediaInfo.AudioFormat;
                }
                break;

            case "DTS":
                mediaInfoAudio = episodeFile.MediaInfo.AudioFormat;
                break;

            default:
                mediaInfoAudio = episodeFile.MediaInfo.AudioFormat;
                break;
            }

            var mediaInfoAudioLanguages = GetLanguagesToken(episodeFile.MediaInfo.AudioLanguages);

            if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
            {
                mediaInfoAudioLanguages = String.Format("[{0}]", mediaInfoAudioLanguages);
            }

            if (mediaInfoAudioLanguages == "[EN]")
            {
                mediaInfoAudioLanguages = String.Empty;
            }

            var mediaInfoSubtitleLanguages = GetLanguagesToken(episodeFile.MediaInfo.Subtitles);

            if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
            {
                mediaInfoSubtitleLanguages = String.Format("[{0}]", mediaInfoSubtitleLanguages);
            }

            tokenHandlers["{MediaInfo Video}"] = m => mediaInfoVideo;
            tokenHandlers["{MediaInfo Audio}"] = m => mediaInfoAudio;

            tokenHandlers["{MediaInfo Simple}"] = m => String.Format("{0} {1}", mediaInfoVideo, mediaInfoAudio);

            tokenHandlers["{MediaInfo Full}"] = m => String.Format("{0} {1}{2} {3}", mediaInfoVideo, mediaInfoAudio, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
        }
예제 #23
0
 public EpisodeFileAddedEvent(EpisodeFile episodeFile)
 {
     EpisodeFile = episodeFile;
 }
예제 #24
0
        private MetadataFile WriteEpisodeMetadata(Series series, EpisodeFile episodeFile, List <MetadataFile> existingMetadataFiles)
        {
            var filename     = GetEpisodeMetadataFilename(episodeFile.Path);
            var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);

            var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeMetadata &&
                                                                         c.EpisodeFileId == episodeFile.Id);

            if (existingMetadata != null)
            {
                var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
                if (!filename.PathEquals(fullPath))
                {
                    _diskProvider.MoveFile(fullPath, filename);
                    existingMetadata.RelativePath = relativePath;
                }
            }

            _logger.Debug("Generating {0} for: {1}", filename, episodeFile.Path);

            var xmlResult = String.Empty;

            foreach (var episode in episodeFile.Episodes.Value)
            {
                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 details = new XElement("video");
                    details.Add(new XElement("title", String.Format("{0} - {1}x{2} - {3}", series.Title, episode.SeasonNumber, episode.EpisodeNumber, episode.Title)));
                    details.Add(new XElement("year", episode.AirDate));
                    details.Add(new XElement("genre", String.Join(" / ", series.Genres)));
                    var actors = String.Join(" , ", series.Actors.ConvertAll(c => c.Name + " - " + c.Character).GetRange(0, Math.Min(3, series.Actors.Count)));
                    details.Add(new XElement("actors", actors));
                    details.Add(new XElement("description", episode.Overview));
                    details.Add(new XElement("length", series.Runtime));
                    details.Add(new XElement("mpaa", ValidCertification.Contains(series.Certification.ToUpperInvariant()) ? series.Certification.ToUpperInvariant() : "UNRATED"));
                    doc.Add(details);
                    doc.Save(xw);

                    xmlResult += doc.ToString();
                    xmlResult += Environment.NewLine;
                }
            }

            _logger.Debug("Saving episodedetails to: {0}", filename);
            _diskProvider.WriteAllText(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));

            var metadata = existingMetadata ??
                           new MetadataFile
            {
                SeriesId      = series.Id,
                EpisodeFileId = episodeFile.Id,
                Consumer      = GetType().Name,
                Type          = MetadataType.EpisodeMetadata,
                RelativePath  = DiskProviderBase.GetRelativePath(series.Path, filename)
            };

            return(metadata);
        }
예제 #25
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);
            }

            var parseResult = Parser.ParsePath(filePath);

            if (parseResult == null)
            {
                return(null);
            }

            var size    = _diskProvider.GetSize(filePath);
            var runTime = _mediaInfoProvider.GetRunTime(filePath);

            if (series.IsDaily || parseResult.SeasonNumber > 0)
            {
                if (size < Constants.IgnoreFileSize && runTime < 180)
                {
                    Logger.Trace("[{0}] appears to be a sample. skipping.", filePath);
                    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.Info("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;

            //Todo: We shouldn't actually import the file until we confirm its the only one we want.
            //Todo: Separate episodeFile creation from importing (pass file to import to import)
            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);
        }
예제 #26
0
 private void AddEpisodeFileTokens(Dictionary <string, Func <TokenMatch, string> > tokenHandlers, EpisodeFile episodeFile)
 {
     tokenHandlers["{Original Title}"]    = m => GetOriginalTitle(episodeFile);
     tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile);
     tokenHandlers["{Release Group}"]     = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
 }
예제 #27
0
 public override List <ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
 {
     return(new List <ImageFileResult>());
 }
예제 #28
0
        private void AddQualityTokens(Dictionary <string, Func <TokenMatch, string> > tokenHandlers, Series series, EpisodeFile episodeFile)
        {
            var qualityTitle  = _qualityDefinitionService.Get(episodeFile.Quality.Quality).Title;
            var qualityProper = GetQualityProper(series, episodeFile.Quality);
            var qualityReal   = GetQualityReal(series, episodeFile.Quality);

            tokenHandlers["{Quality Full}"]   = m => String.Format("{0} {1} {2}", qualityTitle, qualityProper, qualityReal);
            tokenHandlers["{Quality Title}"]  = m => qualityTitle;
            tokenHandlers["{Quality Proper}"] = m => qualityProper;
            tokenHandlers["{Quality Real}"]   = m => qualityReal;
        }
예제 #29
0
        public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile)
        {
            if (!Settings.EpisodeMetadata)
            {
                return(null);
            }

            _logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath));

            var xmlResult = string.Empty;

            foreach (var episode in episodeFile.Episodes.Value)
            {
                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 image = episode.Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);

                    var details = new XElement("episodedetails");
                    details.Add(new XElement("title", episode.Title));
                    details.Add(new XElement("season", episode.SeasonNumber));
                    details.Add(new XElement("episode", episode.EpisodeNumber));
                    details.Add(new XElement("aired", episode.AirDate));
                    details.Add(new XElement("plot", episode.Overview));

                    //If trakt ever gets airs before information for specials we should add set it
                    details.Add(new XElement("displayseason"));
                    details.Add(new XElement("displayepisode"));

                    if (image == null)
                    {
                        details.Add(new XElement("thumb"));
                    }

                    else
                    {
                        details.Add(new XElement("thumb", image.Url));
                    }

                    details.Add(new XElement("watched", "false"));

                    if (episode.Ratings != null && episode.Ratings.Votes > 0)
                    {
                        details.Add(new XElement("rating", episode.Ratings.Value));
                    }

                    if (episodeFile.MediaInfo != null)
                    {
                        var fileInfo      = new XElement("fileinfo");
                        var streamDetails = new XElement("streamdetails");

                        var video = new XElement("video");
                        video.Add(new XElement("aspect", (float)episodeFile.MediaInfo.Width / (float)episodeFile.MediaInfo.Height));
                        video.Add(new XElement("bitrate", episodeFile.MediaInfo.VideoBitrate));
                        video.Add(new XElement("codec", episodeFile.MediaInfo.VideoCodec));
                        video.Add(new XElement("framerate", episodeFile.MediaInfo.VideoFps));
                        video.Add(new XElement("height", episodeFile.MediaInfo.Height));
                        video.Add(new XElement("scantype", episodeFile.MediaInfo.ScanType));
                        video.Add(new XElement("width", episodeFile.MediaInfo.Width));

                        if (episodeFile.MediaInfo.RunTime != null)
                        {
                            video.Add(new XElement("duration", episodeFile.MediaInfo.RunTime.TotalMinutes));
                            video.Add(new XElement("durationinseconds", episodeFile.MediaInfo.RunTime.TotalSeconds));
                        }

                        streamDetails.Add(video);

                        var audio = new XElement("audio");
                        audio.Add(new XElement("bitrate", episodeFile.MediaInfo.AudioBitrate));
                        audio.Add(new XElement("channels", episodeFile.MediaInfo.AudioChannels));
                        audio.Add(new XElement("codec", GetAudioCodec(episodeFile.MediaInfo.AudioFormat)));
                        audio.Add(new XElement("language", episodeFile.MediaInfo.AudioLanguages));
                        streamDetails.Add(audio);

                        if (episodeFile.MediaInfo.Subtitles != null && episodeFile.MediaInfo.Subtitles.Length > 0)
                        {
                            var subtitle = new XElement("subtitle");
                            subtitle.Add(new XElement("language", episodeFile.MediaInfo.Subtitles));
                            streamDetails.Add(subtitle);
                        }

                        fileInfo.Add(streamDetails);
                        details.Add(fileInfo);
                    }

                    //Todo: get guest stars, writer and director
                    //details.Add(new XElement("credits", tvdbEpisode.Writer.FirstOrDefault()));
                    //details.Add(new XElement("director", tvdbEpisode.Directors.FirstOrDefault()));

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

                    xmlResult += doc.ToString();
                    xmlResult += Environment.NewLine;
                }
            }

            return(new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())));
        }
예제 #30
0
        private void AddMediaInfoTokens(Dictionary <string, Func <TokenMatch, string> > tokenHandlers, EpisodeFile episodeFile)
        {
            if (episodeFile.MediaInfo == null)
            {
                return;
            }

            var sceneName = episodeFile.GetSceneOrFileName();

            var videoCodec    = MediaInfoFormatter.FormatVideoCodec(episodeFile.MediaInfo, sceneName);
            var audioCodec    = MediaInfoFormatter.FormatAudioCodec(episodeFile.MediaInfo, sceneName);
            var audioChannels = MediaInfoFormatter.FormatAudioChannels(episodeFile.MediaInfo);

            var mediaInfoAudioLanguages = GetLanguagesToken(episodeFile.MediaInfo.AudioLanguages);

            if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
            {
                mediaInfoAudioLanguages = $"[{mediaInfoAudioLanguages}]";
            }

            if (mediaInfoAudioLanguages == "[EN]")
            {
                mediaInfoAudioLanguages = string.Empty;
            }

            var mediaInfoSubtitleLanguages = GetLanguagesToken(episodeFile.MediaInfo.Subtitles);

            if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
            {
                mediaInfoSubtitleLanguages = $"[{mediaInfoSubtitleLanguages}]";
            }

            var videoBitDepth          = episodeFile.MediaInfo.VideoBitDepth > 0 ? episodeFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
            var audioChannelsFormatted = audioChannels > 0 ?
                                         audioChannels.ToString("F1", CultureInfo.InvariantCulture) :
                                         string.Empty;

            tokenHandlers["{MediaInfo Video}"]         = m => videoCodec;
            tokenHandlers["{MediaInfo VideoCodec}"]    = m => videoCodec;
            tokenHandlers["{MediaInfo VideoBitDepth}"] = m => videoBitDepth;

            tokenHandlers["{MediaInfo Audio}"]         = m => audioCodec;
            tokenHandlers["{MediaInfo AudioCodec}"]    = m => audioCodec;
            tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannelsFormatted;

            tokenHandlers["{MediaInfo Simple}"] = m => $"{videoCodec} {audioCodec}";

            tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{mediaInfoAudioLanguages} {mediaInfoSubtitleLanguages}";
        }
예제 #31
0
        public void ImportEpisode(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly)
        {
            ImportExtraFiles(localEpisode, episodeFile, isReadOnly);

            CreateAfterImport(localEpisode.Series, episodeFile);
        }
예제 #32
0
 protected override bool CanExecute(EpisodeFile episodeFile)
 {
     return (Model.RunningFiles.Contains(episodeFile) && (episodeFile.RetryTimer != null));
 }
예제 #33
0
 public EpisodeFileAction(EpisodeFile file)
 {
     this.file = file;
     this.file.PropertyChanged += (s, e) => OnPropertyChanged("TargetPath");
 }
예제 #34
0
 public EpisodeFileDeletedEvent(EpisodeFile episodeFile, Boolean forUpgrade)
 {
     EpisodeFile = episodeFile;
     ForUpgrade = forUpgrade;
 }
예제 #35
0
 protected override void Execute(EpisodeFile episodeFile)
 {
     episodeFile.Cancel();
     Model.Files.Remove(episodeFile);
 }
예제 #36
0
        private void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly)
        {
            if (!_configService.ImportExtraFiles)
            {
                return;
            }

            var sourcePath     = localEpisode.Path;
            var sourceFolder   = _diskProvider.GetParentFolder(sourcePath);
            var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
            var files          = _diskProvider.GetFiles(sourceFolder, SearchOption.TopDirectoryOnly);

            var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                   .Select(e => e.Trim(' ', '.'))
                                   .ToList();

            var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)).ToList();
            var filteredFilenames = new List <string>();
            var hasNfo            = false;

            foreach (var matchingFilename in matchingFilenames)
            {
                // Filter out duplicate NFO files

                if (matchingFilename.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (hasNfo)
                    {
                        continue;
                    }

                    hasNfo = true;
                }

                filteredFilenames.Add(matchingFilename);
            }

            foreach (var matchingFilename in filteredFilenames)
            {
                var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e));

                if (matchingExtension == null)
                {
                    continue;
                }

                try
                {
                    foreach (var extraFileManager in _extraFileManagers)
                    {
                        var extension = Path.GetExtension(matchingFilename);
                        var extraFile = extraFileManager.Import(localEpisode.Series, episodeFile, matchingFilename, extension, isReadOnly);

                        if (extraFile != null)
                        {
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename);
                }
            }
        }
예제 #37
0
        public List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownload = false)
        {
            var qualifiedImports = decisions.Where(c => c.Approved)
                .GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
                    .OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.QualityProfile))
                    .ThenByDescending(c => c.LocalEpisode.Size))
                .SelectMany(c => c)
                .ToList();

            var imported = new List<ImportDecision>();

            foreach (var importDecision in qualifiedImports.OrderByDescending(e => e.LocalEpisode.Size))
            {
                var localEpisode = importDecision.LocalEpisode;
                var oldFiles = new List<EpisodeFile>();

                try
                {
                    //check if already imported
                    if (imported.SelectMany(r => r.LocalEpisode.Episodes)
                                         .Select(e => e.Id)
                                         .Intersect(localEpisode.Episodes.Select(e => e.Id))
                                         .Any())
                    {
                        continue;
                    }

                    var episodeFile = new EpisodeFile();
                    episodeFile.DateAdded = DateTime.UtcNow;
                    episodeFile.SeriesId = localEpisode.Series.Id;
                    episodeFile.Path = localEpisode.Path.CleanFilePath();
                    episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
                    episodeFile.Quality = localEpisode.Quality;
                    episodeFile.SeasonNumber = localEpisode.SeasonNumber;
                    episodeFile.Episodes = localEpisode.Episodes;
                    episodeFile.ReleaseGroup = localEpisode.ParsedEpisodeInfo.ReleaseGroup;

                    if (newDownload)
                    {
                        episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
                        var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode);
                        oldFiles = moveResult.OldFiles;
                    }

                    _mediaFileService.Add(episodeFile);
                    imported.Add(importDecision);

                    _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, newDownload));

                    if (newDownload)
                    {
                        _eventAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode, episodeFile, oldFiles));
                    }
                }
                catch (Exception e)
                {
                    _logger.WarnException("Couldn't import episode " + localEpisode, e);
                }
            }

            return imported;
        }
예제 #38
0
        public override IEnumerable <ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile)
        {
            var files = new List <MetadataFile>();

            foreach (var consumer in _metadataFactory.Enabled())
            {
                files.AddIfNotNull(ProcessEpisodeMetadata(consumer, series, episodeFile, new List <MetadataFile>()));
                files.AddRange(ProcessEpisodeImages(consumer, series, episodeFile, new List <MetadataFile>()));
            }

            _metadataFileService.Upsert(files);

            return(files);
        }
예제 #39
0
 public EpisodeImportedEvent(LocalEpisode droppedEpisode, EpisodeFile importedEpisode)
 {
     DroppedEpisode = droppedEpisode;
     ImportedEpisode = importedEpisode;
 }
예제 #40
0
 public override ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly)
 {
     return(null);
 }
예제 #41
0
 public EpisodeFolderCreatedEvent(Series series, EpisodeFile episodeFile)
 {
     Series = series;
     EpisodeFile = episodeFile;
 }
예제 #42
0
        private MetadataFile ProcessEpisodeMetadata(IMetadata consumer, Series series, EpisodeFile episodeFile, List <MetadataFile> existingMetadataFiles)
        {
            var episodeMetadata = consumer.EpisodeMetadata(series, episodeFile);

            if (episodeMetadata == null)
            {
                return(null);
            }

            var fullPath = Path.Combine(series.Path, episodeMetadata.RelativePath);

            _otherExtraFileRenamer.RenameOtherExtraFile(series, fullPath);

            var existingMetadata = GetMetadataFile(series, existingMetadataFiles, c => c.Type == MetadataType.EpisodeMetadata &&
                                                   c.EpisodeFileId == episodeFile.Id);

            if (existingMetadata != null)
            {
                var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
                if (fullPath.PathNotEquals(existingFullPath))
                {
                    _diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move);
                    existingMetadata.RelativePath = episodeMetadata.RelativePath;
                }
            }

            var hash = episodeMetadata.Contents.SHA256Hash();

            var metadata = existingMetadata ??
                           new MetadataFile
            {
                SeriesId      = series.Id,
                SeasonNumber  = episodeFile.SeasonNumber,
                EpisodeFileId = episodeFile.Id,
                Consumer      = consumer.GetType().Name,
                Type          = MetadataType.EpisodeMetadata,
                RelativePath  = episodeMetadata.RelativePath,
                Extension     = Path.GetExtension(fullPath)
            };

            if (hash == metadata.Hash)
            {
                return(null);
            }

            _logger.Debug("Writing Episode Metadata to: {0}", fullPath);
            SaveMetadataFile(fullPath, episodeMetadata.Contents);

            metadata.Hash = hash;

            return(metadata);
        }
예제 #43
0
 public EpisodeImportedEvent(LocalEpisode episodeInfo, EpisodeFile importedEpisode, bool newDownload)
 {
     EpisodeInfo = episodeInfo;
     ImportedEpisode = importedEpisode;
     NewDownload = newDownload;
 }
예제 #44
0
        private List <MetadataFile> ProcessEpisodeImages(IMetadata consumer, Series series, EpisodeFile episodeFile, List <MetadataFile> existingMetadataFiles)
        {
            var result = new List <MetadataFile>();

            foreach (var image in consumer.EpisodeImages(series, episodeFile))
            {
                var fullPath = Path.Combine(series.Path, image.RelativePath);

                if (_diskProvider.FileExists(fullPath))
                {
                    _logger.Debug("Episode image already exists: {0}", fullPath);
                    continue;
                }

                _otherExtraFileRenamer.RenameOtherExtraFile(series, fullPath);

                var existingMetadata = GetMetadataFile(series, existingMetadataFiles, c => c.Type == MetadataType.EpisodeImage &&
                                                       c.EpisodeFileId == episodeFile.Id);

                if (existingMetadata != null)
                {
                    var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
                    if (fullPath.PathNotEquals(existingFullPath))
                    {
                        _diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move);
                        existingMetadata.RelativePath = image.RelativePath;

                        return(new List <MetadataFile> {
                            existingMetadata
                        });
                    }
                }

                var metadata = existingMetadata ??
                               new MetadataFile
                {
                    SeriesId      = series.Id,
                    SeasonNumber  = episodeFile.SeasonNumber,
                    EpisodeFileId = episodeFile.Id,
                    Consumer      = consumer.GetType().Name,
                    Type          = MetadataType.EpisodeImage,
                    RelativePath  = image.RelativePath,
                    Extension     = Path.GetExtension(fullPath)
                };

                DownloadImage(series, image);

                result.Add(metadata);
            }

            return(result);
        }
예제 #45
0
 public EpisodeDownloadedEvent(LocalEpisode episode, EpisodeFile episodeFile, List<EpisodeFile> oldFiles)
 {
     Episode = episode;
     EpisodeFile = episodeFile;
     OldFiles = oldFiles;
 }
예제 #46
0
        public void RemoveEpisodeFromCollection(TraktSettings settings, Series series, EpisodeFile episodeFile)
        {
            var payload = new TraktCollectShowsResource
            {
                Shows = new List <TraktCollectShow>()
            };

            var payloadEpisodes = new List <TraktEpisodeResource>();

            foreach (var episode in episodeFile.Episodes.Value)
            {
                payloadEpisodes.Add(new TraktEpisodeResource
                {
                    Number = episode.EpisodeNumber
                });
            }

            var payloadSeasons = new List <TraktSeasonResource>();

            payloadSeasons.Add(new TraktSeasonResource
            {
                Number   = episodeFile.SeasonNumber,
                Episodes = payloadEpisodes
            });

            payload.Shows.Add(new TraktCollectShow
            {
                Title = series.Title,
                Year  = series.Year,
                Ids   = new TraktShowIdsResource
                {
                    Tvdb = series.TvdbId,
                    Imdb = series.ImdbId ?? "",
                },
                Seasons = payloadSeasons,
            });

            _proxy.RemoveFromCollection(payload, settings.AccessToken);
        }
예제 #47
0
 protected abstract void Execute(EpisodeFile episodeFile);
예제 #48
0
        private string MapAudio(EpisodeFile episodeFile)
        {
            var traktAudioFormat = string.Empty;

            var audioCodec = episodeFile.MediaInfo != null?MediaInfoFormatter.FormatAudioCodec(episodeFile.MediaInfo, episodeFile.SceneName) : string.Empty;

            switch (audioCodec)
            {
            case "AC3":
                traktAudioFormat = "dolby_digital";
                break;

            case "EAC3":
                traktAudioFormat = "dolby_digital_plus";
                break;

            case "TrueHD":
                traktAudioFormat = "dolby_truehd";
                break;

            case "EAC3 Atmos":
                traktAudioFormat = "dolby_digital_plus_atmos";
                break;

            case "TrueHD Atmos":
                traktAudioFormat = "dolby_atmos";
                break;

            case "DTS":
            case "DTS-ES":
                traktAudioFormat = "dts";
                break;

            case "DTS-HD MA":
                traktAudioFormat = "dts_ma";
                break;

            case "DTS-HD HRA":
                traktAudioFormat = "dts_hr";
                break;

            case "DTS-X":
                traktAudioFormat = "dts_x";
                break;

            case "MP3":
                traktAudioFormat = "mp3";
                break;

            case "MP2":
                traktAudioFormat = "mp2";
                break;

            case "Vorbis":
                traktAudioFormat = "ogg";
                break;

            case "WMA":
                traktAudioFormat = "wma";
                break;

            case "AAC":
                traktAudioFormat = "aac";
                break;

            case "PCM":
                traktAudioFormat = "lpcm";
                break;

            case "FLAC":
                traktAudioFormat = "flac";
                break;

            case "Opus":
                traktAudioFormat = "ogg_opus";
                break;
            }

            return(traktAudioFormat);
        }
예제 #49
0
        public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null)
        {
            var qualifiedImports = decisions.Where(c => c.Approved)
               .GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
                   .OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.Profile))
                   .ThenByDescending(c => c.LocalEpisode.Size))
               .SelectMany(c => c)
               .ToList();

            var importResults = new List<ImportResult>();

            foreach (var importDecision in qualifiedImports.OrderByDescending(e => e.LocalEpisode.Episodes.Select(episode => episode.EpisodeNumber).MinOrDefault())
                                                           .ThenByDescending(e => e.LocalEpisode.Size))
            {
                var localEpisode = importDecision.LocalEpisode;
                var oldFiles = new List<EpisodeFile>();

                try
                {
                    //check if already imported
                    if (importResults.SelectMany(r => r.ImportDecision.LocalEpisode.Episodes)
                                         .Select(e => e.Id)
                                         .Intersect(localEpisode.Episodes.Select(e => e.Id))
                                         .Any())
                    {
                        importResults.Add(new ImportResult(importDecision, "Episode has already been imported"));
                        continue;
                    }

                    var episodeFile = new EpisodeFile();
                    episodeFile.DateAdded = DateTime.UtcNow;
                    episodeFile.SeriesId = localEpisode.Series.Id;
                    episodeFile.Path = localEpisode.Path.CleanFilePath();
                    episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
                    episodeFile.Quality = localEpisode.Quality;
                    episodeFile.MediaInfo = localEpisode.MediaInfo;
                    episodeFile.SeasonNumber = localEpisode.SeasonNumber;
                    episodeFile.Episodes = localEpisode.Episodes;
                    episodeFile.ReleaseGroup = localEpisode.ParsedEpisodeInfo.ReleaseGroup;

                    if (newDownload)
                    {
                        bool copyOnly = downloadClientItem != null && downloadClientItem.IsReadOnly;

                        episodeFile.SceneName = GetSceneName(downloadClientItem, localEpisode);

                        var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly);
                        oldFiles = moveResult.OldFiles;
                    }
                    else
                    {
                        episodeFile.RelativePath = localEpisode.Series.Path.GetRelativePath(episodeFile.Path);
                    }

                    _mediaFileService.Add(episodeFile);
                    importResults.Add(new ImportResult(importDecision));

                    if (downloadClientItem != null)
                    {
                        _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, newDownload, downloadClientItem.DownloadClient, downloadClientItem.DownloadId));
                    }
                    else
                    {
                        _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, newDownload));
                    }

                    if (newDownload)
                    {
                        _eventAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode, episodeFile, oldFiles));
                    }
                }
                catch (Exception e)
                {
                    _logger.WarnException("Couldn't import episode " + localEpisode, e);
                    importResults.Add(new ImportResult(importDecision, "Failed to import episode"));
                }
            }

            //Adding all the rejected decisions
            importResults.AddRange(decisions.Where(c => !c.Approved)
                                            .Select(d => new ImportResult(d, d.Rejections.Select(r => r.Reason).ToArray())));

            return importResults;
        }
예제 #50
0
        private string MapAudioChannels(EpisodeFile episodeFile, string audioFormat)
        {
            var audioChannels = episodeFile.MediaInfo != null?MediaInfoFormatter.FormatAudioChannels(episodeFile.MediaInfo).ToString("0.0") : string.Empty;

            return(audioChannels);
        }