[rygel] media-export: Add media harvester class
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: svn-commits-list gnome org
- Subject: [rygel] media-export: Add media harvester class
- Date: Thu, 25 Jun 2009 15:47:20 +0000 (UTC)
commit b0c7c60af392db2258d009f6d0d90dfecdf87cb9
Author: Jens Georg <mail jensge org>
Date: Wed Jun 24 22:43:40 2009 +0200
media-export: Add media harvester class
src/plugins/media-export/Makefile.am | 3 +
.../media-export/rygel-media-export-harvester.vala | 261 ++++++++++++++++++++
.../media-export/rygel-media-export-item.vala | 136 ++++++++++
.../rygel-media-export-root-container.vala | 98 ++++----
4 files changed, 453 insertions(+), 45 deletions(-)
---
diff --git a/src/plugins/media-export/Makefile.am b/src/plugins/media-export/Makefile.am
index 8f7fa51..d986c4f 100644
--- a/src/plugins/media-export/Makefile.am
+++ b/src/plugins/media-export/Makefile.am
@@ -13,6 +13,7 @@ AM_CFLAGS = $(LIBGUPNP_CFLAGS) \
BUILT_SOURCES = rygel-media-export-root-container.c \
rygel-media-export-container.c \
rygel-media-export-item.c \
+ rygel-media-export-harvester.c \
rygel-media-export-directory-search-result.c \
rygel-media-export-plugin.c
@@ -23,6 +24,8 @@ librygel_media_export_la_SOURCES = \
rygel-media-export-plugin.vala \
rygel-media-export-root-container.c \
rygel-media-export-root-container.vala \
+ rygel-media-export-harvester.c \
+ rygel-media-export-harvester.vala \
rygel-media-export-container.c \
rygel-media-export-container.vala \
rygel-media-export-item.c \
diff --git a/src/plugins/media-export/rygel-media-export-harvester.vala b/src/plugins/media-export/rygel-media-export-harvester.vala
new file mode 100644
index 0000000..7b828e8
--- /dev/null
+++ b/src/plugins/media-export/rygel-media-export-harvester.vala
@@ -0,0 +1,261 @@
+/*
+ * 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;
+using Rygel;
+
+internal class DummyContainer : Rygel.MediaContainer {
+ public DummyContainer (File file, MediaContainer parent) {
+ var id = Checksum.compute_for_string (ChecksumType.MD5,
+ file.get_uri ());
+ base (id, parent, file.get_basename (), 0);
+ this.parent_ref = parent;
+ }
+
+
+ public override void get_children (uint offset,
+ uint max_count,
+ Cancellable? cancellable,
+ AsyncReadyCallback callback) {}
+
+ public override Gee.List<MediaObject>? get_children_finish (
+ AsyncResult res)
+ throws Error
+ { return null; }
+
+ public override void find_object (string id,
+ Cancellable? cancellable,
+ AsyncReadyCallback callback) { }
+
+ public override MediaObject? find_object_finish (AsyncResult res)
+ throws Error { return
+ null;}
+
+}
+
+public class Rygel.MediaExportHarvester : GLib.Object {
+ private MetadataExtractor extractor;
+ private MediaDB media_db;
+ private Queue<File> directories;
+ private Queue<MediaContainer> containers;
+ private Queue<File> files;
+ private File origin;
+ private MediaContainer parent;
+
+ private void dump_queues () {
+ print ("directories queue\n==================\n");
+ for (int i = 0; i < directories.length; i++) {
+ print ("\t%s\n", directories.peek_nth (i).get_basename ());
+ }
+
+ print ("files queue\n==================\n");
+ for (int i = 0; i < files.length; i++) {
+ print ("\t%s\n", files.peek_nth (i).get_basename ());
+ }
+
+ print ("container queue\n==================\n");
+ for (int i = 0; i < containers.length; i++) {
+ print ("\t%s\n", containers.peek_nth (i).title);
+ }
+ }
+
+ public MediaExportHarvester (MediaContainer parent,
+ MediaDB media_db,
+ MetadataExtractor extractor) {
+ this.parent = parent;
+ 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.directories = new Queue<File> ();
+ this.files = new Queue<File> ();
+ this.containers = new Queue<MediaContainer> ();
+ this.origin = null;
+ Idle.add (this.on_idle);
+ }
+
+ private void on_close_async (Object obj, AsyncResult res) {
+ var enumerator = (FileEnumerator) obj;
+ try {
+ enumerator.close_finish (res);
+ } catch (Error error) {
+ // TODO
+ }
+ this.directories.pop_head ();
+ if (this.files.get_length() == 0 && this.containers.get_length () != 0) {
+ this.containers.pop_head ();
+ }
+
+ Idle.add(this.on_idle);
+ }
+
+ private void on_next_files_ready (Object obj, AsyncResult res) {
+ var enumerator = (FileEnumerator) obj;
+ try {
+ var list = enumerator.next_files_finish (res);
+ if (list != null) {
+ foreach (var info in list) {
+ var dir = this.directories.peek_head ();
+ var file = dir.get_child (info.get_name ());
+ if (info.get_file_type () == FileType.DIRECTORY) {
+ this.directories.push_tail (file);
+ this.containers.push_tail (
+ new DummyContainer (file,
+ this.containers.peek_head ()));
+ this.media_db.save_object (this.containers.peek_tail ());
+ } else {
+ this.files.push_tail (file);
+ }
+ dump_queues ();
+ }
+
+ enumerator.next_files_async (10,
+ Priority.DEFAULT,
+ null,
+ this.on_next_files_ready);
+ } else {
+ enumerator.close_async (Priority.DEFAULT,
+ null,
+ this.on_close_async);
+ }
+ } catch (Error error) {
+ // TODO
+ }
+ }
+
+ private void on_enumerate_ready (Object obj, AsyncResult res) {
+ var file = (File) obj;
+ try {
+ var enumerator = file.enumerate_children_finish (res);
+ enumerator.next_files_async (10,
+ Priority.DEFAULT,
+ null,
+ on_next_files_ready);
+ } catch (Error error) {
+ // TODO
+ }
+ }
+
+ private bool on_idle () {
+ dump_queues ();
+ if (this.files.get_length () > 0) {
+ var candidate = this.files.peek_head ();
+ this.extractor.extract (candidate);
+ } else if (this.directories.get_length () > 0) {
+ var directory = this.directories.peek_head ();
+ directory.enumerate_children_async (
+ FILE_ATTRIBUTE_STANDARD_TYPE + "," +
+ FILE_ATTRIBUTE_STANDARD_NAME,
+ FileQueryInfoFlags.NONE,
+ Priority.DEFAULT,
+ null,
+ this.on_enumerate_ready);
+ } else {
+ // nothing to do
+ harvested (this.origin);
+ this.origin = null;
+ }
+
+ 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 void harvest (File file) {
+ if (file.query_exists (null)) {
+ try {
+ var info = file.query_info (
+ FILE_ATTRIBUTE_STANDARD_NAME + "," +
+ FILE_ATTRIBUTE_STANDARD_TYPE,
+ FileQueryInfoFlags.NONE,
+ null);
+ if (info.get_file_type () == FileType.DIRECTORY) {
+ this.origin = file;
+ this.directories.push_tail (file);
+ this.containers.push_tail (
+ new DummyContainer (file,
+ this.parent));
+
+ this.media_db.save_object (this.containers.peek_tail ());
+ } else {
+ this.origin = file;
+ this.files.push_tail (file);
+ this.containers.push_tail (this.parent);
+ }
+ } catch (Error error) {
+ debug ("Failed to query info for file %s: %s",
+ file.get_uri (),
+ error.message);
+ }
+ }
+ }
+
+ private void on_extracted_cb (File file, Gst.TagList tag_list) {
+ if (file == this.files.peek_head ()) {
+ debug ("successfully harvested file %s", file.get_uri ());
+
+ var item = MediaExportItem.create_from_taglist (
+ this.containers.peek_head (),
+ file,
+ tag_list);
+ item.parent_ref = this.containers.peek_head ();
+ this.media_db.save_object (item);
+
+ this.files.pop_head ();
+ if (this.files.get_length () == 0 &&
+ this.containers.get_length () != 0) {
+ this.containers.pop_head ();
+ }
+ Idle.add(this.on_idle);
+ }
+ }
+
+ private void on_extractor_error_cb (File file, Error error) {
+ if (file == this.files.peek_head ()) {
+ debug ("failed to harvest file %s", file.get_uri ());
+ // yadda yadda
+ this.files.pop_head ();
+ if (this.files.get_length () == 0 &&
+ this.containers.get_length () != 0) {
+ this.containers.pop_head ();
+ }
+ Idle.add(this.on_idle);
+ }
+ }
+}
diff --git a/src/plugins/media-export/rygel-media-export-item.vala b/src/plugins/media-export/rygel-media-export-item.vala
index 7b0a4fa..890d4a5 100644
--- a/src/plugins/media-export/rygel-media-export-item.vala
+++ b/src/plugins/media-export/rygel-media-export-item.vala
@@ -22,6 +22,7 @@
*/
using GUPnP;
+using Gst;
/**
* Represents MediaExport item.
@@ -57,5 +58,140 @@ public class Rygel.MediaExportItem : MediaItem {
this.mime_type = content_type;
this.uris.add (file.get_uri ());
}
+
+ private void fill_from_tags_as_image (Gst.TagList tag_list) {
+
+ tag_list.get_string (MetadataExtractor.TAG_RYGEL_MIME, out this.mime_type);
+ int64 size;
+ tag_list.get_int64 (MetadataExtractor.TAG_RYGEL_SIZE, out size);
+ this.size = (long) size;
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_WIDTH, out this.width);
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_HEIGHT, out this.height);
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_DEPTH, out this.color_depth);
+ }
+
+ private void fill_from_tags_as_audio (Gst.TagList tag_list) {
+ int64 duration;
+ tag_list.get_int64 (MetadataExtractor.TAG_RYGEL_DURATION, out duration);
+ this.duration = (long) (duration / 1000000000);
+
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_CHANNELS, out this.n_audio_channels);
+ tag_list.get_string (MetadataExtractor.TAG_RYGEL_MIME, out this.mime_type);
+
+ int64 size;
+ tag_list.get_int64 (MetadataExtractor.TAG_RYGEL_SIZE, out size);
+ this.size = (long) size;
+
+ tag_list.get_string (TAG_ARTIST, out this.author);
+ tag_list.get_string (TAG_ALBUM, out this.album);
+
+ uint tmp;
+ tag_list.get_uint (TAG_TRACK_NUMBER, out tmp);
+ this.track_number = (int)tmp;
+ tag_list.get_uint (TAG_BITRATE, out tmp);
+ this.bitrate = (int)tmp;
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_RATE, out this.sample_freq);
+
+ GLib.Date? date;
+ if (tag_list.get_date (TAG_DATE, out date)) {
+ char[] datestr = new char[30];
+ date.strftime(datestr, "%F");
+ this.date = (string)datestr;
+ }
+ }
+
+ private void fill_from_tags_as_video (Gst.TagList tag_list) {
+ tag_list.get_string (MetadataExtractor.TAG_RYGEL_MIME,
+ out this.mime_type);
+ int64 size;
+ tag_list.get_int64 (MetadataExtractor.TAG_RYGEL_SIZE,
+ out size);
+ this.size = (long) size;
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_WIDTH,
+ out this.width);
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_HEIGHT,
+ out this.height);
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_DEPTH,
+ out this.color_depth);
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_CHANNELS,
+ out this.n_audio_channels);
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_RATE,
+ out this.sample_freq);
+ }
+
+ public static MediaItem? create_from_taglist (MediaContainer parent,
+ File file,
+ Gst.TagList tag_list) {
+ string id = Checksum.compute_for_string (ChecksumType.MD5,
+ file.get_uri ());
+ int width = -1;
+ int height = -1;
+ string class_guessed = null;
+
+ if (tag_list != null) {
+ string codec;
+
+ if (!tag_list.get_string (TAG_VIDEO_CODEC, out codec)) {
+ if (!tag_list.get_string (TAG_AUDIO_CODEC, out codec)) {
+ if (tag_list.get_int (MetadataExtractor.TAG_RYGEL_WIDTH, out width) ||
+ tag_list.get_int (MetadataExtractor.TAG_RYGEL_HEIGHT, out height)) {
+ class_guessed = MediaItem.IMAGE_CLASS;
+ } else {
+ warning("There's no codec inside and no image for file" +
+ "%s", file.get_uri ());
+ }
+ } else {
+ class_guessed = MediaItem.AUDIO_CLASS;
+ }
+ } else {
+ class_guessed = MediaItem.VIDEO_CLASS;
+ }
+ } else {
+ // throw error. Taglist can't be empty
+ warning("Got empty taglist for file %s", file.get_uri ());
+ return null;
+ }
+
+ return new MediaExportItem.from_taglist (parent,
+ id,
+ file,
+ tag_list,
+ class_guessed);
+ }
+
+ private MediaExportItem.from_taglist (MediaContainer parent,
+ string id,
+ File file,
+ Gst.TagList tag_list,
+ string upnp_class) {
+ string title = null;
+ if (upnp_class == MediaItem.AUDIO_CLASS ||
+ upnp_class == MediaItem.MUSIC_CLASS) {
+
+ if (!tag_list.get_string (TAG_TITLE, out title)) {
+ title = file.get_basename ();
+ }
+
+ } else {
+ title = file.get_basename ();
+ }
+ base (id, parent, title, upnp_class);
+ switch (upnp_class) {
+ case MediaItem.AUDIO_CLASS:
+ case MediaItem.MUSIC_CLASS:
+ fill_from_tags_as_audio (tag_list);
+ break;
+ case MediaItem.VIDEO_CLASS:
+ fill_from_tags_as_video (tag_list);
+ break;
+ case MediaItem.IMAGE_CLASS:
+ fill_from_tags_as_image (tag_list);
+ break;
+ default:
+ break;
+ }
+
+ this.uris.add (file.get_uri ());
+ }
}
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 1f6ca2c..fdce4ff 100644
--- a/src/plugins/media-export/rygel-media-export-root-container.vala
+++ b/src/plugins/media-export/rygel-media-export-root-container.vala
@@ -24,64 +24,44 @@ using Gee;
* Represents the root container.
*/
public class Rygel.MediaExportRootContainer : MediaContainer {
- private ArrayList<MediaObject> children;
+ private MediaDB media_db;
+ private Gee.ArrayList<MediaObject> children;
+ private DatabaseBackedMediaContainer root_container;
+ private MetadataExtractor extractor;
+ private Gee.ArrayList<MediaExportHarvester> harvester;
public override void get_children (uint offset,
uint max_count,
Cancellable? cancellable,
AsyncReadyCallback callback)
{
- uint stop = offset + max_count;
- stop = stop.clamp (0, this.child_count);
- var children = this.children.slice ((int) offset, (int) stop);
- var res = new Rygel.SimpleAsyncResult<Gee.List<MediaObject>> (this,
- callback);
- res.data = children;
- res.complete_in_idle ();
+ debug ("get children called");
+ this.root_container.get_children (offset,
+ max_count,
+ cancellable,
+ callback);
}
public override Gee.List<MediaObject>? get_children_finish (
AsyncResult res)
throws GLib.Error {
- var simple_res = (Rygel.SimpleAsyncResult<Gee.List<MediaObject>>) res;
-
- return simple_res.data;
+ debug ("get children_finish called");
+ return this.root_container.get_children_finish (res);
}
public override void find_object (string id,
Cancellable? cancellable,
AsyncReadyCallback callback) {
- var res = new Rygel.SimpleAsyncResult<string> (this, callback);
-
- res.data = id;
- res.complete_in_idle ();
+ debug ("find object called");
+ this.root_container.find_object (id,
+ cancellable,
+ callback);
}
public override MediaObject? find_object_finish (AsyncResult res)
throws GLib.Error {
- MediaObject media_obj = null;
- var id = ((Rygel.SimpleAsyncResult<string>) res).data;
-
- foreach (var tmp in this.children) {
- if (id == tmp.id) {
- media_obj = tmp;
- break;
- }
- }
-
- if (media_obj == null) {
- foreach (var tmp in this.children) {
- if (tmp is MediaExportContainer) {
- var folder = (MediaExportContainer) tmp;
- media_obj = folder.find_object_sync (id);
- if (media_obj != null) {
- break;
- }
- }
- }
- }
-
- return media_obj;
+ debug ("find object finish called");
+ return this.root_container.find_object_finish (res);
}
/**
@@ -89,6 +69,22 @@ public class Rygel.MediaExportRootContainer : MediaContainer {
*/
public MediaExportRootContainer () {
base.root ("MediaExportRoot", 0);
+ var media_db_path = Path.build_filename (
+ Environment.get_user_cache_dir (),
+ Environment.get_prgname (),
+ "media-export.db");
+
+ debug("Using media database %s", media_db_path);
+
+ this.media_db = new MediaDB(media_db_path);
+ this.extractor = new MetadataExtractor ();
+
+
+ this.root_container = new DatabaseBackedMediaContainer (this.media_db,
+ "0",
+ "MediaExportRoot");
+
+ this.harvester = new Gee.ArrayList<MediaExportHarvester> ();
ArrayList<string> uris;
this.children = new ArrayList<MediaExportContainer> ();
@@ -120,7 +116,11 @@ public class Rygel.MediaExportRootContainer : MediaContainer {
foreach (var uri in uris) {
var f = File.new_for_commandline_arg (uri);
if (f.query_exists (null)) {
- f.query_info_async (
+ var id = Checksum.compute_for_string (ChecksumType.MD5,
+ uri);
+ var obj = media_db.get_object (id);
+ if (obj == null) {
+ f.query_info_async (
FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE + "," +
FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME + "," +
FILE_ATTRIBUTE_STANDARD_TYPE + "," +
@@ -129,6 +129,10 @@ public class Rygel.MediaExportRootContainer : MediaContainer {
Priority.DEFAULT,
null,
this.on_info_ready);
+ } else {
+ this.child_count++;
+ this.updated ();
+ }
}
}
}
@@ -141,15 +145,19 @@ public class Rygel.MediaExportRootContainer : MediaContainer {
MediaObject media_obj = null;
if (info.get_file_type () == FileType.DIRECTORY) {
- media_obj = new MediaExportContainer (this, file);
+ media_obj = new MediaExportContainer (root_container, file);
} else {
- media_obj = new MediaExportItem (this, file, info);
+ media_obj = new MediaExportItem (root_container, file, info);
}
- this.children.add (media_obj);
- this.child_count = this.children.size;
-
- this.updated ();
+ if (media_obj != null) {
+ debug ("Scheduling new harvester");
+ var harvest =
+ new MediaExportHarvester (this.root_container, media_db,
+ extractor);
+ this.harvester.add (harvest);
+ harvest.harvest (file);
+ }
} catch (Error err) {
warning ("Failed to query information on '%s': %s\n",
file.get_uri (),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]