/* Copyright 2016 Software Freedom Conservancy Inc. * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. */ namespace Spit.DataImports { private class CoreImporter { private weak Spit.DataImports.PluginHost host; public int imported_items_count = 0; public BatchImportRoll? current_import_roll = null; public CoreImporter(Spit.DataImports.PluginHost host) { this.host = host; } public void prepare_media_items_for_import( ImportableMediaItem[] items, double progress, double host_progress_delta = 0.0, string? progress_message = null ) { host.update_import_progress_pane(progress, progress_message); // SortedList jobs = new SortedList(import_job_comparator); Gee.ArrayList already_imported = new Gee.ArrayList(); Gee.ArrayList failed = new Gee.ArrayList(); int item_idx = 0; double item_progress_delta = host_progress_delta / items.length; foreach (ImportableMediaItem src_item in items) { DataImportSource import_source = new DataImportSource(src_item); if (!import_source.was_backing_file_found()) { message("Skipping import of %s: backing file not found", import_source.get_filename()); failed.add(new DataImportJob(import_source)); continue; } if (import_source.is_already_imported()) { message("Skipping import of %s: checksum detected in library", import_source.get_filename()); already_imported.add(new DataImportJob(import_source)); continue; } jobs.add(new DataImportJob(import_source)); item_idx++; host.update_import_progress_pane(progress + item_idx * item_progress_delta); } if (jobs.size > 0) { // If there it no current import roll, create one to ensure that all // imported items end up in the same roll even if this method is called // several times if (current_import_roll == null) current_import_roll = new BatchImportRoll(); string db_name = _("%s Database").printf(host.get_data_importer().get_service().get_pluggable_name()); BatchImport batch_import = new BatchImport(jobs, db_name, data_import_reporter, failed, already_imported, null, current_import_roll); LibraryWindow.get_app().enqueue_batch_import(batch_import, true); imported_items_count += jobs.size; } host.update_import_progress_pane(progress + host_progress_delta); } public void finalize_import() { // Send an empty job to the queue to mark the end of the import string db_name = _("%s Database").printf(host.get_data_importer().get_service().get_pluggable_name()); BatchImport batch_import = new BatchImport( new Gee.ArrayList(), db_name, data_import_reporter, null, null, null, current_import_roll ); LibraryWindow.get_app().enqueue_batch_import(batch_import, true); current_import_roll = null; } } public class ConcreteDataImportsHost : Plugins.StandardHostInterface, Spit.DataImports.PluginHost { private Spit.DataImports.DataImporter active_importer = null; private weak DataImportsUI.DataImportsDialog dialog = null; private DataImportsUI.ProgressPane? progress_pane = null; private bool importing_halted = false; private CoreImporter core_importer; public ConcreteDataImportsHost(Service service, DataImportsUI.DataImportsDialog dialog) { base(service, "data_imports"); this.dialog = dialog; this.active_importer = service.create_data_importer(this); this.core_importer = new CoreImporter(this); } public DataImporter get_data_importer() { return active_importer; } public void start_importing() { if (get_data_importer().is_running()) return; debug("ConcreteDataImportsHost.start_importing( ): invoked."); get_data_importer().start(); } public void stop_importing() { debug("ConcreteDataImportsHost.stop_importing( ): invoked."); if (get_data_importer().is_running()) get_data_importer().stop(); clean_up(); importing_halted = true; } private void clean_up() { progress_pane = null; } public void set_button_mode(Spit.DataImports.PluginHost.ButtonMode mode) { if (mode == Spit.DataImports.PluginHost.ButtonMode.CLOSE) dialog.set_close_button_mode(); else if (mode == Spit.DataImports.PluginHost.ButtonMode.CANCEL) dialog.set_cancel_button_mode(); else error("unrecognized button mode enumeration value"); } // Pane handling methods public void post_error(Error err) { post_error_message(err.message); } public void post_error_message(string message) { string msg = _("Importing from %s can't continue because an error occurred:").printf( active_importer.get_service().get_pluggable_name()); msg += GLib.Markup.printf_escaped("\n\n%s\n\n", message); msg += _("To try importing from another service, select one from the above menu."); dialog.install_pane(new DataImportsUI.StaticMessagePane.with_pango(msg)); dialog.set_close_button_mode(); dialog.unlock_service(); get_data_importer().stop(); // post_error_message( ) tells the active_importer to stop importing and displays a // non-removable error pane that effectively ends the publishing interaction, // so no problem calling clean_up( ) here. clean_up(); } public void install_dialog_pane(Spit.DataImports.DialogPane pane, Spit.DataImports.PluginHost.ButtonMode button_mode = Spit.DataImports.PluginHost.ButtonMode.CANCEL) { debug("DataImports.PluginHost: install_dialog_pane( ): invoked."); if (get_data_importer() == null || (!get_data_importer().is_running())) return; dialog.install_pane(pane); set_button_mode(button_mode); } public void install_static_message_pane(string message, Spit.DataImports.PluginHost.ButtonMode button_mode = Spit.DataImports.PluginHost.ButtonMode.CANCEL) { set_button_mode(button_mode); dialog.install_pane(new DataImportsUI.StaticMessagePane.with_pango(message)); } public void install_library_selection_pane( string welcome_message, ImportableLibrary[] discovered_libraries, string? file_select_label ) { if (discovered_libraries.length == 0 && file_select_label == null) post_error_message("Libraries or file option needed"); else dialog.install_pane(new DataImportsUI.LibrarySelectionPane( this, welcome_message, discovered_libraries, file_select_label )); set_button_mode(Spit.DataImports.PluginHost.ButtonMode.CLOSE); } public void install_import_progress_pane( string message ) { progress_pane = new DataImportsUI.ProgressPane(message); dialog.install_pane(progress_pane); set_button_mode(Spit.DataImports.PluginHost.ButtonMode.CANCEL); // initialize the import core_importer.imported_items_count = 0; core_importer.current_import_roll = null; } public void update_import_progress_pane( double progress, string? progress_message = null ) { if (progress_pane != null) { progress_pane.update_progress(progress, progress_message); } } public void prepare_media_items_for_import( ImportableMediaItem[] items, double progress, double host_progress_delta = 0.0, string? progress_message = null ) { core_importer.prepare_media_items_for_import(items, progress, host_progress_delta, progress_message); } public void finalize_import( ImportedItemsCountCallback report_imported_items_count, string? finalize_message = null ) { update_import_progress_pane(1.0, finalize_message); set_button_mode(Spit.DataImports.PluginHost.ButtonMode.CLOSE); core_importer.finalize_import(); report_imported_items_count(core_importer.imported_items_count); if (core_importer.imported_items_count > 0) LibraryWindow.get_app().switch_to_import_queue_page(); } } public class WelcomeDataImportsHost : Plugins.StandardHostInterface, Spit.DataImports.PluginHost { private weak WelcomeImportMetaHost meta_host; private Spit.DataImports.DataImporter active_importer = null; private bool importing_halted = false; private CoreImporter core_importer; public WelcomeDataImportsHost(Service service, WelcomeImportMetaHost meta_host) { base(service, "data_imports"); this.active_importer = service.create_data_importer(this); this.core_importer = new CoreImporter(this); this.meta_host = meta_host; } public DataImporter get_data_importer() { return active_importer; } public void start_importing() { if (get_data_importer().is_running()) return; debug("WelcomeDataImportsHost.start_importing( ): invoked."); get_data_importer().start(); } public void stop_importing() { debug("WelcomeDataImportsHost.stop_importing( ): invoked."); if (get_data_importer().is_running()) get_data_importer().stop(); clean_up(); importing_halted = true; } private void clean_up() { } // Pane handling methods public void post_error(Error err) { post_error_message(err.message); } public void post_error_message(string message) { string msg = _("Importing from %s can't continue because an error occurred:").printf( active_importer.get_service().get_pluggable_name()); debug(msg); get_data_importer().stop(); // post_error_message( ) tells the active_importer to stop importing and displays a // non-removable error pane that effectively ends the publishing interaction, // so no problem calling clean_up( ) here. clean_up(); } public void install_dialog_pane(Spit.DataImports.DialogPane pane, Spit.DataImports.PluginHost.ButtonMode button_mode = Spit.DataImports.PluginHost.ButtonMode.CANCEL) { // do nothing } public void install_static_message_pane(string message, Spit.DataImports.PluginHost.ButtonMode button_mode = Spit.DataImports.PluginHost.ButtonMode.CANCEL) { // do nothing } public void install_library_selection_pane( string welcome_message, ImportableLibrary[] discovered_libraries, string? file_select_label ) { debug("WelcomeDataImportsHost: Installing library selection pane for %s".printf(get_data_importer().get_service().get_pluggable_name())); if (discovered_libraries.length > 0) { meta_host.install_service_entry(new WelcomeImportServiceEntry( this, get_data_importer().get_service().get_pluggable_name(), discovered_libraries )); } } public void install_import_progress_pane( string message ) { // empty implementation } public void update_import_progress_pane( double progress, string? progress_message = null ) { // empty implementation } public void prepare_media_items_for_import( ImportableMediaItem[] items, double progress, double host_progress_delta = 0.0, string? progress_message = null ) { core_importer.prepare_media_items_for_import(items, progress, host_progress_delta, progress_message); } public void finalize_import( ImportedItemsCountCallback report_imported_items_count, string? finalize_message = null ) { core_importer.finalize_import(); report_imported_items_count(core_importer.imported_items_count); meta_host.finalize_import(this); } } //public delegate void WelcomeImporterCallback(); public class WelcomeImportServiceEntry : GLib.Object, WelcomeServiceEntry { private string pluggable_name; private ImportableLibrary[] discovered_libraries; private Spit.DataImports.PluginHost host; public WelcomeImportServiceEntry( Spit.DataImports.PluginHost host, string pluggable_name, ImportableLibrary[] discovered_libraries) { this.host = host; this.pluggable_name = pluggable_name; this.discovered_libraries = discovered_libraries; } public string get_service_name() { return pluggable_name; } public void execute() { foreach (ImportableLibrary library in discovered_libraries) { host.get_data_importer().on_library_selected(library); } } } public class WelcomeImportMetaHost : GLib.Object { private WelcomeDialog dialog; public WelcomeImportMetaHost(WelcomeDialog dialog) { this.dialog = dialog; } public void start() { Service[] services = load_all_services(); foreach (Service service in services) { WelcomeDataImportsHost host = new WelcomeDataImportsHost(service, this); host.start_importing(); } } public void finalize_import(WelcomeDataImportsHost host) { host.stop_importing(); } public void install_service_entry(WelcomeServiceEntry entry) { debug("WelcomeImportMetaHost: Installing service entry for %s".printf(entry.get_service_name())); dialog.install_service_entry(entry); } } public static Spit.DataImports.Service[] load_all_services() { return load_services(true); } public static Spit.DataImports.Service[] load_services(bool load_all = false) { Spit.DataImports.Service[] loaded_services = new Spit.DataImports.Service[0]; // load publishing services from plug-ins Gee.Collection pluggables = Plugins.get_pluggables_for_type( typeof(Spit.DataImports.Service), null, load_all); // TODO: include sorting function to ensure consistent order debug("DataImportsDialog: discovered %d pluggable data import services.", pluggables.size); foreach (Spit.Pluggable pluggable in pluggables) { int pluggable_interface = pluggable.get_pluggable_interface( Spit.DataImports.CURRENT_INTERFACE, Spit.DataImports.CURRENT_INTERFACE); if (pluggable_interface != Spit.DataImports.CURRENT_INTERFACE) { warning("Unable to load data import plugin %s: reported interface %d.", Plugins.get_pluggable_module_id(pluggable), pluggable_interface); continue; } Spit.DataImports.Service service = (Spit.DataImports.Service) pluggable; debug("DataImportsDialog: discovered pluggable data import service '%s'.", service.get_pluggable_name()); loaded_services += service; } // Sort import services by name. // TODO: extract to a function to sort it on initial request Posix.qsort(loaded_services, loaded_services.length, sizeof(Spit.DataImports.Service), (a, b) => {return utf8_cs_compare((*((Spit.DataImports.Service**) a))->get_pluggable_name(), (*((Spit.DataImports.Service**) b))->get_pluggable_name()); }); return loaded_services; } private ImportManifest? meta_manifest = null; private void data_import_reporter(ImportManifest manifest, BatchImportRoll import_roll) { if (manifest.all.size > 0) { if (meta_manifest == null) meta_manifest = new ImportManifest(); foreach (BatchImportResult result in manifest.all) { meta_manifest.add_result(result); } } else { DataImportsUI.DataImportsDialog.terminate_instance(); ImportUI.report_manifest(meta_manifest, true); meta_manifest = null; } } private int64 import_job_comparator(void *a, void *b) { return ((DataImportJob *) a)->get_exposure_time() - ((DataImportJob *) b)->get_exposure_time(); } }