[totem] Bug 164918 – Remember current position when Pause is clicked



commit 454c33d8762e8ed313994ebdf4f8b821596dd80c
Author: Robin Stocker <robin nibor org>
Date:   Sun Jul 26 18:22:03 2009 +0100

    Bug 164918 â?? Remember current position when Pause is clicked
    
    Add functionality to remember the current position of a stream when pausing
    or closing, by saving it in a GIO metadata attribute. Closes: bgo#164918

 data/totem.schemas.in   |   11 ++++++
 data/totem.ui           |   70 ++++++++++++++++++++++++++++++++++++
 src/totem-object.c      |   27 ++++++++------
 src/totem-preferences.c |   42 ++++++++++++++++++++++
 src/totem-private.h     |    1 +
 src/totem-uri.c         |   90 +++++++++++++++++++++++++++++++++++++++++++++++
 src/totem-uri.h         |    3 ++
 7 files changed, 232 insertions(+), 12 deletions(-)
---
diff --git a/data/totem.schemas.in b/data/totem.schemas.in
index 207083e..377d912 100644
--- a/data/totem.schemas.in
+++ b/data/totem.schemas.in
@@ -329,5 +329,16 @@
 	</locale>
       </schema>
 
+      <schema>
+        <key>/schemas/apps/totem/remember_position</key>
+	<applyto>/apps/totem/remember_position</applyto>
+	<owner>totem</owner>
+	<type>bool</type>
+	<default>true</default>
+	<locale name="C">
+	  <short>Whether to remember the position of played audio/video files when pausing or closing them.</short>
+	</locale>
+      </schema>
+
   </schemalist>
 </gconfschemafile>
diff --git a/data/totem.ui b/data/totem.ui
index f894d31..f643f1b 100644
--- a/data/totem.ui
+++ b/data/totem.ui
@@ -733,6 +733,76 @@
 	      <property name="spacing">18</property>
 
 	      <child>
+		<object class="GtkVBox" id="vbox2">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <object class="GtkLabel" id="tpw_playback_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Playback</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </object>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <object class="GtkAlignment" id="alignment2">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">0</property>
+		      <property name="bottom_padding">0</property>
+		      <property name="left_padding">12</property>
+		      <property name="right_padding">0</property>
+
+		      <child>
+			    <object class="GtkCheckButton" id="tpw_remember_position_checkbutton">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="label" translatable="yes">Start playing files from last position</property>
+			      <property name="use_underline">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			      <property name="active">False</property>
+			      <property name="inconsistent">False</property>
+			      <property name="draw_indicator">True</property>
+			      <signal name="toggled" handler="remember_position_checkbutton_toggled_cb" />
+		        </object>
+		      </child>
+		    </object>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</object>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child><child>
 		<object class="GtkVBox" id="vbox3">
 		  <property name="visible">True</property>
 		  <property name="homogeneous">False</property>
diff --git a/src/totem-object.c b/src/totem-object.c
index dda6db7..1c74149 100644
--- a/src/totem-object.c
+++ b/src/totem-object.c
@@ -913,6 +913,7 @@ totem_action_exit (Totem *totem)
 				CLAMP (vol, 0, 100),
 				NULL);
 		totem_action_save_size (totem);
+		totem_save_position (totem);
 	}
 
 	if (totem->app != NULL)
@@ -1352,8 +1353,7 @@ totem_action_stop (Totem *totem)
 void
 totem_action_play_pause (Totem *totem)
 {
-	if (totem->mrl == NULL)
-	{
+	if (totem->mrl == NULL) {
 		char *mrl, *subtitle;
 
 		/* Try to pull an mrl from the playlist */
@@ -1369,13 +1369,15 @@ totem_action_play_pause (Totem *totem)
 		}
 	}
 
-	if (bacon_video_widget_is_playing (totem->bvw) == FALSE)
-	{
+	if (bacon_video_widget_is_playing (totem->bvw) == FALSE) {
 		bacon_video_widget_play (totem->bvw, NULL);
 		play_pause_set_label (totem, STATE_PLAYING);
 	} else {
 		bacon_video_widget_pause (totem->bvw);
 		play_pause_set_label (totem, STATE_PAUSED);
+
+		/* Save the stream position */
+		totem_save_position (totem);
 	}
 }
 
@@ -1392,6 +1394,9 @@ totem_action_pause (Totem *totem)
 	if (bacon_video_widget_is_playing (totem->bvw) != FALSE) {
 		bacon_video_widget_pause (totem->bvw);
 		play_pause_set_label (totem, STATE_PAUSED);
+
+		/* Save the stream position */
+		totem_save_position (totem);
 	}
 }
 
@@ -1657,8 +1662,8 @@ totem_action_set_mrl_with_warning (Totem *totem,
 {
 	gboolean retval = TRUE;
 
-	if (totem->mrl != NULL)
-	{
+	if (totem->mrl != NULL) {
+		totem_save_position (totem);
 		g_free (totem->mrl);
 		totem->mrl = NULL;
 		bacon_video_widget_close (totem->bvw);
@@ -1666,8 +1671,7 @@ totem_action_set_mrl_with_warning (Totem *totem,
 		play_pause_set_label (totem, STATE_STOPPED);
 	}
 
-	if (mrl == NULL)
-	{
+	if (mrl == NULL) {
 		retval = FALSE;
 
 		play_pause_set_label (totem, STATE_STOPPED);
@@ -1711,6 +1715,7 @@ totem_action_set_mrl_with_warning (Totem *totem,
 			autoload_sub = totem_uri_get_subtitle_uri (mrl);
 
 		totem_gdk_window_set_waiting_cursor (totem->win->window);
+		totem_try_restore_position (totem, mrl);
 		retval = bacon_video_widget_open (totem->bvw, mrl, subtitle ? subtitle : autoload_sub, &err);
 		g_free (autoload_sub);
 		gdk_window_set_cursor (totem->win->window, NULL);
@@ -1737,8 +1742,7 @@ totem_action_set_mrl_with_warning (Totem *totem,
 		/* Set the playlist */
 		play_pause_set_label (totem, retval ? STATE_PAUSED : STATE_STOPPED);
 
-		if (retval == FALSE && warn != FALSE)
-		{
+		if (retval == FALSE && warn != FALSE) {
 			char *msg, *disp;
 
 			disp = totem_uri_escape_for_display (totem->mrl);
@@ -1753,8 +1757,7 @@ totem_action_set_mrl_with_warning (Totem *totem,
 			g_free (msg);
 		}
 
-		if (retval == FALSE)
-		{
+		if (retval == FALSE) {
 			if (err)
 				g_error_free (err);
 			g_free (totem->mrl);
diff --git a/src/totem-preferences.c b/src/totem-preferences.c
index 4ef5446..03b7c85 100644
--- a/src/totem-preferences.c
+++ b/src/totem-preferences.c
@@ -51,6 +51,7 @@ G_MODULE_EXPORT void checkbutton1_toggled_cb (GtkToggleButton *togglebutton, Tot
 G_MODULE_EXPORT void checkbutton2_toggled_cb (GtkToggleButton *togglebutton, Totem *totem);
 G_MODULE_EXPORT void checkbutton3_toggled_cb (GtkToggleButton *togglebutton, Totem *totem);
 G_MODULE_EXPORT void checkbutton4_toggled_cb (GtkToggleButton *togglebutton, Totem *totem);
+G_MODULE_EXPORT void remember_position_checkbutton_toggled_cb (GtkToggleButton *togglebutton, Totem *totem);
 G_MODULE_EXPORT void connection_combobox_changed (GtkComboBox *combobox, Totem *totem);
 G_MODULE_EXPORT void visual_menu_changed (GtkComboBox *combobox, Totem *totem);
 G_MODULE_EXPORT void visual_quality_menu_changed (GtkComboBox *combobox, Totem *totem);
@@ -198,6 +199,19 @@ checkbutton4_toggled_cb (GtkToggleButton *togglebutton, Totem *totem)
 			       value, NULL);
 }
 
+void
+remember_position_checkbutton_toggled_cb (GtkToggleButton *togglebutton, Totem *totem)
+{
+	gboolean value;
+
+	value = gtk_toggle_button_get_active (togglebutton);
+
+	gconf_client_set_bool (totem->gc,
+			       GCONF_PREFIX"/remember_position",
+			       value, NULL);
+	totem->remember_position = value;
+}
+
 static void
 deinterlace_changed_cb (GConfClient *client, guint cnxn_id,
 		GConfEntry *entry, Totem *totem)
@@ -219,6 +233,24 @@ deinterlace_changed_cb (GConfClient *client, guint cnxn_id,
 }
 
 static void
+remember_position_changed_cb (GConfClient *client, guint cnxn_id,
+                              GConfEntry *entry, Totem *totem)
+{
+	GObject *item;
+
+	item = gtk_builder_get_object (totem->xml, "tpw_remember_position_checkbutton");
+	g_signal_handlers_disconnect_by_func (item,
+			remember_position_checkbutton_toggled_cb, totem);
+
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item),
+			gconf_client_get_bool (totem->gc,
+				GCONF_PREFIX"/remember_position", NULL));
+
+	g_signal_connect (item, "toggled",
+			G_CALLBACK (remember_position_checkbutton_toggled_cb), totem);
+}
+
+static void
 auto_resize_changed_cb (GConfClient *client, guint cnxn_id,
 		GConfEntry *entry, Totem *totem)
 {
@@ -514,6 +546,7 @@ totem_setup_preferences (Totem *totem)
 	gtk_widget_destroy (GTK_WIDGET (item));
 
 	/* Boldify some labels */
+	totem_interface_boldify_label (totem->xml, "tpw_playback_label");
 	totem_interface_boldify_label (totem->xml, "tpw_network_label");
 	totem_interface_boldify_label (totem->xml, "tpw_text_subtitles_label");
 	totem_interface_boldify_label (totem->xml, "tpw_display_label");
@@ -528,6 +561,15 @@ totem_setup_preferences (Totem *totem)
         g_signal_connect (totem->prefs, "destroy",
                           G_CALLBACK (gtk_widget_destroyed), &totem->prefs);
 
+	/* Remember position */
+	totem->remember_position = gconf_client_get_bool (totem->gc,
+			GCONF_PREFIX"/remember_position", NULL);
+	item = gtk_builder_get_object (totem->xml, "tpw_remember_position_checkbutton");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item), totem->remember_position);
+	gconf_client_notify_add (totem->gc, GCONF_PREFIX"/remember_position",
+	                         (GConfClientNotifyFunc) remember_position_changed_cb,
+	                         totem, NULL, NULL);
+
 	/* Auto-resize */
 	auto_resize = gconf_client_get_bool (totem->gc,
 			GCONF_PREFIX"/auto_resize", NULL);
diff --git a/src/totem-private.h b/src/totem-private.h
index a18e058..17836fc 100644
--- a/src/totem-private.h
+++ b/src/totem-private.h
@@ -160,6 +160,7 @@ struct TotemObject {
 	UniqueApp *app;
 	TotemStates state;
 	TotemOpenLocation *open_location;
+	gboolean remember_position;
 
 	/* Stored source_window of GdkDragContext from a video drag,
 	 * used to detect when the video is dropped on itself. */
diff --git a/src/totem-uri.c b/src/totem-uri.c
index 65efe1f..d825ea7 100644
--- a/src/totem-uri.c
+++ b/src/totem-uri.c
@@ -34,6 +34,15 @@
 #include "totem-uri.h"
 #include "totem-private.h"
 
+/* 5 minute threshold. We don't want to save the position within a 3
+ * minute song for example. */
+#define SAVE_POSITION_THRESHOLD 5 * 60 * 1000
+/* Don't save the position of a stream if we're within 5% of the end so that,
+ * for example, we don't save if the user exits when they reach the credits of a film */
+#define SAVE_POSITION_END_THRESHOLD 0.05
+/* The GIO file attribute used to store the position in a stream */
+#define SAVE_POSITION_FILE_ATTRIBUTE "metadata::totem::position"
+
 static GtkFileFilter *filter_all = NULL;
 static GtkFileFilter *filter_subs = NULL;
 static GtkFileFilter *filter_supported = NULL;
@@ -682,3 +691,84 @@ totem_add_files (GtkWindow *parent, const char *path)
 	return filenames;
 }
 
+void
+totem_save_position (Totem *totem)
+{
+	gint64 stream_length, position;
+	char *mrl, *pos_str;
+	GFile *file;
+	GError *error = NULL;
+
+	if (totem->remember_position == FALSE)
+		return;
+
+	stream_length = bacon_video_widget_get_stream_length (totem->bvw);
+	position = bacon_video_widget_get_current_time (totem->bvw);
+	mrl = totem_get_current_mrl (totem);
+
+	if (mrl == NULL)
+		return;
+
+	/* Don't save if it's:
+	 *  - a live stream
+	 *  - too short to make saving useful
+	 *  - too close to the end to make saving useful
+	 */
+	if (stream_length < SAVE_POSITION_THRESHOLD ||
+	    (stream_length - position) < stream_length * SAVE_POSITION_END_THRESHOLD) {
+		g_debug ("not saving position because the video/track is too short");
+		g_free (mrl);
+		return;
+	}
+
+	g_debug ("saving position: %"G_GINT64_FORMAT, position);
+
+	file = g_file_new_for_uri (mrl);
+	g_free (mrl);
+
+	/* Save the position in the stream as a file attribute */
+	pos_str = g_strdup_printf ("%"G_GINT64_FORMAT, position);
+	g_file_set_attribute (file,
+			      SAVE_POSITION_FILE_ATTRIBUTE,
+			      G_FILE_ATTRIBUTE_TYPE_STRING, pos_str,
+			      G_FILE_QUERY_INFO_NONE, NULL, &error);
+	g_free (pos_str);
+
+	if (error != NULL) {
+		g_warning ("g_file_set_attribute failed: %s", error->message);
+		g_error_free (error);
+	}
+	g_object_unref (file);
+}
+
+void
+totem_try_restore_position (Totem *totem, const char *mrl)
+{
+	GFile *file;
+	GFileInfo *file_info;
+	const char *seek_str;
+
+	if (totem->remember_position == FALSE)
+		return;
+
+	if (mrl == NULL)
+		return;
+
+	file = g_file_new_for_uri (mrl);
+	g_debug ("trying to restore position of: %s", mrl);
+
+	/* Get the file attribute containing the position */
+	file_info = g_file_query_info (file, SAVE_POSITION_FILE_ATTRIBUTE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+	g_object_unref (file);
+
+	if (file_info == NULL)
+		return;
+
+	seek_str = g_file_info_get_attribute_string (file_info, SAVE_POSITION_FILE_ATTRIBUTE);
+	g_debug ("seek time: %s", seek_str);
+
+	if (seek_str != NULL)
+		totem->seek_to = g_ascii_strtoull (seek_str, NULL, 0);
+
+	g_object_unref (file_info);
+}
diff --git a/src/totem-uri.h b/src/totem-uri.h
index e6514d9..c4d9b41 100644
--- a/src/totem-uri.h
+++ b/src/totem-uri.h
@@ -49,6 +49,9 @@ char *		totem_add_subtitle		(GtkWindow *parent,
 						 const char *path);
 void		totem_add_pictures_dir		(GtkWidget *chooser);
 
+void totem_save_position (Totem *totem);
+void totem_try_restore_position (Totem *totem, const char *mrl);
+
 G_END_DECLS
 
 #endif /* TOTEM_URI_H */



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