[rygel] media-export: Refactor filessystem walking



commit b2090101ee66c891c558e0ba068f3776a827438b
Author: Jens Georg <mail jensge org>
Date:   Wed Jul 28 12:29:17 2010 +0200

    media-export: Refactor filessystem walking
    
    Handling harvester events and book-keeping is done by a new Harvester
    class, the old harvester class has been renamed to HarvestingTask

 src/plugins/media-export/Makefile.am               |    1 +
 .../media-export/rygel-media-export-harvester.vala |  370 +++-----------------
 .../rygel-media-export-harvesting-task.vala        |  355 +++++++++++++++++++
 .../rygel-media-export-root-container.vala         |   63 +---
 4 files changed, 404 insertions(+), 385 deletions(-)
---
diff --git a/src/plugins/media-export/Makefile.am b/src/plugins/media-export/Makefile.am
index 5340b61..772aa3a 100644
--- a/src/plugins/media-export/Makefile.am
+++ b/src/plugins/media-export/Makefile.am
@@ -34,6 +34,7 @@ librygel_media_export_la_SOURCES = rygel-media-export-plugin.vala \
 				   rygel-media-export-dbus-service.vala \
 				   rygel-media-export-recursive-file-monitor.vala \
 				   rygel-media-export-harvester.vala \
+				   rygel-media-export-harvesting-task.vala \
 				   rygel-media-export-item.vala \
 				   rygel-media-export-jpeg-writer.vala \
 				   rygel-media-export-object-factory.vala
diff --git a/src/plugins/media-export/rygel-media-export-harvester.vala b/src/plugins/media-export/rygel-media-export-harvester.vala
index ca93aab..8da1940 100644
--- a/src/plugins/media-export/rygel-media-export-harvester.vala
+++ b/src/plugins/media-export/rygel-media-export-harvester.vala
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Jens Georg <mail jensge org>.
+ * Copyright (C) 2010 Jens Georg <mail jensge org>.
  *
  * This file is part of Rygel.
  *
@@ -17,354 +17,64 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
-
-using GLib;
 using Gee;
 
-public class Rygel.MediaExport.Harvester : GLib.Object {
+internal class Rygel.MediaExport.Harvester : GLib.Object {
+    private HashMap<File, HarvestingTask> tasks;
+    private ArrayList<HarvestingTask> trash;
     private MetadataExtractor extractor;
-    private MediaCache media_db;
-    private GLib.Queue<MediaContainer> containers;
-    private Gee.Queue<File> files;
-    private File origin;
-    private MediaContainer parent;
     private RecursiveFileMonitor monitor;
-    private Regex file_filter;
-    public Cancellable cancellable;
-    private string flag;
-    private const string HARVESTER_ATTRIBUTES =
-                                        FILE_ATTRIBUTE_STANDARD_NAME + "," +
-                                        FILE_ATTRIBUTE_STANDARD_TYPE + "," +
-                                        FILE_ATTRIBUTE_TIME_MODIFIED + "," +
-                                        FILE_ATTRIBUTE_STANDARD_SIZE;
-
 
-    public Harvester (MediaContainer       parent,
-                      MediaCache           media_db,
-                      MetadataExtractor    extractor,
-                      RecursiveFileMonitor monitor,
-                      string?              flag = null) {
-        this.parent = parent;
+    public Harvester (MetadataExtractor    extractor,
+                      RecursiveFileMonitor monitor) {
         this.extractor = extractor;
-        this.media_db = media_db;
-        this.extractor.extraction_done.connect (on_extracted_cb);
-        this.extractor.error.connect (on_extractor_error_cb);
-        this.files = new LinkedList<File> ();
-        this.containers = new GLib.Queue<MediaContainer> ();
-        this.origin = null;
         this.monitor = monitor;
-        this.cancellable = new Cancellable ();
-        this.flag = flag;
-        var config = MetaConfig.get_default ();
-
-        try {
-            var extensions = config.get_string_list ("MediaExport",
-                                                     "include-filter");
-
-            // never trust user input
-            string[] escaped_extensions = new string[0];
-            foreach (var extension in extensions) {
-                escaped_extensions += Regex.escape_string (extension);
-            }
-
-            var list = string.joinv ("|", escaped_extensions);
-            file_filter = new Regex ("(%s)$".printf (list),
-                                     RegexCompileFlags.CASELESS |
-                                     RegexCompileFlags.OPTIMIZE);
-        } catch (Error error) {
-            file_filter = null;
-        }
-    }
-
-    private bool push_if_changed_or_unknown (File       file,
-                                             FileInfo   info,
-                                             out string id) {
-        id = Checksum.compute_for_string (ChecksumType.MD5, file.get_uri ());
-        int64 timestamp;
-        try {
-            if (this.media_db.exists (id, out timestamp)) {
-                int64 mtime = (int64) info.get_attribute_uint64 (
-                                        FILE_ATTRIBUTE_TIME_MODIFIED);
-
-                if (mtime > timestamp) {
-                    this.files.offer (file);
-
-                    return true;
-                } else {
-                    // check size
-                    var size = info.get_size ();
-                    var item = media_db.get_item (id);
-                    if (item.size != size) {
-                        this.files.offer (file);
-
-                        return true;
-                    }
-                }
-            } else {
-                this.files.offer (file);
-
-                return true;
-            }
-        } catch (Error error) {
-            warning (_("Failed to query database: %s"), error.message);
-        }
-
-        return false;
+        this.tasks = new HashMap<File, HarvestingTask> (file_hash, file_equal);
+        this.trash = new ArrayList<HarvestingTask> ();
     }
 
-    private bool process_children (GLib.List<FileInfo>? list) {
-        if (list == null || this.cancellable.is_cancelled ()) {
-            return false;
-        }
-
-        foreach (var info in list) {
-            if (info.get_name ()[0] == '.') {
-                continue;
-            }
-            var parent_container =
-                (DummyContainer) this.containers.peek_head ();
-
-            var dir = parent_container.file;
-            var file = dir.get_child (info.get_name ());
-            if (info.get_file_type () == FileType.DIRECTORY) {
-                monitor.monitor (file);
-                var container = new DummyContainer (file,
-                                                    parent_container);
-                this.containers.push_tail (container);
-                parent_container.seen (container.id);
-                try {
-                    int64 timestamp;
-                    if (!this.media_db.exists (container.id,
-                                               out timestamp)) {
-                        this.media_db.save_container (container);
-                    }
-                } catch (Error err) {
-                    warning (_("Failed to update database: %s"), err.message);
-                }
-            } else {
-                string id;
-
-                // Let's see if the file is wanted
-                if (file_filter != null &&
-                    !file_filter.match (file.get_uri ())) {
-                    continue;
-                }
-
-                push_if_changed_or_unknown (file, info, out id);
-                parent_container.seen (id);
-            }
-        }
-
-        return true;
-    }
-
-    private async void enumerate_directory (File directory) {
-        try {
-            var enumerator = yield directory.enumerate_children_async (
-                                        HARVESTER_ATTRIBUTES,
-                                        FileQueryInfoFlags.NONE,
-                                        Priority.DEFAULT,
-                                        this.cancellable);
-
-            GLib.List<FileInfo> list = null;
-            do {
-                list = yield enumerator.next_files_async (256,
-                                                          Priority.DEFAULT,
-                                                          this.cancellable);
-            } while (process_children (list));
-
-            yield enumerator.close_async (Priority.DEFAULT, this.cancellable);
-        } catch (Error err) {
-            warning (_("failed to enumerate folder: %s"), err.message);
-        }
-
-        cleanup_database (this.containers.peek_head () as DummyContainer);
-        this.do_update ();
-    }
+    public void schedule (File           file,
+                           MediaContainer parent,
+                           string?        flag   = null) {
+        if (this.extractor == null) {
+            warning (_("No Metadata extractor available. Will not crawl"));
 
-    void cleanup_database (DummyContainer container) {
-        // delete all children which are not in filesystem anymore
-        container = (DummyContainer) this.containers.peek_head ();
-        try {
-            var children = this.media_db.get_child_ids (container.id);
-
-            foreach (var seen_id in container.seen_children) {
-                children.remove (seen_id);
-            }
-
-            foreach (var child in children) {
-                this.media_db.remove_by_id (child);
-            }
-        } catch (DatabaseError err) {
-            warning (_("Failed to get children of container %s: %s"),
-                     container.id,
-                     err.message);
+            return;
         }
 
+        // Cancel currently running harvester
+        this.cancel (file);
+
+        var task = new HarvestingTask (this.extractor,
+                                       this.monitor,
+                                       file,
+                                       parent,
+                                       flag);
+        task.completed.connect (this.on_file_harvested);
+        this.tasks[file] = task;
+        task.run ();
     }
 
-    private bool on_idle () {
-        if (this.cancellable.is_cancelled ()) {
-            return false;
+    public void cancel (File file) {
+        if (this.tasks.contains (file)) {
+            var task = this.tasks[file];
+            task.completed.disconnect (this.on_file_harvested);
+            this.tasks.remove (file);
+            task.cancellable.cancel ();
+            task.completed.connect (this.on_remove_cancelled_harvester);
+            this.trash.add (task);
         }
-
-        if (this.files.size > 0) {
-            var candidate = this.files.peek ();
-            this.extractor.extract (candidate);
-        } else if (this.containers.get_length () > 0) {
-            var container = this.containers.peek_head () as DummyContainer;
-            var directory = container.file;
-            enumerate_directory (directory);
-        } else {
-            // nothing to do
-            if (this.flag != null) {
-                try {
-                    this.media_db.flag_object (Item.get_id (this.origin),
-                                               this.flag);
-                } catch (Error error) {};
-            }
-            harvested (this.origin);
-        }
-
-        return false;
     }
 
-    /**
-     * Fired for every file passed to harvest.
-     */
-    public signal void harvested (File file);
-
-    /**
-     * Extract all metainformation from a given file.
-     *
-     * What action will be taken depends on the arguments
-     * * file is a simple file. Then only information of this
-     *   file will be extracted
-     * * file is a directory and recursive is false. The children
-     *   of the directory (if not directories themselves) will be
-     *   enqueued for extraction
-     * * file is a directory and recursive is true. ++ All ++ children
-     *   of the directory will be enqueued for extraction, even directories
-     *
-     * No matter how many children are contained within file's hierarchy,
-     * only one event is sent when all the children are done.
-     */
-    public async void harvest (File file) {
-        try {
-            this.cancellable.reset ();
-            var info = yield file.query_info_async (
-                                        HARVESTER_ATTRIBUTES,
-                                        FileQueryInfoFlags.NONE,
-                                        Priority.DEFAULT,
-                                        this.cancellable);
+    private void on_file_harvested (StateMachine state_machine) {
+        var task = state_machine as HarvestingTask;
+        var file = task.origin;
+        message (_("'%s' harvested"), file.get_uri ());
 
-            if (info.get_file_type () == FileType.DIRECTORY) {
-                this.origin = file;
-                monitor.monitor (file);
-                var container = new DummyContainer (file, this.parent);
-                this.containers.push_tail (container);
-
-                int64 timestamp;
-                if (!this.media_db.exists (container.id, out timestamp)) {
-                    this.media_db.save_container (container);
-                }
-
-                Idle.add (this.on_idle);
-            } else {
-                string id;
-                if (push_if_changed_or_unknown (file, info, out id)) {
-                    Idle.add (this.on_idle);
-                    this.origin = file;
-                    this.containers.push_tail (this.parent);
-                } else {
-                    // 'Harvesting' here means extraction of metadata (title,
-                    // codec, bitrate etc) from media files.
-                    debug (_("File %s does not need harvesting"),
-                           file.get_uri ());
-                    harvested (file);
-                }
-            }
-
-        } catch (Error err) {
-            warning (_("Failed to harvest file %s: %s"),
-                     file.get_uri (),
-                     err.message);
-            harvested (file);
-        }
+        this.tasks.remove (file);
     }
 
-    private void on_extracted_cb (File                   file,
-                                  GUPnP.DLNAInformation? dlna,
-                                  string                 mime,
-                                  uint64                 size,
-                                  uint64                 mtime) {
-        if (this.cancellable.is_cancelled ()) {
-            harvested (this.origin);
-        }
-
-        var entry = this.files.peek ();
-        if (entry == null) {
-            // this event may be triggered by another instance
-            // just ignore it
-           return;
-        }
-        if (file == entry) {
-            MediaItem item;
-            if (dlna == null) {
-                item = new Item.simple (this.containers.peek_head (),
-                                        file,
-                                        mime,
-                                        size,
-                                        mtime);
-            } else {
-                item = Item.create_from_info (this.containers.peek_head (),
-                                              file,
-                                              dlna,
-                                              mime,
-                                              size,
-                                              mtime);
-            }
-
-            if (item != null) {
-                item.parent_ref = this.containers.peek_head ();
-                try {
-                    this.media_db.save_item (item);
-                } catch (Error error) {
-                    // Ignore it for now
-                }
-            }
-
-            this.files.poll ();
-            this.do_update ();
-        }
-    }
-
-    private void on_extractor_error_cb (File file, Error error) {
-        var entry = this.files.peek ();
-        if (entry == null) {
-            // this event may be triggered by another instance
-            // just ignore it
-            return;
-        }
-
-        if (file == entry) {
-            this.files.poll ();
-            this.do_update ();
-        }
-    }
-
-    /**
-     * If all files of a container were processed, notify the container
-     * about this and set the updating signal.
-     * Reschedule the iteration and extraction
-     */
-    private void do_update () {
-        if (this.files.size == 0 &&
-            this.containers.get_length () != 0) {
-            this.containers.peek_head ().updated ();
-            this.containers.pop_head ();
-        }
-
-        Idle.add (this.on_idle);
+    private void on_remove_cancelled_harvester (StateMachine state_machine) {
+        this.trash.remove (state_machine as HarvestingTask);
     }
 }
diff --git a/src/plugins/media-export/rygel-media-export-harvesting-task.vala b/src/plugins/media-export/rygel-media-export-harvesting-task.vala
new file mode 100644
index 0000000..8240da5
--- /dev/null
+++ b/src/plugins/media-export/rygel-media-export-harvesting-task.vala
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2009 Jens Georg <mail jensge org>.
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using GLib;
+using Gee;
+
+public class Rygel.MediaExport.HarvestingTask : Rygel.StateMachine, GLib.Object {
+    public File origin;
+    private MetadataExtractor extractor;
+    private MediaCache cache;
+    private GLib.Queue<MediaContainer> containers;
+    private Gee.Queue<File> files;
+    private RecursiveFileMonitor monitor;
+    private Regex file_filter;
+    private string flag;
+    private MediaContainer parent;
+
+    public Cancellable cancellable { get; set; }
+
+    private const string HARVESTER_ATTRIBUTES =
+                                        FILE_ATTRIBUTE_STANDARD_NAME + "," +
+                                        FILE_ATTRIBUTE_STANDARD_TYPE + "," +
+                                        FILE_ATTRIBUTE_TIME_MODIFIED + "," +
+                                        FILE_ATTRIBUTE_STANDARD_SIZE;
+
+
+    public HarvestingTask (MetadataExtractor    extractor,
+                           RecursiveFileMonitor monitor,
+                           File                 file,
+                           MediaContainer       parent,
+                           string?              flag = null) {
+        this.extractor = extractor;
+        this.origin = file;
+        this.parent = parent;
+
+        try {
+            this.cache = MediaCache.get_default ();
+        } catch (Error error) {
+            assert_not_reached ();
+        }
+
+        this.extractor.extraction_done.connect (on_extracted_cb);
+        this.extractor.error.connect (on_extractor_error_cb);
+
+        this.files = new LinkedList<File> ();
+        this.containers = new GLib.Queue<MediaContainer> ();
+        this.monitor = monitor;
+        this.cancellable = new Cancellable ();
+        this.flag = flag;
+        var config = MetaConfig.get_default ();
+
+        try {
+            var extensions = config.get_string_list ("MediaExport",
+                                                     "include-filter");
+
+            // never trust user input
+            string[] escaped_extensions = new string[0];
+            foreach (var extension in extensions) {
+                escaped_extensions += Regex.escape_string (extension);
+            }
+
+            var list = string.joinv ("|", escaped_extensions);
+            file_filter = new Regex ("(%s)$".printf (list),
+                                     RegexCompileFlags.CASELESS |
+                                     RegexCompileFlags.OPTIMIZE);
+        } catch (Error error) {
+            file_filter = null;
+        }
+    }
+
+    /**
+     * Extract all metainformation from a given file.
+     *
+     * What action will be taken depends on the arguments
+     * * file is a simple file. Then only information of this
+     *   file will be extracted
+     * * file is a directory and recursive is false. The children
+     *   of the directory (if not directories themselves) will be
+     *   enqueued for extraction
+     * * file is a directory and recursive is true. ++ All ++ children
+     *   of the directory will be enqueued for extraction, even directories
+     *
+     * No matter how many children are contained within file's hierarchy,
+     * only one event is sent when all the children are done.
+     */
+    public async void run () {
+        try {
+            var info = yield this.origin.query_info_async (
+                                        HARVESTER_ATTRIBUTES,
+                                        FileQueryInfoFlags.NONE,
+                                        Priority.DEFAULT,
+                                        this.cancellable);
+
+            if (process_file (this.origin, info, this.parent)) {
+                if (info.get_file_type () != FileType.DIRECTORY) {
+                    this.containers.push_tail (this.parent);
+                }
+                Idle.add (this.on_idle);
+            } else {
+                this.completed ();
+            }
+        } catch (Error err) {
+            warning (_("Failed to harvest file %s: %s"),
+                     this.origin.get_uri (),
+                     err.message);
+            this.completed ();
+        }
+    }
+
+    private bool push_if_changed_or_unknown (File       file,
+                                             FileInfo   info,
+                                             out string id) {
+        id = Checksum.compute_for_string (ChecksumType.MD5, file.get_uri ());
+        int64 timestamp;
+        try {
+            if (this.cache.exists (id, out timestamp)) {
+                int64 mtime = (int64) info.get_attribute_uint64 (
+                                        FILE_ATTRIBUTE_TIME_MODIFIED);
+
+                if (mtime > timestamp) {
+                    this.files.offer (file);
+
+                    return true;
+                } else {
+                    // check size
+                    var size = info.get_size ();
+                    var item = cache.get_item (id);
+                    if (item.size != size) {
+                        this.files.offer (file);
+
+                        return true;
+                    }
+                }
+            } else {
+                this.files.offer (file);
+
+                return true;
+            }
+        } catch (Error error) {
+            warning (_("Failed to query database: %s"), error.message);
+        }
+
+        return false;
+    }
+
+    private bool process_file (File           file,
+                               FileInfo       info,
+                               MediaContainer parent) {
+        if (info.get_name ()[0] == '.') {
+            return false;
+        }
+
+        if (info.get_file_type () == FileType.DIRECTORY) {
+            monitor.monitor (file);
+            var container = new DummyContainer (file, parent);
+            this.containers.push_tail (container);
+            try {
+                int64 timestamp;
+                if (!this.cache.exists (container.id, out timestamp)) {
+                    this.cache.save_container (container);
+                }
+            } catch (Error err) {
+                warning (_("Failed to update database: %s"), err.message);
+
+                return false;
+            }
+
+            return true;
+        } else {
+            string id;
+
+            // Let's see if the file is wanted
+            if (file_filter != null &&
+                !file_filter.match (file.get_uri ())) {
+                return false;
+            }
+
+             return push_if_changed_or_unknown (file, info, out id);
+        }
+    }
+
+    private bool process_children (GLib.List<FileInfo>? list) {
+        if (list == null || this.cancellable.is_cancelled ()) {
+            return false;
+        }
+
+        var parent_container = (DummyContainer) this.containers.peek_head ();
+
+        foreach (var info in list) {
+            var dir = parent_container.file;
+            var file = dir.get_child (info.get_name ());
+            parent_container.seen (Item.get_id (file));
+            process_file (file, info, parent_container);
+        }
+
+        return true;
+    }
+
+    private async void enumerate_directory (File directory) {
+        try {
+            var enumerator = yield directory.enumerate_children_async (
+                                        HARVESTER_ATTRIBUTES,
+                                        FileQueryInfoFlags.NONE,
+                                        Priority.DEFAULT,
+                                        this.cancellable);
+
+            GLib.List<FileInfo> list = null;
+            do {
+                list = yield enumerator.next_files_async (256,
+                                                          Priority.DEFAULT,
+                                                          this.cancellable);
+            } while (process_children (list));
+
+            yield enumerator.close_async (Priority.DEFAULT, this.cancellable);
+        } catch (Error err) {
+            warning (_("failed to enumerate folder: %s"), err.message);
+        }
+
+        cleanup_database (this.containers.peek_head () as DummyContainer);
+        this.do_update ();
+    }
+
+    private void cleanup_database (DummyContainer container) {
+        // delete all children which are not in filesystem anymore
+        container = (DummyContainer) this.containers.peek_head ();
+        try {
+            foreach (var child in container.children) {
+                this.cache.remove_by_id (child);
+            }
+        } catch (DatabaseError err) {
+            warning (_("Failed to get children of container %s: %s"),
+                     container.id,
+                     err.message);
+        }
+
+    }
+
+    private bool on_idle () {
+        if (this.cancellable.is_cancelled ()) {
+            return false;
+        }
+
+        if (this.files.size > 0) {
+            var candidate = this.files.peek ();
+            this.extractor.extract (candidate);
+        } else if (this.containers.get_length () > 0) {
+            var container = this.containers.peek_head () as DummyContainer;
+            var directory = container.file;
+            enumerate_directory (directory);
+        } else {
+            // nothing to do
+            if (this.flag != null) {
+                try {
+                    this.cache.flag_object (Item.get_id (this.origin),
+                                               this.flag);
+                } catch (Error error) {};
+            }
+            this.completed ();
+        }
+
+        return false;
+    }
+
+    private void on_extracted_cb (File                   file,
+                                  GUPnP.DLNAInformation? dlna,
+                                  string                 mime,
+                                  uint64                 size,
+                                  uint64                 mtime) {
+        if (this.cancellable.is_cancelled ()) {
+            this.completed ();
+        }
+
+        var entry = this.files.peek ();
+        if (entry == null) {
+            // this event may be triggered by another instance
+            // just ignore it
+           return;
+        }
+        if (file == entry) {
+            MediaItem item;
+            if (dlna == null) {
+                item = new Item.simple (this.containers.peek_head (),
+                                        file,
+                                        mime,
+                                        size,
+                                        mtime);
+            } else {
+                item = Item.create_from_info (this.containers.peek_head (),
+                                              file,
+                                              dlna,
+                                              mime,
+                                              size,
+                                              mtime);
+            }
+
+            if (item != null) {
+                item.parent_ref = this.containers.peek_head ();
+                try {
+                    this.cache.save_item (item);
+                } catch (Error error) {
+                    // Ignore it for now
+                }
+            }
+
+            this.files.poll ();
+            this.do_update ();
+        }
+    }
+
+    private void on_extractor_error_cb (File file, Error error) {
+        var entry = this.files.peek ();
+        if (entry == null) {
+            // this event may be triggered by another instance
+            // just ignore it
+            return;
+        }
+
+        if (file == entry) {
+            this.files.poll ();
+            this.do_update ();
+        }
+    }
+
+    /**
+     * If all files of a container were processed, notify the container
+     * about this and set the updating signal.
+     * Reschedule the iteration and extraction
+     */
+    private void do_update () {
+        if (this.files.size == 0 &&
+            this.containers.get_length () != 0) {
+            this.containers.peek_head ().updated ();
+            this.containers.pop_head ();
+        }
+
+        Idle.add (this.on_idle);
+    }
+}
diff --git a/src/plugins/media-export/rygel-media-export-root-container.vala b/src/plugins/media-export/rygel-media-export-root-container.vala
index 0dca896..af40bd6 100644
--- a/src/plugins/media-export/rygel-media-export-root-container.vala
+++ b/src/plugins/media-export/rygel-media-export-root-container.vala
@@ -26,10 +26,9 @@ using GUPnP;
  */
 public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
     private MetadataExtractor extractor;
-    private HashMap<File, Harvester> harvester;
     private RecursiveFileMonitor monitor;
     private DBusService service;
-    private Gee.List<Harvester> harvester_trash;
+    private Harvester harvester;
 
     private static MediaContainer instance = null;
 
@@ -66,7 +65,7 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
 
     public void add_uri (string uri) {
         var file = File.new_for_commandline_arg (uri);
-        this.harvest (file, this, "DBUS");
+        this.harvester.schedule (file, this, "DBUS");
     }
 
     public void remove_uri (string uri) {
@@ -74,7 +73,7 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
         var id = Checksum.compute_for_string (ChecksumType.MD5,
                                               file.get_uri ());
 
-        cancel_harvester (file);
+        this.harvester.cancel (file);
         try {
             this.media_db.remove_by_id (id);
         } catch (Error error) {
@@ -262,13 +261,11 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
         base (db, "0", "MediaExportRoot");
 
         this.extractor = new MetadataExtractor ();
-
-        this.harvester = new HashMap<File, Harvester> (file_hash, file_equal);
-        this.harvester_trash = new ArrayList<Harvester> ();
-
         this.monitor = new RecursiveFileMonitor (null);
         this.monitor.changed.connect (this.on_file_changed);
 
+        this.harvester = new Harvester (this.extractor, this.monitor);
+
         try {
             this.service = new DBusService (this);
         } catch (Error err) {
@@ -297,7 +294,7 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
                 var id = Checksum.compute_for_string (ChecksumType.MD5,
                                                       file.get_uri ());
                 ids.remove (id);
-                this.harvest (file);
+                this.harvester.schedule (file, this);
             }
         }
 
@@ -344,50 +341,6 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
         this.updated ();
     }
 
-    private void on_file_harvested (Harvester harvester,
-                                    File      file) {
-        message (_("'%s' harvested"), file.get_uri ());
-
-        this.harvester.remove (file);
-    }
-
-    private void on_remove_cancelled_harvester (Harvester harvester,
-                                                File      file) {
-        this.harvester_trash.remove (harvester);
-    }
-
-    private void cancel_harvester (File file) {
-        if (this.harvester.contains (file)) {
-            var harvester = this.harvester[file];
-            harvester.harvested.disconnect (this.on_file_harvested);
-            harvester.cancellable.cancel ();
-            harvester.harvested.connect (this.on_remove_cancelled_harvester);
-            this.harvester_trash.add (harvester);
-        }
-    }
-
-    private void harvest (File           file,
-                          MediaContainer parent = this,
-                          string?        flag   = null) {
-        if (this.extractor == null) {
-            warning (_("No Metadata extractor available. Will not crawl"));
-
-            return;
-        }
-
-        // Cancel currently running harvester
-        cancel_harvester (file);
-
-        var harvester = new Harvester (parent,
-                                       this.media_db,
-                                       this.extractor,
-                                       this.monitor,
-                                       flag);
-        harvester.harvested.connect (this.on_file_harvested);
-        this.harvester[file] = harvester;
-        harvester.harvest (file);
-    }
-
     private void on_file_changed (File             file,
                                   File?            other,
                                   FileMonitorEvent event) {
@@ -405,7 +358,7 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
                                            as MediaContainer;
                     assert (parent_container != null);
 
-                    this.harvest (file, parent_container);
+                    this.harvester.schedule (file, parent_container);
                 } catch (DatabaseError error) {
                     warning (_("Error fetching object '%s' from database: %s"),
                              id,
@@ -416,7 +369,7 @@ public class Rygel.MediaExport.RootContainer : Rygel.MediaExport.DBContainer {
                 var id = Checksum.compute_for_string (ChecksumType.MD5,
                                                       file.get_uri ());
 
-                cancel_harvester (file);
+                this.harvester.cancel (file);
                 try {
                     // the full object is fetched instead of simply calling
                     // exists because we need the parent to signalize the



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]