void btnImport_Click(object sender, RoutedEventArgs e)
        {
            FeatureTrackingManager.Instance.UseFeature(Features.Library_ImportFromThirdParty);

            IEnumerable <AugmentedBindable <BibTeXEntry> > allEntries = GetEntries().Where(x => x.Underlying.Selected);

            if (allEntries.Count() == 0)
            {
                MessageBoxes.Error("Please select at least one entry to import, by checking the checkbox.");
                return;
            }

            List <ImportingIntoLibrary.FilenameWithMetadataImport> filename_and_bibtex_imports = new List <ImportingIntoLibrary.FilenameWithMetadataImport>();

            foreach (AugmentedBindable <BibTeXEntry> entry in allEntries)
            {
                ImportingIntoLibrary.FilenameWithMetadataImport filename_with_metadata_import = new ImportingIntoLibrary.FilenameWithMetadataImport
                {
                    filename = entry.Underlying.Filename,
                    bibtex   = entry.Underlying.BibTeX,
                    tags     = entry.Underlying.Tags,
                    notes    = entry.Underlying.Notes
                };

                filename_and_bibtex_imports.Add(filename_with_metadata_import);
            }

            StatusManager.Instance.UpdateStatus("ImportFromThirdParty", "Started importing documents");

            ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(_library, false, false, filename_and_bibtex_imports.ToArray());

            MessageBoxes.Info("{0} files are now being imported - this may take a little while.  You can track the import progress in the status bar.", filename_and_bibtex_imports.Count);

            this.Close();
        }
        private void BtnImport_OnClick(object sender, RoutedEventArgs e)
        {
            try
            {
                var root_folder = bindable.Underlying.SelectedPath;
                if (!Directory.Exists(root_folder))
                {
                    return;
                }

                //  do the import
                ImportingIntoLibrary.AddNewPDFDocumentsToLibraryFromFolder_ASYNCHRONOUS(library, root_folder, bindable.Underlying.RecurseSubfolders, bindable.Underlying.ImportTagsFromSubfolderNames, false, false);

                //  remember settings for next time
                bindable.Underlying.DefaultSelectedPath                 = root_folder;
                bindable.Underlying.DefaultRecurseSubfolders            = bindable.Underlying.RecurseSubfolders;
                bindable.Underlying.DefaultImportTagsFromSubfolderNames = bindable.Underlying.ImportTagsFromSubfolderNames;

                Close();
            }
            catch (Exception exception)
            {
                Logging.Error(exception, "Problem importing files from {0}", bindable.Underlying.SelectedPath);
            }
        }
        private void MoveGuestPreviewPDFDocument(WebLibraryDetail web_library_detail)
        {
            PDFDocument source_pdf_document = pdf_renderer_control_stats.pdf_document;

            SafeThreadPool.QueueUserWorkItem(o =>
            {
                PDFDocument cloned_pdf_document = ImportingIntoLibrary.ClonePDFDocumentsFromOtherLibrary_SYNCHRONOUS(source_pdf_document, web_library_detail.library, false);

                WPFDoEvents.InvokeInUIThread(() =>
                {
                    // Open the new
                    if (null != cloned_pdf_document)
                    {
                        MainWindowServiceDispatcher.Instance.OpenDocument(cloned_pdf_document);
                    }
                    else
                    {
                        MessageBoxes.Warn("There was a problem moving this document to another library.");
                    }

                    // Close the old
                    MainWindowServiceDispatcher.Instance.ClosePDFReadingControl(this);

                    // Delete the old
                    if (cloned_pdf_document != source_pdf_document)
                    {
                        source_pdf_document.Deleted = true;
                        source_pdf_document.Bindable.NotifyPropertyChanged(nameof(source_pdf_document.Deleted));
                    }
                });
            });
        }
Пример #4
0
        private void ProcessTheNewDocuments(List <string> filenames_that_are_new)
        {
            if (0 == filenames_that_are_new.Count)
            {
                return;
            }

            if (Utilities.Shutdownable.ShutdownableManager.Instance.IsShuttingDown)
            {
                Logging.Info("FolderWatcher: Breaking out due to daemon termination");
                return;
            }

            if (Qiqqa.Common.Configuration.ConfigurationManager.Instance.ConfigurationRecord.DisableAllBackgroundTasks)
            {
                Logging.Info("FolderWatcher: Breaking out due to DisableAllBackgroundTasks");
                return;
            }

            // Create the import records
            List <FilenameWithMetadataImport> filename_with_metadata_imports = new List <FilenameWithMetadataImport>();

            foreach (var filename in filenames_that_are_new)
            {
                filename_with_metadata_imports.Add(new FilenameWithMetadataImport
                {
                    filename = filename,
                    tags     = new HashSet <string>(tags)
                });

                // TODO: refactor this: delay until the PDF has actually been processed completely!
                //
                // Add this file to the list of processed files...
                folder_watcher_manager.RememberProcessedFile(filename);
            }

            // Get the library to import all these new files
            ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(library, true, true, filename_with_metadata_imports.ToArray());

            // TODO: refactor the ImportingIntoLibrary class
            //
            // HACK & QUICK PATCH until we have refactored this stuff:
            filenames_that_are_new.Clear();
        }
Пример #5
0
        void ButtonGrabPDFs_Click(object sender, RoutedEventArgs e)
        {
            Uri    current_uri = CurrentWebBrowserControl.CurrentUri;
            string html        = CurrentWebBrowserControl.PageHTML;

            List <string> urls = DownloadableFileGrabber.Grab(html, "pdf");

            List <Uri> uris = new List <Uri>();

            foreach (string url in urls)
            {
                Uri uri;
                if (Uri.TryCreate(current_uri, url, out uri))
                {
                    uris.Add(uri);
                }
            }

            if (0 < uris.Count)
            {
                string msg = String.Format(
                    "Qiqqa has found {0} PDFs on this page.  Please choose the library into which you want to import them."
                    , uris.Count
                    );

                WebLibraryDetail web_library_detail = WebLibraryPicker.PickWebLibrary(msg);
                if (null != web_library_detail)
                {
                    foreach (Uri uri in uris)
                    {
                        ImportingIntoLibrary.AddNewDocumentToLibraryFromInternet_ASYNCHRONOUS(web_library_detail.library, uri.ToString());
                    }
                }
                else
                {
                    MessageBoxes.Warn("No PDFs have been imported.");
                }
            }
            else
            {
                MessageBoxes.Info("Qiqqa could not find links to any PDFs on this page (with URLs ending in .pdf");
            }
        }
        private void MoveOrCopyCommon(Feature feature, bool delete_source_pdf_documents)
        {
            WebLibraryDetail web_library_detail = WebLibraryPicker.PickWebLibrary();

            if (null == web_library_detail)
            {
                Logging.Warn("User did not pick a library to copy or move to: pick = NULL.");
                return;
            }

            // Check that we are not moving any docs into the same library
            bool same_library = false;

            foreach (var pdf_document in pdf_documents)
            {
                if (pdf_document.Library.WebLibraryDetail == web_library_detail)
                {
                    same_library = true;
                }
            }
            if (same_library)
            {
                MessageBoxes.Error("You can not move/copy a PDF from/to the same library.");
                return;
            }

            // Copying / Moving PDFDocuments takes a while, particularly if it's a large set.
            //
            // Hence this WORK should be executed by a background task.
            SafeThreadPool.QueueUserWorkItem(o =>
            {
                FeatureTrackingManager.Instance.UseFeature(feature);

                ImportingIntoLibrary.ClonePDFDocumentsFromOtherLibrary_SYNCHRONOUS(pdf_documents, web_library_detail.library, delegate(PDFDocument target, PDFDocument source)
                {
                    if (delete_source_pdf_documents && null != target && null != source && target != source)
                    {
                        source.Library.DeleteDocument(source);
                    }
                });
            });
        }
        private static void DoImportMyDocuments(object obj)
        {
            if (null == mdd)
            {
                Logging.Warn("Not sure how MendeleyImporter.MendeleyDatabaseDetails is null if we got a command to import...");
                return;
            }

            FeatureTrackingManager.Instance.UseFeature(Features.Library_ImportAutoFromMendeley);

            WebLibraryDetail web_library_detail = null;

            WPFDoEvents.InvokeInUIThread(() =>
                                         web_library_detail = WebLibraryPicker.PickWebLibrary()
                                         );

            if (null != web_library_detail)
            {
                ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(web_library_detail.library, false, false, mdd.metadata_imports.ToArray());
            }
        }
Пример #8
0
        static void DoImportMyDocuments(object obj)
        {
            if (null == mdd)
            {
                Logging.Warn("Not sure how EndnoteImporter.EndnoteDatabaseDetails is null if we got a command to import...");
                return;
            }

            Qiqqa.UtilisationTracking.FeatureTrackingManager.Instance.UseFeature(Features.Library_ImportAutoFromEndNote);

            WebLibraryDetail web_library_detail = null;

            Application.Current.Dispatcher.Invoke(((Action)(() =>
                                                            web_library_detail = WebLibraryPicker.PickWebLibrary()
                                                            )));

            if (null != web_library_detail)
            {
                ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(web_library_detail.library, false, false, mdd.metadata_imports.ToArray());
            }
        }
Пример #9
0
        private void MoveOrCopyCommon(Feature feature, bool delete_source_pdf_documents)
        {
            WebLibraryDetail web_library_detail = WebLibraryPicker.PickWebLibrary();

            if (null == web_library_detail)
            {
                return;
            }

            // Check that we are not moving any docs into the same library
            bool same_library = false;

            foreach (var pdf_document in pdf_documents)
            {
                if (pdf_document.Library.WebLibraryDetail == web_library_detail)
                {
                    same_library = true;;
                }
            }
            if (same_library)
            {
                MessageBoxes.Error("You can not move/copy a PDF from/to the same library.");
                return;
            }

            FeatureTrackingManager.Instance.UseFeature(feature);

            ImportingIntoLibrary.ClonePDFDocumentsFromOtherLibrary_SYNCHRONOUS(pdf_documents, web_library_detail.library);

            if (delete_source_pdf_documents)
            {
                foreach (var pdf_document in pdf_documents)
                {
                    pdf_document.Deleted = true;
                    pdf_document.Bindable.NotifyPropertyChanged(() => pdf_document.Deleted);
                }
            }
        }
 void CmdAutomaticEndnoteImport_Click(object sender, RoutedEventArgs e)
 {
     Qiqqa.UtilisationTracking.FeatureTrackingManager.Instance.UseFeature(Features.Library_ImportAutoFromEndNote);
     ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(this._library, false, false, edd.metadata_imports.ToArray());
     this.Close();
 }
Пример #11
0
 private void CmdAutomaticMendeleyImport_Click(object sender, RoutedEventArgs e)
 {
     FeatureTrackingManager.Instance.UseFeature(Features.Library_ImportAutoFromMendeley);
     ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(_library, false, false, mdd.metadata_imports.ToArray());
     Close();
 }
Пример #12
0
        /// <summary>
        /// The daemon code calls this occasionally to poke it into action to do work
        /// </summary>
        /// <param name="daemon"></param>
        public void ExecuteBackgroundProcess(Daemon daemon)
        {
            // We don't want to start watching files until the library is loaded...
            if (!(LibraryRef?.Xlibrary.LibraryIsLoaded ?? false))
            {
                Logging.Info("Library is not yet loaded, so waiting before watching...");

                // Indicate that the library may still not have been changed...
                FolderContentsHaveChanged = true;
                return;
            }

            // Update our folder system watcher if necessary
            CheckIfFolderNameHasChanged();

            // If the current folder is blank, do nothing
            if (String.IsNullOrEmpty(configured_folder_to_watch))
            {
                return;
            }

            // If the folder does not exist, do nothing
            if (!Directory.Exists(configured_folder_to_watch))
            {
                Logging.Info("Watched folder {0} does not exist: watching this directory has been disabled.", configured_folder_to_watch);
                return;
            }

            // If the folder or its contents has not changed since the last time, do nothing
            if (!FolderContentsHaveChanged)
            {
                return;
            }

            if (!ConfigurationManager.IsEnabled(nameof(FolderWatcher)))
            {
                Logging.Info("Watched folder {0} will not be watched/scanned due to Developer Override setting {1}=false", configured_folder_to_watch, nameof(FolderWatcher));
                return;
            }

            Stopwatch breathing_time = Stopwatch.StartNew();

            Logging.Debug("FolderWatcher BEGIN");

            // To recover from a fatal library failure and re-indexing attempt for very large libraries,
            // we're better off processing a limited number of source files as we'll be able to see
            // *some* results more quickly and we'll have a working, though yet incomplete,
            // index in *reasonable time*.
            //
            // To reconstruct the entire index will take a *long* time. We grow the index and other meta
            // stores a bunch-of-files at a time and then repeat the entire maintenance process until
            // we'll be sure to have run out of files to process for sure...

            // Mark that we are now processing the folder
            while (TestAndReset_FolderContentsHaveChanged())
            {
                // If this library is busy, skip it for now
                if (Library.IsBusyAddingPDFs || Library.IsBusyRegeneratingTags)
                {
                    Logging.Debug特("FolderWatcher: Not daemon processing any library that is busy with adds...");
                    FolderContentsHaveChanged = true;
                    break;
                }

                if (ShutdownableManager.Instance.IsShuttingDown)
                {
                    Logging.Debug特("FolderWatcher: Breaking out of outer processing loop due to daemon termination");
                    FolderContentsHaveChanged = true;
                    break;
                }

                if (Qiqqa.Common.Configuration.ConfigurationManager.Instance.ConfigurationRecord.DisableAllBackgroundTasks)
                {
                    Logging.Debug特("FolderWatcher: Breaking out of outer processing loop due to DisableAllBackgroundTasks");
                    FolderContentsHaveChanged = true;
                    break;
                }

                if (LibraryRef == null || folder_watcher_manager?.TypedTarget == null)
                {
                    Logging.Debug特("FolderWatcher: Breaking out of outer processing loop due to disposed library and/or watch manager");
                    FolderContentsHaveChanged = true;
                    break;
                }

                if (!ConfigurationManager.IsEnabled(nameof(FolderWatcher)))
                {
                    Logging.Info("Watched folder {0} will not be watched/scanned due to Developer Override setting {1}=false", configured_folder_to_watch, nameof(FolderWatcher));
                    break;
                }

                // reset counters for logging/reporting:
                watch_stats.Reset(daemon);

                // If we get this far then there might be some work to do in the folder...
                Stopwatch clk = Stopwatch.StartNew();

                //
                // Summary:
                //     [AlphaFS] Specifies a set of custom filters to be used with enumeration methods
                //     of Alphaleonis.Win32.Filesystem.Directory, e.g., Alphaleonis.Win32.Filesystem.Directory.EnumerateDirectories(System.String),
                //     Alphaleonis.Win32.Filesystem.Directory.EnumerateFiles(System.String), or Alphaleonis.Win32.Filesystem.Directory.EnumerateFileSystemEntries(System.String).
                //
                // Remarks:
                //     Alphaleonis.Win32.Filesystem.DirectoryEnumerationFilters allows scenarios in
                //     which files/directories being enumerated by the methods of Alphaleonis.Win32.Filesystem.Directory
                //     class are accepted only if they match the search pattern, attributes (see Alphaleonis.Win32.Filesystem.DirectoryEnumerationOptions.SkipReparsePoints),
                //     and optionally also the custom criteria tested in the method whose delegate is
                //     specified in Alphaleonis.Win32.Filesystem.DirectoryEnumerationFilters.InclusionFilter.
                //     These criteria could be, e.g., file size exceeding some threshold, pathname matches
                //     a complex regular expression, etc. If the enumeration process is set to be recursive
                //     (see Alphaleonis.Win32.Filesystem.DirectoryEnumerationOptions.Recursive) and
                //     Alphaleonis.Win32.Filesystem.DirectoryEnumerationFilters.RecursionFilter is specified,
                //     the directory is traversed recursively only if it matches the custom criteria
                //     in Alphaleonis.Win32.Filesystem.DirectoryEnumerationFilters.RecursionFilter method.
                //     This allows, for example, custom handling of junctions and symbolic links, e.g.,
                //     detection of cycles. If any error occurs during the enumeration and the enumeration
                //     process is not set to ignore errors (see Alphaleonis.Win32.Filesystem.DirectoryEnumerationOptions.ContinueOnException),
                //     an exception is thrown unless the error is handled (filtered out) by the method
                //     specified in Alphaleonis.Win32.Filesystem.DirectoryEnumerationFilters.ErrorFilter
                //     (if specified). The method may, for example, consume the error by reporting it
                //     in a log, so that the enumeration continues as in the case of Alphaleonis.Win32.Filesystem.DirectoryEnumerationOptions.ContinueOnException
                //     option but the user will be informed about errors.
                //
                global_watch_stats.Inc();

                DirectoryEnumerationFilters filter = new DirectoryEnumerationFilters();
                filter.ErrorFilter     = DecideIfErrorDuringDirScan;
                filter.InclusionFilter = DecideIfIncludeDuringDirScan;
                filter.RecursionFilter = DecideIfRecurseDuringDirScan;
                // Note: don't use the CancellationToken, just throw an exception in the InclusionFilter when it's time to abort the scan.
                //filter.CancellationToken = null;

                IEnumerable <string> filenames_in_folder = Directory.EnumerateFiles(configured_folder_to_watch,
                                                                                    DirectoryEnumerationOptions.Files |
                                                                                    DirectoryEnumerationOptions.BasicSearch |
                                                                                    //DirectoryEnumerationOptions.ContinueOnException |
                                                                                    DirectoryEnumerationOptions.LargeCache |
                                                                                    DirectoryEnumerationOptions.Recursive,
                                                                                    filter);
                // SearchOption.AllDirectories);
                Logging.Debug特("Directory.EnumerateFiles took {0} ms", clk.ElapsedMilliseconds);

                // Do NOT count files which are already present in our library/DB,
                // despite the fact that those also *do* take time and effort to check
                // in the code above.
                //
                // The issue here is that when we would import files A,B,C,D,E,F,G,H,I,J,K,
                // we would do so in tiny batches, resulting in a rescan after each batch
                // where the already processed files will be included in the set, but must
                // be filtered out as 'already in there' in the code above.
                // Iff we had counted *all* files we inspect from the Watch Directory,
                // we would never make it batch the first batch as then our count limit
                // would trigger already for every round through here!

                List <string> filenames_that_are_new = new List <string>();
                foreach (string filename in filenames_in_folder)
                {
                    Logging.Info("FolderWatcher: {0} of {1} files have been processed/inspected (total {2} scanned, {3} skipped, {4} ignored)", watch_stats.processed_file_count, watch_stats.processing_file_count, watch_stats.scanned_file_count, watch_stats.skipped_file_count, watch_stats.scanned_file_count - watch_stats.skipped_file_count - watch_stats.processing_file_count);

                    try
                    {
                        // check the file once again: it MAY have disappeared while we were slowly scanning the remainder of the dirtree.
                        FileSystemEntryInfo info = File.GetFileSystemEntryInfo(filename);

                        watch_stats.processing_file_count++;

                        Logging.Info("FolderWatcher is importing {0}", filename);
                        filenames_that_are_new.Add(filename);
                    }
                    catch (Exception ex)
                    {
                        Logging.Error(ex, "Folder Watcher: skipping file {0} due to file I/O error {1}", filename, ex.Message);
                    }
                }

                Logging.Debug特("Directory.EnumerateFiles took {0} ms", clk.ElapsedMilliseconds);

                // Create the import records
                List <FilenameWithMetadataImport> filename_with_metadata_imports = new List <FilenameWithMetadataImport>();
                foreach (var filename in filenames_that_are_new)
                {
                    filename_with_metadata_imports.Add(new FilenameWithMetadataImport
                    {
                        filename = filename,
                        tags     = new HashSet <string>(tags)
                    });

#if false
                    // delay until the PDF has actually been processed completely!
                    //
                    // Add this file to the list of processed files...
                    folder_watcher_manager.RememberProcessedFile(filename);
#endif
                }

                // Get the library to import all these new files
                if (filename_with_metadata_imports.Count > 0)
                {
                    ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_SYNCHRONOUS(LibraryRef, true, filename_with_metadata_imports.ToArray());

                    // TODO: refactor the ImportingIntoLibrary class
                }

                watch_stats.processed_file_count = watch_stats.processing_file_count;

                Logging.Info("FolderWatcher: {0} of {1} files have been processed/inspected (total {2} scanned, {3} skipped, {4} ignored)", watch_stats.processed_file_count, watch_stats.processing_file_count, watch_stats.scanned_file_count, watch_stats.skipped_file_count, watch_stats.scanned_file_count - watch_stats.skipped_file_count - watch_stats.processing_file_count);

                if (watch_stats.index_processing_clock.ElapsedMilliseconds >= FolderWatcher.MAX_SECONDS_PER_ITERATION)
                {
                    Logging.Info("FolderWatcher: Taking a nap due to MAX_SECONDS_PER_ITERATION: {0} seconds consumed, {1} threads pending", watch_stats.index_processing_clock.ElapsedMilliseconds / 1E3, SafeThreadPool.QueuedThreadCount);

                    watch_stats.daemon.Sleep(SECONDS_TO_RELAX_PER_ITERATION);

                    watch_stats.index_processing_clock.Restart();
                }

                Logging.Debug("FolderWatcher End-Of-Round ({0} ms)", clk.ElapsedMilliseconds);
            }

            Logging.Debug("FolderWatcher END");
        }
        /// <summary>
        /// The daemon code calls this occasionally to poke it into action to do work
        /// </summary>
        /// <param name="daemon"></param>
        public void TaskDaemonEntryPoint(Daemon daemon)
        {
            // We don't want to start watching files until the library is loaded...
            if (!library.LibraryIsLoaded)
            {
                Logging.Info("Library is not yet loaded, so waiting before watching...");

                // Indicate that the library may still not have been changed...
                folder_contents_has_changed = true;
                return;
            }

            // Update our fole system watcher if necessary
            CheckIfFolderNameHasChanged();

            // If the current folder is blank, do nothing
            if (String.IsNullOrEmpty(folder_to_watch))
            {
                return;
            }

            // If the folder does not exist, do nothing
            if (!Directory.Exists(folder_to_watch))
            {
                return;
            }


            // If the folder or its contents has not changed since the last time, do nothing
            if (!folder_contents_has_changed)
            {
                return;
            }

            // Mark that we are now processing the folder
            folder_contents_has_changed = false;

            // If we get this far then there might be some work to do in the folder...
            string[] filenames_in_folder = Directory.GetFiles(previous_folder_to_watch, "*.pdf", SearchOption.AllDirectories);

            List <PDFDocument> pdf_documents_already_in_library = library.PDFDocuments;

            List <string> filenames_that_are_new = new List <string>();

            foreach (string filename in filenames_in_folder)
            {
                // If we already have this file in the "cache since we started", skip it
                if (folder_watcher_manager.HaveProcessedFile(filename))
                {
                    //Logging.Info("FolderWatcher is skipping {0} as it has already been processed", filename);
                    continue;
                }

                // If we already have this file in the "pdf file locations", skip it
                bool is_already_in_library = false;
                foreach (PDFDocument pdf_document in pdf_documents_already_in_library)
                {
                    if (pdf_document.DownloadLocation == filename)
                    {
                        is_already_in_library = true;
                        break;
                    }
                }

                if (is_already_in_library)
                {
                    // Add this file to the list of processed files...
                    folder_watcher_manager.RememberProcessedFile(filename);

                    continue;
                }

                // Check that the file is not still locked - if it is, mark that the folder is still "changed" and come back later..
                if (IsFileLocked(filename))
                {
                    Logging.Info("Watched folder contains file '{0}' which is locked, so coming back later...", filename);
                    folder_contents_has_changed = true;
                    continue;
                }

                Logging.Info("FolderWatcher is importing {0}", filename);
                filenames_that_are_new.Add(filename);

                // Add this file to the list of processed files...
                folder_watcher_manager.RememberProcessedFile(filename);
            }


            // Create the import records
            List <ImportingIntoLibrary.FilenameWithMetadataImport> filename_with_metadata_imports = new List <ImportingIntoLibrary.FilenameWithMetadataImport>();

            foreach (var filename in filenames_that_are_new)
            {
                filename_with_metadata_imports.Add(new ImportingIntoLibrary.FilenameWithMetadataImport {
                    filename = filename, tags = new List <string>(this.tags)
                });
            }

            // Get the library to import all these new files
            ImportingIntoLibrary.AddNewPDFDocumentsToLibraryWithMetadata_ASYNCHRONOUS(library, true, true, filename_with_metadata_imports.ToArray());
        }