private ParsedTrackInfo ReadPdf(string file) { _logger.Trace($"Reading {file}"); var result = new ParsedTrackInfo { Quality = new QualityModel { Quality = Quality.PDF, QualityDetectionSource = QualityDetectionSource.TagLib } }; try { var book = PdfReader.Open(file, PdfDocumentOpenMode.InformationOnly); result.AuthorTitle = book.Info.Author; result.BookTitle = book.Info.Title; _logger.Trace(book.Info.ToJson()); _logger.Trace(book.CustomValues.ToJson()); } catch (Exception e) { _logger.Error(e, "Error reading pdf"); result.Quality.QualityDetectionSource = QualityDetectionSource.Extension; } _logger.Trace($"Got:\n{result.ToJson()}"); return(result); }
private static ParsedTrackInfo ParseMatchMusicCollection(MatchCollection matchCollection) { var authorName = matchCollection[0].Groups["author"].Value./*Removed for cases like Will.I.Am Replace('.', ' ').*/ Replace('_', ' '); authorName = RequestInfoRegex.Replace(authorName, "").Trim(' '); // Coppied from Radarr (https://github.com/Radarr/Radarr/blob/develop/src/NzbDrone.Core/Parser/Parser.cs) // TODO: Split into separate method and write unit tests for. var parts = authorName.Split('.'); authorName = ""; int n = 0; bool previousAcronym = false; string nextPart = ""; foreach (var part in parts) { if (parts.Length >= n + 2) { nextPart = parts[n + 1]; } if (part.Length == 1 && part.ToLower() != "a" && !int.TryParse(part, out n)) { authorName += part + "."; previousAcronym = true; } else if (part.ToLower() == "a" && (previousAcronym == true || nextPart.Length == 1)) { authorName += part + "."; previousAcronym = true; } else { if (previousAcronym) { authorName += " "; previousAcronym = false; } authorName += part + " "; } n++; } authorName = authorName.Trim(' '); var result = new ParsedTrackInfo(); result.Authors = new List <string> { authorName }; Logger.Debug("Track Parsed. {0}", result); return(result); }
public static ParsedTrackInfo ParseMusicPath(string path) { var fileInfo = new FileInfo(path); ParsedTrackInfo result = null; Logger.Debug("Attempting to parse book info using directory and file names. {0}", fileInfo.Directory.Name); result = ParseTitle(fileInfo.Directory.Name + " " + fileInfo.Name); if (result == null) { Logger.Debug("Attempting to parse book info using directory name. {0}", fileInfo.Directory.Name); result = ParseTitle(fileInfo.Directory.Name + fileInfo.Extension); } return(result); }
private ParsedTrackInfo ReadEpub(string file) { _logger.Trace($"Reading {file}"); var result = new ParsedTrackInfo { Quality = new QualityModel { Quality = Quality.EPUB, QualityDetectionSource = QualityDetectionSource.TagLib } }; try { using (var bookRef = EpubReader.OpenBook(file)) { result.AuthorTitle = bookRef.AuthorList.FirstOrDefault(); result.BookTitle = bookRef.Title; var meta = bookRef.Schema.Package.Metadata; _logger.Trace(meta.ToJson()); result.Isbn = GetIsbn(meta?.Identifiers); result.Asin = meta?.Identifiers?.FirstOrDefault(x => x.Scheme?.ToLower().Contains("asin") ?? false)?.Identifier; result.Language = meta?.Languages?.FirstOrDefault(); result.Publisher = meta?.Publishers?.FirstOrDefault(); result.Disambiguation = meta?.Description; result.SeriesTitle = meta?.MetaItems?.FirstOrDefault(x => x.Name == "calibre:series")?.Content; result.SeriesIndex = meta?.MetaItems?.FirstOrDefault(x => x.Name == "calibre:series_index")?.Content; } } catch (Exception e) { _logger.Error(e, "Error reading epub"); result.Quality.QualityDetectionSource = QualityDetectionSource.Extension; } _logger.Trace($"Got:\n{result.ToJson()}"); return(result); }
private ParsedTrackInfo ReadAzw3(string file) { _logger.Trace($"Reading {file}"); var result = new ParsedTrackInfo(); try { var book = new Azw3File(file); result.AuthorTitle = book.Author; result.BookTitle = book.Title; result.Isbn = StripIsbn(book.Isbn); result.Asin = book.Asin; result.Language = book.Language; result.Disambiguation = book.Description; result.Publisher = book.Publisher; result.Label = book.Imprint; result.Source = book.Source; result.Quality = new QualityModel { Quality = book.Version <= 6 ? Quality.MOBI : Quality.AZW3, QualityDetectionSource = QualityDetectionSource.TagLib }; } catch (Exception e) { _logger.Error(e, "Error reading file"); result.Quality = new QualityModel { Quality = Path.GetExtension(file) == ".mobi" ? Quality.MOBI : Quality.AZW3, QualityDetectionSource = QualityDetectionSource.Extension }; } _logger.Trace($"Got {result.ToJson()}"); return(result); }
private List <ImportResult> ProcessFolder(IDirectoryInfo directoryInfo, ImportMode importMode, Author author, DownloadClientItem downloadClientItem) { if (_authorService.AuthorPathExists(directoryInfo.FullName)) { _logger.Warn("Unable to process folder that is mapped to an existing author"); return(new List <ImportResult>()); } var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); var folderInfo = Parser.Parser.ParseBookTitle(directoryInfo.Name); var trackInfo = new ParsedTrackInfo { }; if (folderInfo != null) { _logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality); trackInfo = new ParsedTrackInfo { AlbumTitle = folderInfo.BookTitle, ArtistTitle = folderInfo.AuthorName, Quality = folderInfo.Quality, ReleaseGroup = folderInfo.ReleaseGroup, ReleaseHash = folderInfo.ReleaseHash, }; } else { trackInfo = null; } var audioFiles = _diskScanService.FilterFiles(directoryInfo.FullName, _diskScanService.GetBookFiles(directoryInfo.FullName)); if (downloadClientItem == null) { foreach (var audioFile in audioFiles) { if (_diskProvider.IsFileLocked(audioFile.FullName)) { return(new List <ImportResult> { FileIsLockedResult(audioFile.FullName) }); } } } var idOverrides = new IdentificationOverrides { Author = author }; var idInfo = new ImportDecisionMakerInfo { DownloadClientItem = downloadClientItem, ParsedTrackInfo = trackInfo }; var idConfig = new ImportDecisionMakerConfig { Filter = FilterFilesType.None, NewDownload = true, SingleRelease = false, IncludeExisting = false, AddNewAuthors = false }; var decisions = _importDecisionMaker.GetImportDecisions(audioFiles, idOverrides, idInfo, idConfig); var importResults = _importApprovedTracks.Import(decisions, true, downloadClientItem, importMode); if (importMode == ImportMode.Auto) { importMode = (downloadClientItem == null || downloadClientItem.CanMoveFiles) ? ImportMode.Move : ImportMode.Copy; } if (importMode == ImportMode.Move && importResults.Any(i => i.Result == ImportResultType.Imported) && ShouldDeleteFolder(directoryInfo, author)) { _logger.Debug("Deleting folder after importing valid files"); _diskProvider.DeleteFolder(directoryInfo.FullName, true); } return(importResults); }
public Tuple <List <LocalTrack>, List <ImportDecision <LocalTrack> > > 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 <LocalTrack>(); var decisions = new List <ImportDecision <LocalTrack> >(); _logger.Debug("Analyzing {0}/{1} files.", files.Count, musicFiles.Count); if (!files.Any()) { return(Tuple.Create(localTracks, decisions)); } ParsedAlbumInfo downloadClientItemInfo = null; if (downloadClientItem != null) { downloadClientItemInfo = Parser.Parser.ParseAlbumTitle(downloadClientItem.Title); } int i = 1; foreach (var file in files) { _logger.ProgressInfo($"Reading file {i++}/{files.Count}"); var localTrack = new LocalTrack { DownloadClientAlbumInfo = downloadClientItemInfo, FolderTrackInfo = folderInfo, Path = file.FullName, Size = file.Length, Modified = file.LastWriteTimeUtc, FileTrackInfo = _audioTagService.ReadTags(file.FullName), AdditionalFile = false }; try { // TODO fix otherfiles? _augmentingService.Augment(localTrack, true); localTracks.Add(localTrack); } catch (AugmentingFailedException) { decisions.Add(new ImportDecision <LocalTrack>(localTrack, new Rejection("Unable to parse file"))); } catch (Exception e) { _logger.Error(e, "Couldn't import file. {0}", localTrack.Path); decisions.Add(new ImportDecision <LocalTrack>(localTrack, new Rejection("Unexpected error processing file"))); } } _logger.Debug($"Tags parsed for {files.Count} files in {watch.ElapsedMilliseconds}ms"); return(Tuple.Create(localTracks, decisions)); }
private List <ImportResult> ProcessFolder(IDirectoryInfo directoryInfo, ImportMode importMode, Artist artist, DownloadClientItem downloadClientItem) { if (_artistService.ArtistPathExists(directoryInfo.FullName)) { _logger.Warn("Unable to process folder that is mapped to an existing artist"); return(new List <ImportResult>()); } var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); var folderInfo = Parser.Parser.ParseAlbumTitle(directoryInfo.Name); var trackInfo = new ParsedTrackInfo { }; if (folderInfo != null) { _logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality); trackInfo = new ParsedTrackInfo { AlbumTitle = folderInfo.AlbumTitle, ArtistTitle = folderInfo.ArtistName, Quality = folderInfo.Quality, ReleaseGroup = folderInfo.ReleaseGroup, ReleaseHash = folderInfo.ReleaseHash, }; } else { trackInfo = null; } var audioFiles = _diskScanService.FilterFiles(directoryInfo.FullName, _diskScanService.GetAudioFiles(directoryInfo.FullName)); if (downloadClientItem == null) { foreach (var audioFile in audioFiles) { if (_diskProvider.IsFileLocked(audioFile.FullName)) { return(new List <ImportResult> { FileIsLockedResult(audioFile.FullName) }); } } } var decisions = _importDecisionMaker.GetImportDecisions(audioFiles, artist, trackInfo); var importResults = _importApprovedTracks.Import(decisions, true, downloadClientItem, importMode); if (importMode == ImportMode.Auto) { importMode = (downloadClientItem == null || downloadClientItem.CanMoveFiles) ? ImportMode.Move : ImportMode.Copy; } if (importMode == ImportMode.Move && importResults.Any(i => i.Result == ImportResultType.Imported) && ShouldDeleteFolder(directoryInfo, artist)) { _logger.Debug("Deleting folder after importing valid files"); _diskProvider.DeleteFolder(directoryInfo.FullName, true); } return(importResults); }
public List <ImportDecision <LocalTrack> > GetImportDecisions(List <IFileInfo> musicFiles, Artist artist, Album album, AlbumRelease albumRelease, DownloadClientItem downloadClientItem, ParsedTrackInfo folderInfo, FilterFilesType filter, bool newDownload, bool singleRelease, bool includeExisting) { var watch = new System.Diagnostics.Stopwatch(); watch.Start(); var files = filter != FilterFilesType.None && (artist != null) ? _mediaFileService.FilterUnchangedFiles(musicFiles, artist, filter) : musicFiles; var localTracks = new List <LocalTrack>(); var decisions = new List <ImportDecision <LocalTrack> >(); _logger.Debug("Analyzing {0}/{1} files.", files.Count, musicFiles.Count); if (!files.Any()) { return(decisions); } ParsedAlbumInfo downloadClientItemInfo = null; if (downloadClientItem != null) { downloadClientItemInfo = Parser.Parser.ParseAlbumTitle(downloadClientItem.Title); } foreach (var file in files) { var localTrack = new LocalTrack { Artist = artist, Album = album, DownloadClientAlbumInfo = downloadClientItemInfo, FolderTrackInfo = folderInfo, Path = file.FullName, Size = file.Length, Modified = file.LastWriteTimeUtc, FileTrackInfo = _audioTagService.ReadTags(file.FullName), ExistingFile = !newDownload, AdditionalFile = false }; try { // TODO fix otherfiles? _augmentingService.Augment(localTrack, true); localTracks.Add(localTrack); } catch (AugmentingFailedException) { decisions.Add(new ImportDecision <LocalTrack>(localTrack, new Rejection("Unable to parse file"))); } catch (Exception e) { _logger.Error(e, "Couldn't import file. {0}", localTrack.Path); decisions.Add(new ImportDecision <LocalTrack>(localTrack, new Rejection("Unexpected error processing file"))); } } _logger.Debug($"Tags parsed for {files.Count} files in {watch.ElapsedMilliseconds}ms"); var releases = _identificationService.Identify(localTracks, artist, album, albumRelease, newDownload, singleRelease, includeExisting); foreach (var release in releases) { release.NewDownload = newDownload; var releaseDecision = GetDecision(release); foreach (var localTrack in release.LocalTracks) { if (releaseDecision.Approved) { decisions.AddIfNotNull(GetDecision(localTrack)); } else { decisions.Add(new ImportDecision <LocalTrack>(localTrack, releaseDecision.Rejections.ToArray())); } } } return(decisions); }
public List <ImportDecision <LocalTrack> > GetImportDecisions(List <IFileInfo> musicFiles, Artist artist, ParsedTrackInfo folderInfo) { return(GetImportDecisions(musicFiles, artist, null, null, null, folderInfo, FilterFilesType.None, true, false, false)); }