[totem] Added zeitgeist dataprovider plugin



commit f3e2132a165d855310280569d7da7ae3a50b573c
Author: Michal Hruby <michal mhr gmail com>
Date:   Sat Jan 29 01:24:05 2011 +0100

    Added zeitgeist dataprovider plugin

 configure.in                                       |   16 ++-
 src/plugins/zeitgeist-dp/Makefile.am               |   23 ++
 src/plugins/zeitgeist-dp/bacon-video.vapi          |   40 ++++
 .../zeitgeist-dp/totem-zeitgeist-dp-plugin.vala    |  229 ++++++++++++++++++++
 src/plugins/zeitgeist-dp/zeitgeist-dp.plugin.in    |    8 +
 5 files changed, 315 insertions(+), 1 deletions(-)
---
diff --git a/configure.in b/configure.in
index c8a0c08..641f18f 100644
--- a/configure.in
+++ b/configure.in
@@ -68,7 +68,7 @@ AC_SUBST(TOTEM_API_VERSION)
 AC_DEFINE_UNQUOTED(TOTEM_API_VERSION, ["$TOTEM_API_VERSION"], [Define to the Totem plugin API version])
 
 # The full list of plugins
-allowed_plugins="bemused brasero-disc-recorder chapters coherence_upnp dbus-service galago gromit iplayer jamendo lirc media-player-keys ontop opensubtitles properties publish pythonconsole save-file sample-python sample-vala screensaver screenshot sidebar-test skipto thumbnail tracker youtube"
+allowed_plugins="bemused brasero-disc-recorder chapters coherence_upnp dbus-service galago gromit iplayer jamendo lirc media-player-keys ontop opensubtitles properties publish pythonconsole save-file sample-python sample-vala screensaver screenshot sidebar-test skipto thumbnail tracker youtube zeitgeist-dp"
 
 PLUGINDIR='${libdir}/totem/plugins'
 AC_SUBST(PLUGINDIR)
@@ -532,6 +532,19 @@ for plugin in ${used_plugins}; do
 				add_plugin="0"
 			fi
 		;;
+		zeitgeist-dp)
+			if test "${with_vala}" != "yes" ; then
+				plugin_error_or_ignore "you need vala installed to use the zeitgeist-dp plugin"
+				add_plugin="0"
+			else
+				PKG_CHECK_MODULES(LIBZEITGEIST, zeitgeist-1.0 >= 0.2.12,
+				[HAS_LIBZEITGEIST=yes], [HAS_LIBZEITGEIST=no])
+				if test "${HAS_LIBZEITGEIST}" != "yes" ; then
+					plugin_error_or_ignore "you need zeitgeist-1.0 >= 0.2.12 to use the zeitgeist-dp plugin"
+					add_plugin="0"
+				fi
+			fi
+		;;
 	esac
 
 	# Add the specified plugin
@@ -797,6 +810,7 @@ src/plugins/jamendo/Makefile
 src/plugins/jamendo/org.gnome.totem.plugins.jamendo.gschema.xml.in
 src/plugins/brasero-disc-recorder/Makefile
 src/plugins/chapters/Makefile
+src/plugins/zeitgeist-dp/Makefile
 src/backend/Makefile
 browser-plugin/Makefile
 data/Makefile
diff --git a/src/plugins/zeitgeist-dp/Makefile.am b/src/plugins/zeitgeist-dp/Makefile.am
new file mode 100644
index 0000000..7472b12
--- /dev/null
+++ b/src/plugins/zeitgeist-dp/Makefile.am
@@ -0,0 +1,23 @@
+include $(top_srcdir)/src/plugins/Makefile.plugins
+
+plugindir = $(PLUGINDIR)/zeitgeist-dp
+plugin_LTLIBRARIES = libtotem-zeitgeist-dp-plugin.la
+
+plugin_in_files = zeitgeist-dp.plugin.in
+
+# here we are explicitly specifying gtk+-3.0 to use the vapi because vala still
+# cannot parse the gir
+VALAFLAGS = \
+	--girdir=$(top_srcdir)/src	\
+	--pkg Totem-1.0 --pkg Peas-1.0 --pkg gtk+-3.0 \
+	--pkg zeitgeist-1.0 \
+	bacon-video.vapi
+
+libtotem_zeitgeist_dp_plugin_la_SOURCES = totem-zeitgeist-dp-plugin.vala
+libtotem_zeitgeist_dp_plugin_la_LDFLAGS = $(plugin_ldflags) $(LIBZEITGEIST_LIBS)
+libtotem_zeitgeist_dp_plugin_la_CFLAGS = \
+	$(plugin_cflags) \
+	$(LIBZEITGEIST_CFLAGS) \
+	-I $(top_srcdir)/src/backend
+
+-include $(top_srcdir)/git.mk
diff --git a/src/plugins/zeitgeist-dp/bacon-video.vapi b/src/plugins/zeitgeist-dp/bacon-video.vapi
new file mode 100644
index 0000000..1612a7d
--- /dev/null
+++ b/src/plugins/zeitgeist-dp/bacon-video.vapi
@@ -0,0 +1,40 @@
+[CCode (cprefix = "Bacon", lower_case_cprefix = "bacon_")]
+
+namespace Bacon {
+	[CCode (cheader_filename = "bacon-video-widget.h")]
+	public class VideoWidget : Gtk.EventBox {
+    [CCode (has_construct_function = false)]
+		public VideoWidget (int width, int height, UseType type) throws GLib.Error;
+
+    public void get_metadata (MetadataType type, out GLib.Value val);
+	}
+  [CCode (cprefix = "BVW_USE_TYPE_", cheader_filename = "bacon-video-widget.h")]
+  public enum UseType {
+    VIDEO,
+    AUDIO,
+    CAPTURE,
+    METADATA
+  }
+  [CCode (cprefix = "BVW_INFO_", cheader_filename = "bacon-video-widget.h")]
+  public enum MetadataType {
+    TITLE,
+    ARTIST,
+    YEAR,
+    COMMENT,
+    ALBUM,
+    DURATION,
+    TRACK_NUMBER,
+    COVER,
+    HAS_VIDEO,
+    DIMENSION_X,
+    DIMENSION_Y,
+    VIDEO_BITRATE,
+    VIDEO_CODEC,
+    FPS,
+    HAS_AUDIO,
+    AUDIO_BITRATE,
+    AUDIO_CODEC,
+    AUDIO_SAMPLE_RATE,
+    AUDIO_CHANNELS
+  }
+}
diff --git a/src/plugins/zeitgeist-dp/totem-zeitgeist-dp-plugin.vala b/src/plugins/zeitgeist-dp/totem-zeitgeist-dp-plugin.vala
new file mode 100644
index 0000000..b8affc3
--- /dev/null
+++ b/src/plugins/zeitgeist-dp/totem-zeitgeist-dp-plugin.vala
@@ -0,0 +1,229 @@
+using Totem;
+
+struct MediaInfo {
+  int64 timestamp;
+  bool sent_access;
+  string? mrl;
+  string? mimetype;
+  string? title;
+  string? interpretation;
+  string? artist;
+  string? album;
+}
+
+class ZeitgeistDpPlugin: GLib.Object, Peas.Activatable {
+  private MediaInfo current_media;
+  /* timer waiting while we get some info about current playing media */
+  private uint media_info_timeout;
+  /* timer making sure we don't wait indefinitely */
+  private uint timeout_id;
+  private ulong[] signals;
+
+  private Zeitgeist.Log zg_log;
+  private Zeitgeist.DataSourceRegistry zg_registry;
+
+  public unowned Totem.Object object { get; set; }
+
+  public void activate () {
+    zg_log = new Zeitgeist.Log ();
+    zg_registry = new Zeitgeist.DataSourceRegistry ();
+
+    current_media = MediaInfo ();
+
+    signals += Signal.connect_swapped (object, "file-opened",
+                                       (Callback) file_opened, this);
+    signals += Signal.connect_swapped (object, "file-closed",
+                                       (Callback)file_closed, this);
+    signals += Signal.connect_swapped (object, "metadata-updated",
+                                       (Callback) metadata_changed, this);
+    signals += Signal.connect_swapped (object, "notify::playing",
+                                       (Callback) playing_changed, this);
+
+    PtrArray templates = new PtrArray ();
+    var event = new Zeitgeist.Event.full ("", Zeitgeist.ZG_USER_ACTIVITY,
+                                          "application://totem.desktop", null);
+    templates.add (event);
+    var ds = new Zeitgeist.DataSource.full (
+      "org.gnome.Totem,dataprovider",
+      "Totem dataprovider",
+      "Logs access/leave events for media files played with Totem",
+      (owned) templates
+    );
+    zg_registry.register_data_source.begin (ds, null);
+  }
+
+  public void deactivate () {
+    /* we don't always get file-closed, so lets simulate it */
+    file_closed (object);
+
+    foreach (ulong id in signals) {
+      SignalHandler.disconnect (object, id);
+    }
+    signals = null;
+
+    /* cleanup timers */
+    if (media_info_timeout != 0) Source.remove (media_info_timeout);
+    if (timeout_id != 0) Source.remove (timeout_id);
+
+    media_info_timeout = 0;
+    timeout_id = 0;
+  }
+
+  public void update_state () {
+    /* ignore */
+  }
+
+  private void restart_watcher (uint interval) {
+    if (timeout_id != 0) {
+      Source.remove (timeout_id);
+    }
+    timeout_id = Timeout.add (interval, timeout_cb);
+  }
+
+  private void file_opened (string mrl, Totem.Object totem) {
+    if (current_media.mrl != null) {
+      /* we don't always get file-closed, so lets simulate it */
+      file_closed (totem);
+    }
+
+    current_media = MediaInfo ();
+    current_media.mrl = mrl;
+
+    TimeVal cur_time = TimeVal ();
+    current_media.timestamp = Zeitgeist.Timestamp.from_timeval (cur_time);
+
+    /* wait a bit for the media info */
+    if (media_info_timeout == 0) {
+      media_info_timeout = Timeout.add (250, wait_for_media_info);
+      /* but make sure we dont wait indefinitely */
+      restart_watcher (15000);
+    }
+  }
+
+  private void file_closed (Totem.Object totem) {
+    if (current_media.sent_access && current_media.mrl != null) {
+      /* send close event */
+      TimeVal cur_time = TimeVal ();
+      current_media.timestamp = Zeitgeist.Timestamp.from_timeval (cur_time);
+      send_event_to_zg (true);
+
+      current_media.mrl = null;
+    }
+
+    /* kill timers */
+    if (media_info_timeout != 0) Source.remove (media_info_timeout);
+    media_info_timeout = 0;
+    if (timeout_id != 0) Source.remove (timeout_id);
+    timeout_id = 0;
+  }
+
+  private void metadata_changed (string? artist, string? title, string? album,
+                                 uint track_num, Totem.Object totem) {
+    /* we can get some notification after sending event to ZG, so ignore it */
+    if (media_info_timeout != 0) {
+      current_media.artist = artist;
+      current_media.title = title;
+      current_media.album = album;
+    }
+  }
+
+  private bool timeout_cb () {
+    if (media_info_timeout != 0) {
+      /* we don't have any info besides the url, so use the short_title */
+
+      Source.remove (media_info_timeout);
+      media_info_timeout = 0;
+
+      current_media.title = Totem.get_short_title (object);
+      timeout_id = 0;
+      wait_for_media_info ();
+    }
+
+    timeout_id = 0;
+    return false;
+  }
+
+  private async void query_media_mimetype (string current_mrl) {
+    string mrl = current_mrl;
+    var f = File.new_for_uri (mrl);
+
+    try {
+      var fi = yield f.query_info_async (FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+                                         0, Priority.DEFAULT_IDLE, null);
+
+      if (current_media.mrl != mrl || !object.is_playing ()) return;
+      current_media.mimetype = fi.get_content_type ();
+
+      /* send event */
+      send_event_to_zg ();
+      current_media.sent_access = true;
+    } catch (GLib.Error err) {
+      /* most likely invalid uri */
+    }
+  }
+
+  private bool wait_for_media_info () {
+    if (current_media.title != null && object.is_playing ()) {
+      Value val;
+      var video = Totem.get_video_widget (object) as Bacon.VideoWidget;
+      video.get_metadata (Bacon.MetadataType.HAS_VIDEO, out val);
+      current_media.interpretation = val.get_boolean () ?
+        Zeitgeist.NFO_VIDEO : Zeitgeist.NFO_AUDIO;
+
+      query_media_mimetype (current_media.mrl);
+
+      /* cleanup timers */
+      if (timeout_id != 0) Source.remove (timeout_id);
+      timeout_id = 0;
+      media_info_timeout = 0;
+      return false;
+    }
+    /* wait longer */
+    return true;
+  }
+
+  private void playing_changed () {
+    if (media_info_timeout == 0 && current_media.sent_access == false) {
+      wait_for_media_info ();
+    }
+
+    /* end of playlist */
+    if (!object.is_playing () && current_media.sent_access) {
+      /* sends leave event even if the user just pauses the playback
+         for a little while, but we don't want too many access events
+         for the same uri */
+      file_closed (object);
+    }
+  }
+
+  private void send_event_to_zg (bool leave_event = false) {
+    if (current_media.mrl != null && current_media.title != null) {
+      string event_interpretation = leave_event ?
+        Zeitgeist.ZG_LEAVE_EVENT : Zeitgeist.ZG_ACCESS_EVENT;
+      string origin = Path.get_dirname (current_media.mrl);
+      var subject = new Zeitgeist.Subject.full (
+        current_media.mrl,
+        current_media.interpretation,
+        Zeitgeist.manifestation_for_uri (current_media.mrl),
+        current_media.mimetype,
+        origin,
+        current_media.title,
+        "");
+      var event = new Zeitgeist.Event.full (event_interpretation,
+                                            Zeitgeist.ZG_USER_ACTIVITY,
+                                            "application://totem.desktop",
+                                            subject, null);
+      event.set_timestamp (current_media.timestamp);
+      zg_log.insert_events_no_reply (event, null);
+    }
+  }
+}
+
+[ModuleInit]
+public void peas_register_types (GLib.TypeModule module)
+{
+  var objmodule = module as Peas.ObjectModule;
+  objmodule.register_extension_type (typeof (Peas.Activatable),
+                                     typeof (ZeitgeistDpPlugin));
+}
+
diff --git a/src/plugins/zeitgeist-dp/zeitgeist-dp.plugin.in b/src/plugins/zeitgeist-dp/zeitgeist-dp.plugin.in
new file mode 100644
index 0000000..71a6908
--- /dev/null
+++ b/src/plugins/zeitgeist-dp/zeitgeist-dp.plugin.in
@@ -0,0 +1,8 @@
+[Plugin]
+Module=totem-zeitgeist-dp-plugin
+IAge=1
+_Name=Zeitgeist Plugin
+_Description=A plugin sending events to Zeitgeist
+Authors=Michal Hruby <michal mhr gmail com>
+Copyright=Copyright © 2010 Michal Hruby
+Website=http://www.gnome.org/projects/totem/



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