Exemplo n.º 1
0
        public void WriteTags(BookFile bookFile, bool newDownload, bool force = false)
        {
            var extension = Path.GetExtension(bookFile.Path);

            if (MediaFileExtensions.AudioExtensions.Contains(extension))
            {
                _audioTagService.WriteTags(bookFile, newDownload, force);
            }
            else if (bookFile.CalibreId > 0)
            {
                _eBookTagService.WriteTags(bookFile, newDownload, force);
            }
        }
Exemplo n.º 2
0
        public TrackFileMoveResult UpgradeTrackFile(TrackFile trackFile, LocalTrack localTrack, bool copyOnly = false)
        {
            var moveFileResult = new TrackFileMoveResult();
            var existingFiles  = localTrack.Tracks
                                 .Where(e => e.TrackFileId > 0)
                                 .Select(e => e.TrackFile.Value)
                                 .Where(e => e != null)
                                 .GroupBy(e => e.Id)
                                 .ToList();

            var rootFolder = _diskProvider.GetParentFolder(localTrack.Artist.Path);

            // If there are existing track files and the root folder is missing, throw, so the old file isn't left behind during the import process.
            if (existingFiles.Any() && !_diskProvider.FolderExists(rootFolder))
            {
                throw new RootFolderNotFoundException($"Root folder '{rootFolder}' was not found.");
            }

            foreach (var existingFile in existingFiles)
            {
                var file          = existingFile.First();
                var trackFilePath = file.Path;
                var subfolder     = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(trackFilePath));

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

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

            if (copyOnly)
            {
                moveFileResult.TrackFile = _trackFileMover.CopyTrackFile(trackFile, localTrack);
            }
            else
            {
                moveFileResult.TrackFile = _trackFileMover.MoveTrackFile(trackFile, localTrack);
            }

            _audioTagService.WriteTags(trackFile, true);

            return(moveFileResult);
        }
Exemplo n.º 3
0
        public List <ImportResult> Import(List <ImportDecision <LocalBook> > decisions, bool replaceExisting, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto)
        {
            var importResults         = new List <ImportResult>();
            var allImportedTrackFiles = new List <BookFile>();
            var allOldTrackFiles      = new List <BookFile>();
            var addedAuthors          = new List <Author>();

            var bookDecisions = decisions.Where(e => e.Item.Book != null && e.Approved)
                                .GroupBy(e => e.Item.Book.ForeignBookId).ToList();

            var iDecision = 1;

            foreach (var albumDecision in bookDecisions)
            {
                _logger.ProgressInfo($"Importing book {iDecision++}/{bookDecisions.Count} {albumDecision.First().Item.Book}");

                var decisionList = albumDecision.ToList();

                var author = EnsureAuthorAdded(decisionList, addedAuthors);

                if (author == null)
                {
                    // failed to add the author, carry on with next book
                    continue;
                }

                var book = EnsureBookAdded(decisionList);

                if (book == null)
                {
                    // failed to add the book, carry on with next one
                    continue;
                }

                if (replaceExisting)
                {
                    RemoveExistingTrackFiles(author, book);
                }

                // set the correct release to be monitored before importing the new files
                var newRelease = albumDecision.First().Item.Edition;
                _logger.Debug("Updating release to {0}", newRelease);
                book.Editions = _editionService.SetMonitored(newRelease);

                // Publish book edited event.
                // Deliberatly don't put in the old book since we don't want to trigger an ArtistScan.
                _eventAggregator.PublishEvent(new BookEditedEvent(book, book));
            }

            var qualifiedImports = decisions.Where(c => c.Approved)
                                   .GroupBy(c => c.Item.Author.Id, (i, s) => s
                                            .OrderByDescending(c => c.Item.Quality, new QualityModelComparer(s.First().Item.Author.QualityProfile))
                                            .ThenByDescending(c => c.Item.Size))
                                   .SelectMany(c => c)
                                   .ToList();

            _logger.ProgressInfo($"Importing {qualifiedImports.Count} files");
            _logger.Debug($"Importing {qualifiedImports.Count} files. replaceExisting: {replaceExisting}");

            var filesToAdd          = new List <BookFile>(qualifiedImports.Count);
            var trackImportedEvents = new List <TrackImportedEvent>(qualifiedImports.Count);

            foreach (var importDecision in qualifiedImports.OrderByDescending(e => e.Item.Size))
            {
                var localTrack = importDecision.Item;
                var oldFiles   = new List <BookFile>();

                try
                {
                    //check if already imported
                    if (importResults.Select(r => r.ImportDecision.Item.Book.Id).Contains(localTrack.Book.Id))
                    {
                        if (!(localTrack.FileTrackInfo.TrackNumbers != null && localTrack.FileTrackInfo.TrackNumbers.Any()))
                        {
                            importResults.Add(new ImportResult(importDecision, "Book has already been imported"));
                            continue;
                        }
                        else
                        {
                            var matchingImportResults = importResults.Where(r => r.ImportDecision.Item.Book.Id == localTrack.Book.Id);
                            if (matchingImportResults.Select(r => r.ImportDecision.Item.FileTrackInfo.TrackNumbers).Contains(localTrack.FileTrackInfo.TrackNumbers))
                            {
                                importResults.Add(new ImportResult(importDecision, "Audiobook track has already been imported"));
                                continue;
                            }
                        }
                    }

                    localTrack.Book.Author = localTrack.Author;

                    var bookFile = new BookFile
                    {
                        Path         = localTrack.Path.CleanFilePath(),
                        Size         = localTrack.Size,
                        Modified     = localTrack.Modified,
                        DateAdded    = DateTime.UtcNow,
                        ReleaseGroup = localTrack.ReleaseGroup,
                        Quality      = localTrack.Quality,
                        MediaInfo    = localTrack.FileTrackInfo.MediaInfo,
                        EditionId    = localTrack.Edition.Id,
                        Author       = localTrack.Author,
                        Edition      = localTrack.Edition
                    };

                    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 (!localTrack.ExistingFile)
                    {
                        bookFile.SceneName = GetSceneReleaseName(downloadClientItem);

                        var moveResult = _bookFileUpgrader.UpgradeBookFile(bookFile, localTrack, copyOnly);
                        oldFiles = moveResult.OldFiles;
                    }
                    else
                    {
                        // Delete existing files from the DB mapped to this path
                        var previousFile = _mediaFileService.GetFileWithPath(bookFile.Path);

                        if (previousFile != null)
                        {
                            _mediaFileService.Delete(previousFile, DeleteMediaFileReason.ManualOverride);
                        }

                        var rootFolder = _rootFolderService.GetBestRootFolder(localTrack.Path);
                        if (rootFolder.IsCalibreLibrary)
                        {
                            bookFile.CalibreId = bookFile.Path.ParseCalibreId();
                        }

                        _audioTagService.WriteTags(bookFile, false);
                    }

                    filesToAdd.Add(bookFile);
                    importResults.Add(new ImportResult(importDecision));

                    if (!localTrack.ExistingFile)
                    {
                        _extraService.ImportTrack(localTrack, bookFile, copyOnly);
                    }

                    allImportedTrackFiles.Add(bookFile);
                    allOldTrackFiles.AddRange(oldFiles);

                    // create all the import events here, but we can't publish until the trackfiles have been
                    // inserted and ids created
                    trackImportedEvents.Add(new TrackImportedEvent(localTrack, bookFile, oldFiles, !localTrack.ExistingFile, downloadClientItem));
                }
                catch (RootFolderNotFoundException e)
                {
                    _logger.Warn(e, "Couldn't import book " + localTrack);
                    _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem));

                    importResults.Add(new ImportResult(importDecision, "Failed to import book, Root folder missing."));
                }
                catch (DestinationAlreadyExistsException e)
                {
                    _logger.Warn(e, "Couldn't import book " + localTrack);
                    importResults.Add(new ImportResult(importDecision, "Failed to import book, Destination already exists."));
                }
                catch (UnauthorizedAccessException e)
                {
                    _logger.Warn(e, "Couldn't import book " + localTrack);
                    _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem));

                    importResults.Add(new ImportResult(importDecision, "Failed to import book, Permissions error"));
                }
                catch (Exception)
                {
                    throw;
                }
            }

            var watch = new System.Diagnostics.Stopwatch();

            watch.Start();
            _mediaFileService.AddMany(filesToAdd);
            _logger.Debug($"Inserted new trackfiles in {watch.ElapsedMilliseconds}ms");

            // now that trackfiles have been inserted and ids generated, publish the import events
            foreach (var trackImportedEvent in trackImportedEvents)
            {
                _eventAggregator.PublishEvent(trackImportedEvent);
            }

            var albumImports = importResults.Where(e => e.ImportDecision.Item.Book != null)
                               .GroupBy(e => e.ImportDecision.Item.Book.Id).ToList();

            foreach (var albumImport in albumImports)
            {
                var book   = albumImport.First().ImportDecision.Item.Book;
                var author = albumImport.First().ImportDecision.Item.Author;

                if (albumImport.Where(e => e.Errors.Count == 0).ToList().Count > 0 && author != null && book != null)
                {
                    _eventAggregator.PublishEvent(new BookImportedEvent(
                                                      author,
                                                      book,
                                                      allImportedTrackFiles.Where(s => s.EditionId == book.Id).ToList(),
                                                      allOldTrackFiles.Where(s => s.EditionId == book.Id).ToList(),
                                                      replaceExisting,
                                                      downloadClientItem));
                }
            }

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

            // Refresh any artists we added
            if (addedAuthors.Any())
            {
                _commandQueueManager.Push(new BulkRefreshAuthorCommand(addedAuthors.Select(x => x.Id).ToList(), true));
            }

            return(importResults);
        }
Exemplo n.º 4
0
        public List <ImportResult> Import(List <ImportDecision <LocalTrack> > decisions, bool replaceExisting, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto)
        {
            var qualifiedImports = decisions.Where(c => c.Approved)
                                   .GroupBy(c => c.Item.Artist.Id, (i, s) => s
                                            .OrderByDescending(c => c.Item.Quality, new QualityModelComparer(s.First().Item.Artist.QualityProfile))
                                            .ThenByDescending(c => c.Item.Size))
                                   .SelectMany(c => c)
                                   .ToList();

            _logger.Debug($"Importing {qualifiedImports.Count} files. replaceExisting: {replaceExisting}");

            var importResults         = new List <ImportResult>();
            var allImportedTrackFiles = new List <TrackFile>();
            var allOldTrackFiles      = new List <TrackFile>();

            var albumDecisions = decisions.Where(e => e.Item.Album != null && e.Approved)
                                 .GroupBy(e => e.Item.Album.Id).ToList();

            foreach (var albumDecision in albumDecisions)
            {
                var album      = albumDecision.First().Item.Album;
                var newRelease = albumDecision.First().Item.Release;

                if (replaceExisting)
                {
                    var artist        = albumDecision.First().Item.Artist;
                    var rootFolder    = _diskProvider.GetParentFolder(artist.Path);
                    var previousFiles = _mediaFileService.GetFilesByAlbum(album.Id);

                    _logger.Debug($"Deleting {previousFiles.Count} existing files for {album}");

                    foreach (var previousFile in previousFiles)
                    {
                        var subfolder = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(previousFile.Path));
                        if (_diskProvider.FileExists(previousFile.Path))
                        {
                            _logger.Debug("Removing existing track file: {0}", previousFile);
                            _recycleBinProvider.DeleteFile(previousFile.Path, subfolder);
                        }
                        _mediaFileService.Delete(previousFile, DeleteMediaFileReason.Upgrade);
                    }
                }

                // set the correct release to be monitored before importing the new files
                _logger.Debug("Updating release to {0} [{1} tracks]", newRelease, newRelease.TrackCount);
                album.AlbumReleases = _releaseService.SetMonitored(newRelease);

                // Publish album edited event.
                // Deliberatly don't put in the old album since we don't want to trigger an ArtistScan.
                _eventAggregator.PublishEvent(new AlbumEditedEvent(album, album));
            }

            var filesToAdd          = new List <TrackFile>(qualifiedImports.Count);
            var albumReleasesDict   = new Dictionary <int, List <AlbumRelease> >(albumDecisions.Count);
            var trackImportedEvents = new List <TrackImportedEvent>(qualifiedImports.Count);

            foreach (var importDecision in qualifiedImports.OrderBy(e => e.Item.Tracks.Select(track => track.AbsoluteTrackNumber).MinOrDefault())
                     .ThenByDescending(e => e.Item.Size))
            {
                var localTrack = importDecision.Item;
                var oldFiles   = new List <TrackFile>();

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

                    // cache album releases and set artist to speed up firing the TrackImported events
                    // (otherwise they'll be retrieved from the DB for each track)
                    if (!albumReleasesDict.ContainsKey(localTrack.Album.Id))
                    {
                        albumReleasesDict.Add(localTrack.Album.Id, localTrack.Album.AlbumReleases.Value);
                    }
                    if (!localTrack.Album.AlbumReleases.IsLoaded)
                    {
                        localTrack.Album.AlbumReleases = albumReleasesDict[localTrack.Album.Id];
                    }
                    localTrack.Album.Artist = localTrack.Artist;

                    foreach (var track in localTrack.Tracks)
                    {
                        track.Artist       = localTrack.Artist;
                        track.AlbumRelease = localTrack.Release;
                        track.Album        = localTrack.Album;
                    }

                    var trackFile = new TrackFile {
                        Path         = localTrack.Path.CleanFilePath(),
                        Size         = localTrack.Size,
                        Modified     = localTrack.Modified,
                        DateAdded    = DateTime.UtcNow,
                        ReleaseGroup = localTrack.ReleaseGroup,
                        Quality      = localTrack.Quality,
                        MediaInfo    = localTrack.FileTrackInfo.MediaInfo,
                        AlbumId      = localTrack.Album.Id,
                        Artist       = localTrack.Artist,
                        Album        = localTrack.Album,
                        Tracks       = localTrack.Tracks
                    };

                    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 (!localTrack.ExistingFile)
                    {
                        trackFile.SceneName = GetSceneReleaseName(downloadClientItem, localTrack);

                        var moveResult = _trackFileUpgrader.UpgradeTrackFile(trackFile, localTrack, copyOnly);
                        oldFiles = moveResult.OldFiles;
                    }
                    else
                    {
                        // Delete existing files from the DB mapped to this path
                        var previousFile = _mediaFileService.GetFileWithPath(trackFile.Path);

                        if (previousFile != null)
                        {
                            _mediaFileService.Delete(previousFile, DeleteMediaFileReason.ManualOverride);
                        }

                        _audioTagService.WriteTags(trackFile, false);
                    }

                    filesToAdd.Add(trackFile);
                    importResults.Add(new ImportResult(importDecision));

                    if (!localTrack.ExistingFile)
                    {
                        _extraService.ImportTrack(localTrack, trackFile, copyOnly);
                    }

                    allImportedTrackFiles.Add(trackFile);
                    allOldTrackFiles.AddRange(oldFiles);

                    // create all the import events here, but we can't publish until the trackfiles have been
                    // inserted and ids created
                    trackImportedEvents.Add(new TrackImportedEvent(localTrack, trackFile, oldFiles, !localTrack.ExistingFile, downloadClientItem));
                }
                catch (RootFolderNotFoundException e)
                {
                    _logger.Warn(e, "Couldn't import track " + localTrack);
                    _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem));

                    importResults.Add(new ImportResult(importDecision, "Failed to import track, Root folder missing."));
                }
                catch (DestinationAlreadyExistsException e)
                {
                    _logger.Warn(e, "Couldn't import track " + localTrack);
                    importResults.Add(new ImportResult(importDecision, "Failed to import track, Destination already exists."));
                }
                catch (UnauthorizedAccessException e)
                {
                    _logger.Warn(e, "Couldn't import track " + localTrack);
                    _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem));

                    importResults.Add(new ImportResult(importDecision, "Failed to import track, Permissions error"));
                }
                catch (Exception e)
                {
                    _logger.Warn(e, "Couldn't import track " + localTrack);
                    importResults.Add(new ImportResult(importDecision, "Failed to import track"));
                }
            }

            var watch = new System.Diagnostics.Stopwatch();

            watch.Start();
            _mediaFileService.AddMany(filesToAdd);
            _logger.Debug($"Inserted new trackfiles in {watch.ElapsedMilliseconds}ms");
            filesToAdd.ForEach(f => f.Tracks.Value.ForEach(t => t.TrackFileId = f.Id));
            _trackService.SetFileIds(filesToAdd.SelectMany(x => x.Tracks.Value).ToList());
            _logger.Debug($"TrackFileIds updated, total {watch.ElapsedMilliseconds}ms");

            // now that trackfiles have been inserted and ids generated, publish the import events
            foreach (var trackImportedEvent in trackImportedEvents)
            {
                _eventAggregator.PublishEvent(trackImportedEvent);
            }

            var albumImports = importResults.Where(e => e.ImportDecision.Item.Album != null)
                               .GroupBy(e => e.ImportDecision.Item.Album.Id).ToList();

            foreach (var albumImport in albumImports)
            {
                var release = albumImport.First().ImportDecision.Item.Release;
                var album   = albumImport.First().ImportDecision.Item.Album;
                var artist  = albumImport.First().ImportDecision.Item.Artist;

                if (albumImport.Where(e => e.Errors.Count == 0).ToList().Count > 0 && artist != null && album != null)
                {
                    _eventAggregator.PublishEvent(new AlbumImportedEvent(
                                                      artist,
                                                      album,
                                                      release,
                                                      allImportedTrackFiles.Where(s => s.AlbumId == album.Id).ToList(),
                                                      allOldTrackFiles.Where(s => s.AlbumId == album.Id).ToList(), replaceExisting,
                                                      downloadClientItem));
                }
            }

            //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);
        }
Exemplo n.º 5
0
        public BookFileMoveResult UpgradeBookFile(BookFile bookFile, LocalBook localBook, bool copyOnly = false)
        {
            var moveFileResult = new BookFileMoveResult();
            var existingFiles  = localBook.Book.BookFiles.Value;

            var rootFolderPath = _diskProvider.GetParentFolder(localBook.Author.Path);
            var rootFolder     = _rootFolderService.GetBestRootFolder(rootFolderPath);
            var isCalibre      = rootFolder.IsCalibreLibrary && rootFolder.CalibreSettings != null;

            var settings = rootFolder.CalibreSettings;

            // If there are existing book files and the root folder is missing, throw, so the old file isn't left behind during the import process.
            if (existingFiles.Any() && !_diskProvider.FolderExists(rootFolderPath))
            {
                throw new RootFolderNotFoundException($"Root folder '{rootFolderPath}' was not found.");
            }

            foreach (var file in existingFiles)
            {
                var bookFilePath = file.Path;
                var subfolder    = rootFolderPath.GetRelativePath(_diskProvider.GetParentFolder(bookFilePath));

                bookFile.CalibreId = file.CalibreId;

                if (_diskProvider.FileExists(bookFilePath))
                {
                    _logger.Debug("Removing existing book file: {0}", file);

                    if (!isCalibre)
                    {
                        _recycleBinProvider.DeleteFile(bookFilePath, subfolder);
                    }
                    else
                    {
                        _calibre.RemoveFormats(file.CalibreId,
                                               new[]
                        {
                            Path.GetExtension(bookFile.Path)
                        },
                                               settings);
                    }
                }

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

            if (!isCalibre)
            {
                if (copyOnly)
                {
                    moveFileResult.BookFile = _bookFileMover.CopyBookFile(bookFile, localBook);
                }
                else
                {
                    moveFileResult.BookFile = _bookFileMover.MoveBookFile(bookFile, localBook);
                }

                _audioTagService.WriteTags(bookFile, true);
            }
            else
            {
                var source = bookFile.Path;

                moveFileResult.BookFile = CalibreAddAndConvert(bookFile, settings);

                if (!copyOnly)
                {
                    _diskProvider.DeleteFile(source);
                }
            }

            return(moveFileResult);
        }