Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
probe.c
Go to the documentation of this file.
00001 /*
00002  * probe.c
00003  * Copyright 2009-2010 John Lindgren
00004  *
00005  * This file is part of Audacious.
00006  *
00007  * Audacious is free software: you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation, version 2 or version 3 of the License.
00010  *
00011  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
00012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00013  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License along with
00016  * Audacious. If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * The Audacious team does not consider modular code linking to Audacious or
00019  * using our public API to be a derived work.
00020  */
00021 
00022 #include <stdio.h>
00023 #include <string.h>
00024 
00025 #include <libaudcore/audstrings.h>
00026 
00027 #include "debug.h"
00028 #include "misc.h"
00029 #include "playlist.h"
00030 #include "plugin.h"
00031 #include "plugins.h"
00032 #include "probe-buffer.h"
00033 
00034 typedef struct
00035 {
00036     gchar * filename;
00037     VFSFile * handle;
00038     gboolean buffered;
00039     PluginHandle * plugin;
00040 }
00041 ProbeState;
00042 
00043 static gboolean check_opened (ProbeState * state)
00044 {
00045     if (state->handle != NULL)
00046         return TRUE;
00047 
00048     AUDDBG ("Opening %s.\n", state->filename);
00049     if ((state->buffered = vfs_is_remote (state->filename)))
00050         state->handle = probe_buffer_new (state->filename);
00051     else
00052         state->handle = vfs_fopen (state->filename, "r");
00053 
00054     if (state->handle != NULL)
00055         return TRUE;
00056 
00057     AUDDBG ("FAILED.\n");
00058     return FALSE;
00059 }
00060 
00061 static gboolean probe_func (PluginHandle * plugin, ProbeState * state)
00062 {
00063     AUDDBG ("Trying %s.\n", plugin_get_name (plugin));
00064     InputPlugin * decoder = plugin_get_header (plugin);
00065     if (decoder == NULL)
00066         return TRUE;
00067 
00068     if (decoder->is_our_file_from_vfs != NULL)
00069     {
00070         if (! check_opened (state))
00071             return FALSE;
00072 
00073         if (state->buffered)
00074             probe_buffer_set_decoder (state->handle, plugin_get_name (plugin));
00075 
00076         if (decoder->is_our_file_from_vfs (state->filename, state->handle))
00077         {
00078             state->plugin = plugin;
00079             return FALSE;
00080         }
00081 
00082         if (vfs_fseek (state->handle, 0, SEEK_SET) < 0)
00083             return FALSE;
00084     }
00085 
00086     return TRUE;
00087 }
00088 
00089 /* Optimization: If we have found plugins with a key match, assume that at least
00090  * one of them will succeed.  This means that we need not check the very last
00091  * plugin.  (If there is only one, we do not need to check it at all.)  This is
00092  * implemented as follows:
00093  *
00094  * 1. On the first call, assume until further notice the plugin passed is the
00095  *    last one and will therefore succeed.
00096  * 2. On a subsequent call, think twice and probe the plugin we assumed would
00097  *    succeed.  If it does in fact succeed, then we are done.  If not, assume
00098  *    similarly that the plugin passed in this call is the last one.
00099  */
00100 
00101 static gboolean probe_func_fast (PluginHandle * plugin, ProbeState * state)
00102 {
00103     if (state->plugin != NULL)
00104     {
00105         PluginHandle * prev = state->plugin;
00106         state->plugin = NULL;
00107 
00108         if (prev != NULL && ! probe_func (prev, state))
00109             return FALSE;
00110     }
00111 
00112     AUDDBG ("Guessing %s.\n", plugin_get_name (plugin));
00113     state->plugin = plugin;
00114     return TRUE;
00115 }
00116 
00117 static void probe_by_scheme (ProbeState * state)
00118 {
00119     gchar * s = strstr (state->filename, "://");
00120     gchar c;
00121 
00122     if (s == NULL)
00123         return;
00124 
00125     AUDDBG ("Probing by scheme.\n");
00126     c = s[3];
00127     s[3] = 0;
00128     input_plugin_for_key (INPUT_KEY_SCHEME, state->filename, (PluginForEachFunc)
00129      probe_func_fast, state);
00130     s[3] = c;
00131 }
00132 
00133 static void probe_by_extension (ProbeState * state)
00134 {
00135     gchar * s = strrchr (state->filename, '.');
00136 
00137     if (s == NULL)
00138         return;
00139 
00140     AUDDBG ("Probing by extension.\n");
00141     s = g_ascii_strdown (s + 1, -1);
00142 
00143     gchar * q = strrchr (s, '?');
00144     if (q != NULL)
00145         * q = 0;
00146 
00147     input_plugin_for_key (INPUT_KEY_EXTENSION, s, (PluginForEachFunc)
00148      probe_func_fast, state);
00149     g_free (s);
00150 }
00151 
00152 static void probe_by_mime (ProbeState * state)
00153 {
00154     gchar * mime;
00155 
00156     if (! check_opened (state))
00157         return;
00158 
00159     if ((mime = vfs_get_metadata (state->handle, "content-type")) == NULL)
00160         return;
00161 
00162     AUDDBG ("Probing by MIME type.\n");
00163     input_plugin_for_key (INPUT_KEY_MIME, mime, (PluginForEachFunc)
00164      probe_func_fast, state);
00165     g_free (mime);
00166 }
00167 
00168 static void probe_by_content (ProbeState * state)
00169 {
00170     AUDDBG ("Probing by content.\n");
00171     plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc) probe_func, state);
00172 }
00173 
00174 PluginHandle * file_find_decoder (const gchar * filename, gboolean fast)
00175 {
00176     ProbeState state;
00177 
00178     AUDDBG ("Probing %s.\n", filename);
00179     state.plugin = NULL;
00180     state.filename = filename_split_subtune (filename, NULL);
00181     state.handle = NULL;
00182 
00183     probe_by_scheme (& state);
00184 
00185     if (state.plugin != NULL)
00186         goto DONE;
00187 
00188     probe_by_extension (& state);
00189 
00190     if (state.plugin != NULL || fast)
00191         goto DONE;
00192 
00193     probe_by_mime (& state);
00194 
00195     if (state.plugin != NULL)
00196         goto DONE;
00197 
00198     probe_by_content (& state);
00199 
00200 DONE:
00201     g_free (state.filename);
00202 
00203     if (state.handle != NULL)
00204         vfs_fclose (state.handle);
00205 
00206     return state.plugin;
00207 }
00208 
00209 Tuple * file_read_tuple (const gchar * filename, PluginHandle * decoder)
00210 {
00211     InputPlugin * ip = plugin_get_header (decoder);
00212     g_return_val_if_fail (ip, NULL);
00213     g_return_val_if_fail (ip->probe_for_tuple, NULL);
00214 
00215     gchar * real = filename_split_subtune (filename, NULL);
00216     VFSFile * handle = vfs_fopen (real, "r");
00217     g_free (real);
00218 
00219     Tuple * tuple = ip->probe_for_tuple (filename, handle);
00220 
00221     if (handle)
00222         vfs_fclose (handle);
00223 
00224     return tuple;
00225 }
00226 
00227 gboolean file_read_image (const gchar * filename, PluginHandle * decoder,
00228  void * * data, gint * size)
00229 {
00230     if (! input_plugin_has_images (decoder))
00231         return FALSE;
00232 
00233     InputPlugin * ip = plugin_get_header (decoder);
00234     g_return_val_if_fail (ip, FALSE);
00235     g_return_val_if_fail (ip->get_song_image, FALSE);
00236 
00237     gchar * real = filename_split_subtune (filename, NULL);
00238     VFSFile * handle = vfs_fopen (real, "r");
00239     g_free (real);
00240 
00241     gboolean success = ip->get_song_image (filename, handle, data, size);
00242 
00243     if (handle)
00244         vfs_fclose (handle);
00245 
00246     return success;
00247 }
00248 
00249 gboolean file_can_write_tuple (const gchar * filename, PluginHandle * decoder)
00250 {
00251     return input_plugin_can_write_tuple (decoder);
00252 }
00253 
00254 gboolean file_write_tuple (const gchar * filename, PluginHandle * decoder,
00255  const Tuple * tuple)
00256 {
00257     InputPlugin * ip = plugin_get_header (decoder);
00258     g_return_val_if_fail (ip, FALSE);
00259     g_return_val_if_fail (ip->update_song_tuple, FALSE);
00260 
00261     gchar * real = filename_split_subtune (filename, NULL);
00262     VFSFile * handle = vfs_fopen (real, "r+");
00263     g_free (real);
00264 
00265     if (! handle)
00266         return FALSE;
00267 
00268     gboolean success = ip->update_song_tuple (tuple, handle);
00269 
00270     if (handle)
00271         vfs_fclose (handle);
00272 
00273     if (success)
00274         playlist_rescan_file (filename);
00275 
00276     return success;
00277 }
00278 
00279 gboolean custom_infowin (const gchar * filename, PluginHandle * decoder)
00280 {
00281     if (! input_plugin_has_infowin (decoder))
00282         return FALSE;
00283 
00284     InputPlugin * ip = plugin_get_header (decoder);
00285     g_return_val_if_fail (ip, FALSE);
00286     g_return_val_if_fail (ip->file_info_box, FALSE);
00287 
00288     ip->file_info_box (filename);
00289     return TRUE;
00290 }