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

Reply via email to