Revision: 1933
http://gtkpod.svn.sourceforge.net/gtkpod/?rev=1933&view=rev
Author: Sikon
Date: 2008-01-13 01:06:06 -0800 (Sun, 13 Jan 2008)
Log Message:
-----------
Patch by seanius for video thumbnail generation
Modified Paths:
--------------
gtkpod/trunk/ChangeLog
gtkpod/trunk/data/gtkpod.glade
gtkpod/trunk/src/file.c
gtkpod/trunk/src/prefs.c
gtkpod/trunk/src/prefsdlg.c
Modified: gtkpod/trunk/ChangeLog
===================================================================
--- gtkpod/trunk/ChangeLog 2008-01-13 08:06:20 UTC (rev 1932)
+++ gtkpod/trunk/ChangeLog 2008-01-13 09:06:06 UTC (rev 1933)
@@ -1,3 +1,13 @@
+2008-01-13 Matvey Kozhev <sikon at users.sourceforge.net>
+
+ * data/gtkpod.glade:
+ * src/prefs.c:
+ * src/prefsdlg.c:
+ * src/file.c:
+
+ Support for video thumbnail generation via totem-video-thumbnailer
+ (patch by M. Sean Finney <[EMAIL PROTECTED]>).
+
2008-01-06 Matvey Kozhev <sikon at users.sourceforge.net>
* data/gtkpod.glade:
Modified: gtkpod/trunk/data/gtkpod.glade
===================================================================
--- gtkpod/trunk/data/gtkpod.glade 2008-01-13 08:06:20 UTC (rev 1932)
+++ gtkpod/trunk/data/gtkpod.glade 2008-01-13 09:06:06 UTC (rev 1933)
@@ -11101,6 +11101,52 @@
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <widget class="GtkHBox" id="hbox79">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkCheckButton"
id="generate_video_thumbnails">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property
name="has_tooltip">True</property>
+ <property
name="tooltip_text">Automatically generate a thumbnail from the video, if no
thumbnail is otherwise provided.</property>
+ <property name="label"
translatable="yes">Automatically generate video thumbnails</property>
+ <property
name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property
name="draw_indicator">True</property>
+ <signal name="toggled"
handler="on_simple_checkbox_toggled"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton"
id="customize_video_thumbnailer">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label"
translatable="yes">Customize...</property>
+ <property
name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <signal name="clicked"
handler="on_customize_video_thumbnailer_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property
name="pack_type">GTK_PACK_END</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
</widget>
</child>
</widget>
@@ -12052,6 +12098,96 @@
</widget>
</child>
</widget>
+ <widget class="GtkDialog" id="prefs_video_thumbnailer_dialog">
+ <property name="events">GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Video Thumbnail
Generation</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox17">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkVBox" id="vbox48">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox75">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label99">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Video
thumbnailing program:</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="video_thumbnailer">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="changed" handler="on_video_thumbnailer_changed"/>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label101">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Provide a shell
command to generate a thumbnail image of your video file. The following format
strings will be expanded:
+- %f: the input file
+- %o: the output file (which is automatically generated)
+</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area17">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="close_button4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
<widget class="GtkDialog" id="prefs_exclusions_dialog">
<property name="events">GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
Modified: gtkpod/trunk/src/file.c
===================================================================
--- gtkpod/trunk/src/file.c 2008-01-13 08:06:20 UTC (rev 1932)
+++ gtkpod/trunk/src/file.c 2008-01-13 09:06:06 UTC (rev 1933)
@@ -37,6 +37,8 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <glib.h>
+#include <glib/gstdio.h>
#include "charset.h"
@@ -1010,8 +1012,125 @@
return success;
}
+/* ensure that a cache directory is available for video thumbnail generation.
+ * largely copied from conversion_setup_cachedir, only slightly simplified
+ * and modified to return the path to the caller for convenience.
+ */
+static gchar* video_thumbnail_setup_cache()
+{
+ gchar *cachedir = NULL;
+ gchar *cfgdir = prefs_get_cfgdir();
+ if (!cfgdir)
+ return NULL;
+
+ cachedir = g_build_filename (cfgdir, "video_thumbnail_cache", NULL);
+ g_free (cfgdir);
+ if (!g_file_test (cachedir, G_FILE_TEST_IS_DIR) && (g_mkdir (cachedir,
0777) == -1))
+ {
+ gtkpod_warning (_("Could not create '%s'"), cachedir);
+ }
+ else
+ {
+ return cachedir;
+ }
+}
+
+/*
+ * automatically generate a thumbnail for video input files using
+ * an external program (the totem thumbnailer by default).
+ * @input is the path of the video file to be thumbnailed.
+ * Returns a path to a temporary file containing the thumbnail. The
+ * temporary file is stored in a dedicated cache directory.
+ */
+static gchar* create_video_thumbnail (gchar* input)
+{
+ GString *cmd = NULL;
+ GError *err = NULL;
+ int fd, retval, forkstatus;
+ gchar *thumbnailer = NULL, *tmp_fn = NULL, *tmp = NULL, *p = NULL;
+ /* find (and set up if necessary) the thumbnail cache dir */
+ gchar *tdir = video_thumbnail_setup_cache();
+
+ if (!tdir)
+ {
+ return NULL;
+ }
+
+ /* safely generate a temporary file. we don't actually need the fd
+ * so we close it on succesful generation */
+ tmp_fn = g_build_filename (tdir, "thumb.XXXXXX", NULL);
+ g_free(tdir);
+
+ if ((fd = g_mkstemp(tmp_fn)) == -1 || close(fd))
+ {
+ gtkpod_warning (_("Error creating thumbnail file"));
+ return NULL;
+ }
+
+ /* get the string containing the (template) command for thumbnailing */
+ thumbnailer = prefs_get_string("video_thumbnailer_prog");
+ /* copy it into cmd, expanding any format characters */
+ cmd = g_string_new("");
+ p = thumbnailer;
+
+ while (*p)
+ {
+ if (*p == '%')
+ {
+ ++p;
+
+ switch (*p)
+ {
+ /* %f: input file */
+ case 'f':
+ tmp = g_shell_quote(input);
+ break;
+ /* %o: temporary output file */
+ case 'o':
+ tmp = g_shell_quote(tmp_fn);
+ break;
+ case '%':
+ cmd = g_string_append_c (cmd, '%');
+ break;
+ default:
+ gtkpod_warning (_("Unknown token '%%%c' in
template '%s'"),
+ *p, thumbnailer);
+ break;
+ }
+
+ if (tmp)
+ {
+ cmd = g_string_append (cmd, tmp);
+ g_free (tmp);
+ tmp = NULL;
+ }
+ }
+ else
+ {
+ cmd = g_string_append_c (cmd, *p);
+ }
+
+ ++p;
+ }
+ /* run the thumbnailing program */
+ forkstatus = g_spawn_command_line_sync(cmd->str, NULL, NULL, &retval,
&err);
+
+ if(!forkstatus)
+ {
+ gtkpod_warning (_("Unable to start video thumbnail
generator\n(command line was: '%s'"), cmd->str);
+ }
+ else if(retval)
+ {
+ gtkpod_warning (_("Thumbnail generator returned status %d"),
retval);
+ }
+
+ g_string_free(cmd, TRUE);
+ /* thumbnail is in tmp_fn */
+ return tmp_fn;
+}
+
/* look for a picture specified by coverart_template */
static void add_coverart (Track *tr)
{
@@ -1020,14 +1139,18 @@
gchar **templates, **tplp;
gchar *dirname;
gchar *filename_local = NULL;
+ FileType type;
+ gint vid_thumbnailer;
g_return_if_fail (tr);
etr = tr->userdata;
g_return_if_fail (etr);
dirname = g_path_get_dirname (etr->pc_path_utf8);
+ type = determine_file_type(etr->pc_path_utf8);
full_template = prefs_get_string("coverart_template");
+ vid_thumbnailer = prefs_get_int("video_thumbnailer");
templates = g_strsplit (full_template, ";", 0);
tplp = templates;
@@ -1092,6 +1215,23 @@
{
gp_track_set_thumbnails (tr, filename_local);
}
+ else if(vid_thumbnailer)
+ {
+ /* if a template match was not made, and we're dealing with a video
+ * file, generate an arbitrary thumbnail if appropriate */
+ switch(type)
+ {
+ case FILE_TYPE_M4V:
+ case FILE_TYPE_MP4:
+ case FILE_TYPE_MOV:
+ case FILE_TYPE_MPG:
+ filename_local = create_video_thumbnail (etr->pc_path_utf8);
+ gp_track_set_thumbnails (tr, filename_local);
+ break;
+ default:
+ break;
+ }
+ }
g_strfreev (templates);
g_free(full_template);
Modified: gtkpod/trunk/src/prefs.c
===================================================================
--- gtkpod/trunk/src/prefs.c 2008-01-13 08:06:20 UTC (rev 1932)
+++ gtkpod/trunk/src/prefs.c 2008-01-13 09:06:06 UTC (rev 1933)
@@ -284,6 +284,7 @@
prefs_set_int("coverart_apic", TRUE);
prefs_set_int("coverart_file", TRUE);
prefs_set_string("coverart_template", "%A;folder.jpg");
+ prefs_set_string("video_thumbnailer_prog", "totem-video-thumbnailer %f
%o");
prefs_set_int("mserv_use", FALSE);
prefs_set_string("mserv_username", "");
prefs_set_int("startup_messages", TRUE);
Modified: gtkpod/trunk/src/prefsdlg.c
===================================================================
--- gtkpod/trunk/src/prefsdlg.c 2008-01-13 08:06:20 UTC (rev 1932)
+++ gtkpod/trunk/src/prefsdlg.c 2008-01-13 09:06:06 UTC (rev 1933)
@@ -88,6 +88,7 @@
{ "mass_modify_tags", "multi_edit", NULL },
{ "read_coverart", "coverart_apic", NULL },
{ "template_coverart", "coverart_file", "customize_coverart" },
+ { "generate_video_thumbnails", "video_thumbnailer",
"customize_video_thumbnailer" },
/* Feedback tab */
{ "confirm_del_tracks", NULL,
"confirm_from_ipod,confirm_from_hdd,confirm_from_db" },
{ "confirm_from_ipod", "delete_ipod", NULL },
@@ -870,6 +871,32 @@
/*
glade callback
*/
+G_MODULE_EXPORT void on_customize_video_thumbnailer_clicked (GtkButton
*sender, gpointer e)
+{
+ GladeXML *xml = gtkpod_xml_new (xml_file,
"prefs_video_thumbnailer_dialog");
+ GtkWidget *dlg = gtkpod_xml_get_widget (xml,
"prefs_video_thumbnailer_dialog");
+ gchar *temp = prefs_get_string("video_thumbnailer_prog");
+
+ gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW
(prefs_dialog));
+
+ if(temp)
+ {
+ gtk_entry_set_text (GTK_ENTRY (gtkpod_xml_get_widget (xml,
"video_thumbnailer")),
+ temp);
+
+ g_free (temp);
+ }
+
+ glade_xml_signal_autoconnect (xml);
+ gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+ g_object_unref (xml);
+}
+
+
+/*
+ glade callback
+*/
G_MODULE_EXPORT void on_coverart_pattern_changed (GtkEditable *sender,
gpointer e)
{
prefs_set_string ("coverart_template", gtk_entry_get_text (GTK_ENTRY
(sender)));
@@ -878,6 +905,14 @@
/*
glade callback
*/
+G_MODULE_EXPORT void on_video_thumbnailer_changed (GtkEditable *sender,
gpointer e)
+{
+ prefs_set_string ("video_thumbnailer_prog", gtk_entry_get_text
(GTK_ENTRY (sender)));
+}
+
+/*
+ glade callback
+*/
G_MODULE_EXPORT void on_exclusions_clicked (GtkButton *sender, gpointer e)
{
GladeXML *xml = gtkpod_xml_new (xml_file, "prefs_exclusions_dialog");
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace
_______________________________________________
gtkpod-cvs2 mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gtkpod-cvs2