totem r6219 - in trunk: . po src src/backend src/plugins/youtube
- From: pwithnall svn gnome org
- To: svn-commits-list gnome org
- Subject: totem r6219 - in trunk: . po src src/backend src/plugins/youtube
- Date: Wed, 1 Apr 2009 18:04:30 +0000 (UTC)
Author: pwithnall
Date: Wed Apr 1 18:04:30 2009
New Revision: 6219
URL: http://svn.gnome.org/viewvc/totem?rev=6219&view=rev
Log:
2009-04-01 Philip Withnall <philip tecnocode co uk>
* configure.in:
* src/backend/bacon-video-widget-gst-0.10.c
(bacon_video_widget_can_play_youtube_videos):
* src/backend/bacon-video-widget-xine.c
(bacon_video_widget_can_play_youtube_videos):
* src/backend/bacon-video-widget.h:
* src/plugins/youtube/Makefile.am:
* src/plugins/youtube/totem-youtube.c
(totem_youtube_plugin_class_init), (totem_youtube_plugin_init),
(set_up_tree_view), (impl_activate), (impl_deactivate),
(get_fmt_param), (progress_bar_pulse_cb), (set_progress_bar_text),
(increment_progress_bar_fraction), (resolve_t_param_cb),
(resolve_t_param), (thumbnail_loaded_cb), (thumbnail_opened_cb),
(query_finished_cb), (query_progress_cb), (execute_query),
(search_button_clicked_cb), (search_entry_activate_cb),
(load_related_videos), (notebook_switch_page_cb),
(open_in_web_browser_activate_cb), (value_changed_cb),
(button_press_event_cb), (button_release_event_cb),
(starting_video_cb):
* src/plugins/youtube/youtube.py:
* src/plugins/youtube/youtube.totem-plugin.in:
* src/plugins/youtube/youtube.ui:
* src/totem-cell-renderer-video.c
(totem_cell_renderer_video_class_init),
(totem_cell_renderer_video_init),
(totem_cell_renderer_video_dispose),
(totem_cell_renderer_video_finalize),
(totem_cell_renderer_video_set_property), (get_size),
(totem_cell_renderer_video_render):
* src/totem-video-list.c (totem_video_list_set_property),
(row_activated_cb): Ported the YouTube plugin to C, using the shiny
new libgdata library.
2009-04-01 Philip Withnall <philip tecnocode co uk>
* POTFILES.in: Ported the YouTube plugin to C.
Added:
trunk/src/plugins/youtube/totem-youtube.c
Removed:
trunk/src/plugins/youtube/youtube.py
Modified:
trunk/ChangeLog
trunk/configure.in
trunk/po/ChangeLog
trunk/po/POTFILES.in
trunk/src/backend/bacon-video-widget-gst-0.10.c
trunk/src/backend/bacon-video-widget-xine.c
trunk/src/backend/bacon-video-widget.h
trunk/src/plugins/youtube/Makefile.am
trunk/src/plugins/youtube/youtube.totem-plugin.in
trunk/src/plugins/youtube/youtube.ui
trunk/src/totem-cell-renderer-video.c
trunk/src/totem-video-list.c
Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in (original)
+++ trunk/configure.in Wed Apr 1 18:04:30 2009
@@ -608,6 +608,14 @@
add_plugin="0"
fi
;;
+ youtube)
+ PKG_CHECK_MODULES(LIBGDATA, libgdata,
+ [HAVE_LIBGDATA=yes], [HAVE_LIBGDATA=no])
+ if test "${HAVE_LIBGDATA}" != "yes" ; then
+ plugin_error_or_ignore "you need libgdata installed for the YouTube plugin"
+ add_plugin="0"
+ fi
+ ;;
esac
# Add the specified plugin
Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in (original)
+++ trunk/po/POTFILES.in Wed Apr 1 18:04:30 2009
@@ -82,7 +82,7 @@
[type: gettext/ini]src/plugins/tracker/tracker.totem-plugin.in
[type: gettext/ini]src/plugins/youtube/youtube.totem-plugin.in
[type: gettext/glade]src/plugins/youtube/youtube.ui
-src/plugins/youtube/youtube.py
+src/plugins/youtube/totem-youtube.c
browser-plugin/totem-plugin-viewer.c
[type: gettext/ini]src/plugins/pythonconsole/pythonconsole.totem-plugin.in
src/plugins/pythonconsole/pythonconsole.py
Modified: trunk/src/backend/bacon-video-widget-gst-0.10.c
==============================================================================
--- trunk/src/backend/bacon-video-widget-gst-0.10.c (original)
+++ trunk/src/backend/bacon-video-widget-gst-0.10.c Wed Apr 1 18:04:30 2009
@@ -5349,6 +5349,12 @@
}
}
+gboolean
+bacon_video_widget_can_play_youtube_videos (BaconVideoWidget *bvw)
+{
+ return gst_default_registry_check_feature_version ("souphttpsrc", 0, 10, 0);
+}
+
/*
* vim: sw=2 ts=8 cindent noai bs=2
*/
Modified: trunk/src/backend/bacon-video-widget-xine.c
==============================================================================
--- trunk/src/backend/bacon-video-widget-xine.c (original)
+++ trunk/src/backend/bacon-video-widget-xine.c Wed Apr 1 18:04:30 2009
@@ -4339,3 +4339,9 @@
return pixbuf;
}
+
+gboolean
+bacon_video_widget_can_play_youtube_videos (BaconVideoWidget *bvw)
+{
+ return TRUE;
+}
Modified: trunk/src/backend/bacon-video-widget.h
==============================================================================
--- trunk/src/backend/bacon-video-widget.h (original)
+++ trunk/src/backend/bacon-video-widget.h Wed Apr 1 18:04:30 2009
@@ -307,6 +307,9 @@
gboolean bacon_video_widget_has_next_track (BaconVideoWidget *bvw);
gboolean bacon_video_widget_has_previous_track (BaconVideoWidget *bvw);
+/* YouTube functions */
+gboolean bacon_video_widget_can_play_youtube_videos (BaconVideoWidget *bvw);
+
/* Screenshot functions */
gboolean bacon_video_widget_can_get_frames (BaconVideoWidget *bvw,
GError **error);
Modified: trunk/src/plugins/youtube/Makefile.am
==============================================================================
--- trunk/src/plugins/youtube/Makefile.am (original)
+++ trunk/src/plugins/youtube/Makefile.am Wed Apr 1 18:04:30 2009
@@ -1,15 +1,45 @@
+modules_flags = -export_dynamic -avoid-version -module
+
plugindir = $(PLUGINDIR)/youtube
+plugin_LTLIBRARIES = libyoutube.la
+
uidir = $(plugindir)
-plugin_PYTHON = youtube.py
+ui_DATA = youtube.ui
plugin_in_files = youtube.totem-plugin.in
%.totem-plugin: %.totem-plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
plugin_DATA = $(plugin_in_files:.totem-plugin.in=.totem-plugin)
-ui_DATA = youtube.ui
-EXTRA_DIST = $(plugin_in_files) $(ui_DATA) youtube.py
+common_defines = \
+ -D_REENTRANT \
+ -DDBUS_API_SUBJECT_TO_CHANGE \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DGCONF_PREFIX=\""/apps/totem"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DTOTEM_PLUGIN_DIR=\""$(libdir)/totem/plugins"\"\
+ $(DISABLE_DEPRECATED)
-CLEANFILES = $(plugin_DATA)
+libyoutube_la_SOURCES = totem-youtube.c
+libyoutube_la_LDFLAGS = $(modules_flags)
+libyoutube_la_LIBADD = $(LIBGDATA_LIBS)
+libyoutube_la_CPPFLAGS = $(common_defines)
+
+libyoutube_la_CFLAGS = \
+ $(DEPENDENCY_CFLAGS) \
+ $(LIBGDATA_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(AM_CFLAGS) \
+ -I$(top_srcdir)/ \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/plugins
+
+EXTRA_DIST = $(plugin_in_files) $(ui_DATA)
+
+CLEANFILES = $(plugin_DATA) $(BUILT_SOURCES)
DISTCLEANFILES = $(plugin_DATA)
+
Added: trunk/src/plugins/youtube/totem-youtube.c
==============================================================================
--- (empty file)
+++ trunk/src/plugins/youtube/totem-youtube.c Wed Apr 1 18:04:30 2009
@@ -0,0 +1,798 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Philip Withnall <philip tecnocode co uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *
+ * The Totem project hereby grant permission for non-GPL compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Totem. This
+ * permission are above and beyond the permissions granted by the GPL license
+ * Totem is covered by.
+ *
+ * See license_change file for details.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n-lib.h>
+#include <gdata/gdata.h>
+
+#include "totem-plugin.h"
+#include "totem.h"
+#include "totem-video-list.h"
+#include "totem-interface.h"
+#include "backend/bacon-video-widget.h"
+
+/* Notebook pages */
+enum {
+ SEARCH_TREE_VIEW = 0,
+ RELATED_TREE_VIEW,
+ NUM_TREE_VIEWS
+};
+
+#define DEVELOPER_KEY "AI39si5D82T7zgTGS9fmUQAZ7KO5EvKNN_Hf1yoEPf1bpVOTD0At-z7Ovgjupke6o0xdS4drF8SDLfjfmuIXLQQNdE3foPfIdg"
+#define CLIENT_ID "ytapi-GNOME-Totem-444fubtt-1"
+#define MAX_RESULTS 10
+#define THUMBNAIL_WIDTH 180
+#define PULSE_INTERVAL 200
+
+#define TOTEM_TYPE_YOUTUBE_PLUGIN (totem_youtube_plugin_get_type ())
+#define TOTEM_YOUTUBE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TOTEM_TYPE_YOUTUBE_PLUGIN, TotemYouTubePlugin))
+#define TOTEM_YOUTUBE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TOTEM_TYPE_YOUTUBE_PLUGIN, TotemYouTubePluginClass))
+#define TOTEM_IS_YOUTUBE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TOTEM_TYPE_YOUTUBE_PLUGIN))
+#define TOTEM_IS_YOUTUBE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TOTEM_TYPE_YOUTUBE_PLUGIN))
+#define TOTEM_YOUTUBE_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TOTEM_TYPE_YOUTUBE_PLUGIN, TotemYouTubePluginClass))
+
+typedef struct {
+ TotemPlugin parent;
+ Totem *totem;
+ GDataYouTubeService *service;
+ BaconVideoWidget *bvw;
+
+ guint current_tree_view;
+ GDataQuery *query[NUM_TREE_VIEWS];
+ GCancellable *cancellable[NUM_TREE_VIEWS];
+ GRegex *regex;
+ gboolean button_down;
+ GDataYouTubeVideo *playing_video;
+
+ GtkEntry *search_entry;
+ GtkButton *search_button;
+ GtkProgressBar *progress_bar[NUM_TREE_VIEWS];
+ gfloat progress_bar_increment[NUM_TREE_VIEWS];
+ GtkNotebook *notebook;
+ GtkWidget *vbox;
+ GtkAdjustment *vadjust[NUM_TREE_VIEWS];
+ GtkListStore *list_store[NUM_TREE_VIEWS];
+ GtkTreeView *tree_view[NUM_TREE_VIEWS];
+} TotemYouTubePlugin;
+
+typedef struct {
+ TotemPluginClass parent_class;
+} TotemYouTubePluginClass;
+
+G_MODULE_EXPORT GType register_totem_plugin (GTypeModule *module);
+GType totem_youtube_plugin_get_type (void) G_GNUC_CONST;
+
+static gboolean impl_activate (TotemPlugin *plugin, TotemObject *totem, GError **error);
+static void impl_deactivate (TotemPlugin *plugin, TotemObject *totem);
+
+/* GtkBuilder callbacks */
+void notebook_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, TotemYouTubePlugin *self);
+void search_button_clicked_cb (GtkButton *button, TotemYouTubePlugin *self);
+void search_entry_activate_cb (GtkEntry *entry, TotemYouTubePlugin *self);
+gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *event, TotemYouTubePlugin *self);
+gboolean button_release_event_cb (GtkWidget *widget, GdkEventButton *event, TotemYouTubePlugin *self);
+void open_in_web_browser_activate_cb (GtkAction *action, TotemYouTubePlugin *self);
+void value_changed_cb (GtkAdjustment *adjustment, TotemYouTubePlugin *self);
+gboolean starting_video_cb (TotemVideoList *video_list, GtkTreeView *tree_view, GtkTreePath *path, TotemYouTubePlugin *self);
+
+TOTEM_PLUGIN_REGISTER (TotemYouTubePlugin, totem_youtube_plugin)
+
+static void
+totem_youtube_plugin_class_init (TotemYouTubePluginClass *klass)
+{
+ TotemPluginClass *plugin_class = TOTEM_PLUGIN_CLASS (klass);
+
+ plugin_class->activate = impl_activate;
+ plugin_class->deactivate = impl_deactivate;
+}
+
+static void
+totem_youtube_plugin_init (TotemYouTubePlugin *plugin)
+{
+ /* Nothing to see here; move along */
+}
+
+static void
+set_up_tree_view (TotemYouTubePlugin *self, GtkBuilder *builder, guint key)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GtkAction *action, *menu_item;
+ GtkWidget *vscroll, *tree_view;
+
+ /* Give the video lists a handle to Totem and connect their scrollbar signals */
+ if (key == SEARCH_TREE_VIEW) {
+ tree_view = GTK_WIDGET (gtk_builder_get_object (builder, "yt_treeview_search"));
+ vscroll = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "yt_scrolled_window_search")));
+ self->list_store[key] = GTK_LIST_STORE (gtk_builder_get_object (builder, "yt_list_store_search"));
+ self->tree_view[key] = GTK_TREE_VIEW (tree_view);
+ self->progress_bar[key] = GTK_PROGRESS_BAR (gtk_builder_get_object (builder, "yt_progress_bar_search"));
+ } else {
+ tree_view = GTK_WIDGET (gtk_builder_get_object (builder, "yt_treeview_related"));
+ vscroll = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "yt_scrolled_window_related")));
+ self->list_store[key] = GTK_LIST_STORE (gtk_builder_get_object (builder, "yt_list_store_related"));
+ self->tree_view[key] = GTK_TREE_VIEW (tree_view);
+ self->progress_bar[key] = GTK_PROGRESS_BAR (gtk_builder_get_object (builder, "yt_progress_bar_related"));
+ }
+ g_object_set (tree_view, "totem", self->totem, NULL);
+ g_signal_connect (vscroll, "button-press-event", G_CALLBACK (button_press_event_cb), self);
+ g_signal_connect (vscroll, "button-release-event", G_CALLBACK (button_release_event_cb), self);
+
+ /* Add the extra popup menu options. This is done here rather than in the UI file, because it's done for multiple treeviews;
+ * if it were done in the UI file, the same action group would be used multiple times, which GTK+ doesn't like. */
+ ui_manager = totem_video_list_get_ui_manager (TOTEM_VIDEO_LIST (tree_view));
+ action_group = gtk_action_group_new ("youtube-action-group");
+ action = gtk_action_new ("open-in-web-browser", _("_Open in Web Browser"), _("Open the video in your web browser"), "gtk-jump-to");
+ gtk_action_group_add_action_with_accel (action_group, action, NULL);
+
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 1);
+ gtk_ui_manager_add_ui (ui_manager, gtk_ui_manager_new_merge_id (ui_manager),
+ "/ui/totem-video-list-popup/",
+ "open-in-web-browser",
+ "open-in-web-browser",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ menu_item = gtk_ui_manager_get_action (ui_manager, "/ui/totem-video-list-popup/open-in-web-browser");
+ g_signal_connect (menu_item, "activate", G_CALLBACK (open_in_web_browser_activate_cb), self);
+
+ /* Connect to more scroll events */
+ self->vadjust[key] = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (tree_view));
+ g_signal_connect (self->vadjust[key], "value-changed", G_CALLBACK (value_changed_cb), self);
+}
+
+static gboolean
+impl_activate (TotemPlugin *plugin, TotemObject *totem, GError **error)
+{
+ TotemYouTubePlugin *self = TOTEM_YOUTUBE_PLUGIN (plugin);
+ GtkWindow *main_window;
+ GtkBuilder *builder;
+ guint i;
+
+ self->totem = g_object_ref (totem);
+ self->bvw = BACON_VIDEO_WIDGET (totem_get_video_widget (totem));
+
+ /* Set up the interface */
+ main_window = totem_get_main_window (totem);
+ builder = totem_plugin_load_interface (plugin, "youtube.ui", TRUE, main_window, self);
+ g_object_unref (main_window);
+
+ self->search_entry = GTK_ENTRY (gtk_builder_get_object (builder, "yt_search_entry"));
+ self->search_button = GTK_BUTTON (gtk_builder_get_object (builder, "yt_search_button"));
+ self->notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "yt_notebook"));
+
+ /* Set up the tree view pages */
+ for (i = 0; i < NUM_TREE_VIEWS; i++)
+ set_up_tree_view (self, builder, i);
+ self->current_tree_view = SEARCH_TREE_VIEW;
+
+ self->vbox = GTK_WIDGET (gtk_builder_get_object (builder, "yt_vbox"));
+ gtk_widget_show_all (self->vbox);
+
+ /* Add the sidebar page */
+ totem_add_sidebar_page (totem, "youtube", _("YouTube"), self->vbox);
+ g_object_unref (builder);
+
+ return TRUE;
+}
+
+static void
+impl_deactivate (TotemPlugin *plugin, TotemObject *totem)
+{
+ guint i;
+ TotemYouTubePlugin *self = TOTEM_YOUTUBE_PLUGIN (plugin);
+
+ totem_remove_sidebar_page (self->totem, "youtube");
+
+ for (i = 0; i < NUM_TREE_VIEWS; i++) {
+ if (self->cancellable[i] != NULL) {
+ g_cancellable_cancel (self->cancellable[i]);
+ g_object_unref (self->cancellable[i]);
+ }
+ if (self->query[i] != NULL)
+ g_object_unref (self->query[i]);
+ }
+
+ if (self->playing_video != NULL)
+ g_object_unref (self->playing_video);
+ if (self->service != NULL)
+ g_object_unref (self->service);
+ g_object_unref (self->bvw);
+ g_object_unref (self->totem);
+ if (self->regex != NULL)
+ g_regex_unref (self->regex);
+}
+
+static const gchar *
+get_fmt_param (TotemYouTubePlugin *self)
+{
+ if (bacon_video_widget_get_connection_speed (self->bvw) >= 10)
+ return "&fmt=18";
+ return "";
+}
+
+typedef struct {
+ TotemYouTubePlugin *plugin;
+ guint tree_view;
+} ProgressBarData;
+
+static gboolean
+progress_bar_pulse_cb (ProgressBarData *data)
+{
+ TotemYouTubePlugin *self = data->plugin;
+
+ if (self->progress_bar_increment[data->tree_view] != 0.0) {
+ g_slice_free (ProgressBarData, data);
+ return FALSE; /* The first entry has been retrieved */
+ }
+
+ gtk_progress_bar_pulse (self->progress_bar[data->tree_view]);
+ return TRUE;
+}
+
+static void
+set_progress_bar_text (TotemYouTubePlugin *self, const gchar *text, guint tree_view)
+{
+ ProgressBarData *data;
+ GdkCursor *cursor;
+
+ /* Set the cursor to a watch */
+ cursor = gdk_cursor_new (GDK_WATCH);
+ gdk_window_set_cursor (gtk_widget_get_window (self->vbox), cursor);
+ gdk_cursor_unref (cursor);
+
+ /* Call the pulse method */
+ data = g_slice_new (ProgressBarData);
+ data->plugin = self;
+ data->tree_view = tree_view;
+
+ gtk_progress_bar_set_text (self->progress_bar[tree_view], text);
+ gtk_progress_bar_set_fraction (self->progress_bar[tree_view], 0.0);
+ self->progress_bar_increment[tree_view] = 0.0;
+ g_timeout_add (PULSE_INTERVAL, (GSourceFunc) progress_bar_pulse_cb, data);
+}
+
+static void
+increment_progress_bar_fraction (TotemYouTubePlugin *self, guint tree_view)
+{
+ gdouble new_value = MIN (gtk_progress_bar_get_fraction (self->progress_bar[tree_view]) + self->progress_bar_increment[tree_view], 1.0);
+
+ g_debug ("Incrementing progress bar by %f (new value: %f)", self->progress_bar_increment[tree_view], new_value);
+ gtk_progress_bar_set_fraction (self->progress_bar[tree_view], new_value);
+
+ /* Update the UI */
+ if (gtk_progress_bar_get_fraction (self->progress_bar[tree_view]) == 1.0) {
+ /* The entire search process (including loading thumbnails and t params) is finished, so update the progress bar */
+ gdk_window_set_cursor (gtk_widget_get_window (self->vbox), NULL);
+ gtk_progress_bar_set_text (self->progress_bar[tree_view], "");
+ gtk_progress_bar_set_fraction (self->progress_bar[tree_view], 0.0);
+ }
+}
+
+typedef struct {
+ TotemYouTubePlugin *plugin;
+ GDataEntry *entry;
+ GtkTreeIter iter;
+ guint tree_view;
+} TParamData;
+
+static void
+resolve_t_param_cb (GObject *source_object, GAsyncResult *result, TParamData *data)
+{
+ gchar *contents, *video_uri = NULL;
+ const gchar *video_id;
+ gsize length;
+ GMatchInfo *match_info;
+ GError *error = NULL;
+ TotemYouTubePlugin *self = data->plugin;
+
+ /* Finish loading the page */
+ if (g_file_load_contents_finish (G_FILE (source_object), result, &contents, &length, NULL, &error) == FALSE) {
+ GtkWindow *window;
+
+ /* Couldn't load the page contents; error */
+ window = totem_get_main_window (data->plugin->totem);
+ totem_interface_error (_("Error Looking Up Video URI"), error->message, window);
+ g_object_unref (window);
+ g_error_free (error);
+ goto free_data;
+ }
+
+ video_id = gdata_youtube_video_get_video_id (GDATA_YOUTUBE_VIDEO (data->entry));
+
+ /* Check for the t parameter, which is now in a JavaScript array on the video page */
+ g_regex_match (self->regex, contents, 0, &match_info);
+ if (g_match_info_matches (match_info) == TRUE) {
+ gchar *t_param;
+ const gchar *fmt_param;
+ GString *video_uri_string;
+
+ /* We have a match */
+ t_param = g_match_info_fetch (match_info, 1);
+ fmt_param = get_fmt_param (self);
+
+ video_uri_string = g_string_new ("http://www.youtube.com/get_video?video_id=");
+ g_string_append_uri_escaped (video_uri_string, video_id, NULL, TRUE);
+ g_string_append (video_uri_string, "&t=");
+ g_string_append_uri_escaped (video_uri_string, t_param, NULL, TRUE);
+ g_string_append (video_uri_string, fmt_param);
+
+ video_uri = g_string_free (video_uri_string, FALSE);
+ } else {
+ GDataMediaContent *content;
+
+ /* We don't have a match, which is odd; fall back to the FLV URI as advertised by the YouTube API */
+ content = gdata_youtube_video_look_up_content (GDATA_YOUTUBE_VIDEO (data->entry), "application/x-shockwave-flash");
+ if (content != NULL) {
+ video_uri = g_strdup (content->uri);
+ g_debug ("Couldn't find the t param of entry %s; falling back to its FLV URI (\"%s\")", video_id, video_uri);
+ } else {
+ /* Cop out */
+ g_warning ("Couldn't find the t param of entry %s or its FLV URI.", video_uri);
+ video_uri = NULL;
+ }
+ }
+ g_match_info_free (match_info);
+ g_free (contents);
+
+ /* Update the tree view with the new MRL */
+ gtk_list_store_set (self->list_store[data->tree_view], &(data->iter), 2, video_uri, -1);
+ g_debug ("Updated list store with new video URI (\"%s\") for entry %s", video_uri, video_id);
+
+ g_free (video_uri);
+
+free_data:
+ /* Update the progress bar */
+ increment_progress_bar_fraction (self, data->tree_view);
+
+ g_object_unref (data->plugin);
+ g_object_unref (data->entry);
+ g_slice_free (TParamData, data);
+}
+
+static void
+resolve_t_param (TotemYouTubePlugin *self, GDataEntry *entry, GtkTreeIter *iter, guint tree_view)
+{
+ GDataLink *link;
+ GFile *video_page;
+ TParamData *data;
+
+ /* We have to get the t parameter from the actual HTML video page, since Google changed how their URIs work */
+ link = gdata_entry_lookup_link (entry, "alternate");
+ g_assert (link != NULL);
+
+ data = g_slice_new (TParamData);
+ data->plugin = g_object_ref (self);
+ data->entry = g_object_ref (entry);
+ data->iter = *iter;
+ data->tree_view = tree_view;
+
+ video_page = g_file_new_for_uri (link->href);
+ g_file_load_contents_async (video_page, self->cancellable[tree_view], (GAsyncReadyCallback) resolve_t_param_cb, data);
+ g_object_unref (video_page);
+}
+
+typedef struct {
+ TotemYouTubePlugin *plugin;
+ GtkTreeIter iter;
+ guint tree_view;
+} ThumbnailData;
+
+static void
+thumbnail_loaded_cb (GObject *source_object, GAsyncResult *result, ThumbnailData *data)
+{
+ GdkPixbuf *thumbnail;
+ GError *error = NULL;
+ TotemYouTubePlugin *self = data->plugin;
+
+ /* Finish loading the thumbnail */
+ thumbnail = gdk_pixbuf_new_from_stream_finish (result, &error);
+
+ if (thumbnail == NULL) {
+ GtkWindow *window;
+
+ /* Display an error message */
+ window = totem_get_main_window (data->plugin->totem);
+ totem_interface_error (_("Error Loading Video Thumbnail"), error->message, window);
+ g_object_unref (window);
+ g_error_free (error);
+ goto free_data;
+ }
+
+ g_debug ("Finished creating thumbnail from stream");
+
+ /* Update the tree view */
+ gtk_list_store_set (self->list_store[data->tree_view], &(data->iter), 0, thumbnail, -1);
+ g_debug ("Updated list store with new thumbnail");
+
+ g_object_unref (thumbnail);
+
+free_data:
+ /* Update the progress bar */
+ increment_progress_bar_fraction (self, data->tree_view);
+
+ g_object_unref (data->plugin);
+ g_slice_free (ThumbnailData, data);
+}
+
+static void
+thumbnail_opened_cb (GObject *source_object, GAsyncResult *result, ThumbnailData *data)
+{
+ GFile *thumbnail_file;
+ GFileInputStream *input_stream;
+ GError *error = NULL;
+ TotemYouTubePlugin *self = data->plugin;
+
+ /* Finish opening the thumbnail */
+ thumbnail_file = G_FILE (source_object);
+ input_stream = g_file_read_finish (thumbnail_file, result, &error);
+
+ if (input_stream == NULL) {
+ GtkWindow *window;
+
+ /* Display an error message */
+ window = totem_get_main_window (data->plugin->totem);
+ totem_interface_error (_("Error Loading Video Thumbnail"), error->message, window);
+ g_object_unref (window);
+ g_error_free (error);
+ return;
+ }
+
+ g_debug ("Creating thumbnail from stream");
+ gdk_pixbuf_new_from_stream_at_scale_async (G_INPUT_STREAM (input_stream), THUMBNAIL_WIDTH, -1, TRUE,
+ self->cancellable[data->tree_view], (GAsyncReadyCallback) thumbnail_loaded_cb, data);
+ g_object_unref (input_stream);
+}
+
+typedef struct {
+ TotemYouTubePlugin *plugin;
+ guint tree_view;
+} QueryData;
+
+static void
+query_finished_cb (GObject *source_object, GAsyncResult *result, QueryData *data)
+{
+ GDataFeed *feed;
+ GError *error = NULL;
+ TotemYouTubePlugin *self = data->plugin;
+
+ g_debug ("Search finished!");
+
+ /* Unref cancellable */
+ g_object_unref (self->cancellable[data->tree_view]);
+ self->cancellable[data->tree_view] = NULL;
+
+ feed = gdata_service_query_finish (GDATA_SERVICE (self->service), result, &error);
+ if (feed == NULL) {
+ GtkWindow *window;
+
+ /* Error! */
+ window = totem_get_main_window (data->plugin->totem);
+ totem_interface_error (_("Error Searching for Videos"), error->message, window);
+ g_object_unref (window);
+ g_error_free (error);
+ goto free_data;
+ }
+
+ g_object_unref (feed);
+
+free_data:
+ g_object_unref (data->plugin);
+ g_slice_free (QueryData, data);
+}
+
+static void
+query_progress_cb (GDataEntry *entry, guint entry_key, guint entry_count, QueryData *data)
+{
+ GList *thumbnails;
+ GDataMediaThumbnail *thumbnail = NULL;
+ gint delta = G_MININT;
+ GtkTreeIter iter;
+ const gchar *title, *id;
+ GtkProgressBar *progress_bar;
+ TotemYouTubePlugin *self = data->plugin;
+
+ /* Check this query hasn't finished */
+ g_assert (self->cancellable[data->tree_view] != NULL);
+
+ /* Add the entry to the tree view */
+ title = gdata_youtube_video_get_title (GDATA_YOUTUBE_VIDEO (entry));
+ id = gdata_youtube_video_get_video_id (GDATA_YOUTUBE_VIDEO (entry));
+
+ gtk_list_store_append (self->list_store[data->tree_view], &iter);
+ gtk_list_store_set (self->list_store[data->tree_view], &iter,
+ 0, NULL, /* the thumbnail will be downloaded asynchronously and added to the tree view later */
+ 1, title,
+ 2, NULL, /* the video URI will be resolved asynchronously and added to the tree view later */
+ 3, entry,
+ -1);
+ g_debug ("Added entry %s to tree view (title: \"%s\")", id, title);
+
+ /* Update the progress bar; we have three steps for each entry in the results: the entry, its thumbnail, and its t parameter */
+ g_assert (entry_count > 0);
+ progress_bar = self->progress_bar[data->tree_view];
+ self->progress_bar_increment[data->tree_view] = 1.0 / (entry_count * 3.0);
+ g_debug ("Setting progress_bar_increment to 1.0 / (%u * 3.0) = %f", entry_count, self->progress_bar_increment[data->tree_view]);
+ gtk_progress_bar_set_fraction (progress_bar, gtk_progress_bar_get_fraction (progress_bar) + self->progress_bar_increment[data->tree_view]);
+
+ /* Resolve the t parameter for the video, which is required before it can be played */
+ resolve_t_param (self, entry, &iter, data->tree_view);
+
+ /* Download the entry's thumbnail, ready for adding it to the tree view.
+ * Find the thumbnail size which is closest to the wanted size (THUMBNAIL_WIDTH), so that we:
+ * a) avoid fuzzy images due to scaling up, and
+ * b) avoid downloading too much just to scale down by a factor of 10. */
+ thumbnails = gdata_youtube_video_get_thumbnails (GDATA_YOUTUBE_VIDEO (entry));
+ for (; thumbnails != NULL; thumbnails = thumbnails->next) {
+ gint new_delta;
+ GDataMediaThumbnail *current_thumb = (GDataMediaThumbnail*) thumbnails->data;
+
+ g_debug ("%u pixel wide thumbnail available for entry %s", current_thumb->width, id);
+
+ new_delta = current_thumb->width - THUMBNAIL_WIDTH;
+ if (delta == 0) {
+ break;
+ } else if ((delta == G_MININT) ||
+ (delta < 0 && new_delta > delta) ||
+ (delta > 0 && new_delta > 0 && new_delta < delta)) {
+ delta = new_delta;
+ thumbnail = current_thumb;
+ g_debug ("Choosing a %u pixel wide thumbnail (delta: %i) for entry %s", current_thumb->width, new_delta, id);
+ }
+ }
+
+ if (thumbnail != NULL) {
+ GFile *thumbnail_file;
+ ThumbnailData *t_data;
+
+ t_data = g_slice_new (ThumbnailData);
+ t_data->plugin = g_object_ref (self);
+ t_data->iter = iter;
+ t_data->tree_view = data->tree_view;
+
+ g_debug ("Starting thumbnail download for entry %s", id);
+ thumbnail_file = g_file_new_for_uri (thumbnail->uri);
+ g_file_read_async (thumbnail_file, G_PRIORITY_DEFAULT, self->cancellable[data->tree_view],
+ (GAsyncReadyCallback) thumbnail_opened_cb, t_data);
+ g_object_unref (thumbnail_file);
+ }
+}
+
+static void
+execute_query (TotemYouTubePlugin *self, guint tree_view, gboolean clear_tree_view)
+{
+ QueryData *data;
+
+ /* Cancel previous searches on this tree view */
+ if (self->cancellable[tree_view] != NULL)
+ g_cancellable_cancel (self->cancellable[tree_view]);
+
+ /* Clear the tree views */
+ if (clear_tree_view == TRUE)
+ gtk_list_store_clear (self->list_store[tree_view]);
+
+ /* Do the query */
+ self->cancellable[tree_view] = g_cancellable_new ();
+
+ data = g_slice_new (QueryData);
+ data->plugin = g_object_ref (self);
+ data->tree_view = tree_view;
+
+ if (tree_view == SEARCH_TREE_VIEW) {
+ gdata_youtube_service_query_videos_async (self->service, self->query[tree_view], self->cancellable[tree_view],
+ (GDataQueryProgressCallback) query_progress_cb, data,
+ (GAsyncReadyCallback) query_finished_cb, data);
+ } else {
+ gdata_youtube_service_query_related_async (self->service, self->playing_video, self->query[tree_view], self->cancellable[tree_view],
+ (GDataQueryProgressCallback) query_progress_cb, data,
+ (GAsyncReadyCallback) query_finished_cb, data);
+ }
+}
+
+void
+search_button_clicked_cb (GtkButton *button, TotemYouTubePlugin *self)
+{
+ const gchar *search_terms;
+
+ search_terms = gtk_entry_get_text (self->search_entry);
+ g_debug ("Searching for \"%s\"", search_terms);
+
+ /* Focus the "Search" page */
+ gtk_notebook_set_current_page (self->notebook, SEARCH_TREE_VIEW);
+
+ /* Update the UI */
+ set_progress_bar_text (self, _("Fetching search resultsâ"), SEARCH_TREE_VIEW);
+
+ /* Clear details pertaining to related videos, since we're doing a new search */
+ gtk_list_store_clear (self->list_store[RELATED_TREE_VIEW]);
+ if (self->playing_video != NULL)
+ g_object_unref (self->playing_video);
+ self->playing_video = NULL;
+
+ /* If this is the first query, set up some stuff which we didn't do before to save memory */
+ if (self->query[SEARCH_TREE_VIEW] == NULL) {
+ /* If this is the first query, compile the regex used to resolve the t param. Doing this here rather than when
+ * activating the plugin means we don't waste cycles if the plugin's never used. It also means we don't waste
+ * cycles repeatedly creating new regexes for each video whose t param we resolve. */
+ self->regex = g_regex_new ("swfArgs.*\"t\": \"([^\"]+)\"", G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (self->regex != NULL);
+
+ /* Set up the GData service (needed for the tree views' queries) */
+ self->service = gdata_youtube_service_new (DEVELOPER_KEY, CLIENT_ID);
+
+ /* Set up the queries */
+ self->query[SEARCH_TREE_VIEW] = gdata_query_new_with_limits (NULL, 0, MAX_RESULTS);
+ self->query[RELATED_TREE_VIEW] = gdata_query_new_with_limits (NULL, 0, MAX_RESULTS);
+ }
+
+ /* Do the query */
+ gdata_query_set_q (self->query[SEARCH_TREE_VIEW], search_terms);
+ execute_query (self, SEARCH_TREE_VIEW, TRUE);
+}
+
+void
+search_entry_activate_cb (GtkEntry *entry, TotemYouTubePlugin *self)
+{
+ search_button_clicked_cb (self->search_button, self);
+}
+
+static void
+load_related_videos (TotemYouTubePlugin *self)
+{
+ g_assert (self->playing_video != NULL);
+ g_debug ("Loading related videos for %s", gdata_youtube_video_get_video_id (self->playing_video));
+
+ /* Update the UI */
+ set_progress_bar_text (self, _("Fetching related videosâ"), RELATED_TREE_VIEW);
+
+ /* Clear the existing results and do the query */
+ gtk_list_store_clear (self->list_store[RELATED_TREE_VIEW]);
+ execute_query (self, RELATED_TREE_VIEW, FALSE);
+}
+
+void
+notebook_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, TotemYouTubePlugin *self)
+{
+ /* Change the tree view */
+ self->current_tree_view = page_num;
+
+ /* If we're changing to the "Related Videos" tree view and have played a video, load
+ * the related videos for that video; but only if the related tree view's empty first */
+ if (page_num == RELATED_TREE_VIEW && self->playing_video != NULL &&
+ gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->list_store[RELATED_TREE_VIEW]), NULL) == 0) {
+ load_related_videos (self);
+ }
+}
+
+void
+open_in_web_browser_activate_cb (GtkAction *action, TotemYouTubePlugin *self)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GList *paths, *path;
+
+ selection = gtk_tree_view_get_selection (self->tree_view[self->current_tree_view]);
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (path = paths; path != NULL; path = path->next) {
+ GtkTreeIter iter;
+ GDataYouTubeVideo *video;
+ GDataLink *link;
+ GError *error = NULL;
+
+ if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) (path->data)) == FALSE)
+ continue;
+
+ /* Get the HTML page for the video; its <link rel="alternate" ... /> */
+ gtk_tree_model_get (model, &iter, 3, &video, -1);
+ link = gdata_entry_lookup_link (GDATA_ENTRY (video), "alternate");
+ g_object_unref (video);
+
+ /* Display the page */
+ if (gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (self->bvw)), link->href, GDK_CURRENT_TIME, &error) == FALSE) {
+ GtkWindow *window = totem_get_main_window (self->totem);
+ totem_interface_error (_("Error Opening Video in Web Browser"), error->message, window);
+ g_object_unref (window);
+ g_error_free (error);
+ }
+ }
+
+ g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (paths);
+}
+
+void
+value_changed_cb (GtkAdjustment *adjustment, TotemYouTubePlugin *self)
+{
+ if (self->button_down == FALSE &&
+ gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->list_store[self->current_tree_view]), NULL) >= MAX_RESULTS &&
+ (gtk_adjustment_get_value (adjustment) + gtk_adjustment_get_page_size (adjustment)) / gtk_adjustment_get_upper (adjustment) > 0.8) {
+ /* Only load more results if we're not already querying */
+ if (self->cancellable[self->current_tree_view] != NULL)
+ return;
+
+ set_progress_bar_text (self, _("Fetching more videosâ"), self->current_tree_view);
+ gdata_query_next_page (self->query[self->current_tree_view]);
+ execute_query (self, self->current_tree_view, FALSE);
+ }
+}
+
+gboolean
+button_press_event_cb (GtkWidget *widget, GdkEventButton *event, TotemYouTubePlugin *self)
+{
+ self->button_down = TRUE;
+ return FALSE;
+}
+
+gboolean
+button_release_event_cb (GtkWidget *widget, GdkEventButton *event, TotemYouTubePlugin *self)
+{
+ self->button_down = FALSE;
+ value_changed_cb (self->vadjust[self->current_tree_view], self);
+ return FALSE;
+}
+
+gboolean
+starting_video_cb (TotemVideoList *video_list, GtkTreeView *tree_view, GtkTreePath *path, TotemYouTubePlugin *self)
+{
+ GtkTreeIter iter;
+ GDataYouTubeVideo *video_entry;
+
+ if (bacon_video_widget_can_play_youtube_videos (self->bvw) == FALSE) {
+ gchar *title;
+ GtkWindow *main_window;
+
+ /* Display an error if the required GStreamer plugins aren't installed */
+ title = g_strdup_printf (_("Totem cannot play this type of media (%s) because you do not have the appropriate plugins to handle it."), _("YouTube"));
+ main_window = totem_get_main_window (self->totem);
+
+ totem_interface_error_with_link (title, _("Please install the necessary plugins and restart Totem to be able to play this media."),
+ "http://projects.gnome.org/totem/#codecs", _("More information about media plugins"),
+ main_window, self->totem);
+
+ g_object_unref (main_window);
+ g_free (title);
+
+ return FALSE;
+ }
+
+ /* Store the current entry */
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->list_store[self->current_tree_view]), &iter, path) == FALSE)
+ return FALSE;
+ gtk_tree_model_get (GTK_TREE_MODEL (self->list_store[self->current_tree_view]), &iter, 3, &video_entry, -1);
+
+ if (self->playing_video != NULL)
+ g_object_unref (self->playing_video);
+ self->playing_video = g_object_ref (video_entry);
+
+ /* If we're currently viewing the related videos page, load the new related videos */
+ if (self->current_tree_view == RELATED_TREE_VIEW)
+ load_related_videos (self);
+
+ return TRUE;
+}
Modified: trunk/src/plugins/youtube/youtube.totem-plugin.in
==============================================================================
--- trunk/src/plugins/youtube/youtube.totem-plugin.in (original)
+++ trunk/src/plugins/youtube/youtube.totem-plugin.in Wed Apr 1 18:04:30 2009
@@ -1,5 +1,4 @@
[Totem Plugin]
-Loader=python
Module=youtube
IAge=1
_Name=YouTube Browser
Modified: trunk/src/plugins/youtube/youtube.ui
==============================================================================
--- trunk/src/plugins/youtube/youtube.ui (original)
+++ trunk/src/plugins/youtube/youtube.ui Wed Apr 1 18:04:30 2009
@@ -2,33 +2,34 @@
<!--*- mode: xml -*--><!DOCTYPE glade-interface
SYSTEM 'http://glade.gnome.org/glade-2.0.dtd'>
<interface>
-<object class="GtkListStore" id="yt_liststore_search">
+<object class="GtkListStore" id="yt_list_store_search">
<columns>
<column type="GdkPixbuf"/><!--Thumbnail-->
<column type="gchararray"/><!--Title-->
<column type="gchararray"/><!--MRL-->
- <column type="gchararray"/><!--YouTube ID-->
+ <column type="GObject"/><!--Video entry; TODO: should be GDataYouTubeVideo, see bug #576285-->
</columns>
</object>
-<object class="GtkListStore" id="yt_liststore_related">
+<object class="GtkListStore" id="yt_list_store_related">
<columns>
<column type="GdkPixbuf"/><!--Thumbnail-->
<column type="gchararray"/><!--Title-->
<column type="gchararray"/><!--MRL-->
- <column type="gchararray"/><!--YouTube ID-->
+ <column type="GObject"/><!--Video entry; TODO: should be GDataYouTubeVideo, see bug #576285-->
</columns>
</object>
<object class="GtkVBox" id="yt_vbox">
- <property name="border_width">5</property>
- <property name="visible">True</property>
+ <property name="border-width">5</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="yt_hbox">
<property name="spacing">4</property>
<child>
- <object class="GtkEntry" id="yt_search_entry"/>
+ <object class="GtkEntry" id="yt_search_entry">
+ <signal name="activate" handler="search_entry_activate_cb"/>
+ </object>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
@@ -39,6 +40,7 @@
<object class="GtkButton" id="yt_search_button">
<property name="use-stock">True</property>
<property name="label">gtk-find</property>
+ <signal name="clicked" handler="search_button_clicked_cb"/>
</object>
<packing>
<property name="padding">0</property>
@@ -55,22 +57,49 @@
</child>
<child>
<object class="GtkNotebook" id="yt_notebook">
+ <signal name="switch-page" handler="notebook_switch_page_cb"/>
<child>
- <object class="GtkScrolledWindow" id="yt_scrolled_window_search">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <property name="shadow_type">GTK_SHADOW_IN</property>
- <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <object class="GtkVBox" id="yt_vbox_related">
<child>
- <object class="TotemVideoList" id="yt_treeview_search">
- <property name="headers-visible">False</property>
- <property name="fixed-height-mode">False</property>
- <property name="tooltip-column">1</property>
- <property name="mrl-column">2</property>
+ <object class="GtkScrolledWindow" id="yt_scrolled_window_search">
+ <property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow-type">GTK_SHADOW_IN</property>
+ <property name="window-placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="TotemVideoList" id="yt_treeview_search">
+ <property name="headers-visible">False</property>
+ <property name="fixed-height-mode">False</property>
+ <property name="tooltip-column">1</property>
+ <property name="mrl-column">2</property>
+ <property name="model">yt_list_store_search</property>
+ <signal name="starting-video" handler="starting_video_cb"/>
+ <child>
+ <object class="GtkTreeViewColumn" id="yt_treeview_search_column">
+ <property name="title" translatable="yes">Videos</property>
+ <child>
+ <object class="TotemCellRendererVideo" id="yt_treeview_search_renderer">
+ <property name="use-placeholder">True</property>
+ </object>
+ <attributes>
+ <attribute name="thumbnail">0</attribute>
+ <attribute name="title">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
</child>
+ <child>
+ <object class="GtkProgressBar" id="yt_progress_bar_search"/>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
</object>
</child>
<child type="tab">
@@ -82,21 +111,47 @@
</packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="yt_scrolled_window_related">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <property name="shadow_type">GTK_SHADOW_IN</property>
- <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <object class="GtkVBox" id="yt_vbox_related">
<child>
- <object class="TotemVideoList" id="yt_treeview_related">
- <property name="headers-visible">False</property>
- <property name="fixed-height-mode">False</property>
- <property name="tooltip-column">1</property>
- <property name="mrl-column">2</property>
+ <object class="GtkScrolledWindow" id="yt_scrolled_window_related">
+ <property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow-type">GTK_SHADOW_IN</property>
+ <property name="window-placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="TotemVideoList" id="yt_treeview_related">
+ <property name="headers-visible">False</property>
+ <property name="fixed-height-mode">False</property>
+ <property name="tooltip-column">1</property>
+ <property name="mrl-column">2</property>
+ <property name="model">yt_list_store_related</property>
+ <signal name="starting-video" handler="starting_video_cb"/>
+ <child>
+ <object class="GtkTreeViewColumn" id="yt_treeview_related_column">
+ <property name="title" translatable="yes">Videos</property>
+ <child>
+ <object class="TotemCellRendererVideo" id="yt_treeview_related_renderer">
+ <property name="use-placeholder">True</property>
+ </object>
+ <attributes>
+ <attribute name="thumbnail">0</attribute>
+ <attribute name="title">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
</child>
+ <child>
+ <object class="GtkProgressBar" id="yt_progress_bar_related"/>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
</object>
</child>
<child type="tab">
@@ -114,14 +169,6 @@
<property name="fill">True</property>
</packing>
</child>
- <child>
- <object class="GtkProgressBar" id="yt_progress_bar"/>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- </packing>
- </child>
</object>
</interface>
Modified: trunk/src/totem-cell-renderer-video.c
==============================================================================
--- trunk/src/totem-cell-renderer-video.c (original)
+++ trunk/src/totem-cell-renderer-video.c Wed Apr 1 18:04:30 2009
@@ -48,7 +48,6 @@
#include "totem-private.h"
struct _TotemCellRendererVideoPrivate {
- gboolean dispose_has_run;
gchar *title;
GdkPixbuf *thumbnail;
PangoAlignment alignment;
@@ -67,6 +66,7 @@
static void totem_cell_renderer_video_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
static void totem_cell_renderer_video_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
static void totem_cell_renderer_video_dispose (GObject *object);
+static void totem_cell_renderer_video_finalize (GObject *object);
static void totem_cell_renderer_video_get_size (GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *cell_area, gint *x_offset, gint *y_offset, gint *width, gint *height);
static void totem_cell_renderer_video_render (GtkCellRenderer *cell, GdkDrawable *window, GtkWidget *widget, GdkRectangle *background_area, GdkRectangle *cell_area, GdkRectangle *expose_area, GtkCellRendererState flags);
@@ -102,6 +102,7 @@
object_class->set_property = totem_cell_renderer_video_set_property;
object_class->get_property = totem_cell_renderer_video_get_property;
object_class->dispose = totem_cell_renderer_video_dispose;
+ object_class->finalize = totem_cell_renderer_video_finalize;
renderer_class->get_size = totem_cell_renderer_video_get_size;
renderer_class->render = totem_cell_renderer_video_render;
@@ -149,7 +150,6 @@
totem_cell_renderer_video_init (TotemCellRendererVideo *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TOTEM_TYPE_CELL_RENDERER_VIDEO, TotemCellRendererVideoPrivate);
- self->priv->dispose_has_run = FALSE;
self->priv->title = NULL;
self->priv->thumbnail = NULL;
self->priv->alignment = PANGO_ALIGN_CENTER;
@@ -164,20 +164,26 @@
{
TotemCellRendererVideo *self = TOTEM_CELL_RENDERER_VIDEO (object);
- /* Make sure we only run once */
- if (self->priv->dispose_has_run)
- return;
- self->priv->dispose_has_run = TRUE;
-
- g_free (self->priv->title);
if (self->priv->thumbnail != NULL)
g_object_unref (self->priv->thumbnail);
+ self->priv->thumbnail = NULL;
/* Chain up to the parent class */
G_OBJECT_CLASS (totem_cell_renderer_video_parent_class)->dispose (object);
}
static void
+totem_cell_renderer_video_finalize (GObject *object)
+{
+ TotemCellRendererVideo *self = TOTEM_CELL_RENDERER_VIDEO (object);
+
+ g_free (self->priv->title);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (totem_cell_renderer_video_parent_class)->finalize (object);
+}
+
+static void
totem_cell_renderer_video_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
TotemCellRendererVideoPrivate *priv = TOTEM_CELL_RENDERER_VIDEO_GET_PRIVATE (object);
@@ -187,7 +193,7 @@
case PROP_THUMBNAIL:
if (priv->thumbnail != NULL)
g_object_unref (priv->thumbnail);
- priv->thumbnail = (GdkPixbuf*) g_value_dup_object (value);
+ priv->thumbnail = GDK_PIXBUF (g_value_dup_object (value));
break;
case PROP_TITLE:
g_free (priv->title);
@@ -266,17 +272,16 @@
if (priv->thumbnail != NULL)
pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD);
context = gtk_widget_get_pango_context (widget);
- metrics = pango_context_get_metrics (context,
- font_desc,
- pango_context_get_language (context));
+ metrics = pango_context_get_metrics (context, font_desc, pango_context_get_language (context));
if (cell_area)
title_width = cell_area->width;
- else
+ else if (cell->width != -1)
title_width = cell->width;
+ else
+ title_width = pixbuf_width;
- title_height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
- pango_font_metrics_get_descent (metrics));
+ title_height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics));
pango_font_metrics_unref (metrics);
pango_font_description_free (font_desc);
@@ -302,10 +307,10 @@
draw_area->height = calc_height;
/*if (cell_area) {
- g_message ("Cell area: X: %i, Y: %i, W: %i, H: %i", cell_area->x, cell_area->y, cell_area->width, cell_area->height);
- g_message ("X-align: %f, Y-align: %f", cell->xalign, cell->yalign);
+ g_debug ("Cell area: X: %i, Y: %i, W: %i, H: %i", cell_area->x, cell_area->y, cell_area->width, cell_area->height);
+ g_debug ("X-align: %f, Y-align: %f", cell->xalign, cell->yalign);
}
- g_message ("Draw area: X: %i, Y: %i, W: %i, H: %i", draw_area->x, draw_area->y, draw_area->width, draw_area->height);*/
+ g_debug ("Draw area: X: %i, Y: %i, W: %i, H: %i", draw_area->x, draw_area->y, draw_area->width, draw_area->height);*/
if (title_area) {
if (cell_area) {
@@ -322,7 +327,7 @@
else
title_area->y = draw_area->y;
- /*g_message ("Title area: X: %i, Y: %i, W: %i, H: %i", title_area->x, title_area->y, title_area->width, title_area->height);*/
+ /*g_debug ("Title area: X: %i, Y: %i, W: %i, H: %i", title_area->x, title_area->y, title_area->width, title_area->height);*/
}
if (pixbuf_height > 0 && thumbnail_area) {
@@ -334,10 +339,10 @@
thumbnail_area->width = cell->xpad * 2 + pixbuf_width;
thumbnail_area->height = pixbuf_height;
- /*g_message ("Thumbnail area: X: %i, Y: %i, W: %i, H: %i", thumbnail_area->x, thumbnail_area->y, thumbnail_area->width, thumbnail_area->height);*/
+ /*g_debug ("Thumbnail area: X: %i, Y: %i, W: %i, H: %i", thumbnail_area->x, thumbnail_area->y, thumbnail_area->width, thumbnail_area->height);*/
}
- /*g_message ("---");*/
+ /*g_debug ("---");*/
}
}
@@ -391,17 +396,14 @@
draw_area.height -= cell->ypad * 2;
if (!gdk_rectangle_intersect (cell_area, &draw_area, &draw_rect) ||
- !gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect))
+ !gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect))
return;
/* Sort out the thumbnail */
if (priv->thumbnail != NULL)
pixbuf = priv->thumbnail;
else if (priv->use_placeholder == TRUE)
- pixbuf = gtk_widget_render_icon (widget,
- GTK_STOCK_MISSING_IMAGE,
- GTK_ICON_SIZE_DIALOG,
- NULL);
+ pixbuf = gtk_widget_render_icon (widget, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_DIALOG, NULL);
else
pixbuf = NULL;
Modified: trunk/src/totem-video-list.c
==============================================================================
--- trunk/src/totem-video-list.c (original)
+++ trunk/src/totem-video-list.c Wed Apr 1 18:04:30 2009
@@ -232,14 +232,17 @@
{
case PROP_TOOLTIP_COLUMN:
priv->tooltip_column = g_value_get_int (value);
+ g_object_notify (object, "tooltip-column");
break;
case PROP_MRL_COLUMN:
priv->mrl_column = g_value_get_int (value);
+ g_object_notify (object, "mrl-column");
break;
case PROP_TOTEM:
if (priv->totem != NULL)
g_object_unref (priv->totem);
priv->totem = (Totem*) g_value_dup_object (value);
+ g_object_notify (object, "totem");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -342,7 +345,8 @@
self->priv->tooltip_column, &display_name,
-1);
- totem_add_to_playlist_and_play (self->priv->totem, mrl, display_name, FALSE);
+ if (mrl != NULL)
+ totem_add_to_playlist_and_play (self->priv->totem, mrl, display_name, FALSE);
g_free (mrl);
g_free (display_name);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]