summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Hruby <michal.mhr@gmail.com>2011-01-29 01:24:05 +0100
committerSiegfried-Angel Gevatter Pujals <rainct@ubuntu.com>2011-02-11 12:39:56 +0100
commitf3e2132a165d855310280569d7da7ae3a50b573c (patch)
tree96285fd26d24ff99c27244dfb4658c32241af802
parent5be9be414e03bca8741e556474ec9179e084c5f0 (diff)
downloadtotem-f3e2132a165d855310280569d7da7ae3a50b573c.tar.gz
Added zeitgeist dataprovider plugin
-rw-r--r--configure.in16
-rw-r--r--src/plugins/zeitgeist-dp/Makefile.am23
-rw-r--r--src/plugins/zeitgeist-dp/bacon-video.vapi40
-rw-r--r--src/plugins/zeitgeist-dp/totem-zeitgeist-dp-plugin.vala229
-rw-r--r--src/plugins/zeitgeist-dp/zeitgeist-dp.plugin.in8
5 files changed, 315 insertions, 1 deletions
diff --git a/configure.in b/configure.in
index c8a0c0853..641f18ffe 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 000000000..7472b1256
--- /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 000000000..1612a7d63
--- /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 000000000..b8affc364
--- /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 000000000..71a690825
--- /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/