diff options
author | Michal Hruby <michal.mhr@gmail.com> | 2011-01-29 01:24:05 +0100 |
---|---|---|
committer | Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com> | 2011-02-11 12:39:56 +0100 |
commit | f3e2132a165d855310280569d7da7ae3a50b573c (patch) | |
tree | 96285fd26d24ff99c27244dfb4658c32241af802 | |
parent | 5be9be414e03bca8741e556474ec9179e084c5f0 (diff) | |
download | totem-f3e2132a165d855310280569d7da7ae3a50b573c.tar.gz |
Added zeitgeist dataprovider plugin
-rw-r--r-- | configure.in | 16 | ||||
-rw-r--r-- | src/plugins/zeitgeist-dp/Makefile.am | 23 | ||||
-rw-r--r-- | src/plugins/zeitgeist-dp/bacon-video.vapi | 40 | ||||
-rw-r--r-- | src/plugins/zeitgeist-dp/totem-zeitgeist-dp-plugin.vala | 229 | ||||
-rw-r--r-- | 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 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/ |