示例#1
0
        private CachedSeedConfiguration FetchIndexer(string infoHash)
        {
            var historyItem = _downloadHistoryService.GetLatestGrab(infoHash);

            if (historyItem == null)
            {
                _logger.Debug("No download history item for infohash {0}, unable to provide seed configuration", infoHash);
                return(null);
            }

            ParsedBookInfo parsedBookInfo = null;

            if (historyItem.Release != null)
            {
                parsedBookInfo = Parser.Parser.ParseBookTitle(historyItem.Release.Title);
            }

            if (parsedBookInfo == null)
            {
                _logger.Debug("No parsed title in download history item for infohash {0}, unable to provide seed configuration", infoHash);
                return(null);
            }

            return(new CachedSeedConfiguration
            {
                IndexerId = historyItem.IndexerId,
                Discography = parsedBookInfo.Discography
            });
        }
示例#2
0
        private Author GetAuthor(ParsedBookInfo parsedBookInfo, SearchCriteriaBase searchCriteria)
        {
            Author author = null;

            if (searchCriteria != null)
            {
                if (searchCriteria.Author.CleanName == parsedBookInfo.AuthorName.CleanAuthorName())
                {
                    return(searchCriteria.Author);
                }
            }

            author = _authorService.FindByName(parsedBookInfo.AuthorName);

            if (author == null)
            {
                _logger.Debug("Trying inexact author match for {0}", parsedBookInfo.AuthorName);
                author = _authorService.FindByNameInexact(parsedBookInfo.AuthorName);
            }

            if (author == null)
            {
                _logger.Debug("No matching author {0}", parsedBookInfo.AuthorName);
                return(null);
            }

            return(author);
        }
示例#3
0
 public RemoteBook Map(ParsedBookInfo parsedBookInfo, int authorId, IEnumerable <int> bookIds)
 {
     return(new RemoteBook
     {
         ParsedBookInfo = parsedBookInfo,
         Author = _authorService.GetAuthor(authorId),
         Books = _bookService.GetBooks(bookIds)
     });
 }
示例#4
0
        private static ParsedBookInfo ParseBookMatchCollection(MatchCollection matchCollection)
        {
            var authorName     = matchCollection[0].Groups["author"].Value.Replace('.', ' ').Replace('_', ' ');
            var bookTitle      = matchCollection[0].Groups["book"].Value.Replace('.', ' ').Replace('_', ' ');
            var releaseVersion = matchCollection[0].Groups["version"].Value.Replace('.', ' ').Replace('_', ' ');

            authorName     = RequestInfoRegex.Replace(authorName, "").Trim(' ');
            bookTitle      = RequestInfoRegex.Replace(bookTitle, "").Trim(' ');
            releaseVersion = RequestInfoRegex.Replace(releaseVersion, "").Trim(' ');

            int releaseYear;

            int.TryParse(matchCollection[0].Groups["releaseyear"].Value, out releaseYear);

            ParsedBookInfo result;

            result = new ParsedBookInfo();

            result.AuthorName      = authorName;
            result.BookTitle       = bookTitle;
            result.AuthorTitleInfo = GetAuthorTitleInfo(result.AuthorName);
            result.ReleaseDate     = releaseYear.ToString();
            result.ReleaseVersion  = releaseVersion;

            if (matchCollection[0].Groups["discography"].Success)
            {
                int discStart;
                int discEnd;
                int.TryParse(matchCollection[0].Groups["startyear"].Value, out discStart);
                int.TryParse(matchCollection[0].Groups["endyear"].Value, out discEnd);
                result.Discography = true;

                if (discStart > 0 && discEnd > 0)
                {
                    result.DiscographyStart = discStart;
                    result.DiscographyEnd   = discEnd;
                }
                else if (discEnd > 0)
                {
                    result.DiscographyEnd = discEnd;
                }

                result.BookTitle = "Discography";
            }

            Logger.Debug("Book Parsed. {0}", result);

            return(result);
        }
示例#5
0
        public RemoteBook Map(ParsedBookInfo parsedBookInfo, SearchCriteriaBase searchCriteria = null)
        {
            var remoteBook = new RemoteBook
            {
                ParsedBookInfo = parsedBookInfo,
            };

            var author = GetAuthor(parsedBookInfo, searchCriteria);

            if (author == null)
            {
                return(remoteBook);
            }

            remoteBook.Author = author;
            remoteBook.Books  = GetBooks(parsedBookInfo, author, searchCriteria);

            return(remoteBook);
        }
示例#6
0
        public void should_not_fail_if_search_criteria_contains_multiple_albums_with_the_same_name()
        {
            var artist = Builder <Author> .CreateNew().Build();

            var albums = Builder <Book> .CreateListOfSize(2).All().With(x => x.Title = "IdenticalTitle").Build().ToList();

            var criteria = new BookSearchCriteria
            {
                Author = artist,
                Books  = albums
            };

            var parsed = new ParsedBookInfo
            {
                BookTitle = "IdenticalTitle"
            };

            Subject.GetAlbums(parsed, artist, criteria).Should().BeEquivalentTo(new List <Book>());

            Mocker.GetMock <IBookService>()
            .Verify(s => s.FindByTitle(artist.AuthorMetadataId, "IdenticalTitle"), Times.Once());
        }
示例#7
0
        public void Setup()
        {
            Mocker.Resolve <UpgradableSpecification>();

            _parsedBookInfo = Builder <ParsedBookInfo> .CreateNew()
                              .With(p => p.Quality = new QualityModel(Quality.FLAC,
                                                                      new Revision(2, 0, false)))
                              .With(p => p.ReleaseGroup = "Readarr")
                              .Build();

            _albums = Builder <Book> .CreateListOfSize(1)
                      .All()
                      .BuildList();

            _trackFiles = Builder <BookFile> .CreateListOfSize(3)
                          .All()
                          .With(t => t.EditionId = _albums.First().Id)
                          .BuildList();

            Mocker.GetMock <IMediaFileService>()
            .Setup(c => c.GetFilesByBook(It.IsAny <int>()))
            .Returns(_trackFiles);
        }
示例#8
0
        public static ParsedBookInfo ParseBookTitleWithSearchCriteria(string title, Author author, List <Book> books)
        {
            try
            {
                if (!ValidateBeforeParsing(title))
                {
                    return(null);
                }

                var authorName = author.Name == "Various Authors" ? "VA" : author.Name.RemoveAccent();

                Logger.Debug("Parsing string '{0}' using search criteria author: '{1}' books: '{2}'",
                             title,
                             authorName.RemoveAccent(),
                             string.Join(", ", books.Select(a => a.Title.RemoveAccent())));

                var releaseTitle = RemoveFileExtension(title);

                var simpleTitle = SimpleTitleRegex.Replace(releaseTitle);

                simpleTitle = WebsitePrefixRegex.Replace(simpleTitle);
                simpleTitle = WebsitePostfixRegex.Replace(simpleTitle);

                simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle);

                var bestBook = books
                               .OrderByDescending(x => simpleTitle.FuzzyMatch(x.Editions.Value.Single(x => x.Monitored).Title, wordDelimiters: WordDelimiters))
                               .First()
                               .Editions.Value
                               .Single(x => x.Monitored);

                var foundAuthor = GetTitleFuzzy(simpleTitle, authorName, out var remainder);

                if (foundAuthor == null)
                {
                    foundAuthor = GetTitleFuzzy(simpleTitle, authorName.ToLastFirst(), out remainder);
                }

                var foundBook = GetTitleFuzzy(remainder, bestBook.Title, out _);

                if (foundBook == null)
                {
                    foundBook = GetTitleFuzzy(remainder, bestBook.Title.SplitBookTitle(authorName).Item1, out _);
                }

                Logger.Trace($"Found {foundAuthor} - {foundBook} with fuzzy parser");

                if (foundAuthor == null || foundBook == null)
                {
                    return(null);
                }

                var result = new ParsedBookInfo
                {
                    AuthorName      = foundAuthor,
                    AuthorTitleInfo = GetAuthorTitleInfo(foundAuthor),
                    BookTitle       = foundBook
                };

                try
                {
                    result.Quality = QualityParser.ParseQuality(title);
                    Logger.Debug("Quality parsed: {0}", result.Quality);

                    result.ReleaseGroup = ParseReleaseGroup(releaseTitle);

                    Logger.Debug("Release Group parsed: {0}", result.ReleaseGroup);

                    return(result);
                }
                catch (InvalidDateException ex)
                {
                    Logger.Debug(ex, ex.Message);
                }
            }
            catch (Exception e)
            {
                if (!title.ToLower().Contains("password") && !title.ToLower().Contains("yenc"))
                {
                    Logger.Error(e, "An error has occurred while trying to parse {0}", title);
                }
            }

            Logger.Debug("Unable to parse {0}", title);
            return(null);
        }
示例#9
0
        public void Setup()
        {
            _author = Builder <Author> .CreateNew()
                      .Build();

            _book = Builder <Book> .CreateNew()
                    .Build();

            _profile = new QualityProfile
            {
                Name   = "Test",
                Cutoff = Quality.MP3_320.Id,
                Items  = new List <QualityProfileQualityItem>
                {
                    new QualityProfileQualityItem {
                        Allowed = true, Quality = Quality.MP3_320
                    },
                    new QualityProfileQualityItem {
                        Allowed = true, Quality = Quality.MP3_320
                    },
                    new QualityProfileQualityItem {
                        Allowed = true, Quality = Quality.MP3_320
                    }
                },
            };

            _author.QualityProfile = new LazyLoaded <QualityProfile>(_profile);

            _release = Builder <ReleaseInfo> .CreateNew().Build();

            _parsedBookInfo = Builder <ParsedBookInfo> .CreateNew().Build();

            _parsedBookInfo.Quality = new QualityModel(Quality.MP3_320);

            _remoteBook       = new RemoteBook();
            _remoteBook.Books = new List <Book> {
                _book
            };
            _remoteBook.Author         = _author;
            _remoteBook.ParsedBookInfo = _parsedBookInfo;
            _remoteBook.Release        = _release;

            _temporarilyRejected = new DownloadDecision(_remoteBook, new Rejection("Temp Rejected", RejectionType.Temporary));

            Mocker.GetMock <IPendingReleaseRepository>()
            .Setup(s => s.All())
            .Returns(new List <PendingRelease>());

            Mocker.GetMock <IAuthorService>()
            .Setup(s => s.GetAuthor(It.IsAny <int>()))
            .Returns(_author);

            Mocker.GetMock <IAuthorService>()
            .Setup(s => s.GetAuthors(It.IsAny <IEnumerable <int> >()))
            .Returns(new List <Author> {
                _author
            });

            Mocker.GetMock <IParsingService>()
            .Setup(s => s.GetBooks(It.IsAny <ParsedBookInfo>(), _author, null))
            .Returns(new List <Book> {
                _book
            });

            Mocker.GetMock <IPrioritizeDownloadDecision>()
            .Setup(s => s.PrioritizeDecisions(It.IsAny <List <DownloadDecision> >()))
            .Returns((List <DownloadDecision> d) => d);
        }
示例#10
0
        public List <Book> GetBooks(ParsedBookInfo parsedBookInfo, Author author, SearchCriteriaBase searchCriteria = null)
        {
            var bookTitle = parsedBookInfo.BookTitle;
            var result    = new List <Book>();

            if (parsedBookInfo.BookTitle == null)
            {
                return(new List <Book>());
            }

            Book bookInfo = null;

            if (parsedBookInfo.Discography)
            {
                if (parsedBookInfo.DiscographyStart > 0)
                {
                    return(_bookService.AuthorBooksBetweenDates(author,
                                                                new DateTime(parsedBookInfo.DiscographyStart, 1, 1),
                                                                new DateTime(parsedBookInfo.DiscographyEnd, 12, 31),
                                                                false));
                }

                if (parsedBookInfo.DiscographyEnd > 0)
                {
                    return(_bookService.AuthorBooksBetweenDates(author,
                                                                new DateTime(1800, 1, 1),
                                                                new DateTime(parsedBookInfo.DiscographyEnd, 12, 31),
                                                                false));
                }

                return(_bookService.GetBooksByAuthor(author.Id));
            }

            if (searchCriteria != null)
            {
                var cleanTitle = Parser.CleanAuthorName(parsedBookInfo.BookTitle);
                bookInfo = searchCriteria.Books.ExclusiveOrDefault(e => e.Title == bookTitle || e.CleanTitle == cleanTitle);
            }

            if (bookInfo == null)
            {
                // TODO: Search by Title and Year instead of just Title when matching
                bookInfo = _bookService.FindByTitle(author.AuthorMetadataId, parsedBookInfo.BookTitle);
            }

            if (bookInfo == null)
            {
                var edition = _editionService.FindByTitle(author.AuthorMetadataId, parsedBookInfo.BookTitle);
                bookInfo = edition?.Book.Value;
            }

            if (bookInfo == null)
            {
                _logger.Debug("Trying inexact book match for {0}", parsedBookInfo.BookTitle);
                bookInfo = _bookService.FindByTitleInexact(author.AuthorMetadataId, parsedBookInfo.BookTitle);
            }

            if (bookInfo == null)
            {
                _logger.Debug("Trying inexact edition match for {0}", parsedBookInfo.BookTitle);
                var edition = _editionService.FindByTitleInexact(author.AuthorMetadataId, parsedBookInfo.BookTitle);
                bookInfo = edition?.Book.Value;
            }

            if (bookInfo != null)
            {
                result.Add(bookInfo);
            }
            else
            {
                _logger.Debug("Unable to find {0}", parsedBookInfo);
            }

            return(result);
        }
示例#11
0
        private IEnumerable <DownloadDecision> GetBookDecisions(List <ReleaseInfo> reports, SearchCriteriaBase searchCriteria = null)
        {
            if (reports.Any())
            {
                _logger.ProgressInfo("Processing {0} releases", reports.Count);
            }
            else
            {
                _logger.ProgressInfo("No results found");
            }

            var reportNumber = 1;

            foreach (var report in reports)
            {
                DownloadDecision decision = null;
                _logger.ProgressTrace("Processing release {0}/{1}", reportNumber, reports.Count);
                _logger.Debug("Processing release '{0}' from '{1}'", report.Title, report.Indexer);

                try
                {
                    var parsedBookInfo = Parser.Parser.ParseBookTitle(report.Title);

                    if (parsedBookInfo == null)
                    {
                        if (searchCriteria != null)
                        {
                            parsedBookInfo = Parser.Parser.ParseBookTitleWithSearchCriteria(report.Title,
                                                                                            searchCriteria.Author,
                                                                                            searchCriteria.Books);
                        }
                        else
                        {
                            // try parsing fuzzy
                            parsedBookInfo = _parsingService.ParseAlbumTitleFuzzy(report.Title);
                        }
                    }

                    if (parsedBookInfo != null && !parsedBookInfo.AuthorName.IsNullOrWhiteSpace())
                    {
                        var remoteBook = _parsingService.Map(parsedBookInfo, searchCriteria);

                        // try parsing again using the search criteria, in case it parsed but parsed incorrectly
                        if ((remoteBook.Author == null || remoteBook.Books.Empty()) && searchCriteria != null)
                        {
                            _logger.Debug("Author/Book null for {0}, reparsing with search criteria", report.Title);
                            var parsedBookInfoWithCriteria = Parser.Parser.ParseBookTitleWithSearchCriteria(report.Title,
                                                                                                            searchCriteria.Author,
                                                                                                            searchCriteria.Books);

                            if (parsedBookInfoWithCriteria != null && parsedBookInfoWithCriteria.AuthorName.IsNotNullOrWhiteSpace())
                            {
                                remoteBook = _parsingService.Map(parsedBookInfoWithCriteria, searchCriteria);
                            }
                        }

                        remoteBook.Release = report;

                        if (remoteBook.Author == null)
                        {
                            decision = new DownloadDecision(remoteBook, new Rejection("Unknown Author"));

                            // shove in the searched author in case of forced download in interactive search
                            if (searchCriteria != null)
                            {
                                remoteBook.Author = searchCriteria.Author;
                                remoteBook.Books  = searchCriteria.Books;
                            }
                        }
                        else if (remoteBook.Books.Empty())
                        {
                            decision = new DownloadDecision(remoteBook, new Rejection("Unable to parse books from release name"));
                            if (searchCriteria != null)
                            {
                                remoteBook.Books = searchCriteria.Books;
                            }
                        }
                        else
                        {
                            _aggregationService.Augment(remoteBook);
                            remoteBook.DownloadAllowed = remoteBook.Books.Any();
                            decision = GetDecisionForReport(remoteBook, searchCriteria);
                        }
                    }

                    if (searchCriteria != null)
                    {
                        if (parsedBookInfo == null)
                        {
                            parsedBookInfo = new ParsedBookInfo
                            {
                                Quality = QualityParser.ParseQuality(report.Title)
                            };
                        }

                        if (parsedBookInfo.AuthorName.IsNullOrWhiteSpace())
                        {
                            var remoteBook = new RemoteBook
                            {
                                Release        = report,
                                ParsedBookInfo = parsedBookInfo
                            };

                            decision = new DownloadDecision(remoteBook, new Rejection("Unable to parse release"));
                        }
                    }
                }
                catch (Exception e)
                {
                    _logger.Error(e, "Couldn't process release.");

                    var remoteBook = new RemoteBook {
                        Release = report
                    };
                    decision = new DownloadDecision(remoteBook, new Rejection("Unexpected error processing release"));
                }

                reportNumber++;

                if (decision != null)
                {
                    if (decision.Rejections.Any())
                    {
                        _logger.Debug("Release rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
                    }
                    else
                    {
                        _logger.Debug("Release accepted");
                    }

                    yield return(decision);
                }
            }
        }
示例#12
0
        public Tuple <List <LocalBook>, List <ImportDecision <LocalBook> > > GetLocalTracks(List <IFileInfo> musicFiles, DownloadClientItem downloadClientItem, ParsedTrackInfo folderInfo, FilterFilesType filter)
        {
            var watch = new System.Diagnostics.Stopwatch();

            watch.Start();

            var files = _mediaFileService.FilterUnchangedFiles(musicFiles, filter);

            var localTracks = new List <LocalBook>();
            var decisions   = new List <ImportDecision <LocalBook> >();

            _logger.Debug("Analyzing {0}/{1} files.", files.Count, musicFiles.Count);

            if (!files.Any())
            {
                return(Tuple.Create(localTracks, decisions));
            }

            ParsedBookInfo downloadClientItemInfo = null;

            if (downloadClientItem != null)
            {
                downloadClientItemInfo = Parser.Parser.ParseBookTitle(downloadClientItem.Title);
            }

            var i = 1;

            foreach (var file in files)
            {
                _logger.ProgressInfo($"Reading file {i++}/{files.Count}");

                var localTrack = new LocalBook
                {
                    DownloadClientAlbumInfo = downloadClientItemInfo,
                    FolderTrackInfo         = folderInfo,
                    Path           = file.FullName,
                    Size           = file.Length,
                    Modified       = file.LastWriteTimeUtc,
                    FileTrackInfo  = _eBookTagService.ReadTags(file),
                    AdditionalFile = false
                };

                try
                {
                    // TODO fix otherfiles?
                    _augmentingService.Augment(localTrack, true);
                    localTracks.Add(localTrack);
                }
                catch (AugmentingFailedException)
                {
                    decisions.Add(new ImportDecision <LocalBook>(localTrack, new Rejection("Unable to parse file")));
                }
                catch (Exception e)
                {
                    _logger.Error(e, "Couldn't import file. {0}", localTrack.Path);

                    decisions.Add(new ImportDecision <LocalBook>(localTrack, new Rejection("Unexpected error processing file")));
                }
            }

            _logger.Debug($"Tags parsed for {files.Count} files in {watch.ElapsedMilliseconds}ms");

            return(Tuple.Create(localTracks, decisions));
        }