[totem] Bug 164918 – Remember current position when Pause is clicked
- From: Philip Withnall <pwithnall src gnome org>
- To: svn-commits-list gnome org
- Subject: [totem] Bug 164918 – Remember current position when Pause is clicked
- Date: Sun, 26 Jul 2009 17:25:52 +0000 (UTC)
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]